Merge "Controls UI - Add 'reset' option for seeding" into rvc-dev
diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp
index 99e82e7..579963b 100644
--- a/apex/media/framework/Android.bp
+++ b/apex/media/framework/Android.bp
@@ -94,7 +94,6 @@
// TODO(b/135922046) remove this
include_dirs: ["frameworks/base/core/java"],
},
- sdk_version: "system_current",
}
droidstubs {
diff --git a/apex/permission/framework/Android.bp b/apex/permission/framework/Android.bp
index 793247e..fc9052e 100644
--- a/apex/permission/framework/Android.bp
+++ b/apex/permission/framework/Android.bp
@@ -46,12 +46,10 @@
name: "framework-permission-stubs-defaults",
srcs: [ ":framework-permission-sources" ],
libs: [ "framework-annotations-lib" ],
- sdk_version: "system_current",
}
droidstubs {
name: "framework-permission-stubs-srcs-publicapi",
- sdk_version: "system_current",
defaults: [
"framework-module-stubs-defaults-publicapi",
"framework-permission-stubs-defaults",
@@ -60,7 +58,6 @@
droidstubs {
name: "framework-permission-stubs-srcs-systemapi",
- sdk_version: "system_current",
defaults: [
"framework-module-stubs-defaults-systemapi",
"framework-permission-stubs-defaults",
@@ -69,7 +66,6 @@
droidstubs {
name: "framework-permission-api-module_libs_api",
- sdk_version: "system_current",
defaults: [
"framework-module-api-defaults-module_libs_api",
"framework-permission-stubs-defaults",
@@ -78,7 +74,6 @@
droidstubs {
name: "framework-permission-stubs-srcs-module_libs_api",
- sdk_version: "system_current",
defaults: [
"framework-module-stubs-defaults-module_libs_api",
"framework-permission-stubs-defaults",
diff --git a/apex/sdkextensions/framework/Android.bp b/apex/sdkextensions/framework/Android.bp
index 707113b..3eabb88 100644
--- a/apex/sdkextensions/framework/Android.bp
+++ b/apex/sdkextensions/framework/Android.bp
@@ -48,7 +48,6 @@
name: "framework-sdkextensions-stubs-defaults",
srcs: [ ":framework-sdkextensions-sources" ],
libs: [ "framework-annotations-lib" ],
- sdk_version: "system_current",
}
droidstubs {
diff --git a/api/test-current.txt b/api/test-current.txt
index c29f1c4..641767c 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -214,6 +214,7 @@
field public static final String OPSTR_GPS = "android:gps";
field public static final String OPSTR_INSTANT_APP_START_FOREGROUND = "android:instant_app_start_foreground";
field public static final String OPSTR_LEGACY_STORAGE = "android:legacy_storage";
+ field public static final String OPSTR_MANAGE_EXTERNAL_STORAGE = "android:manage_external_storage";
field public static final String OPSTR_MANAGE_IPSEC_TUNNELS = "android:manage_ipsec_tunnels";
field public static final String OPSTR_MUTE_MICROPHONE = "android:mute_microphone";
field public static final String OPSTR_NEIGHBORING_CELLS = "android:neighboring_cells";
@@ -5215,10 +5216,10 @@
method public void onDisplayAreaAppeared(@NonNull android.window.WindowContainerToken);
method public void onDisplayAreaVanished(@NonNull android.window.WindowContainerToken);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void registerOrganizer(int);
+ field public static final int FEATURE_DEFAULT_TASK_CONTAINER = 1; // 0x1
field public static final int FEATURE_ROOT = 0; // 0x0
field public static final int FEATURE_SYSTEM_FIRST = 0; // 0x0
field public static final int FEATURE_SYSTEM_LAST = 10000; // 0x2710
- field public static final int FEATURE_TASK_CONTAINER = 1; // 0x1
field public static final int FEATURE_UNDEFINED = -1; // 0xffffffff
field public static final int FEATURE_VENDOR_FIRST = 10001; // 0x2711
field public static final int FEATURE_WINDOW_TOKENS = 2; // 0x2
diff --git a/cmds/statsd/benchmark/filter_value_benchmark.cpp b/cmds/statsd/benchmark/filter_value_benchmark.cpp
index 28bf21a..743ccc4 100644
--- a/cmds/statsd/benchmark/filter_value_benchmark.cpp
+++ b/cmds/statsd/benchmark/filter_value_benchmark.cpp
@@ -14,12 +14,14 @@
* limitations under the License.
*/
#include <vector>
-#include "benchmark/benchmark.h"
+
#include "FieldValue.h"
#include "HashableDimensionKey.h"
+#include "benchmark/benchmark.h"
#include "logd/LogEvent.h"
-#include "stats_log_util.h"
+#include "metric_util.h"
#include "stats_event.h"
+#include "stats_log_util.h"
namespace android {
namespace os {
@@ -34,24 +36,13 @@
std::vector<int> attributionUids = {100, 100};
std::vector<string> attributionTags = {"LOCATION", "LOCATION"};
+ writeAttribution(statsEvent, attributionUids, attributionTags);
- vector<const char*> cTags(attributionTags.size());
- for (int i = 0; i < cTags.size(); i++) {
- cTags[i] = attributionTags[i].c_str();
- }
-
- AStatsEvent_writeAttributionChain(statsEvent,
- reinterpret_cast<const uint32_t*>(attributionUids.data()),
- cTags.data(), attributionUids.size());
AStatsEvent_writeFloat(statsEvent, 3.2f);
AStatsEvent_writeString(statsEvent, "LOCATION");
AStatsEvent_writeInt64(statsEvent, 990);
- AStatsEvent_build(statsEvent);
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
- event->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, event);
field_matcher->set_field(1);
auto child = field_matcher->add_child();
diff --git a/cmds/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp b/cmds/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp
index c7d01cc..7a45565 100644
--- a/cmds/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp
+++ b/cmds/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp
@@ -14,12 +14,14 @@
* limitations under the License.
*/
#include <vector>
-#include "benchmark/benchmark.h"
+
#include "FieldValue.h"
#include "HashableDimensionKey.h"
+#include "benchmark/benchmark.h"
#include "logd/LogEvent.h"
-#include "stats_log_util.h"
+#include "metric_util.h"
#include "stats_event.h"
+#include "stats_log_util.h"
namespace android {
namespace os {
@@ -34,24 +36,13 @@
std::vector<int> attributionUids = {100, 100};
std::vector<string> attributionTags = {"LOCATION", "LOCATION"};
+ writeAttribution(statsEvent, attributionUids, attributionTags);
- vector<const char*> cTags(attributionTags.size());
- for (int i = 0; i < cTags.size(); i++) {
- cTags[i] = attributionTags[i].c_str();
- }
-
- AStatsEvent_writeAttributionChain(statsEvent,
- reinterpret_cast<const uint32_t*>(attributionUids.data()),
- cTags.data(), attributionUids.size());
AStatsEvent_writeFloat(statsEvent, 3.2f);
AStatsEvent_writeString(statsEvent, "LOCATION");
AStatsEvent_writeInt64(statsEvent, 990);
- AStatsEvent_build(statsEvent);
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
- event->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, event);
link->conditionId = 1;
diff --git a/cmds/statsd/benchmark/metric_util.cpp b/cmds/statsd/benchmark/metric_util.cpp
index 482d66f..89fd3d9 100644
--- a/cmds/statsd/benchmark/metric_util.cpp
+++ b/cmds/statsd/benchmark/metric_util.cpp
@@ -247,21 +247,37 @@
return dimensions;
}
+void writeAttribution(AStatsEvent* statsEvent, const vector<int>& attributionUids,
+ const vector<string>& attributionTags) {
+ vector<const char*> cTags(attributionTags.size());
+ for (int i = 0; i < cTags.size(); i++) {
+ cTags[i] = attributionTags[i].c_str();
+ }
+
+ AStatsEvent_writeAttributionChain(statsEvent,
+ reinterpret_cast<const uint32_t*>(attributionUids.data()),
+ cTags.data(), attributionUids.size());
+}
+
+void parseStatsEventToLogEvent(AStatsEvent* statsEvent, LogEvent* logEvent) {
+ AStatsEvent_build(statsEvent);
+
+ size_t size;
+ uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+ logEvent->parseBuffer(buf, size);
+
+ AStatsEvent_release(statsEvent);
+}
+
std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
uint64_t timestampNs, const android::view::DisplayStateEnum state) {
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, util::SCREEN_STATE_CHANGED);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
-
AStatsEvent_writeInt32(statsEvent, state);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
@@ -272,24 +288,12 @@
AStatsEvent_setAtomId(statsEvent, util::SCHEDULED_JOB_STATE_CHANGED);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
- vector<const char*> cTags(attributionTags.size());
- for (int i = 0; i < cTags.size(); i++) {
- cTags[i] = attributionTags[i].c_str();
- }
-
- AStatsEvent_writeAttributionChain(statsEvent,
- reinterpret_cast<const uint32_t*>(attributionUids.data()),
- cTags.data(), attributionUids.size());
+ writeAttribution(statsEvent, attributionUids, attributionTags);
AStatsEvent_writeString(statsEvent, jobName.c_str());
AStatsEvent_writeInt32(statsEvent, state);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
@@ -319,24 +323,12 @@
AStatsEvent_setAtomId(statsEvent, util::SYNC_STATE_CHANGED);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
- vector<const char*> cTags(attributionTags.size());
- for (int i = 0; i < cTags.size(); i++) {
- cTags[i] = attributionTags[i].c_str();
- }
-
- AStatsEvent_writeAttributionChain(statsEvent,
- reinterpret_cast<const uint32_t*>(attributionUids.data()),
- cTags.data(), attributionUids.size());
+ writeAttribution(statsEvent, attributionUids, attributionTags);
AStatsEvent_writeString(statsEvent, name.c_str());
AStatsEvent_writeInt32(statsEvent, state);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
diff --git a/cmds/statsd/benchmark/metric_util.h b/cmds/statsd/benchmark/metric_util.h
index c5fcf7c..3efaa85 100644
--- a/cmds/statsd/benchmark/metric_util.h
+++ b/cmds/statsd/benchmark/metric_util.h
@@ -18,6 +18,7 @@
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "src/StatsLogProcessor.h"
#include "src/logd/LogEvent.h"
+#include "stats_event.h"
#include "statslog.h"
namespace android {
@@ -92,6 +93,11 @@
FieldMatcher CreateAttributionUidDimensions(const int atomId,
const std::vector<Position>& positions);
+void writeAttribution(AStatsEvent* statsEvent, const vector<int>& attributionUids,
+ const vector<string>& attributionTags);
+
+void parseStatsEventToLogEvent(AStatsEvent* statsEvent, LogEvent* logEvent);
+
// Create log event for screen state changed.
std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
uint64_t timestampNs, const android::view::DisplayStateEnum state);
diff --git a/cmds/statsd/src/FieldValue.cpp b/cmds/statsd/src/FieldValue.cpp
index 4385964..cfc1de4 100644
--- a/cmds/statsd/src/FieldValue.cpp
+++ b/cmds/statsd/src/FieldValue.cpp
@@ -120,9 +120,9 @@
}
int32_t getUidIfExists(const FieldValue& value) {
- // the field is uid field if the field is the uid field in attribution node or marked as
- // is_uid in atoms.proto
- bool isUid = isAttributionUidField(value) || isUidField(value.mField, value.mValue);
+ // the field is uid field if the field is the uid field in attribution node
+ // or annotated as such in the atom
+ bool isUid = isAttributionUidField(value) || isUidField(value);
return isUid ? value.mValue.int_value : -1;
}
@@ -134,16 +134,8 @@
return false;
}
-bool isUidField(const Field& field, const Value& value) {
- auto it = android::util::AtomsInfo::kAtomsWithUidField.find(field.getTag());
-
- if (it != android::util::AtomsInfo::kAtomsWithUidField.end()) {
- int uidField = it->second; // uidField is the field number in proto
- return field.getDepth() == 0 && field.getPosAtDepth(0) == uidField &&
- value.getType() == INT;
- }
-
- return false;
+bool isUidField(const FieldValue& fieldValue) {
+ return fieldValue.mAnnotations.isUidField();
}
Value::Value(const Value& from) {
diff --git a/cmds/statsd/src/FieldValue.h b/cmds/statsd/src/FieldValue.h
index 3536e5a..92e09ea 100644
--- a/cmds/statsd/src/FieldValue.h
+++ b/cmds/statsd/src/FieldValue.h
@@ -367,7 +367,8 @@
enum {
NESTED_POS = 0x0,
PRIMARY_POS = 0x1,
- EXCLUSIVE_POS = 0x2
+ EXCLUSIVE_POS = 0x2,
+ UID_POS = 0x3
};
inline void setNested(bool nested) { setBitmaskAtPos(NESTED_POS, nested); }
@@ -376,6 +377,8 @@
inline void setExclusiveState(bool exclusive) { setBitmaskAtPos(EXCLUSIVE_POS, exclusive); }
+ inline void setUidField(bool isUid) { setBitmaskAtPos(UID_POS, isUid); }
+
inline void setResetState(int resetState) { mResetState = resetState; }
// Default value = false
@@ -387,6 +390,9 @@
// Default value = false
inline bool isExclusiveState() const { return getValueFromBitmask(EXCLUSIVE_POS); }
+ // 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 int getResetState() const { return mResetState; }
@@ -402,7 +408,7 @@
}
// This is a bitmask over all annotations stored in boolean form. Because
- // there are only 3 booleans, just one byte is required.
+ // there are only 4 booleans, just one byte is required.
uint8_t mBooleanBitmask = 0;
int mResetState = -1;
@@ -449,7 +455,7 @@
void translateFieldMatcher(const FieldMatcher& matcher, std::vector<Matcher>* output);
bool isAttributionUidField(const Field& field, const Value& value);
-bool isUidField(const Field& field, const Value& value);
+bool isUidField(const FieldValue& fieldValue);
bool equalDimensions(const std::vector<Matcher>& dimension_a,
const std::vector<Matcher>& dimension_b);
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 982a63e..325cbc7 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -138,13 +138,6 @@
}
}
-void updateUid(Value* value, int hostUid) {
- int uid = value->int_value;
- if (uid != hostUid) {
- value->setInt(hostUid);
- }
-}
-
void StatsLogProcessor::mapIsolatedUidToHostUidIfNecessaryLocked(LogEvent* event) const {
if (android::util::AtomsInfo::kAtomsWithAttributionChain.find(event->GetTagId()) !=
android::util::AtomsInfo::kAtomsWithAttributionChain.end()) {
@@ -154,22 +147,15 @@
}
if (isAttributionUidField(value)) {
const int hostUid = mUidMap->getHostUidOrSelf(value.mValue.int_value);
- updateUid(&value.mValue, hostUid);
+ value.mValue.setInt(hostUid);
}
}
} else {
- auto it = android::util::AtomsInfo::kAtomsWithUidField.find(event->GetTagId());
- if (it != android::util::AtomsInfo::kAtomsWithUidField.end()) {
- int uidField = it->second; // uidField is the field number in proto,
- // starting from 1
- if (uidField > 0 && (int)event->getValues().size() >= uidField &&
- (event->getValues())[uidField - 1].mValue.getType() == INT) {
- Value& value = (*event->getMutableValues())[uidField - 1].mValue;
- const int hostUid = mUidMap->getHostUidOrSelf(value.int_value);
- updateUid(&value, hostUid);
- } else {
- ALOGE("Malformed log, uid not found. %s", event->ToString().c_str());
- }
+ int uidFieldIndex = event->getUidFieldIndex();
+ if (uidFieldIndex != -1) {
+ Value& value = (*event->getMutableValues())[uidFieldIndex].mValue;
+ const int hostUid = mUidMap->getHostUidOrSelf(value.int_value);
+ value.setInt(hostUid);
}
}
}
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index bd15264..2a1716e 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -418,7 +418,8 @@
AppStandbyBucketChanged app_standby_bucket_changed = 258 [(module) = "framework"];
SharesheetStarted sharesheet_started = 259 [(module) = "framework"];
RankingSelected ranking_selected = 260 [(module) = "framework"];
- TvSettingsUIInteracted tvsettings_ui_interacted = 261;
+ TvSettingsUIInteracted tvsettings_ui_interacted = 261 [(module) = "tv_settings"];
+ LauncherStaticLayout launcher_snapshot = 262 [(module) = "sysui"];
SdkExtensionStatus sdk_extension_status = 354;
}
@@ -2979,14 +2980,98 @@
optional int32 duration_millis = 7;
}
+/**
+ * Logs when Launcher (HomeScreen) UI has changed or was interacted.
+ *
+ * Logged from:
+ * packages/apps/Launcher3
+ */
message LauncherUIChanged {
- optional android.stats.launcher.LauncherAction action = 1;
+ optional android.stats.launcher.LauncherAction action = 1 [deprecated = true];
optional android.stats.launcher.LauncherState src_state = 2;
optional android.stats.launcher.LauncherState dst_state = 3;
- optional android.stats.launcher.LauncherExtension extension = 4 [(log_mode) = MODE_BYTES];
- optional bool is_swipe_up_enabled = 5;
+ optional android.stats.launcher.LauncherExtension extension = 4 [(log_mode) = MODE_BYTES, deprecated = true];
+ optional bool is_swipe_up_enabled = 5 [deprecated = true];
+
+ // The event id (e.g., app launch, drag and drop, long press)
+ optional int32 event_id = 6;
+ // The event's source or target id (e.g., icon, task, button)
+ optional int32 target_id = 7;
+ // If the target needs to be tracked, use this id field
+ optional int32 instance_id = 8;
+ optional int32 uid = 9 [(is_uid) = true];
+ optional string package_name = 10;
+ optional string component_name = 11;
+
+ // (x, y) coordinate and the index information of the target on the container
+ optional int32 grid_x = 12;
+ optional int32 grid_y = 13;
+ optional int32 page_id = 14;
+
+ // e.g., folder icon's (x, y) location and index information on the workspace
+ optional int32 grid_x_parent = 15;
+ optional int32 grid_y_parent = 16;
+ optional int32 page_id_parent = 17;
+
+ // e.g., SEARCHBOX_ALLAPPS, FOLDER_WORKSPACE
+ optional int32 hierarchy = 18;
+
+ optional bool is_work_profile = 19;
+
+ // Used to store the predicted rank of the target
+ optional int32 rank = 20;
+
+ // e.g., folderLabelState can be captured in the following two fields
+ optional int32 from_state = 21;
+ optional int32 to_state = 22;
+
+ // e.g., autofilled or suggested texts that are not user entered
+ optional string edittext = 23;
}
+/**
+ * Used for snapshot of the HomeScreen UI elements
+ *
+ * Logged from:
+ * packages/apps/Launcher3
+ */
+message LauncherStaticLayout {
+ // The event id (e.g., snapshot, drag and drop)
+ optional int32 event_id = 1;
+ // The event's source or target id (e.g., icon, shortcut, widget)
+ optional int32 target_id = 2;
+ // If the target needs to be tracked, use this id field
+ optional int32 instance_id = 3;
+ optional int32 uid = 4 [(is_uid) = true];
+ optional string package_name = 5;
+ optional string component_name = 6;
+
+ // (x, y) coordinate and the index information of the target on the container
+ optional int32 grid_x = 7;
+ optional int32 grid_y = 8;
+ optional int32 page_id = 9;
+
+ // e.g., folder icon's (x, y) location and index information on the workspace
+ optional int32 grid_x_parent = 10;
+ optional int32 grid_y_parent = 11;
+ optional int32 page_id_parent = 12;
+
+ // e.g., WORKSPACE, HOTSEAT, FOLDER_WORKSPACE, FOLDER_HOTSEAT
+ optional int32 hierarchy = 13;
+
+ optional bool is_work_profile = 14;
+
+ // e.g., PIN, WIDGET TRAY, APPS TRAY, PREDICTION
+ optional int32 origin = 15;
+}
+
+/**
+ * Logs when Wallpaper or ThemePicker UI has changed.
+ *
+ * Logged from:
+ * packages/apps/ThemePicker
+ * packages/apps/WallpaperPicker2
+ */
message StyleUIChanged {
optional android.stats.style.Action action = 1;
optional int32 color_package_hash = 2;
diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
index 6d9c644..bbae3fe 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -43,20 +43,23 @@
using std::unique_ptr;
struct ConfigReceiverDeathCookie {
- ConfigReceiverDeathCookie(sp<ConfigManager> configManager, const ConfigKey& configKey,
- const shared_ptr<IPendingIntentRef>& pir):
- mConfigManager(configManager),
- mConfigKey(configKey),
- mPir(pir) {}
+ ConfigReceiverDeathCookie(const wp<ConfigManager>& configManager, const ConfigKey& configKey,
+ const shared_ptr<IPendingIntentRef>& pir) :
+ mConfigManager(configManager), mConfigKey(configKey), mPir(pir) {
+ }
- sp<ConfigManager> mConfigManager;
+ wp<ConfigManager> mConfigManager;
ConfigKey mConfigKey;
shared_ptr<IPendingIntentRef> mPir;
};
void ConfigManager::configReceiverDied(void* cookie) {
auto cookie_ = static_cast<ConfigReceiverDeathCookie*>(cookie);
- sp<ConfigManager>& thiz = cookie_->mConfigManager;
+ sp<ConfigManager> thiz = cookie_->mConfigManager.promote();
+ if (!thiz) {
+ return;
+ }
+
ConfigKey& configKey = cookie_->mConfigKey;
shared_ptr<IPendingIntentRef>& pir = cookie_->mPir;
@@ -74,20 +77,23 @@
}
struct ActiveConfigChangedReceiverDeathCookie {
- ActiveConfigChangedReceiverDeathCookie(sp<ConfigManager> configManager, const int uid,
- const shared_ptr<IPendingIntentRef>& pir):
- mConfigManager(configManager),
- mUid(uid),
- mPir(pir) {}
+ ActiveConfigChangedReceiverDeathCookie(const wp<ConfigManager>& configManager, const int uid,
+ const shared_ptr<IPendingIntentRef>& pir) :
+ mConfigManager(configManager), mUid(uid), mPir(pir) {
+ }
- sp<ConfigManager> mConfigManager;
+ wp<ConfigManager> mConfigManager;
int mUid;
shared_ptr<IPendingIntentRef> mPir;
};
void ConfigManager::activeConfigChangedReceiverDied(void* cookie) {
auto cookie_ = static_cast<ActiveConfigChangedReceiverDeathCookie*>(cookie);
- sp<ConfigManager>& thiz = cookie_->mConfigManager;
+ sp<ConfigManager> thiz = cookie_->mConfigManager.promote();
+ if (!thiz) {
+ return;
+ }
+
int uid = cookie_->mUid;
shared_ptr<IPendingIntentRef>& pir = cookie_->mPir;
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 79a7e8d..ebe9610 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -44,19 +44,23 @@
// Stores the puller as a wp to avoid holding a reference in case it is unregistered and
// pullAtomCallbackDied is never called.
struct PullAtomCallbackDeathCookie {
- PullAtomCallbackDeathCookie(sp<StatsPullerManager> pullerManager, const PullerKey& pullerKey,
- const wp<StatsPuller>& puller)
- : mPullerManager(pullerManager), mPullerKey(pullerKey), mPuller(puller) {
+ PullAtomCallbackDeathCookie(const wp<StatsPullerManager>& pullerManager,
+ const PullerKey& pullerKey, const wp<StatsPuller>& puller) :
+ mPullerManager(pullerManager), mPullerKey(pullerKey), mPuller(puller) {
}
- sp<StatsPullerManager> mPullerManager;
+ wp<StatsPullerManager> mPullerManager;
PullerKey mPullerKey;
wp<StatsPuller> mPuller;
};
void StatsPullerManager::pullAtomCallbackDied(void* cookie) {
PullAtomCallbackDeathCookie* cookie_ = static_cast<PullAtomCallbackDeathCookie*>(cookie);
- sp<StatsPullerManager>& thiz = cookie_->mPullerManager;
+ sp<StatsPullerManager> thiz = cookie_->mPullerManager.promote();
+ if (!thiz) {
+ return;
+ }
+
const PullerKey& pullerKey = cookie_->mPullerKey;
wp<StatsPuller> puller = cookie_->mPuller;
diff --git a/cmds/statsd/src/external/puller_util.cpp b/cmds/statsd/src/external/puller_util.cpp
index aee7256..90247cf 100644
--- a/cmds/statsd/src/external/puller_util.cpp
+++ b/cmds/statsd/src/external/puller_util.cpp
@@ -49,10 +49,14 @@
*/
void mapAndMergeIsolatedUidsToHostUid(vector<shared_ptr<LogEvent>>& data, const sp<UidMap>& uidMap,
int tagId, const vector<int>& additiveFieldsVec) {
- if ((android::util::AtomsInfo::kAtomsWithAttributionChain.find(tagId) ==
- android::util::AtomsInfo::kAtomsWithAttributionChain.end()) &&
- (android::util::AtomsInfo::kAtomsWithUidField.find(tagId) ==
- android::util::AtomsInfo::kAtomsWithUidField.end())) {
+ bool hasAttributionChain = (android::util::AtomsInfo::kAtomsWithAttributionChain.find(tagId) !=
+ android::util::AtomsInfo::kAtomsWithAttributionChain.end());
+ // To check if any LogEvent has a uid field, we can just check the first
+ // LogEvent because all atoms with this tagId should have the uid
+ // annotation.
+ bool hasUidField = (data[0]->getUidFieldIndex() != -1);
+
+ if (!hasAttributionChain && !hasUidField) {
VLOG("No uid or attribution chain to merge, atom %d", tagId);
return;
}
@@ -75,19 +79,13 @@
}
}
} else {
- auto it = android::util::AtomsInfo::kAtomsWithUidField.find(tagId);
- if (it != android::util::AtomsInfo::kAtomsWithUidField.end()) {
- int uidField = it->second; // uidField is the field number in proto,
- // starting from 1
- if (uidField > 0 && (int)event->getValues().size() >= uidField &&
- (event->getValues())[uidField - 1].mValue.getType() == INT) {
- Value& value = (*event->getMutableValues())[uidField - 1].mValue;
- const int hostUid = uidMap->getHostUidOrSelf(value.int_value);
- value.setInt(hostUid);
- } else {
- ALOGE("Malformed log, uid not found. %s", event->ToString().c_str());
- return;
- }
+ int uidFieldIndex = event->getUidFieldIndex();
+ if (uidFieldIndex != -1) {
+ Value& value = (*event->getMutableValues())[uidFieldIndex].mValue;
+ const int hostUid = uidMap->getHostUidOrSelf(value.int_value);
+ value.setInt(hostUid);
+ } else {
+ ALOGE("Malformed log, uid not found. %s", event->ToString().c_str());
}
}
}
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 96bf04f..8b6a864 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -240,14 +240,20 @@
last[1] = last[2] = false;
}
+// Assumes that mValues is not empty
+bool LogEvent::checkPreviousValueType(Type expected) {
+ return mValues[mValues.size() - 1].mValue.getType() == expected;
+}
+
void LogEvent::parseIsUidAnnotation(uint8_t annotationType) {
- if (mValues.empty() || annotationType != BOOL_TYPE) {
+ if (mValues.empty() || !checkPreviousValueType(INT) || annotationType != BOOL_TYPE) {
mValid = false;
return;
}
bool isUid = readNextValue<uint8_t>();
if (isUid) mUidFieldIndex = mValues.size() - 1;
+ mValues[mValues.size() - 1].mAnnotations.setUidField(isUid);
}
void LogEvent::parseTruncateTimestampAnnotation(uint8_t annotationType) {
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 9e21c77..4eeb7d6 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -213,6 +213,7 @@
void parseExclusiveStateAnnotation(uint8_t annotationType);
void parseTriggerStateResetAnnotation(uint8_t annotationType);
void parseStateNestedAnnotation(uint8_t annotationType);
+ bool checkPreviousValueType(Type expected);
/**
* The below three variables are only valid during the execution of
diff --git a/cmds/statsd/src/matchers/matcher_util.cpp b/cmds/statsd/src/matchers/matcher_util.cpp
index 1f8bbd7..2b4c6a3 100644
--- a/cmds/statsd/src/matchers/matcher_util.cpp
+++ b/cmds/statsd/src/matchers/matcher_util.cpp
@@ -81,18 +81,17 @@
return matched;
}
-bool tryMatchString(const UidMap& uidMap, const Field& field, const Value& value,
- const string& str_match) {
- if (isAttributionUidField(field, value) || isUidField(field, value)) {
- int uid = value.int_value;
+bool tryMatchString(const UidMap& uidMap, const FieldValue& fieldValue, const string& str_match) {
+ if (isAttributionUidField(fieldValue) || isUidField(fieldValue)) {
+ int uid = fieldValue.mValue.int_value;
auto aidIt = UidMap::sAidToUidMapping.find(str_match);
if (aidIt != UidMap::sAidToUidMapping.end()) {
return ((int)aidIt->second) == uid;
}
std::set<string> packageNames = uidMap.getAppNamesFromUid(uid, true /* normalize*/);
return packageNames.find(str_match) != packageNames.end();
- } else if (value.getType() == STRING) {
- return value.str_value == str_match;
+ } else if (fieldValue.mValue.getType() == STRING) {
+ return fieldValue.mValue.str_value == str_match;
}
return false;
}
@@ -228,8 +227,7 @@
}
case FieldValueMatcher::ValueMatcherCase::kEqString: {
for (int i = start; i < end; i++) {
- if (tryMatchString(uidMap, values[i].mField, values[i].mValue,
- matcher.eq_string())) {
+ if (tryMatchString(uidMap, values[i], matcher.eq_string())) {
return true;
}
}
@@ -240,7 +238,7 @@
for (int i = start; i < end; i++) {
bool notEqAll = true;
for (const auto& str : str_list.str_value()) {
- if (tryMatchString(uidMap, values[i].mField, values[i].mValue, str)) {
+ if (tryMatchString(uidMap, values[i], str)) {
notEqAll = false;
break;
}
@@ -255,7 +253,7 @@
const auto& str_list = matcher.eq_any_string();
for (int i = start; i < end; i++) {
for (const auto& str : str_list.str_value()) {
- if (tryMatchString(uidMap, values[i].mField, values[i].mValue, str)) {
+ if (tryMatchString(uidMap, values[i], str)) {
return true;
}
}
diff --git a/cmds/statsd/tests/FieldValue_test.cpp b/cmds/statsd/tests/FieldValue_test.cpp
index 0bf24f1..f5ba8fd 100644
--- a/cmds/statsd/tests/FieldValue_test.cpp
+++ b/cmds/statsd/tests/FieldValue_test.cpp
@@ -41,22 +41,10 @@
AStatsEvent_setAtomId(statsEvent, atomId);
AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
- vector<const char*> cTags(attributionTags.size());
- for (int i = 0; i < cTags.size(); i++) {
- cTags[i] = attributionTags[i].c_str();
- }
-
- AStatsEvent_writeAttributionChain(statsEvent,
- reinterpret_cast<const uint32_t*>(attributionUids.data()),
- cTags.data(), attributionUids.size());
+ writeAttribution(statsEvent, attributionUids, attributionTags);
AStatsEvent_writeString(statsEvent, name.c_str());
- AStatsEvent_build(statsEvent);
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
- logEvent->parseBuffer(buf, size);
-
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent);
}
void makeLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp,
@@ -66,22 +54,10 @@
AStatsEvent_setAtomId(statsEvent, atomId);
AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
- vector<const char*> cTags(attributionTags.size());
- for (int i = 0; i < cTags.size(); i++) {
- cTags[i] = attributionTags[i].c_str();
- }
-
- AStatsEvent_writeAttributionChain(statsEvent,
- reinterpret_cast<const uint32_t*>(attributionUids.data()),
- cTags.data(), attributionUids.size());
+ writeAttribution(statsEvent, attributionUids, attributionTags);
AStatsEvent_writeInt32(statsEvent, value);
- AStatsEvent_build(statsEvent);
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
- logEvent->parseBuffer(buf, size);
-
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent);
}
} // anonymous namespace
diff --git a/cmds/statsd/tests/LogEntryMatcher_test.cpp b/cmds/statsd/tests/LogEntryMatcher_test.cpp
index 6f4c8e4..26423d4 100644
--- a/cmds/statsd/tests/LogEntryMatcher_test.cpp
+++ b/cmds/statsd/tests/LogEntryMatcher_test.cpp
@@ -18,6 +18,7 @@
#include <log/logprint.h>
#include <stdio.h>
+#include "annotations.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "matchers/matcher_util.h"
#include "stats_event.h"
@@ -48,15 +49,9 @@
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, atomId);
AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
-
AStatsEvent_writeInt32(statsEvent, value);
- AStatsEvent_build(statsEvent);
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
- logEvent->parseBuffer(buf, size);
-
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent);
}
void makeFloatLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp,
@@ -64,15 +59,9 @@
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, atomId);
AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
-
AStatsEvent_writeFloat(statsEvent, floatValue);
- AStatsEvent_build(statsEvent);
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
- logEvent->parseBuffer(buf, size);
-
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent);
}
void makeStringLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp,
@@ -80,32 +69,20 @@
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, atomId);
AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
-
AStatsEvent_writeString(statsEvent, name.c_str());
- AStatsEvent_build(statsEvent);
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
- logEvent->parseBuffer(buf, size);
-
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent);
}
-void makeIntStringLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp,
- const int32_t value, const string& name) {
+void makeIntWithBoolAnnotationLogEvent(LogEvent* logEvent, const int32_t atomId,
+ const int32_t field, const uint8_t annotationId,
+ const bool annotationValue) {
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, atomId);
- AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
+ AStatsEvent_writeInt32(statsEvent, field);
+ AStatsEvent_addBoolAnnotation(statsEvent, annotationId, annotationValue);
- AStatsEvent_writeInt32(statsEvent, value);
- AStatsEvent_writeString(statsEvent, name.c_str());
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
- logEvent->parseBuffer(buf, size);
-
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent);
}
void makeAttributionLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp,
@@ -115,22 +92,10 @@
AStatsEvent_setAtomId(statsEvent, atomId);
AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
- vector<const char*> cTags(attributionTags.size());
- for (int i = 0; i < cTags.size(); i++) {
- cTags[i] = attributionTags[i].c_str();
- }
-
- AStatsEvent_writeAttributionChain(statsEvent,
- reinterpret_cast<const uint32_t*>(attributionUids.data()),
- cTags.data(), attributionUids.size());
+ writeAttribution(statsEvent, attributionUids, attributionTags);
AStatsEvent_writeString(statsEvent, name.c_str());
- AStatsEvent_build(statsEvent);
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
- logEvent->parseBuffer(buf, size);
-
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent);
}
void makeBoolLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp,
@@ -141,13 +106,8 @@
AStatsEvent_writeBool(statsEvent, bool1);
AStatsEvent_writeBool(statsEvent, bool2);
- AStatsEvent_build(statsEvent);
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
- logEvent->parseBuffer(buf, size);
-
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent);
}
} // anonymous namespace
@@ -416,21 +376,20 @@
simpleMatcher->add_field_value_matcher()->set_field(1);
simpleMatcher->mutable_field_value_matcher(0)->set_eq_string("pkg0");
- // Set up the event
+ // Make event without is_uid annotation.
LogEvent event1(/*uid=*/0, /*pid=*/0);
makeIntLogEvent(&event1, TAG_ID, 0, 1111);
-
- LogEvent event2(/*uid=*/0, /*pid=*/0);
- makeIntStringLogEvent(&event2, TAG_ID_2, 0, 1111, "some value");
-
- // Tag not in kAtomsWithUidField
EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1));
- // Tag found in kAtomsWithUidField and has matching uid
+ // Make event with is_uid annotation.
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ makeIntWithBoolAnnotationLogEvent(&event2, TAG_ID_2, 1111, ANNOTATION_ID_IS_UID, true);
+
+ // Event has is_uid annotation, so mapping from uid to package name occurs.
simpleMatcher->set_atom_id(TAG_ID_2);
EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2));
- // Tag found in kAtomsWithUidField but has non-matching uid
+ // Event has is_uid annotation, but uid maps to different package name.
simpleMatcher->mutable_field_value_matcher(0)->set_eq_string("Pkg2");
EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event2));
}
diff --git a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
index 7febb35..ba5b032 100644
--- a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
+++ b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
@@ -67,22 +67,12 @@
AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
vector<std::string> tags(uids.size()); // vector of empty strings
- vector<const char*> cTags(uids.size());
- for (int i = 0; i < cTags.size(); i++) {
- cTags[i] = tags[i].c_str();
- }
- AStatsEvent_writeAttributionChain(statsEvent, reinterpret_cast<const uint32_t*>(uids.data()),
- cTags.data(), uids.size());
+ writeAttribution(statsEvent, uids, tags);
AStatsEvent_writeString(statsEvent, wl.c_str());
AStatsEvent_writeInt32(statsEvent, acquire);
- AStatsEvent_build(statsEvent);
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
- logEvent->parseBuffer(buf, size);
-
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent);
}
} // anonymous namespace
diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
index 81e1c05..60403f2 100644
--- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
+++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
@@ -84,14 +84,9 @@
AStatsEvent_writeString(statsEvent, calling_pkg_name.c_str());
AStatsEvent_writeInt32(statsEvent, is_instant_app);
AStatsEvent_writeInt32(statsEvent, activity_start_msec);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
diff --git a/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp b/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
index 6aff9ef..4b9bac1 100644
--- a/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
+++ b/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
@@ -190,13 +190,13 @@
int32_t uid = 123;
values.push_back(value);
- StatsPullerManager pullerManager;
- pullerManager.RegisterPullAtomCallback(uid, pullTagId, pullCoolDownNs, pullTimeoutNs,
- vector<int32_t>(), cb);
+ sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+ pullerManager->RegisterPullAtomCallback(uid, pullTagId, pullCoolDownNs, pullTimeoutNs,
+ vector<int32_t>(), cb);
vector<shared_ptr<LogEvent>> dataHolder;
int64_t startTimeNs = getElapsedRealtimeNs();
// Returns false, since StatsPuller code will evaluate the timeout.
- EXPECT_FALSE(pullerManager.Pull(pullTagId, {uid}, &dataHolder));
+ EXPECT_FALSE(pullerManager->Pull(pullTagId, {uid}, &dataHolder));
int64_t endTimeNs = getElapsedRealtimeNs();
int64_t actualPullDurationNs = endTimeNs - startTimeNs;
diff --git a/cmds/statsd/tests/external/StatsPuller_test.cpp b/cmds/statsd/tests/external/StatsPuller_test.cpp
index e8200d5..5043358 100644
--- a/cmds/statsd/tests/external/StatsPuller_test.cpp
+++ b/cmds/statsd/tests/external/StatsPuller_test.cpp
@@ -64,16 +64,10 @@
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, pullTagId);
AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
-
AStatsEvent_writeInt64(statsEvent, value);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
diff --git a/cmds/statsd/tests/external/puller_util_test.cpp b/cmds/statsd/tests/external/puller_util_test.cpp
index 15425d8..c2cfb37 100644
--- a/cmds/statsd/tests/external/puller_util_test.cpp
+++ b/cmds/statsd/tests/external/puller_util_test.cpp
@@ -21,8 +21,10 @@
#include <vector>
#include "../metrics/metrics_test_helper.h"
+#include "annotations.h"
#include "stats_event.h"
#include "statslog_statsdtest.h"
+#include "tests/statsd_test_util.h"
#ifdef __ANDROID__
@@ -52,15 +54,15 @@
void extractIntoVector(vector<shared_ptr<LogEvent>> events,
vector<vector<int>>& ret) {
- ret.clear();
- status_t err;
- for (const auto& event : events) {
- vector<int> vec;
- vec.push_back(event->GetInt(1, &err));
- vec.push_back(event->GetInt(2, &err));
- vec.push_back(event->GetInt(3, &err));
- ret.push_back(vec);
- }
+ ret.clear();
+ status_t err;
+ for (const auto& event : events) {
+ vector<int> vec;
+ vec.push_back(event->GetInt(1, &err));
+ vec.push_back(event->GetInt(2, &err));
+ vec.push_back(event->GetInt(3, &err));
+ ret.push_back(vec);
+ }
}
std::shared_ptr<LogEvent> makeUidLogEvent(uint64_t timestampNs, int uid, int data1, int data2) {
@@ -69,16 +71,12 @@
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
AStatsEvent_writeInt32(statsEvent, uid);
+ AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_IS_UID, true);
AStatsEvent_writeInt32(statsEvent, data1);
AStatsEvent_writeInt32(statsEvent, data2);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::shared_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
@@ -86,16 +84,10 @@
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, nonUidAtomTagId);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
-
AStatsEvent_writeInt32(statsEvent, data1);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::shared_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
diff --git a/cmds/statsd/tests/log_event/LogEventQueue_test.cpp b/cmds/statsd/tests/log_event/LogEventQueue_test.cpp
index 6dc041f..a15f95b 100644
--- a/cmds/statsd/tests/log_event/LogEventQueue_test.cpp
+++ b/cmds/statsd/tests/log_event/LogEventQueue_test.cpp
@@ -21,6 +21,7 @@
#include <thread>
#include "stats_event.h"
+#include "tests/statsd_test_util.h"
namespace android {
namespace os {
@@ -37,14 +38,9 @@
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, 10);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
index d55996c..65f8de6 100644
--- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
@@ -46,26 +46,17 @@
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, atomId);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
- AStatsEvent_build(statsEvent);
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent);
}
void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId, string uid) {
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, atomId);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
-
AStatsEvent_writeString(statsEvent, uid.c_str());
- AStatsEvent_build(statsEvent);
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent);
}
} // namespace
diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
index 6143dc0..30f8159 100644
--- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
@@ -26,6 +26,7 @@
#include "src/condition/ConditionWizard.h"
#include "src/stats_log_util.h"
#include "stats_event.h"
+#include "tests/statsd_test_util.h"
using namespace android::os::statsd;
using namespace testing;
@@ -48,12 +49,8 @@
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, atomId);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
- AStatsEvent_build(statsEvent);
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent);
}
} // namespace
diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
index e58bbb7..97647a7 100644
--- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
@@ -43,14 +43,9 @@
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, atomId);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
-
AStatsEvent_writeString(statsEvent, str.c_str());
- AStatsEvent_build(statsEvent);
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent);
}
} // anonymous namespace
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index 2fe05a4..42d0d5d 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -64,14 +64,9 @@
AStatsEvent_writeInt32(statsEvent, value1);
AStatsEvent_writeString(statsEvent, str1.c_str());
AStatsEvent_writeInt32(statsEvent, value2);
- AStatsEvent_build(statsEvent);
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
-
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
} // anonymous namespace
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index b623a09..009e49a5 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -611,7 +611,7 @@
vector<shared_ptr<LogEvent>> allData;
allData.clear();
- allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1, 110));
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 110));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs - 8});
@@ -656,7 +656,7 @@
valueProducer.prepareFirstBucket();
LogEvent event1(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10);
+ CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
@@ -665,14 +665,14 @@
EXPECT_EQ(bucketStartTimeNs + 150, valueProducer.mCurrentBucketStartTimeNs);
LogEvent event2(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event2, tagId, bucketStartTimeNs + 59 * NS_PER_SEC, 1, 10);
+ CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 59 * NS_PER_SEC, 10);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
EXPECT_EQ(bucketStartTimeNs + 150, valueProducer.mCurrentBucketStartTimeNs);
// Next value should create a new bucket.
LogEvent event3(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event3, tagId, bucketStartTimeNs + 65 * NS_PER_SEC, 1, 10);
+ CreateRepeatedValueLogEvent(&event3, tagId, bucketStartTimeNs + 65 * NS_PER_SEC, 10);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
EXPECT_EQ(2UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, valueProducer.mCurrentBucketStartTimeNs);
@@ -812,10 +812,10 @@
valueProducer.prepareFirstBucket();
LogEvent event1(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10);
+ CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10);
LogEvent event2(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 1, 20);
+ CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 20);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
// has one slice
@@ -856,7 +856,7 @@
valueProducer.mCondition = ConditionState::kFalse;
LogEvent event1(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10);
+ CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
// has 1 slice
EXPECT_EQ(0UL, valueProducer.mCurrentSlicedBucket.size());
@@ -864,7 +864,7 @@
valueProducer.onConditionChangedLocked(true, bucketStartTimeNs + 15);
LogEvent event2(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 1, 20);
+ CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 20);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
// has one slice
@@ -875,7 +875,7 @@
EXPECT_EQ(20, curInterval.value.long_value);
LogEvent event3(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event3, tagId, bucketStartTimeNs + 30, 1, 30);
+ CreateRepeatedValueLogEvent(&event3, tagId, bucketStartTimeNs + 30, 30);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
// has one slice
@@ -886,7 +886,7 @@
valueProducer.onConditionChangedLocked(false, bucketStartTimeNs + 35);
LogEvent event4(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event4, tagId, bucketStartTimeNs + 40, 1, 40);
+ CreateRepeatedValueLogEvent(&event4, tagId, bucketStartTimeNs + 40, 40);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
// has one slice
@@ -1195,10 +1195,10 @@
valueProducer.prepareFirstBucket();
LogEvent event1(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10);
+ CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10);
LogEvent event2(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 1, 20);
+ CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 20);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
// has one slice
@@ -1238,10 +1238,10 @@
valueProducer.prepareFirstBucket();
LogEvent event1(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10);
+ CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10);
LogEvent event2(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 1, 20);
+ CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 20);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
// has one slice
@@ -1283,10 +1283,10 @@
valueProducer.prepareFirstBucket();
LogEvent event1(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10);
+ CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10);
LogEvent event2(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 1, 15);
+ CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 15);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
@@ -1331,10 +1331,10 @@
valueProducer.prepareFirstBucket();
LogEvent event1(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10);
+ CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10);
LogEvent event2(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 1, 15);
+ CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 15);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
@@ -1374,10 +1374,10 @@
valueProducer.prepareFirstBucket();
LogEvent event1(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10);
+ CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10);
LogEvent event2(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event2, tagId, bucketStartTimeNs + 15, 1, 15);
+ CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 15, 15);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
@@ -1398,7 +1398,7 @@
// no change in data.
LogEvent event3(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event3, tagId, bucket2StartTimeNs + 10, 1, 15);
+ CreateRepeatedValueLogEvent(&event3, tagId, bucket2StartTimeNs + 10, 15);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
@@ -1408,7 +1408,7 @@
EXPECT_EQ(true, curInterval.hasValue);
LogEvent event4(/*uid=*/0, /*pid=*/0);
- CreateTwoValueLogEvent(&event4, tagId, bucket2StartTimeNs + 15, 1, 15);
+ CreateRepeatedValueLogEvent(&event4, tagId, bucket2StartTimeNs + 15, 15);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
@@ -2166,7 +2166,7 @@
// Bucket start.
vector<shared_ptr<LogEvent>> allData;
allData.clear();
- allData.push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 1, 1, 110));
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 110));
valueProducer->onDataPulled(allData, /** succeed */ false, bucketStartTimeNs);
valueProducer->onConditionChanged(false, bucketStartTimeNs + 2);
@@ -2174,7 +2174,7 @@
// Bucket end.
allData.clear();
- allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1, 140));
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 140));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
valueProducer->flushIfNeededLocked(bucket2StartTimeNs + 1);
diff --git a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
index ac3ad69..7b952d7 100644
--- a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
+++ b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
@@ -171,13 +171,9 @@
AStatsEvent_overwriteTimestamp(statsEvent, 1111L);
AStatsEvent_writeInt32(statsEvent, uid);
AStatsEvent_writeInt64(statsEvent, timeMillis);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
diff --git a/cmds/statsd/tests/state/StateTracker_test.cpp b/cmds/statsd/tests/state/StateTracker_test.cpp
index a5b8e1c..78c80bc 100644
--- a/cmds/statsd/tests/state/StateTracker_test.cpp
+++ b/cmds/statsd/tests/state/StateTracker_test.cpp
@@ -62,7 +62,7 @@
// START: build event functions.
// Incorrect event - missing fields
-std::shared_ptr<LogEvent> buildIncorrectOverlayEvent(int uid, const std::string& packageName,
+std::unique_ptr<LogEvent> buildIncorrectOverlayEvent(int uid, const std::string& packageName,
int state) {
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, util::OVERLAY_STATE_CHANGED);
@@ -72,14 +72,9 @@
AStatsEvent_writeString(statsEvent, packageName.c_str());
// Missing field 3 - using_alert_window.
AStatsEvent_writeInt32(statsEvent, state);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
@@ -93,14 +88,9 @@
AStatsEvent_writeString(statsEvent, packageName.c_str());
AStatsEvent_writeInt32(statsEvent, true); // using_alert_window
AStatsEvent_writeString(statsEvent, "string"); // exclusive state: string instead of int
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
// END: build event functions.
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index 7d765d3..ed3cf5b 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -507,23 +507,26 @@
}
// END: get primary key functions
-shared_ptr<LogEvent> CreateTwoValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value1,
- int32_t value2) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, atomId);
- AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
+void writeAttribution(AStatsEvent* statsEvent, const vector<int>& attributionUids,
+ const vector<string>& attributionTags) {
+ vector<const char*> cTags(attributionTags.size());
+ for (int i = 0; i < cTags.size(); i++) {
+ cTags[i] = attributionTags[i].c_str();
+ }
- AStatsEvent_writeInt32(statsEvent, value1);
- AStatsEvent_writeInt32(statsEvent, value2);
+ AStatsEvent_writeAttributionChain(statsEvent,
+ reinterpret_cast<const uint32_t*>(attributionUids.data()),
+ cTags.data(), attributionUids.size());
+}
+
+void parseStatsEventToLogEvent(AStatsEvent* statsEvent, LogEvent* logEvent) {
AStatsEvent_build(statsEvent);
size_t size;
uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
- shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
- return logEvent;
+ AStatsEvent_release(statsEvent);
}
void CreateTwoValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs, int32_t value1,
@@ -534,31 +537,14 @@
AStatsEvent_writeInt32(statsEvent, value1);
AStatsEvent_writeInt32(statsEvent, value2);
- AStatsEvent_build(statsEvent);
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent);
}
-shared_ptr<LogEvent> CreateThreeValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value1,
- int32_t value2, int32_t value3) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, atomId);
- AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
-
- AStatsEvent_writeInt32(statsEvent, value1);
- AStatsEvent_writeInt32(statsEvent, value2);
- AStatsEvent_writeInt32(statsEvent, value3);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+shared_ptr<LogEvent> CreateTwoValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value1,
+ int32_t value2) {
shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
-
+ CreateTwoValueLogEvent(logEvent.get(), atomId, eventTimeNs, value1, value2);
return logEvent;
}
@@ -571,29 +557,14 @@
AStatsEvent_writeInt32(statsEvent, value1);
AStatsEvent_writeInt32(statsEvent, value2);
AStatsEvent_writeInt32(statsEvent, value3);
- AStatsEvent_build(statsEvent);
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent);
}
-shared_ptr<LogEvent> CreateRepeatedValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, atomId);
- AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
-
- AStatsEvent_writeInt32(statsEvent, value);
- AStatsEvent_writeInt32(statsEvent, value);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+shared_ptr<LogEvent> CreateThreeValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value1,
+ int32_t value2, int32_t value3) {
shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
-
+ CreateThreeValueLogEvent(logEvent.get(), atomId, eventTimeNs, value1, value2, value3);
return logEvent;
}
@@ -605,26 +576,13 @@
AStatsEvent_writeInt32(statsEvent, value);
AStatsEvent_writeInt32(statsEvent, value);
- AStatsEvent_build(statsEvent);
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent);
}
-shared_ptr<LogEvent> CreateNoValuesLogEvent(int atomId, int64_t eventTimeNs) {
- AStatsEvent* statsEvent = AStatsEvent_obtain();
- AStatsEvent_setAtomId(statsEvent, atomId);
- AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+shared_ptr<LogEvent> CreateRepeatedValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value) {
shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
-
+ CreateRepeatedValueLogEvent(logEvent.get(), atomId, eventTimeNs, value);
return logEvent;
}
@@ -632,12 +590,14 @@
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, atomId);
AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
- AStatsEvent_build(statsEvent);
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent);
+}
+
+shared_ptr<LogEvent> CreateNoValuesLogEvent(int atomId, int64_t eventTimeNs) {
+ shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
+ CreateNoValuesLogEvent(logEvent.get(), atomId, eventTimeNs);
+ return logEvent;
}
std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
@@ -645,16 +605,10 @@
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, util::SCREEN_STATE_CHANGED);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
-
AStatsEvent_writeInt32(statsEvent, state);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
@@ -662,16 +616,10 @@
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, util::BATTERY_SAVER_MODE_STATE_CHANGED);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
-
AStatsEvent_writeInt32(statsEvent, BatterySaverModeStateChanged::ON);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
@@ -679,16 +627,10 @@
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, util::BATTERY_SAVER_MODE_STATE_CHANGED);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
-
AStatsEvent_writeInt32(statsEvent, BatterySaverModeStateChanged::OFF);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
@@ -696,16 +638,10 @@
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, util::PLUGGED_STATE_CHANGED);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
-
AStatsEvent_writeInt32(statsEvent, state);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
@@ -713,16 +649,10 @@
AStatsEvent* statsEvent = AStatsEvent_obtain();
AStatsEvent_setAtomId(statsEvent, util::SCREEN_BRIGHTNESS_CHANGED);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
-
AStatsEvent_writeInt32(statsEvent, level);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
@@ -733,24 +663,12 @@
AStatsEvent_setAtomId(statsEvent, util::SCHEDULED_JOB_STATE_CHANGED);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
- vector<const char*> cTags(attributionTags.size());
- for (int i = 0; i < cTags.size(); i++) {
- cTags[i] = attributionTags[i].c_str();
- }
-
- AStatsEvent_writeAttributionChain(statsEvent,
- reinterpret_cast<const uint32_t*>(attributionUids.data()),
- cTags.data(), attributionUids.size());
+ writeAttribution(statsEvent, attributionUids, attributionTags);
AStatsEvent_writeString(statsEvent, jobName.c_str());
AStatsEvent_writeInt32(statsEvent, state);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
@@ -780,25 +698,13 @@
AStatsEvent_setAtomId(statsEvent, util::WAKELOCK_STATE_CHANGED);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
- vector<const char*> cTags(attributionTags.size());
- for (int i = 0; i < cTags.size(); i++) {
- cTags[i] = attributionTags[i].c_str();
- }
-
- AStatsEvent_writeAttributionChain(statsEvent,
- reinterpret_cast<const uint32_t*>(attributionUids.data()),
- cTags.data(), attributionUids.size());
+ writeAttribution(statsEvent, attributionUids, attributionTags);
AStatsEvent_writeInt32(statsEvent, android::os::WakeLockLevelEnum::PARTIAL_WAKE_LOCK);
AStatsEvent_writeString(statsEvent, wakelockName.c_str());
AStatsEvent_writeInt32(statsEvent, state);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
@@ -828,14 +734,9 @@
AStatsEvent_writeString(statsEvent, "pkg_name");
AStatsEvent_writeString(statsEvent, "class_name");
AStatsEvent_writeInt32(statsEvent, state);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
@@ -858,24 +759,12 @@
AStatsEvent_setAtomId(statsEvent, util::SYNC_STATE_CHANGED);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
- vector<const char*> cTags(attributionTags.size());
- for (int i = 0; i < cTags.size(); i++) {
- cTags[i] = attributionTags[i].c_str();
- }
-
- AStatsEvent_writeAttributionChain(statsEvent,
- reinterpret_cast<const uint32_t*>(attributionUids.data()),
- cTags.data(), attributionUids.size());
+ writeAttribution(statsEvent, attributionUids, attributionTags);
AStatsEvent_writeString(statsEvent, name.c_str());
AStatsEvent_writeInt32(statsEvent, state);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
@@ -904,14 +793,9 @@
AStatsEvent_writeInt32(statsEvent, uid);
AStatsEvent_writeString(statsEvent, "");
AStatsEvent_writeInt32(statsEvent, state);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
@@ -928,14 +812,9 @@
AStatsEvent_writeInt32(statsEvent, uid);
AStatsEvent_writeString(statsEvent, "eventType");
AStatsEvent_writeString(statsEvent, "processName");
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
@@ -948,14 +827,9 @@
AStatsEvent_writeInt32(statsEvent, hostUid);
AStatsEvent_writeInt32(statsEvent, isolatedUid);
AStatsEvent_writeInt32(statsEvent, is_create);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
@@ -967,14 +841,9 @@
AStatsEvent_writeInt32(statsEvent, uid);
AStatsEvent_writeInt32(statsEvent, state);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
@@ -988,26 +857,14 @@
AStatsEvent_setAtomId(statsEvent, util::BLE_SCAN_STATE_CHANGED);
AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
- vector<const char*> cTags(attributionTags.size());
- for (int i = 0; i < cTags.size(); i++) {
- cTags[i] = attributionTags[i].c_str();
- }
-
- AStatsEvent_writeAttributionChain(statsEvent,
- reinterpret_cast<const uint32_t*>(attributionUids.data()),
- cTags.data(), attributionUids.size());
+ writeAttribution(statsEvent, attributionUids, attributionTags);
AStatsEvent_writeInt32(statsEvent, state);
AStatsEvent_writeInt32(statsEvent, filtered); // filtered
AStatsEvent_writeInt32(statsEvent, firstMatch); // first match
AStatsEvent_writeInt32(statsEvent, opportunistic); // opportunistic
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
@@ -1023,14 +880,9 @@
AStatsEvent_writeString(statsEvent, packageName.c_str());
AStatsEvent_writeInt32(statsEvent, usingAlertWindow);
AStatsEvent_writeInt32(statsEvent, state);
- AStatsEvent_build(statsEvent);
-
- size_t size;
- uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
- logEvent->parseBuffer(buf, size);
- AStatsEvent_release(statsEvent);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h
index f24705a..d6ea77eb 100644
--- a/cmds/statsd/tests/statsd_test_util.h
+++ b/cmds/statsd/tests/statsd_test_util.h
@@ -25,6 +25,7 @@
#include "src/hash.h"
#include "src/logd/LogEvent.h"
#include "src/stats_log_util.h"
+#include "stats_event.h"
#include "statslog_statsdtest.h"
namespace android {
@@ -189,6 +190,12 @@
void getPartialWakelockKey(int uid, HashableDimensionKey* key);
// END: get primary key functions
+void writeAttribution(AStatsEvent* statsEvent, const vector<int>& attributionUids,
+ const vector<string>& attributionTags);
+
+// Builds statsEvent to get buffer that is parsed into logEvent then releases statsEvent.
+void parseStatsEventToLogEvent(AStatsEvent* statsEvent, LogEvent* logEvent);
+
shared_ptr<LogEvent> CreateTwoValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value1,
int32_t value2);
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 46b06fb..3a708a6 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1395,6 +1395,7 @@
public static final String OPSTR_QUERY_ALL_PACKAGES = "android:query_all_packages";
/** @hide Access all external storage */
@SystemApi
+ @TestApi
public static final String OPSTR_MANAGE_EXTERNAL_STORAGE =
"android:manage_external_storage";
diff --git a/core/java/android/app/IWindowToken.aidl b/core/java/android/app/IWindowToken.aidl
index 8ea881f..3627b0f 100644
--- a/core/java/android/app/IWindowToken.aidl
+++ b/core/java/android/app/IWindowToken.aidl
@@ -30,4 +30,6 @@
*/
oneway interface IWindowToken {
void onConfigurationChanged(in Configuration newConfig, int newDisplayId);
+
+ void onWindowTokenRemoved();
}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 91a8572..e599a5c 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -1658,6 +1658,9 @@
public final T getService(ContextImpl ctx) {
final Object[] cache = ctx.mServiceCache;
final int[] gates = ctx.mServiceInitializationStateArray;
+ boolean interrupted = false;
+
+ T ret = null;
for (;;) {
boolean doInitialize = false;
@@ -1665,7 +1668,8 @@
// Return it if we already have a cached instance.
T service = (T) cache[mCacheIndex];
if (service != null || gates[mCacheIndex] == ContextImpl.STATE_NOT_FOUND) {
- return service;
+ ret = service;
+ break; // exit the for (;;)
}
// If we get here, there's no cached instance.
@@ -1708,24 +1712,33 @@
cache.notifyAll();
}
}
- return service;
+ ret = service;
+ break; // exit the for (;;)
}
// The other threads will wait for the first thread to call notifyAll(),
// and go back to the top and retry.
synchronized (cache) {
+ // Repeat until the state becomes STATE_READY or STATE_NOT_FOUND.
+ // We can't respond to interrupts here; just like we can't in the "doInitialize"
+ // path, so we remember the interrupt state here and re-interrupt later.
while (gates[mCacheIndex] < ContextImpl.STATE_READY) {
try {
+ // Clear the interrupt state.
+ interrupted |= Thread.interrupted();
cache.wait();
} catch (InterruptedException e) {
// This shouldn't normally happen, but if someone interrupts the
// thread, it will.
- Slog.wtf(TAG, "getService() interrupted");
- Thread.currentThread().interrupt();
- return null;
+ Slog.w(TAG, "getService() interrupted");
+ interrupted = true;
}
}
}
}
+ if (interrupted) {
+ Thread.currentThread().interrupt();
+ }
+ return ret;
}
public abstract T createService(ContextImpl ctx) throws ServiceNotFoundException;
diff --git a/core/java/android/app/WindowContext.java b/core/java/android/app/WindowContext.java
index 878993e..3a06c9d 100644
--- a/core/java/android/app/WindowContext.java
+++ b/core/java/android/app/WindowContext.java
@@ -28,6 +28,8 @@
import android.view.WindowManagerGlobal;
import android.view.WindowManagerImpl;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.lang.ref.Reference;
/**
@@ -75,8 +77,6 @@
// config back to the client.
result = mWms.addWindowTokenWithOptions(
mToken, type, getDisplayId(), options, getPackageName());
-
- // TODO(window-context): remove token with a DeathObserver
} catch (RemoteException e) {
mOwnsToken = false;
throw e.rethrowFromSystemServer();
@@ -100,6 +100,13 @@
@Override
protected void finalize() throws Throwable {
+ release();
+ super.finalize();
+ }
+
+ /** Used for test to invoke because we can't invoke finalize directly. */
+ @VisibleForTesting
+ public void release() {
if (mOwnsToken) {
try {
mWms.removeWindowToken(mToken, getDisplayId());
@@ -108,6 +115,12 @@
throw e.rethrowFromSystemServer();
}
}
- super.finalize();
+ destroy();
+ }
+
+ void destroy() {
+ final ContextImpl impl = (ContextImpl) getBaseContext();
+ impl.scheduleFinalCleanup(getClass().getName(), "WindowContext");
+ Reference.reachabilityFence(this);
}
}
diff --git a/core/java/android/app/WindowTokenClient.java b/core/java/android/app/WindowTokenClient.java
index ed0179b..301960e 100644
--- a/core/java/android/app/WindowTokenClient.java
+++ b/core/java/android/app/WindowTokenClient.java
@@ -20,6 +20,9 @@
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.IBinder;
+import android.view.WindowManagerGlobal;
+
+import java.lang.ref.WeakReference;
/**
* Client implementation of {@link IWindowToken}. It can receive configuration change callbacks from
@@ -31,9 +34,9 @@
public class WindowTokenClient extends IWindowToken.Stub {
/**
* Attached {@link Context} for this window token to update configuration and resources.
- * Initialized by {@link #attachContext(Context)}.
+ * Initialized by {@link #attachContext(WindowContext)}.
*/
- private Context mContext = null;
+ private WeakReference<WindowContext> mContextRef = null;
private final ResourcesManager mResourcesManager = ResourcesManager.getInstance();
@@ -47,30 +50,46 @@
* @param context context to be attached
* @throws IllegalStateException if attached context has already existed.
*/
- void attachContext(@NonNull Context context) {
- if (mContext != null) {
+ void attachContext(@NonNull WindowContext context) {
+ if (mContextRef != null) {
throw new IllegalStateException("Context is already attached.");
}
- mContext = context;
- ContextImpl impl = ContextImpl.getImpl(mContext);
+ mContextRef = new WeakReference<>(context);
+ final ContextImpl impl = ContextImpl.getImpl(context);
impl.setResources(impl.createWindowContextResources());
}
@Override
public void onConfigurationChanged(Configuration newConfig, int newDisplayId) {
- final int currentDisplayId = mContext.getDisplayId();
+ final Context context = mContextRef.get();
+ if (context == null) {
+ return;
+ }
+ final int currentDisplayId = context.getDisplayId();
final boolean displayChanged = newDisplayId != currentDisplayId;
- final Configuration config = new Configuration(mContext.getResources()
+ final Configuration config = new Configuration(context.getResources()
.getConfiguration());
final boolean configChanged = config.isOtherSeqNewer(newConfig)
&& config.updateFrom(newConfig) != 0;
if (displayChanged || configChanged) {
// TODO(ag/9789103): update resource manager logic to track non-activity tokens
- mResourcesManager.updateResourcesForActivity(asBinder(), config, newDisplayId,
+ mResourcesManager.updateResourcesForActivity(this, config, newDisplayId,
displayChanged);
}
if (displayChanged) {
- mContext.updateDisplay(newDisplayId);
+ context.updateDisplay(newDisplayId);
}
}
+
+ @Override
+ public void onWindowTokenRemoved() {
+ final WindowContext context = mContextRef.get();
+ if (context != null) {
+ context.destroy();
+ mContextRef.clear();
+ }
+ // If a secondary display is detached, release all views attached to this token.
+ WindowManagerGlobal.getInstance().closeAll(this, mContextRef.getClass().getName(),
+ "WindowContext");
+ }
}
diff --git a/core/java/android/app/admin/DevicePolicyKeyguardService.java b/core/java/android/app/admin/DevicePolicyKeyguardService.java
index db833ec..473725f 100644
--- a/core/java/android/app/admin/DevicePolicyKeyguardService.java
+++ b/core/java/android/app/admin/DevicePolicyKeyguardService.java
@@ -16,12 +16,15 @@
package android.app.admin;
+import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.app.Service;
import android.content.Intent;
+import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;
import android.view.SurfaceControlViewHost;
@@ -41,27 +44,34 @@
@SystemApi
public class DevicePolicyKeyguardService extends Service {
private static final String TAG = "DevicePolicyKeyguardService";
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
private IKeyguardCallback mCallback;
private final IKeyguardClient mClient = new IKeyguardClient.Stub() {
+ @MainThread
@Override
public void onCreateKeyguardSurface(@Nullable IBinder hostInputToken,
- IKeyguardCallback callback) {
+ @NonNull IKeyguardCallback callback) {
mCallback = callback;
- SurfaceControlViewHost.SurfacePackage surfacePackage =
- DevicePolicyKeyguardService.this.onCreateKeyguardSurface(hostInputToken);
+ mHandler.post(() -> {
+ SurfaceControlViewHost.SurfacePackage surfacePackage =
+ DevicePolicyKeyguardService.this.onCreateKeyguardSurface(hostInputToken);
- if (mCallback != null) {
try {
mCallback.onRemoteContentReady(surfacePackage);
} catch (RemoteException e) {
Log.e(TAG, "Failed to return created SurfacePackage", e);
}
- }
+ });
}
};
@Override
+ public void onDestroy() {
+ mHandler.removeCallbacksAndMessages(null);
+ }
+
+ @Override
@Nullable
public final IBinder onBind(@Nullable Intent intent) {
return mClient.asBinder();
@@ -97,6 +107,10 @@
*/
@Nullable
public void dismiss() {
+ if (mCallback == null) {
+ Log.w(TAG, "KeyguardCallback was unexpectedly null");
+ return;
+ }
try {
mCallback.onDismiss();
} catch (RemoteException e) {
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index fc48e7f..f216db6 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -979,14 +979,17 @@
8, BLUETOOTH_GET_STATE_CACHE_PROPERTY) {
@Override
protected Integer recompute(Void query) {
- // This function must be called while holding the
- // mServiceLock, and with mService not null. The public
- // getState() method makes this guarantee.
try {
- return mService.getState();
+ mServiceLock.readLock().lock();
+ if (mService != null) {
+ return mService.getState();
+ }
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ Log.e(TAG, "", e);
+ } finally {
+ mServiceLock.readLock().unlock();
}
+ return BluetoothAdapter.STATE_OFF;
}
};
@@ -1013,24 +1016,7 @@
@RequiresPermission(Manifest.permission.BLUETOOTH)
@AdapterState
public int getState() {
- int state = BluetoothAdapter.STATE_OFF;
-
- try {
- mServiceLock.readLock().lock();
- // The test for mService must either be outside the cache, or
- // the cache must be invalidated when mService changes.
- if (mService != null) {
- state = mBluetoothGetStateCache.query(null);
- }
- } catch (RuntimeException e) {
- if (e.getCause() instanceof RemoteException) {
- Log.e(TAG, "", e.getCause());
- } else {
- throw e;
- }
- } finally {
- mServiceLock.readLock().unlock();
- }
+ int state = mBluetoothGetStateCache.query(null);
// Consider all internal states as OFF
if (state == BluetoothAdapter.STATE_BLE_ON || state == BluetoothAdapter.STATE_BLE_TURNING_ON
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index e446f4f..0a4627d 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -687,6 +687,19 @@
public static final int NOTIFY_DELETE = 1 << 4;
/**
+ * Flag for {@link #notifyChange(Uri, ContentObserver, int)}: typically set
+ * by a {@link ContentProvider} to indicate that this notification should
+ * not be subject to any delays when dispatching to apps running in the
+ * background.
+ * <p>
+ * Using this flag may negatively impact system health and performance, and
+ * should be used sparingly.
+ *
+ * @hide
+ */
+ public static final int NOTIFY_NO_DELAY = 1 << 15;
+
+ /**
* No exception, throttled by app standby normally.
* @hide
*/
diff --git a/core/java/android/database/sqlite/SQLiteCantOpenDatabaseException.java b/core/java/android/database/sqlite/SQLiteCantOpenDatabaseException.java
index 6f01796..5d4b48d 100644
--- a/core/java/android/database/sqlite/SQLiteCantOpenDatabaseException.java
+++ b/core/java/android/database/sqlite/SQLiteCantOpenDatabaseException.java
@@ -22,4 +22,9 @@
public SQLiteCantOpenDatabaseException(String error) {
super(error);
}
+
+ /** @hide */
+ public SQLiteCantOpenDatabaseException(String error, Throwable cause) {
+ super(error, cause);
+ }
}
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index bcb3934..f7c96a3 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -36,6 +36,9 @@
import java.io.File;
import java.io.IOException;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
@@ -215,12 +218,31 @@
}
private void open() {
+ final String file = mConfiguration.path;
final int cookie = mRecentOperations.beginOperation("open", null, null);
try {
- mConnectionPtr = nativeOpen(mConfiguration.path, mConfiguration.openFlags,
+ mConnectionPtr = nativeOpen(file, mConfiguration.openFlags,
mConfiguration.label,
NoPreloadHolder.DEBUG_SQL_STATEMENTS, NoPreloadHolder.DEBUG_SQL_TIME,
mConfiguration.lookasideSlotSize, mConfiguration.lookasideSlotCount);
+ } catch (SQLiteCantOpenDatabaseException e) {
+ String message = String.format("Cannot open database '%s'", file);
+
+ final Path path = FileSystems.getDefault().getPath(file);
+ final Path dir = path.getParent();
+
+ if (!Files.isDirectory(dir)) {
+ message += ": Directory " + dir + " doesn't exist";
+ } else if (!Files.exists(path)) {
+ message += ": File " + path + " doesn't exist";
+ } else if (!Files.isReadable(path)) {
+ message += ": File " + path + " is not readable";
+ } else if (Files.isDirectory(path)) {
+ message += ": Path " + path + " is a directory";
+ } else {
+ message += ": Unknown reason";
+ }
+ throw new SQLiteCantOpenDatabaseException(message, e);
} finally {
mRecentOperations.endOperation(cookie);
}
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 91dae66..aa75f60 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -17,6 +17,7 @@
package android.hardware.camera2;
import android.annotation.NonNull;
+import android.compat.annotation.UnsupportedAppUsage;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.impl.PublicKey;
import android.hardware.camera2.impl.SyntheticKey;
@@ -100,6 +101,8 @@
*
* @hide
*/
+ @UnsupportedAppUsage(publicAlternatives = "This method is exposed for native "
+ + "{@code ACameraMetadata_fromCameraMetadata} in {@code libcamera2ndk}.")
public long getNativeMetadataPtr() {
if (mNativeInstance == null) {
return 0;
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index b7b3c4f..5d2c9d1 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -607,6 +607,9 @@
* started.
* @param pkgDataInfoMap Map from related package names to private data directory
* volume UUID and inode number.
+ * @param whitelistedDataInfoMap Map from whitelisted package names to private data directory
+ * volume UUID and inode number.
+ * @param bindMountAppsData whether zygote needs to mount CE and DE data.
* @param bindMountAppStorageDirs whether zygote needs to mount Android/obb and Android/data.
* @param zygoteArgs Additional arguments to supply to the zygote process.
* @return An object that describes the result of the attempt to start the process.
@@ -631,13 +634,17 @@
@Nullable long[] disabledCompatChanges,
@Nullable Map<String, Pair<String, Long>>
pkgDataInfoMap,
+ @Nullable Map<String, Pair<String, Long>>
+ whitelistedDataInfoMap,
+ boolean bindMountAppsData,
boolean bindMountAppStorageDirs,
@Nullable String[] zygoteArgs) {
return ZYGOTE_PROCESS.start(processClass, niceName, uid, gid, gids,
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, packageName,
zygotePolicyFlags, isTopApp, disabledCompatChanges,
- pkgDataInfoMap, bindMountAppStorageDirs, zygoteArgs);
+ pkgDataInfoMap, whitelistedDataInfoMap, bindMountAppsData,
+ bindMountAppStorageDirs, zygoteArgs);
}
/** @hide */
@@ -661,7 +668,8 @@
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, packageName,
/*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, /*isTopApp=*/ false,
- disabledCompatChanges, /* pkgDataInfoMap */ null, false, zygoteArgs);
+ disabledCompatChanges, /* pkgDataInfoMap */ null,
+ /* whitelistedDataInfoMap */ null, false, false, zygoteArgs);
}
/**
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 5f3f14f..a4c99c0 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -333,6 +333,9 @@
* started.
* @param pkgDataInfoMap Map from related package names to private data directory
* volume UUID and inode number.
+ * @param whitelistedDataInfoMap Map from whitelisted package names to private data directory
+ * volume UUID and inode number.
+ * @param bindMountAppsData whether zygote needs to mount CE and DE data.
* @param bindMountAppStorageDirs whether zygote needs to mount Android/obb and Android/data.
*
* @param zygoteArgs Additional arguments to supply to the Zygote process.
@@ -355,6 +358,9 @@
@Nullable long[] disabledCompatChanges,
@Nullable Map<String, Pair<String, Long>>
pkgDataInfoMap,
+ @Nullable Map<String, Pair<String, Long>>
+ whitelistedDataInfoMap,
+ boolean bindMountAppsData,
boolean bindMountAppStorageDirs,
@Nullable String[] zygoteArgs) {
// TODO (chriswailes): Is there a better place to check this value?
@@ -367,7 +373,8 @@
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, /*startChildZygote=*/ false,
packageName, zygotePolicyFlags, isTopApp, disabledCompatChanges,
- pkgDataInfoMap, bindMountAppStorageDirs, zygoteArgs);
+ pkgDataInfoMap, whitelistedDataInfoMap, bindMountAppsData,
+ bindMountAppStorageDirs, zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
Log.e(LOG_TAG,
"Starting VM process through Zygote failed");
@@ -608,6 +615,9 @@
* @param disabledCompatChanges a list of disabled compat changes for the process being started.
* @param pkgDataInfoMap Map from related package names to private data directory volume UUID
* and inode number.
+ * @param whitelistedDataInfoMap Map from whitelisted package names to private data directory
+ * volume UUID and inode number.
+ * @param bindMountAppsData whether zygote needs to mount CE and DE data.
* @param bindMountAppStorageDirs whether zygote needs to mount Android/obb and Android/data.
* @param extraArgs Additional arguments to supply to the zygote process.
* @return An object that describes the result of the attempt to start the process.
@@ -631,6 +641,9 @@
@Nullable long[] disabledCompatChanges,
@Nullable Map<String, Pair<String, Long>>
pkgDataInfoMap,
+ @Nullable Map<String, Pair<String, Long>>
+ whitelistedDataInfoMap,
+ boolean bindMountAppsData,
boolean bindMountAppStorageDirs,
@Nullable String[] extraArgs)
throws ZygoteStartFailedEx {
@@ -728,11 +741,33 @@
}
argsForZygote.add(sb.toString());
}
+ if (whitelistedDataInfoMap != null && whitelistedDataInfoMap.size() > 0) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(Zygote.WHITELISTED_DATA_INFO_MAP);
+ sb.append("=");
+ boolean started = false;
+ for (Map.Entry<String, Pair<String, Long>> entry : whitelistedDataInfoMap.entrySet()) {
+ if (started) {
+ sb.append(',');
+ }
+ started = true;
+ sb.append(entry.getKey());
+ sb.append(',');
+ sb.append(entry.getValue().first);
+ sb.append(',');
+ sb.append(entry.getValue().second);
+ }
+ argsForZygote.add(sb.toString());
+ }
if (bindMountAppStorageDirs) {
argsForZygote.add(Zygote.BIND_MOUNT_APP_STORAGE_DIRS);
}
+ if (bindMountAppsData) {
+ argsForZygote.add(Zygote.BIND_MOUNT_APP_DATA_DIRS);
+ }
+
if (disabledCompatChanges != null && disabledCompatChanges.length > 0) {
StringBuilder sb = new StringBuilder();
sb.append("--disabled-compat-changes=");
@@ -1291,6 +1326,7 @@
true /* startChildZygote */, null /* packageName */,
ZYGOTE_POLICY_FLAG_SYSTEM_PROCESS /* zygotePolicyFlags */, false /* isTopApp */,
null /* disabledCompatChanges */, null /* pkgDataInfoMap */,
+ null /* whitelistedDataInfoMap */, false /* bindMountAppsData*/,
/* bindMountAppStorageDirs */ false, extraArgs);
} catch (ZygoteStartFailedEx ex) {
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 84ac90b..b0bacb9 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -124,13 +124,20 @@
* @param type Window type to be used with this token.
* @param options A bundle used to pass window-related options.
* @param displayId The ID of the display where this token should be added.
- * @param packageName The name of package to request to add window token.
+ * @param packageName The name of package to request to add window token. Could be {@code null}
+ * if callers holds the MANAGE_APP_TOKENS permission.
* @return {@link WindowManagerGlobal#ADD_OKAY} if the addition was successful, an error code
* otherwise.
*/
int addWindowTokenWithOptions(IBinder token, int type, int displayId, in Bundle options,
String packageName);
void addWindowToken(IBinder token, int type, int displayId);
+ /**
+ * Remove window token on a specific display.
+ *
+ * @param token Token to be removed
+ * @displayId The ID of the display where this token should be removed.
+ */
void removeWindowToken(IBinder token, int displayId);
void prepareAppTransition(int transit, boolean alwaysKeepCurrent);
@@ -735,4 +742,11 @@
* Called to show global actions.
*/
void showGlobalActions();
+
+ /**
+ * Sets layer tracing flags for SurfaceFlingerTrace.
+ *
+ * @param flags see definition in SurfaceTracing.cpp
+ */
+ void setLayerTracingFlags(int flags);
}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index ab65e3a..1086774 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -2888,7 +2888,8 @@
/**
* Acquire a frame rate flexibility token, which allows surface flinger to freely switch display
* frame rates. This is used by CTS tests to put the device in a consistent state. See
- * ISurfaceComposer::acquireFrameRateFlexibilityToken().
+ * ISurfaceComposer::acquireFrameRateFlexibilityToken(). The caller must have the
+ * ACCESS_SURFACE_FLINGER permission, or else the call will fail, returning 0.
* @hide
*/
@TestApi
diff --git a/core/java/android/view/textclassifier/ConversationActions.java b/core/java/android/view/textclassifier/ConversationActions.java
index 842ba29..6ad5cb9 100644
--- a/core/java/android/view/textclassifier/ConversationActions.java
+++ b/core/java/android/view/textclassifier/ConversationActions.java
@@ -27,8 +27,6 @@
import android.os.Parcelable;
import android.text.SpannedString;
-import com.android.internal.util.Preconditions;
-
import java.lang.annotation.Retention;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
@@ -491,7 +489,11 @@
*/
@NonNull
public Builder setMaxSuggestions(@IntRange(from = -1) int maxSuggestions) {
- mMaxSuggestions = Preconditions.checkArgumentNonnegative(maxSuggestions);
+ if (maxSuggestions < -1) {
+ throw new IllegalArgumentException("maxSuggestions has to be greater than or "
+ + "equal to -1.");
+ }
+ mMaxSuggestions = maxSuggestions;
return this;
}
diff --git a/core/java/android/window/DisplayAreaOrganizer.java b/core/java/android/window/DisplayAreaOrganizer.java
index eee222b..6ae70b7 100644
--- a/core/java/android/window/DisplayAreaOrganizer.java
+++ b/core/java/android/window/DisplayAreaOrganizer.java
@@ -33,8 +33,8 @@
public static final int FEATURE_SYSTEM_FIRST = 0;
// The Root display area on a display
public static final int FEATURE_ROOT = FEATURE_SYSTEM_FIRST;
- // Display area hosting the task container.
- public static final int FEATURE_TASK_CONTAINER = FEATURE_SYSTEM_FIRST + 1;
+ // Display area hosting the default task container.
+ public static final int FEATURE_DEFAULT_TASK_CONTAINER = FEATURE_SYSTEM_FIRST + 1;
// Display area hosting non-activity window tokens.
public static final int FEATURE_WINDOW_TOKENS = FEATURE_SYSTEM_FIRST + 2;
diff --git a/core/java/android/window/VirtualDisplayTaskEmbedder.java b/core/java/android/window/VirtualDisplayTaskEmbedder.java
index 1afbfeb..0f26d5d 100644
--- a/core/java/android/window/VirtualDisplayTaskEmbedder.java
+++ b/core/java/android/window/VirtualDisplayTaskEmbedder.java
@@ -16,7 +16,6 @@
package android.window;
-import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
@@ -247,7 +246,6 @@
protected ActivityOptions prepareActivityOptions(ActivityOptions options) {
options = super.prepareActivityOptions(options);
options.setLaunchDisplayId(getDisplayId());
- options.setLaunchWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
return options;
}
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 34f4957..505a05e 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -205,9 +205,15 @@
/** List of packages with the same uid, and its app data info: volume uuid and inode. */
public static final String PKG_DATA_INFO_MAP = "--pkg-data-info-map";
+ /** List of whitelisted packages and its app data info: volume uuid and inode. */
+ public static final String WHITELISTED_DATA_INFO_MAP = "--whitelisted-data-info-map";
+
/** Bind mount app storage dirs to lower fs not via fuse */
public static final String BIND_MOUNT_APP_STORAGE_DIRS = "--bind-mount-storage-dirs";
+ /** Bind mount app storage dirs to lower fs not via fuse */
+ public static final String BIND_MOUNT_APP_DATA_DIRS = "--bind-mount-data-dirs";
+
/**
* An extraArg passed when a zygote process is forking a child-zygote, specifying a name
* in the abstract socket namespace. This socket name is what the new child zygote
@@ -313,6 +319,8 @@
* @param isTopApp true if the process is for top (high priority) application.
* @param pkgDataInfoList A list that stores related packages and its app data
* info: volume uuid and inode.
+ * @param whitelistedDataInfoList Like pkgDataInfoList, but it's for whitelisted apps.
+ * @param bindMountAppDataDirs True if the zygote needs to mount data dirs.
* @param bindMountAppStorageDirs True if the zygote needs to mount storage dirs.
*
* @return 0 if this is the child, pid of the child
@@ -321,13 +329,15 @@
static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
- boolean isTopApp, String[] pkgDataInfoList, boolean bindMountAppStorageDirs) {
+ boolean isTopApp, String[] pkgDataInfoList, String[] whitelistedDataInfoList,
+ boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs) {
ZygoteHooks.preFork();
int pid = nativeForkAndSpecialize(
uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
fdsToIgnore, startChildZygote, instructionSet, appDataDir, isTopApp,
- pkgDataInfoList, bindMountAppStorageDirs);
+ pkgDataInfoList, whitelistedDataInfoList, bindMountAppDataDirs,
+ bindMountAppStorageDirs);
if (pid == 0) {
// Note that this event ends at the end of handleChildProc,
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
@@ -344,6 +354,7 @@
int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName,
int[] fdsToClose, int[] fdsToIgnore, boolean startChildZygote, String instructionSet,
String appDataDir, boolean isTopApp, String[] pkgDataInfoList,
+ String[] whitelistedDataInfoList, boolean bindMountAppDataDirs,
boolean bindMountAppStorageDirs);
/**
@@ -371,15 +382,19 @@
* volume uuid and CE dir inode. For example, pkgDataInfoList = [app_a_pkg_name,
* app_a_data_volume_uuid, app_a_ce_inode, app_b_pkg_name, app_b_data_volume_uuid,
* app_b_ce_inode, ...];
+ * @param whitelistedDataInfoList Like pkgDataInfoList, but it's for whitelisted apps.
+ * @param bindMountAppDataDirs True if the zygote needs to mount data dirs.
* @param bindMountAppStorageDirs True if the zygote needs to mount storage dirs.
*/
private static void specializeAppProcess(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, int mountExternal, String seInfo, String niceName,
boolean startChildZygote, String instructionSet, String appDataDir, boolean isTopApp,
- String[] pkgDataInfoList, boolean bindMountAppStorageDirs) {
+ String[] pkgDataInfoList, String[] whitelistedDataInfoList,
+ boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs) {
nativeSpecializeAppProcess(uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo,
niceName, startChildZygote, instructionSet, appDataDir, isTopApp,
- pkgDataInfoList, bindMountAppStorageDirs);
+ pkgDataInfoList, whitelistedDataInfoList,
+ bindMountAppDataDirs, bindMountAppStorageDirs);
// Note that this event ends at the end of handleChildProc.
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
@@ -399,7 +414,8 @@
private static native void nativeSpecializeAppProcess(int uid, int gid, int[] gids,
int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName,
boolean startChildZygote, String instructionSet, String appDataDir, boolean isTopApp,
- String[] pkgDataInfoList, boolean bindMountAppStorageDirs);
+ String[] pkgDataInfoList, String[] whitelistedDataInfoList,
+ boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs);
/**
* Called to do any initialization before starting an application.
@@ -724,7 +740,8 @@
args.mRuntimeFlags, rlimits, args.mMountExternal,
args.mSeInfo, args.mNiceName, args.mStartChildZygote,
args.mInstructionSet, args.mAppDataDir, args.mIsTopApp,
- args.mPkgDataInfoList, args.mBindMountAppStorageDirs);
+ args.mPkgDataInfoList, args.mWhitelistedDataInfoList,
+ args.mBindMountAppDataDirs, args.mBindMountAppStorageDirs);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
diff --git a/core/java/com/android/internal/os/ZygoteArguments.java b/core/java/com/android/internal/os/ZygoteArguments.java
index 1a63765..94c1f71 100644
--- a/core/java/com/android/internal/os/ZygoteArguments.java
+++ b/core/java/com/android/internal/os/ZygoteArguments.java
@@ -227,11 +227,22 @@
String[] mPkgDataInfoList;
/**
+ * A list that stores all whitelisted app data info: volume uuid and inode.
+ * Null if it does need to do app data isolation.
+ */
+ String[] mWhitelistedDataInfoList;
+
+ /**
* @see Zygote#BIND_MOUNT_APP_STORAGE_DIRS
*/
boolean mBindMountAppStorageDirs;
/**
+ * @see Zygote#BIND_MOUNT_APP_DATA_DIRS
+ */
+ boolean mBindMountAppDataDirs;
+
+ /**
* Constructs instance and parses args
*
* @param args zygote command-line args
@@ -452,8 +463,12 @@
}
} else if (arg.startsWith(Zygote.PKG_DATA_INFO_MAP)) {
mPkgDataInfoList = getAssignmentList(arg);
+ } else if (arg.startsWith(Zygote.WHITELISTED_DATA_INFO_MAP)) {
+ mWhitelistedDataInfoList = getAssignmentList(arg);
} else if (arg.equals(Zygote.BIND_MOUNT_APP_STORAGE_DIRS)) {
mBindMountAppStorageDirs = true;
+ } else if (arg.equals(Zygote.BIND_MOUNT_APP_DATA_DIRS)) {
+ mBindMountAppDataDirs = true;
} else {
break;
}
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index bc8dfd4..e6a3029 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -258,7 +258,8 @@
parsedArgs.mRuntimeFlags, rlimits, parsedArgs.mMountExternal, parsedArgs.mSeInfo,
parsedArgs.mNiceName, fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mIsTopApp,
- parsedArgs.mPkgDataInfoList, parsedArgs.mBindMountAppStorageDirs);
+ parsedArgs.mPkgDataInfoList, parsedArgs.mWhitelistedDataInfoList,
+ parsedArgs.mBindMountAppDataDirs, parsedArgs.mBindMountAppStorageDirs);
try {
if (pid == 0) {
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 71cf5ca..b6c58e1 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -68,7 +68,6 @@
import android.graphics.drawable.Drawable;
import android.graphics.drawable.InsetDrawable;
import android.graphics.drawable.LayerDrawable;
-import android.os.Build.VERSION_CODES;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Pair;
@@ -120,7 +119,6 @@
import com.android.internal.widget.FloatingToolbar;
import java.util.List;
-import java.util.function.Function;
/** @hide */
public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
@@ -283,11 +281,6 @@
private Insets mLastBackgroundInsets = Insets.NONE;
private boolean mDrawLegacyNavigationBarBackground;
- /**
- * Whether the app targets an SDK that uses the new insets APIs.
- */
- private boolean mUseNewInsetsApi;
-
private PendingInsetsController mPendingInsetsController = new PendingInsetsController();
DecorView(Context context, int featureId, PhoneWindow window,
@@ -319,7 +312,6 @@
initResizingPaints();
mLegacyNavigationBarBackgroundPaint.setColor(Color.BLACK);
- mUseNewInsetsApi = context.getApplicationInfo().targetSdkVersion >= VERSION_CODES.R;
}
void setBackgroundFallback(@Nullable Drawable fallbackDrawable) {
@@ -1189,23 +1181,23 @@
// these flags wouldn't make the window draw behind the navigation bar, unless
// LAYOUT_HIDE_NAVIGATION was set.
//
- // Note: Once the app targets R+, we no longer do this logic because we can't rely on
- // SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION to indicate whether the app wants to handle it by
- // themselves.
+ // Note: Once the app uses the R+ Window.setDecorFitsSystemWindows(false) API we no longer
+ // consume insets because they might no longer set SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION.
boolean hideNavigation = (sysUiVisibility & SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0
|| !(controller == null || controller.isRequestedVisible(ITYPE_NAVIGATION_BAR));
+ boolean decorFitsSystemWindows = mWindow.mDecorFitsSystemWindows;
boolean forceConsumingNavBar = (mForceWindowDrawsBarBackgrounds
&& (attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0
&& (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0
+ && decorFitsSystemWindows
&& !hideNavigation)
|| (mLastShouldAlwaysConsumeSystemBars && hideNavigation);
boolean consumingNavBar =
((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
&& (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0
- && !hideNavigation
- // TODO IME wrap_content windows need to have margin to work properly
- && (!mUseNewInsetsApi || isImeWindow))
+ && decorFitsSystemWindows
+ && !hideNavigation)
|| forceConsumingNavBar;
// If we didn't request fullscreen layout, but we still got it because of the
@@ -1216,6 +1208,7 @@
|| (attrs.flags & FLAG_FULLSCREEN) != 0
|| !(controller == null || controller.isRequestedVisible(ITYPE_STATUS_BAR));
boolean consumingStatusBar = (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) == 0
+ && decorFitsSystemWindows
&& (attrs.flags & FLAG_LAYOUT_IN_SCREEN) == 0
&& (attrs.flags & FLAG_LAYOUT_INSET_DECOR) == 0
&& mForceWindowDrawsBarBackgrounds
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 138d0dd..25c114f 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -343,8 +343,7 @@
/** @see ViewRootImpl#mActivityConfigCallback */
private ActivityConfigCallback mActivityConfigCallback;
- private OnContentApplyWindowInsetsListener mPendingOnContentApplyWindowInsetsListener =
- sDefaultContentInsetsApplier;
+ boolean mDecorFitsSystemWindows = true;
static class WindowManagerHolder {
static final IWindowManager sWindowManager = IWindowManager.Stub.asInterface(
@@ -2138,9 +2137,7 @@
/** Notify when decor view is attached to window and {@link ViewRootImpl} is available. */
void onViewRootImplSet(ViewRootImpl viewRoot) {
viewRoot.setActivityConfigCallback(mActivityConfigCallback);
- viewRoot.setOnContentApplyWindowInsetsListener(
- mPendingOnContentApplyWindowInsetsListener);
- mPendingOnContentApplyWindowInsetsListener = null;
+ applyDecorFitsSystemWindows();
}
static private final String FOCUSED_ID_TAG = "android:focusedViewId";
@@ -3907,14 +3904,16 @@
@Override
public void setDecorFitsSystemWindows(boolean decorFitsSystemWindows) {
+ mDecorFitsSystemWindows = decorFitsSystemWindows;
+ applyDecorFitsSystemWindows();
+ }
+
+ private void applyDecorFitsSystemWindows() {
ViewRootImpl impl = getViewRootImplOrNull();
- OnContentApplyWindowInsetsListener listener = decorFitsSystemWindows
- ? sDefaultContentInsetsApplier
- : null;
if (impl != null) {
- impl.setOnContentApplyWindowInsetsListener(listener);
- } else {
- mPendingOnContentApplyWindowInsetsListener = listener;
+ impl.setOnContentApplyWindowInsetsListener(mDecorFitsSystemWindows
+ ? sDefaultContentInsetsApplier
+ : null);
}
}
}
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 38b88ec..4b30359 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -110,7 +110,6 @@
using android::base::StringPrintf;
using android::base::WriteStringToFile;
using android::base::GetBoolProperty;
-using android::base::GetProperty;
#define CREATE_ERROR(...) StringPrintf("%s:%d: ", __FILE__, __LINE__). \
append(StringPrintf(__VA_ARGS__))
@@ -170,18 +169,6 @@
static constexpr int DEFAULT_DATA_DIR_PERMISSION = 0751;
-/**
- * Property to control if app data isolation is enabled.
- */
-static const std::string ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY =
- "persist.zygote.app_data_isolation";
-
-/**
- * Property to enable app data isolation for sdcard obb or data in vold.
- */
-static const std::string ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY =
- "persist.sys.vold_app_data_isolation_enabled";
-
static constexpr const uint64_t UPPER_HALF_WORD_MASK = 0xFFFF'FFFF'0000'0000;
static constexpr const uint64_t LOWER_HALF_WORD_MASK = 0x0000'0000'FFFF'FFFF;
@@ -1319,20 +1306,13 @@
* be decrypted after storage is decrypted.
*
*/
-static void isolateAppData(JNIEnv* env, jobjectArray pkg_data_info_list,
- uid_t uid, const char* process_name, jstring managed_nice_name,
- fail_fn_t fail_fn) {
+static void isolateAppData(JNIEnv* env, const std::vector<std::string>& merged_data_info_list,
+ uid_t uid, const char* process_name,
+ jstring managed_nice_name, fail_fn_t fail_fn) {
const userid_t userId = multiuser_get_user_id(uid);
- auto extract_fn = std::bind(ExtractJString, env, process_name, managed_nice_name, _1);
-
- int size = (pkg_data_info_list != nullptr) ? env->GetArrayLength(pkg_data_info_list) : 0;
- // Size should be a multiple of 3, as it contains list of <package_name, volume_uuid, inode>
- if ((size % 3) != 0) {
- fail_fn(CREATE_ERROR("Wrong pkg_inode_list size %d", size));
- }
- ensureInAppMountNamespace(fail_fn);
+ int size = merged_data_info_list.size();
// Mount tmpfs on all possible data directories, so app no longer see the original apps data.
char internalCePath[PATH_MAX];
@@ -1377,14 +1357,10 @@
bool legacySymlinkCreated = false;
for (int i = 0; i < size; i += 3) {
- jstring package_str = (jstring) (env->GetObjectArrayElement(pkg_data_info_list, i));
- std::string packageName = extract_fn(package_str).value();
+ std::string const & packageName = merged_data_info_list[i];
+ std::string const & volUuid = merged_data_info_list[i + 1];
+ std::string const & inode = merged_data_info_list[i + 2];
- jstring vol_str = (jstring) (env->GetObjectArrayElement(pkg_data_info_list, i + 1));
- std::string volUuid = extract_fn(vol_str).value();
-
- jstring inode_str = (jstring) (env->GetObjectArrayElement(pkg_data_info_list, i + 2));
- std::string inode = extract_fn(inode_str).value();
std::string::size_type sz;
long long ceDataInode = std::stoll(inode, &sz);
@@ -1482,6 +1458,48 @@
freecon(dataDataContext);
}
+static void insertPackagesToMergedList(JNIEnv* env,
+ std::vector<std::string>& merged_data_info_list,
+ jobjectArray data_info_list, const char* process_name,
+ jstring managed_nice_name, fail_fn_t fail_fn) {
+
+ auto extract_fn = std::bind(ExtractJString, env, process_name, managed_nice_name, _1);
+
+ int size = (data_info_list != nullptr) ? env->GetArrayLength(data_info_list) : 0;
+ // Size should be a multiple of 3, as it contains list of <package_name, volume_uuid, inode>
+ if ((size % 3) != 0) {
+ fail_fn(CREATE_ERROR("Wrong data_info_list size %d", size));
+ }
+
+ for (int i = 0; i < size; i += 3) {
+ jstring package_str = (jstring) (env->GetObjectArrayElement(data_info_list, i));
+ std::string packageName = extract_fn(package_str).value();
+ merged_data_info_list.push_back(packageName);
+
+ jstring vol_str = (jstring) (env->GetObjectArrayElement(data_info_list, i + 1));
+ std::string volUuid = extract_fn(vol_str).value();
+ merged_data_info_list.push_back(volUuid);
+
+ jstring inode_str = (jstring) (env->GetObjectArrayElement(data_info_list, i + 2));
+ std::string inode = extract_fn(inode_str).value();
+ merged_data_info_list.push_back(inode);
+ }
+}
+
+static void isolateAppData(JNIEnv* env, jobjectArray pkg_data_info_list,
+ jobjectArray whitelisted_data_info_list, uid_t uid, const char* process_name,
+ jstring managed_nice_name, fail_fn_t fail_fn) {
+
+ ensureInAppMountNamespace(fail_fn);
+ std::vector<std::string> merged_data_info_list;
+ insertPackagesToMergedList(env, merged_data_info_list, pkg_data_info_list,
+ process_name, managed_nice_name, fail_fn);
+ insertPackagesToMergedList(env, merged_data_info_list, whitelisted_data_info_list,
+ process_name, managed_nice_name, fail_fn);
+
+ isolateAppData(env, merged_data_info_list, uid, process_name, managed_nice_name, fail_fn);
+}
+
/**
* Like isolateAppData(), isolate jit profile directories, so apps don't see what
* other apps are installed by reading content inside /data/misc/profiles/cur.
@@ -1594,7 +1612,9 @@
jstring managed_nice_name, bool is_system_server,
bool is_child_zygote, jstring managed_instruction_set,
jstring managed_app_data_dir, bool is_top_app,
- jobjectArray pkg_data_info_list, bool mount_storage_dirs) {
+ jobjectArray pkg_data_info_list,
+ jobjectArray whitelisted_data_info_list,
+ bool mount_data_dirs, bool mount_storage_dirs) {
const char* process_name = is_system_server ? "system_server" : "zygote";
auto fail_fn = std::bind(ZygoteFailure, env, process_name, managed_nice_name, _1);
auto extract_fn = std::bind(ExtractJString, env, process_name, managed_nice_name, _1);
@@ -1628,9 +1648,9 @@
// give a null in same_uid_pkgs and private_volumes so they don't need app data isolation.
// Isolated process / webview / app zygote should be gated by SELinux and file permission
// so they can't even traverse CE / DE directories.
- if (pkg_data_info_list != nullptr
- && GetBoolProperty(ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY, true)) {
- isolateAppData(env, pkg_data_info_list, uid, process_name, managed_nice_name, fail_fn);
+ if (mount_data_dirs) {
+ isolateAppData(env, pkg_data_info_list, whitelisted_data_info_list,
+ uid, process_name, managed_nice_name, fail_fn);
isolateJitProfile(env, pkg_data_info_list, uid, process_name, managed_nice_name, fail_fn);
}
if ((mount_external != MOUNT_EXTERNAL_INSTALLER) && mount_storage_dirs) {
@@ -2003,7 +2023,8 @@
jint mount_external, jstring se_info, jstring nice_name,
jintArray managed_fds_to_close, jintArray managed_fds_to_ignore, jboolean is_child_zygote,
jstring instruction_set, jstring app_data_dir, jboolean is_top_app,
- jobjectArray pkg_data_info_list, jboolean mount_storage_dirs) {
+ jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list,
+ jboolean mount_data_dirs, jboolean mount_storage_dirs) {
jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote);
if (UNLIKELY(managed_fds_to_close == nullptr)) {
@@ -2041,6 +2062,8 @@
mount_external, se_info, nice_name, false,
is_child_zygote == JNI_TRUE, instruction_set, app_data_dir,
is_top_app == JNI_TRUE, pkg_data_info_list,
+ whitelisted_data_info_list,
+ mount_data_dirs == JNI_TRUE,
mount_storage_dirs == JNI_TRUE);
}
return pid;
@@ -2076,7 +2099,8 @@
permitted_capabilities, effective_capabilities,
MOUNT_EXTERNAL_DEFAULT, nullptr, nullptr, true,
false, nullptr, nullptr, /* is_top_app= */ false,
- /* pkg_data_info_list */ nullptr, false);
+ /* pkg_data_info_list */ nullptr,
+ /* whitelisted_data_info_list */ nullptr, false, false);
} else if (pid > 0) {
// The zygote process checks whether the child process has died or not.
ALOGI("System server process %d has been created", pid);
@@ -2206,15 +2230,16 @@
jint runtime_flags, jobjectArray rlimits,
jint mount_external, jstring se_info, jstring nice_name,
jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app,
- jobjectArray pkg_data_info_list, jboolean mount_storage_dirs) {
+ jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list,
+ jboolean mount_data_dirs, jboolean mount_storage_dirs) {
jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote);
SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
capabilities, capabilities,
mount_external, se_info, nice_name, false,
is_child_zygote == JNI_TRUE, instruction_set, app_data_dir,
- is_top_app == JNI_TRUE, pkg_data_info_list,
- mount_storage_dirs == JNI_TRUE);
+ is_top_app == JNI_TRUE, pkg_data_info_list, whitelisted_data_info_list,
+ mount_data_dirs == JNI_TRUE, mount_storage_dirs == JNI_TRUE);
}
/**
@@ -2417,7 +2442,7 @@
static const JNINativeMethod gMethods[] = {
{"nativeForkAndSpecialize",
"(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/"
- "String;Z[Ljava/lang/String;Z)I",
+ "String;Z[Ljava/lang/String;[Ljava/lang/String;ZZ)I",
(void*)com_android_internal_os_Zygote_nativeForkAndSpecialize},
{"nativeForkSystemServer", "(II[II[[IJJ)I",
(void*)com_android_internal_os_Zygote_nativeForkSystemServer},
@@ -2430,7 +2455,7 @@
{"nativeForkUsap", "(II[IZ)I", (void*)com_android_internal_os_Zygote_nativeForkUsap},
{"nativeSpecializeAppProcess",
"(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/"
- "String;Z[Ljava/lang/String;Z)V",
+ "String;Z[Ljava/lang/String;[Ljava/lang/String;ZZ)V",
(void*)com_android_internal_os_Zygote_nativeSpecializeAppProcess},
{"nativeInitNativeState", "(Z)V",
(void*)com_android_internal_os_Zygote_nativeInitNativeState},
diff --git a/core/proto/android/stats/devicepolicy/device_policy_enums.proto b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
index 684a292..896ee4f 100644
--- a/core/proto/android/stats/devicepolicy/device_policy_enums.proto
+++ b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
@@ -186,4 +186,15 @@
RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK= 159;
RESOLVER_EMPTY_STATE_NO_APPS_RESOLVED= 160;
RESOLVER_AUTOLAUNCH_CROSS_PROFILE_TARGET = 161;
+ CROSS_PROFILE_SETTINGS_PAGE_LAUNCHED_FROM_APP = 162;
+ CROSS_PROFILE_SETTINGS_PAGE_LAUNCHED_FROM_SETTINGS = 163;
+ CROSS_PROFILE_SETTINGS_PAGE_ADMIN_RESTRICTED = 164;
+ CROSS_PROFILE_SETTINGS_PAGE_MISSING_WORK_APP = 165;
+ CROSS_PROFILE_SETTINGS_PAGE_MISSING_PERSONAL_APP = 166;
+ CROSS_PROFILE_SETTINGS_PAGE_MISSING_INSTALL_BANNER_INTENT = 167;
+ CROSS_PROFILE_SETTINGS_PAGE_INSTALL_BANNER_CLICKED = 168;
+ CROSS_PROFILE_SETTINGS_PAGE_INSTALL_BANNER_NO_INTENT_CLICKED = 169;
+ CROSS_PROFILE_SETTINGS_PAGE_USER_CONSENTED = 170;
+ CROSS_PROFILE_SETTINGS_PAGE_USER_DECLINED_CONSENT = 171;
+ CROSS_PROFILE_SETTINGS_PAGE_PERMISSION_REVOKED = 172;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 451363f..c72239c 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4122,7 +4122,10 @@
set of pages referenced over time.
<p>Declaring the permission implies intention to use the API and the user of the
device can grant permission through the Settings application.
- <p>Protection level: signature|privileged|appop -->
+ <p>Protection level: signature|privileged|appop
+ <p>A data loader has to be the one which provides data to install an app.
+ <p>A data loader has to have both permission:LOADER_USAGE_STATS AND
+ appop:LOADER_USAGE_STATS allowed to be able to access the read logs. -->
<permission android:name="android.permission.LOADER_USAGE_STATS"
android:protectionLevel="signature|privileged|appop" />
<uses-permission android:name="android.permission.LOADER_USAGE_STATS" />
diff --git a/core/tests/coretests/src/android/app/WindowContextTest.java b/core/tests/coretests/src/android/app/WindowContextTest.java
new file mode 100644
index 0000000..630e16a
--- /dev/null
+++ b/core/tests/coretests/src/android/app/WindowContextTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.hardware.display.DisplayManager;
+import android.os.IBinder;
+import android.platform.test.annotations.Presubmit;
+import android.view.Display;
+import android.view.IWindowManager;
+import android.view.WindowManagerGlobal;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link WindowContext}
+ *
+ * <p>Build/Install/Run:
+ * atest FrameworksCoreTests:WindowContextTest
+ *
+ * <p>This test class is a part of Window Manager Service tests and specified in
+ * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}.
+ */
+@FlakyTest(bugId = 150812449, detail = "Remove after confirmed it's stable.")
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class WindowContextTest {
+ @Test
+ public void testWindowContextRelease_doRemoveWindowToken() throws Throwable {
+ final Context instContext = InstrumentationRegistry.getInstrumentation()
+ .getTargetContext();
+ final Display display = instContext.getSystemService(DisplayManager.class)
+ .getDisplay(DEFAULT_DISPLAY);
+ final Context context = instContext.createDisplayContext(display);
+ final WindowContext windowContext = new WindowContext(context, TYPE_APPLICATION_OVERLAY,
+ null /* options */);
+
+ final IBinder token = windowContext.getActivityToken();
+
+ final IWindowManager wms = WindowManagerGlobal.getWindowManagerService();
+ assertTrue("Token must be registered to WMS", wms.isWindowToken(token));
+
+ windowContext.release();
+
+ assertFalse("Token must be unregistered to WMS", wms.isWindowToken(token));
+ }
+}
diff --git a/core/tests/coretests/src/android/view/WindowMetricsTest.java b/core/tests/coretests/src/android/view/WindowMetricsTest.java
index 74524bf..ddc977d 100644
--- a/core/tests/coretests/src/android/view/WindowMetricsTest.java
+++ b/core/tests/coretests/src/android/view/WindowMetricsTest.java
@@ -56,17 +56,17 @@
@Before
public void setUp() {
- final Context insetContext = InstrumentationRegistry.getInstrumentation()
+ final Context instContext = InstrumentationRegistry.getInstrumentation()
.getTargetContext();
- final Display display = insetContext.getSystemService(DisplayManager.class)
+ final Display display = instContext.getSystemService(DisplayManager.class)
.getDisplay(DEFAULT_DISPLAY);
- mWindowContext = insetContext.createDisplayContext(display)
+ mWindowContext = instContext.createDisplayContext(display)
.createWindowContext(TYPE_APPLICATION_OVERLAY, null /* options */);
mWm = mWindowContext.getSystemService(WindowManager.class);
}
@Test
- public void testAddViewANdRemoveView_GetMetrics_DoNotCrash() {
+ public void testAddViewAndRemoveView_GetMetrics_DoNotCrash() {
final View view = new View(mWindowContext);
final WindowManager.LayoutParams params =
new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 59bdf3d..0389639 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -137,6 +137,7 @@
<permission name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
<permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME" />
<permission name="android.permission.PACKAGE_USAGE_STATS" />
+ <permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" />
</privapp-permissions>
<privapp-permissions package="com.android.phone">
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 07cf415..19ad6f7 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1507,6 +1507,18 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
},
+ "838570988": {
+ "message": "Could not report token removal to the window token client.",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowToken.java"
+ },
+ "845234215": {
+ "message": "App is requesting an orientation, return %d for display id=%d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/DisplayContent.java"
+ },
"853091290": {
"message": "Moved stack=%s behind stack=%s",
"level": "DEBUG",
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 4150926..75ea0cb 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -47,10 +47,10 @@
Location getLastLocation(in LocationRequest request, String packageName, String featureId);
boolean getCurrentLocation(in LocationRequest request,
in ICancellationSignal cancellationSignal, in ILocationListener listener,
- String packageName, String featureId);
+ String packageName, String featureId, String listenerId);
void requestLocationUpdates(in LocationRequest request, in ILocationListener listener,
- in PendingIntent intent, String packageName, String featureId);
+ in PendingIntent intent, String packageName, String featureId, String listenerId);
void removeUpdates(in ILocationListener listener, in PendingIntent intent);
void requestGeofence(in LocationRequest request, in Geofence geofence,
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index fcbd3e5..d1b41df 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -718,7 +718,7 @@
currentLocationRequest.setExpireIn(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS);
}
- GetCurrentLocationTransport listenerTransport = new GetCurrentLocationTransport(executor,
+ GetCurrentLocationTransport transport = new GetCurrentLocationTransport(executor,
consumer);
if (cancellationSignal != null) {
@@ -729,14 +729,15 @@
try {
if (mService.getCurrentLocation(currentLocationRequest, remoteCancellationSignal,
- listenerTransport, mContext.getPackageName(), mContext.getAttributionTag())) {
- listenerTransport.register(mContext.getSystemService(AlarmManager.class),
+ transport, mContext.getPackageName(), mContext.getAttributionTag(),
+ transport.getListenerId())) {
+ transport.register(mContext.getSystemService(AlarmManager.class),
remoteCancellationSignal);
if (cancellationSignal != null) {
- cancellationSignal.setOnCancelListener(listenerTransport::cancel);
+ cancellationSignal.setOnCancelListener(transport::cancel);
}
} else {
- listenerTransport.fail();
+ transport.fail();
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -1175,7 +1176,8 @@
boolean registered = false;
try {
mService.requestLocationUpdates(locationRequest, transport, null,
- mContext.getPackageName(), mContext.getAttributionTag());
+ mContext.getPackageName(), mContext.getAttributionTag(),
+ transport.getListenerId());
registered = true;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -1220,7 +1222,7 @@
try {
mService.requestLocationUpdates(locationRequest, null, pendingIntent,
- mContext.getPackageName(), mContext.getAttributionTag());
+ mContext.getPackageName(), mContext.getAttributionTag(), null);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2558,6 +2560,10 @@
mRemoteCancellationSignal = null;
}
+ public String getListenerId() {
+ return mConsumer.getClass().getName() + "@" + System.identityHashCode(mConsumer);
+ }
+
public synchronized void register(AlarmManager alarmManager,
ICancellationSignal remoteCancellationSignal) {
if (mConsumer == null) {
@@ -2683,6 +2689,10 @@
return mListener;
}
+ public String getListenerId() {
+ return mListener.getClass().getName() + "@" + System.identityHashCode(mListener);
+ }
+
public void register(@NonNull Executor executor) {
Preconditions.checkArgument(executor != null, "invalid null executor");
mExecutor = executor;
diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java
index 4dd1a29..5f0acc8 100644
--- a/location/java/android/location/LocationRequest.java
+++ b/location/java/android/location/LocationRequest.java
@@ -264,8 +264,8 @@
/* numUpdates= */ Integer.MAX_VALUE,
/* smallestDisplacement= */ 0,
/* hideFromAppOps= */ false,
- /* lowPowerMode= */ false,
/* locationSettingsIgnored= */ false,
+ /* lowPowerMode= */ false,
/* workSource= */ null);
}
@@ -282,8 +282,8 @@
src.mNumUpdates,
src.mSmallestDisplacement,
src.mHideFromAppOps,
- src.mLowPowerMode,
src.mLocationSettingsIgnored,
+ src.mLowPowerMode,
src.mWorkSource);
}
diff --git a/packages/CarSystemUI/res/layout/notification_center_activity.xml b/packages/CarSystemUI/res/layout/notification_center_activity.xml
index 4fef489..0af74c4 100644
--- a/packages/CarSystemUI/res/layout/notification_center_activity.xml
+++ b/packages/CarSystemUI/res/layout/notification_center_activity.xml
@@ -20,8 +20,6 @@
android:id="@+id/notification_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:visibility="invisible"
- android:layout_marginBottom="@dimen/navigation_bar_height"
android:background="@color/notification_shade_background_color">
<View
diff --git a/packages/CarSystemUI/res/layout/notification_panel_container.xml b/packages/CarSystemUI/res/layout/notification_panel_container.xml
index bf71396..3b53c6a 100644
--- a/packages/CarSystemUI/res/layout/notification_panel_container.xml
+++ b/packages/CarSystemUI/res/layout/notification_panel_container.xml
@@ -18,4 +18,5 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/notification_container"
android:layout_width="match_parent"
- android:layout_height="match_parent"/>
+ android:layout_height="match_parent"
+ android:visibility="invisible"/>
diff --git a/packages/CarSystemUI/res/layout/sysui_overlay_window.xml b/packages/CarSystemUI/res/layout/sysui_overlay_window.xml
index 1b0a211..067e359 100644
--- a/packages/CarSystemUI/res/layout/sysui_overlay_window.xml
+++ b/packages/CarSystemUI/res/layout/sysui_overlay_window.xml
@@ -22,10 +22,12 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
+ <!-- TODO(b/151617493): replace marginBottom with insets. -->
<ViewStub android:id="@+id/notification_panel_stub"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout="@layout/notification_panel_container"/>
+ android:layout="@layout/notification_panel_container"
+ android:layout_marginBottom="@dimen/navigation_bar_height"/>
<ViewStub android:id="@+id/fullscreen_user_switcher_stub"
android:layout_width="match_parent"
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarDeviceProvisionedController.java b/packages/CarSystemUI/src/com/android/systemui/car/CarDeviceProvisionedController.java
index b057198..44e43fe 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/CarDeviceProvisionedController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/CarDeviceProvisionedController.java
@@ -24,7 +24,7 @@
*/
public interface CarDeviceProvisionedController extends DeviceProvisionedController {
/**
- * Returns {@code true} then SUW is in progress for the given user.
+ * Returns {@code true} when SUW is in progress for the given user.
*/
boolean isUserSetupInProgress(int user);
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
index 1901a2d..d8a894c 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
@@ -16,9 +16,6 @@
package com.android.systemui.car.notification;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
import android.app.ActivityManager;
import android.car.Car;
import android.car.drivingstate.CarUxRestrictionsManager;
@@ -33,7 +30,6 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
@@ -54,7 +50,7 @@
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.window.OverlayViewController;
+import com.android.systemui.window.OverlayPanelViewController;
import com.android.systemui.window.OverlayViewGlobalStateController;
import javax.inject.Inject;
@@ -62,39 +58,22 @@
/** View controller for the notification panel. */
@Singleton
-public class NotificationPanelViewController extends OverlayViewController {
+public class NotificationPanelViewController extends OverlayPanelViewController {
- // used to calculate how fast to open or close the window
- private static final float DEFAULT_FLING_VELOCITY = 0;
- // max time a fling animation takes
- private static final float FLING_ANIMATION_MAX_TIME = 0.5f;
- // acceleration rate for the fling animation
- private static final float FLING_SPEED_UP_FACTOR = 0.6f;
-
- private static final int SWIPE_DOWN_MIN_DISTANCE = 25;
- private static final int SWIPE_MAX_OFF_PATH = 75;
- private static final int SWIPE_THRESHOLD_VELOCITY = 200;
private static final boolean DEBUG = true;
private static final String TAG = "NotificationPanelViewController";
private final Context mContext;
private final Resources mResources;
private final CarServiceProvider mCarServiceProvider;
- private final CarDeviceProvisionedController mCarDeviceProvisionedController;
private final IStatusBarService mBarService;
private final CommandQueue mCommandQueue;
private final NotificationDataManager mNotificationDataManager;
private final CarUxRestrictionManagerWrapper mCarUxRestrictionManagerWrapper;
private final CarNotificationListener mCarNotificationListener;
private final NotificationClickHandlerFactory mNotificationClickHandlerFactory;
- private final FlingAnimationUtils mFlingAnimationUtils;
private final StatusBarStateController mStatusBarStateController;
- private final int mSettleClosePercentage;
-
- private float mOpeningVelocity = DEFAULT_FLING_VELOCITY;
- private float mClosingVelocity = DEFAULT_FLING_VELOCITY;
-
private float mInitialBackgroundAlpha;
private float mBackgroundAlphaDiff;
@@ -108,13 +87,7 @@
private float mFirstTouchDownOnGlassPane;
private boolean mNotificationListAtBottomAtTimeOfTouch;
private boolean mIsSwipingVerticallyToClose;
- private int mPercentageFromBottom;
- private boolean mIsNotificationAnimating;
private boolean mIsNotificationCardSwiping;
- private boolean mPanelExpanded = false;
-
- private View.OnTouchListener mTopNavBarNotificationTouchListener;
- private View.OnTouchListener mNavBarNotificationTouchListener;
private OnUnseenCountUpdateListener mUnseenCountUpdateListener;
@@ -123,6 +96,7 @@
Context context,
@Main Resources resources,
OverlayViewGlobalStateController overlayViewGlobalStateController,
+ FlingAnimationUtils.Builder flingAnimationUtilsBuilder,
/* Other things */
CarServiceProvider carServiceProvider,
@@ -135,26 +109,21 @@
CarUxRestrictionManagerWrapper carUxRestrictionManagerWrapper,
CarNotificationListener carNotificationListener,
NotificationClickHandlerFactory notificationClickHandlerFactory,
- FlingAnimationUtils.Builder flingAnimationUtilsBuilder,
/* Things that need to be replaced */
StatusBarStateController statusBarStateController
) {
- super(R.id.notification_panel_stub, overlayViewGlobalStateController);
+ super(context, resources, R.id.notification_panel_stub, overlayViewGlobalStateController,
+ flingAnimationUtilsBuilder, carDeviceProvisionedController);
mContext = context;
mResources = resources;
mCarServiceProvider = carServiceProvider;
- mCarDeviceProvisionedController = carDeviceProvisionedController;
mBarService = barService;
mCommandQueue = commandQueue;
mNotificationDataManager = notificationDataManager;
mCarUxRestrictionManagerWrapper = carUxRestrictionManagerWrapper;
mCarNotificationListener = carNotificationListener;
mNotificationClickHandlerFactory = notificationClickHandlerFactory;
- mFlingAnimationUtils = flingAnimationUtilsBuilder
- .setMaxLengthSeconds(FLING_ANIMATION_MAX_TIME)
- .setSpeedUpFactor(FLING_SPEED_UP_FACTOR)
- .build();
mStatusBarStateController = statusBarStateController;
// Notification background setup.
@@ -175,60 +144,6 @@
+ " percentage");
}
mBackgroundAlphaDiff = finalBackgroundAlpha - mInitialBackgroundAlpha;
-
- // Notification Panel param setup
- mSettleClosePercentage = mResources.getInteger(
- R.integer.notification_settle_close_percentage);
-
- // Attached to the top navigation bar (i.e. status bar) to detect pull down of the
- // notification shade.
- GestureDetector openGestureDetector = new GestureDetector(mContext,
- new OpenNotificationGestureListener() {
- @Override
- protected void openNotification() {
- animateExpandNotificationsPanel();
- }
- });
-
- // Attached to the NavBars to close the notification shade
- GestureDetector navBarCloseNotificationGestureDetector = new GestureDetector(mContext,
- new NavBarCloseNotificationGestureListener() {
- @Override
- protected void close() {
- if (mPanelExpanded) {
- animateCollapsePanels();
- }
- }
- });
-
- mTopNavBarNotificationTouchListener = (v, event) -> {
- if (!isInflated()) {
- getOverlayViewGlobalStateController().inflateView(this);
- }
- if (!mCarDeviceProvisionedController.isCurrentUserFullySetup()) {
- return true;
- }
-
- boolean consumed = openGestureDetector.onTouchEvent(event);
- if (consumed) {
- return true;
- }
- maybeCompleteAnimation(event);
- return true;
- };
-
- mNavBarNotificationTouchListener =
- (v, event) -> {
- if (!isInflated()) {
- return true;
- }
- boolean consumed = navBarCloseNotificationGestureDetector.onTouchEvent(event);
- if (consumed) {
- return true;
- }
- maybeCompleteAnimation(event);
- return true;
- };
}
@Override
@@ -252,14 +167,13 @@
private void onNotificationViewInflated() {
// Find views.
mNotificationView = getLayout().findViewById(R.id.notification_view);
- View glassPane = mNotificationView.findViewById(R.id.glass_pane);
- mHandleBar = mNotificationView.findViewById(R.id.handle_bar);
- mNotificationList = mNotificationView.findViewById(R.id.notifications);
+ setupHandleBar();
+ setupNotificationPanel();
mNotificationClickHandlerFactory.registerClickListener((launchResult, alertEntry) -> {
if (launchResult == ActivityManager.START_TASK_TO_FRONT
|| launchResult == ActivityManager.START_SUCCESS) {
- animateCollapsePanels();
+ animateCollapsePanel();
}
});
@@ -269,39 +183,52 @@
mNotificationDataManager.getUnseenNotificationCount());
}
});
+
mNotificationClickHandlerFactory.setNotificationDataManager(mNotificationDataManager);
mNotificationView.setClickHandlerFactory(mNotificationClickHandlerFactory);
mNotificationView.setNotificationDataManager(mNotificationDataManager);
- mNotificationList.addOnScrollListener(new RecyclerView.OnScrollListener() {
- @Override
- public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
- super.onScrolled(recyclerView, dx, dy);
- if (!mNotificationList.canScrollVertically(1)) {
- mNotificationListAtBottom = true;
- return;
- }
- mNotificationListAtBottom = false;
- mIsSwipingVerticallyToClose = false;
- mNotificationListAtBottomAtTimeOfTouch = false;
- }
- });
+ mCarServiceProvider.addListener(car -> {
+ CarUxRestrictionsManager carUxRestrictionsManager =
+ (CarUxRestrictionsManager)
+ car.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE);
+ mCarUxRestrictionManagerWrapper.setCarUxRestrictionsManager(
+ carUxRestrictionsManager);
- // Attached to the notification ui to detect close request of the notification shade.
+ mNotificationViewController = new NotificationViewController(
+ mNotificationView,
+ PreprocessingManager.getInstance(mContext),
+ mCarNotificationListener,
+ mCarUxRestrictionManagerWrapper,
+ mNotificationDataManager);
+ mNotificationViewController.enable();
+ });
+ }
+
+ private void setupHandleBar() {
+ mHandleBar = mNotificationView.findViewById(R.id.handle_bar);
+ GestureDetector handleBarCloseNotificationGestureDetector = new GestureDetector(mContext,
+ new HandleBarCloseGestureListener());
+ mHandleBar.setOnTouchListener((v, event) -> {
+ handleBarCloseNotificationGestureDetector.onTouchEvent(event);
+ maybeCompleteAnimation(event);
+ return true;
+ });
+ }
+
+ private void setupNotificationPanel() {
+ View glassPane = mNotificationView.findViewById(R.id.glass_pane);
+ mNotificationList = mNotificationView.findViewById(R.id.notifications);
GestureDetector closeGestureDetector = new GestureDetector(mContext,
- new CloseNotificationGestureListener() {
+ new CloseGestureListener() {
@Override
protected void close() {
- if (mPanelExpanded) {
- animateCollapsePanels();
+ if (isPanelExpanded()) {
+ animateCollapsePanel();
}
}
});
- // Attached to the Handle bar to close the notification shade
- GestureDetector handleBarCloseNotificationGestureDetector = new GestureDetector(mContext,
- new HandleBarCloseNotificationGestureListener());
-
// The glass pane is used to view touch events before passed to the notification list.
// This allows us to initialize gesture listeners and detect when to close the notifications
glassPane.setOnTouchListener((v, event) -> {
@@ -320,6 +247,21 @@
return false;
});
+ mNotificationList.addOnScrollListener(new RecyclerView.OnScrollListener() {
+ @Override
+ public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
+ super.onScrolled(recyclerView, dx, dy);
+ // Check if we can scroll vertically downwards.
+ if (!mNotificationList.canScrollVertically(/* direction= */ 1)) {
+ mNotificationListAtBottom = true;
+ return;
+ }
+ mNotificationListAtBottom = false;
+ mIsSwipingVerticallyToClose = false;
+ mNotificationListAtBottomAtTimeOfTouch = false;
+ }
+ });
+
mNotificationList.setOnTouchListener((v, event) -> {
mIsNotificationCardSwiping = Math.abs(mFirstTouchDownOnGlassPane - event.getRawX())
> SWIPE_MAX_OFF_PATH;
@@ -341,19 +283,19 @@
boolean handled = closeGestureDetector.onTouchEvent(event);
boolean isTracking = mIsTracking;
- Rect rect = mNotificationView.getClipBounds();
+ Rect rect = getLayout().getClipBounds();
float clippedHeight = 0;
if (rect != null) {
clippedHeight = rect.bottom;
}
if (!handled && event.getActionMasked() == MotionEvent.ACTION_UP
&& mIsSwipingVerticallyToClose) {
- if (mSettleClosePercentage < mPercentageFromBottom && isTracking) {
- animateNotificationPanel(DEFAULT_FLING_VELOCITY, false);
- } else if (clippedHeight != mNotificationView.getHeight() && isTracking) {
+ if (getSettleClosePercentage() < getPercentageFromBottom() && isTracking) {
+ animatePanel(DEFAULT_FLING_VELOCITY, false);
+ } else if (clippedHeight != getLayout().getHeight() && isTracking) {
// this can be caused when user is at the end of the list and trying to
// fling to top of the list by scrolling down.
- animateNotificationPanel(DEFAULT_FLING_VELOCITY, true);
+ animatePanel(DEFAULT_FLING_VELOCITY, true);
}
}
@@ -365,28 +307,6 @@
}
return handled || isTracking;
});
-
- mCarServiceProvider.addListener(car -> {
- CarUxRestrictionsManager carUxRestrictionsManager =
- (CarUxRestrictionsManager)
- car.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE);
- mCarUxRestrictionManagerWrapper.setCarUxRestrictionsManager(
- carUxRestrictionsManager);
-
- mNotificationViewController = new NotificationViewController(
- mNotificationView,
- PreprocessingManager.getInstance(mContext),
- mCarNotificationListener,
- mCarUxRestrictionManagerWrapper,
- mNotificationDataManager);
- mNotificationViewController.enable();
- });
-
- mHandleBar.setOnTouchListener((v, event) -> {
- handleBarCloseNotificationGestureDetector.onTouchEvent(event);
- maybeCompleteAnimation(event);
- return true;
- });
}
/** Called when the car power state is changed to ON. */
@@ -397,139 +317,40 @@
mNotificationDataManager.clearAll();
}
- View.OnTouchListener getTopNavBarNotificationTouchListener() {
- return mTopNavBarNotificationTouchListener;
+ @Override
+ protected boolean shouldAnimateCollapsePanel() {
+ return true;
}
- View.OnTouchListener getNavBarNotificationTouchListener() {
- return mNavBarNotificationTouchListener;
+ @Override
+ protected void onAnimateCollapsePanel() {
+ // No op.
}
- private void maybeCompleteAnimation(MotionEvent event) {
- if (event.getActionMasked() == MotionEvent.ACTION_UP
- && mNotificationView.getVisibility() == View.VISIBLE) {
- if (mSettleClosePercentage < mPercentageFromBottom) {
- animateNotificationPanel(DEFAULT_FLING_VELOCITY, false);
- } else {
- animateNotificationPanel(DEFAULT_FLING_VELOCITY, true);
- }
- }
+ @Override
+ protected boolean shouldAnimateExpandPanel() {
+ return mCommandQueue.panelsEnabled();
}
- /**
- * Animates the notification shade from one position to other. This is used to either open or
- * close the notification shade completely with a velocity. If the animation is to close the
- * notification shade this method also makes the view invisible after animation ends.
- */
- private void animateNotificationPanel(float velocity, boolean isClosing) {
- float to = 0;
- if (!isClosing) {
- to = mNotificationView.getHeight();
- }
-
- Rect rect = mNotificationView.getClipBounds();
- if (rect != null && rect.bottom != to) {
- float from = rect.bottom;
- animate(from, to, velocity, isClosing);
- return;
- }
-
- // We will only be here if the shade is being opened programmatically or via button when
- // height of the layout was not calculated.
- ViewTreeObserver notificationTreeObserver = mNotificationView.getViewTreeObserver();
- notificationTreeObserver.addOnGlobalLayoutListener(
- new ViewTreeObserver.OnGlobalLayoutListener() {
- @Override
- public void onGlobalLayout() {
- ViewTreeObserver obs = mNotificationView.getViewTreeObserver();
- obs.removeOnGlobalLayoutListener(this);
- float to = mNotificationView.getHeight();
- animate(/* from= */ 0, to, velocity, isClosing);
- }
- });
- }
-
- private void animateCollapsePanels() {
- if (!mPanelExpanded || mNotificationView.getVisibility() == View.INVISIBLE) {
- return;
- }
- getOverlayViewGlobalStateController().setWindowFocusable(false);
- animateNotificationPanel(mClosingVelocity, true);
- }
-
- private void animateExpandNotificationsPanel() {
- if (!mCommandQueue.panelsEnabled()
- || !mCarDeviceProvisionedController.isCurrentUserFullySetup()) {
- return;
- }
- // scroll to top
+ @Override
+ protected void onAnimateExpandPanel() {
mNotificationList.scrollToPosition(0);
- setPanelVisible(true);
- mNotificationView.setVisibility(View.VISIBLE);
- animateNotificationPanel(mOpeningVelocity, false);
-
- setPanelExpanded(true);
}
- private void animate(float from, float to, float velocity, boolean isClosing) {
- if (mIsNotificationAnimating) {
- return;
- }
- mIsNotificationAnimating = true;
- mIsTracking = true;
- ValueAnimator animator = ValueAnimator.ofFloat(from, to);
- animator.addUpdateListener(
- animation -> {
- float animatedValue = (Float) animation.getAnimatedValue();
- setNotificationViewClipBounds((int) animatedValue);
- });
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- super.onAnimationEnd(animation);
- mIsNotificationAnimating = false;
- mIsTracking = false;
- mOpeningVelocity = DEFAULT_FLING_VELOCITY;
- mClosingVelocity = DEFAULT_FLING_VELOCITY;
- if (isClosing) {
- setPanelVisible(false);
- mNotificationView.setVisibility(View.INVISIBLE);
- mNotificationView.setClipBounds(null);
- mNotificationViewController.onVisibilityChanged(false);
- // let the status bar know that the panel is closed
- setPanelExpanded(false);
- } else {
- mNotificationViewController.onVisibilityChanged(true);
- // let the status bar know that the panel is open
- mNotificationView.setVisibleNotificationsAsSeen();
- setPanelExpanded(true);
- }
- }
- });
- mFlingAnimationUtils.apply(animator, from, to, Math.abs(velocity));
- animator.start();
+ @Override
+ protected void onCollapseAnimationEnd() {
+ mNotificationViewController.onVisibilityChanged(false);
}
- /**
- * Set the panel view to be visible.
- */
- public void setPanelVisible(boolean visible) {
- if (visible && !getOverlayViewGlobalStateController().isWindowVisible()) {
- getOverlayViewGlobalStateController().setWindowVisible(true);
- }
- if (!visible && getOverlayViewGlobalStateController().isWindowVisible()) {
- getOverlayViewGlobalStateController().setWindowVisible(false);
- }
- getLayout().setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
- getOverlayViewGlobalStateController().setWindowFocusable(visible);
+ @Override
+ protected void onExpandAnimationEnd() {
+ mNotificationViewController.onVisibilityChanged(true);
+ mNotificationView.setVisibleNotificationsAsSeen();
}
- /**
- * Set the panel state to expanded. This will expand or collapse the overlay window if
- * necessary.
- */
- public void setPanelExpanded(boolean expand) {
- mPanelExpanded = expand;
+ @Override
+ protected void onPanelExpanded(boolean expand) {
+ super.onPanelExpanded(expand);
if (expand && mStatusBarStateController.getState() != StatusBarState.KEYGUARD) {
if (DEBUG) {
@@ -550,19 +371,19 @@
}
}
- private void setNotificationViewClipBounds(int height) {
- if (height > mNotificationView.getHeight()) {
- height = mNotificationView.getHeight();
- }
- Rect clipBounds = new Rect();
- clipBounds.set(0, 0, mNotificationView.getWidth(), height);
- // Sets the clip region on the notification list view.
- mNotificationView.setClipBounds(clipBounds);
+ @Override
+ protected void onOpenScrollStart() {
+ mNotificationList.scrollToPosition(0);
+ }
+
+ @Override
+ protected void onScroll(int height) {
if (mHandleBar != null) {
ViewGroup.MarginLayoutParams lp =
(ViewGroup.MarginLayoutParams) mHandleBar.getLayoutParams();
mHandleBar.setTranslationY(height - mHandleBar.getHeight() - lp.bottomMargin);
}
+
if (mNotificationView.getHeight() > 0) {
Drawable background = mNotificationView.getBackground().mutate();
background.setAlpha((int) (getBackgroundAlpha(height) * 255));
@@ -570,6 +391,13 @@
}
}
+ @Override
+ protected boolean shouldAllowClosingScroll() {
+ // Unless the notification list is at the bottom, the panel shouldn't be allowed to
+ // collapse on scroll.
+ return mNotificationListAtBottomAtTimeOfTouch;
+ }
+
/**
* Calculates the alpha value for the background based on how much of the notification
* shade is visible to the user. When the notification shade is completely open then
@@ -580,30 +408,6 @@
+ ((float) height / mNotificationView.getHeight() * mBackgroundAlphaDiff);
}
- private void calculatePercentageFromBottom(float height) {
- if (mNotificationView.getHeight() > 0) {
- mPercentageFromBottom = (int) Math.abs(
- height / mNotificationView.getHeight() * 100);
- }
- }
-
- /** Toggles the visibility of the notification panel. */
- public void toggle() {
- if (!isInflated()) {
- getOverlayViewGlobalStateController().inflateView(this);
- }
- if (mPanelExpanded) {
- animateCollapsePanels();
- } else {
- animateExpandNotificationsPanel();
- }
- }
-
- /** Returns {@code true} if the notification panel is expanded. */
- public boolean isPanelExpanded() {
- return mPanelExpanded;
- }
-
/** Sets the unseen count listener. */
public void setOnUnseenCountUpdateListener(OnUnseenCountUpdateListener listener) {
mUnseenCountUpdateListener = listener;
@@ -619,154 +423,9 @@
}
/**
- * Only responsible for open hooks. Since once the panel opens it covers all elements
- * there is no need to merge with close.
- */
- private abstract class OpenNotificationGestureListener extends
- GestureDetector.SimpleOnGestureListener {
-
- @Override
- public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX,
- float distanceY) {
-
- if (mNotificationView.getVisibility() == View.INVISIBLE) {
- // when the on-scroll is called for the first time to open.
- mNotificationList.scrollToPosition(0);
- }
- setPanelVisible(true);
- mNotificationView.setVisibility(View.VISIBLE);
-
- // clips the view for the notification shade when the user scrolls to open.
- setNotificationViewClipBounds((int) event2.getRawY());
-
- // Initially the scroll starts with height being zero. This checks protects from divide
- // by zero error.
- calculatePercentageFromBottom(event2.getRawY());
-
- mIsTracking = true;
- return true;
- }
-
-
- @Override
- public boolean onFling(MotionEvent event1, MotionEvent event2,
- float velocityX, float velocityY) {
- if (velocityY > SWIPE_THRESHOLD_VELOCITY) {
- mOpeningVelocity = velocityY;
- openNotification();
- return true;
- }
- animateNotificationPanel(DEFAULT_FLING_VELOCITY, true);
-
- return false;
- }
-
- protected abstract void openNotification();
- }
-
- /**
- * To be installed on the open panel notification panel
- */
- private abstract class CloseNotificationGestureListener extends
- GestureDetector.SimpleOnGestureListener {
-
- @Override
- public boolean onSingleTapUp(MotionEvent motionEvent) {
- if (mPanelExpanded) {
- animateNotificationPanel(DEFAULT_FLING_VELOCITY, true);
- }
- return true;
- }
-
- @Override
- public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX,
- float distanceY) {
- // should not clip while scroll to the bottom of the list.
- if (!mNotificationListAtBottomAtTimeOfTouch) {
- return false;
- }
- float actualNotificationHeight =
- mNotificationView.getHeight() - (event1.getRawY() - event2.getRawY());
- if (actualNotificationHeight > mNotificationView.getHeight()) {
- actualNotificationHeight = mNotificationView.getHeight();
- }
- if (mNotificationView.getHeight() > 0) {
- mPercentageFromBottom = (int) Math.abs(
- actualNotificationHeight / mNotificationView.getHeight() * 100);
- boolean isUp = distanceY > 0;
-
- // This check is to figure out if onScroll was called while swiping the card at
- // bottom of the list. At that time we should not allow notification shade to
- // close. We are also checking for the upwards swipe gesture here because it is
- // possible if a user is closing the notification shade and while swiping starts
- // to open again but does not fling. At that time we should allow the
- // notification shade to close fully or else it would stuck in between.
- if (Math.abs(mNotificationView.getHeight() - actualNotificationHeight)
- > SWIPE_DOWN_MIN_DISTANCE && isUp) {
- setNotificationViewClipBounds((int) actualNotificationHeight);
- mIsTracking = true;
- } else if (!isUp) {
- setNotificationViewClipBounds((int) actualNotificationHeight);
- }
- }
- // if we return true the items in RV won't be scrollable.
- return false;
- }
-
-
- @Override
- public boolean onFling(MotionEvent event1, MotionEvent event2,
- float velocityX, float velocityY) {
- // should not fling if the touch does not start when view is at the bottom of the list.
- if (!mNotificationListAtBottomAtTimeOfTouch) {
- return false;
- }
- if (Math.abs(event1.getX() - event2.getX()) > SWIPE_MAX_OFF_PATH
- || Math.abs(velocityY) < SWIPE_THRESHOLD_VELOCITY) {
- // swipe was not vertical or was not fast enough
- return false;
- }
- boolean isUp = velocityY < 0;
- if (isUp) {
- close();
- return true;
- } else {
- // we should close the shade
- animateNotificationPanel(velocityY, false);
- }
- return false;
- }
-
- protected abstract void close();
- }
-
- /**
- * To be installed on the nav bars.
- */
- private abstract class NavBarCloseNotificationGestureListener extends
- CloseNotificationGestureListener {
- @Override
- public boolean onSingleTapUp(MotionEvent e) {
- mClosingVelocity = DEFAULT_FLING_VELOCITY;
- if (mPanelExpanded) {
- close();
- }
- return super.onSingleTapUp(e);
- }
-
- @Override
- public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX,
- float distanceY) {
- calculatePercentageFromBottom(event2.getRawY());
- setNotificationViewClipBounds((int) event2.getRawY());
- return true;
- }
- }
-
- /**
* To be installed on the handle bar.
*/
- private class HandleBarCloseNotificationGestureListener extends
+ private class HandleBarCloseGestureListener extends
GestureDetector.SimpleOnGestureListener {
@Override
@@ -777,9 +436,8 @@
// the handle bar we should calculate the height using the diff of event1 and event2.
// This will help the notification shade to clip smoothly as the event2 value changes
// as event1 value will be fixed.
- int clipHeight =
- mNotificationView.getHeight() - (int) (event1.getRawY() - event2.getRawY());
- setNotificationViewClipBounds(clipHeight);
+ int clipHeight = getLayout().getHeight() - (int) (event1.getRawY() - event2.getRawY());
+ setViewClipBounds(clipHeight);
return true;
}
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java
index 110c2ee..9d71797 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java
@@ -59,13 +59,13 @@
@Override
public void registerListeners() {
mCarNavigationBarController.registerTopBarTouchListener(
- mNotificationPanelViewController.getTopNavBarNotificationTouchListener());
+ mNotificationPanelViewController.getDragOpenTouchListener());
mCarNavigationBarController.registerBottomBarTouchListener(
- mNotificationPanelViewController.getNavBarNotificationTouchListener());
+ mNotificationPanelViewController.getDragCloseTouchListener());
mCarNavigationBarController.registerLeftBarTouchListener(
- mNotificationPanelViewController.getNavBarNotificationTouchListener());
+ mNotificationPanelViewController.getDragCloseTouchListener());
mCarNavigationBarController.registerRightBarTouchListener(
- mNotificationPanelViewController.getNavBarNotificationTouchListener());
+ mNotificationPanelViewController.getDragCloseTouchListener());
mCarNavigationBarController.registerNotificationController(
new CarNavigationBarController.NotificationsShadeController() {
diff --git a/packages/CarSystemUI/src/com/android/systemui/window/OverlayPanelViewController.java b/packages/CarSystemUI/src/com/android/systemui/window/OverlayPanelViewController.java
new file mode 100644
index 0000000..58022f1
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/window/OverlayPanelViewController.java
@@ -0,0 +1,563 @@
+/*
+ * 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.window;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.util.Log;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewTreeObserver;
+
+import androidx.annotation.CallSuper;
+
+import com.android.systemui.R;
+import com.android.systemui.car.CarDeviceProvisionedController;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.statusbar.FlingAnimationUtils;
+
+/**
+ * The {@link OverlayPanelViewController} provides additional dragging animation capabilities to
+ * {@link OverlayViewController}.
+ */
+public abstract class OverlayPanelViewController extends OverlayViewController {
+
+ private static final boolean DEBUG = true;
+ private static final String TAG = "OverlayPanelViewController";
+
+ // used to calculate how fast to open or close the window
+ protected static final float DEFAULT_FLING_VELOCITY = 0;
+ // max time a fling animation takes
+ protected static final float FLING_ANIMATION_MAX_TIME = 0.5f;
+ // acceleration rate for the fling animation
+ protected static final float FLING_SPEED_UP_FACTOR = 0.6f;
+
+ protected static final int SWIPE_DOWN_MIN_DISTANCE = 25;
+ protected static final int SWIPE_MAX_OFF_PATH = 75;
+ protected static final int SWIPE_THRESHOLD_VELOCITY = 200;
+
+ private final FlingAnimationUtils mFlingAnimationUtils;
+ private final CarDeviceProvisionedController mCarDeviceProvisionedController;
+ private final View.OnTouchListener mDragOpenTouchListener;
+ private final View.OnTouchListener mDragCloseTouchListener;
+
+ private final int mSettleClosePercentage;
+ private int mPercentageFromBottom;
+
+ private boolean mPanelVisible;
+ private boolean mPanelExpanded;
+
+ private float mOpeningVelocity = DEFAULT_FLING_VELOCITY;
+ private float mClosingVelocity = DEFAULT_FLING_VELOCITY;
+
+ private boolean mIsAnimating;
+ private boolean mIsTracking;
+
+ public OverlayPanelViewController(
+ Context context,
+ @Main Resources resources,
+ int stubId,
+ OverlayViewGlobalStateController overlayViewGlobalStateController,
+ FlingAnimationUtils.Builder flingAnimationUtilsBuilder,
+ CarDeviceProvisionedController carDeviceProvisionedController
+ ) {
+ super(stubId, overlayViewGlobalStateController);
+
+ mFlingAnimationUtils = flingAnimationUtilsBuilder
+ .setMaxLengthSeconds(FLING_ANIMATION_MAX_TIME)
+ .setSpeedUpFactor(FLING_SPEED_UP_FACTOR)
+ .build();
+ mCarDeviceProvisionedController = carDeviceProvisionedController;
+
+ mSettleClosePercentage = resources.getInteger(
+ R.integer.notification_settle_close_percentage);
+
+ // Attached to the top navigation bar (i.e. status bar) to detect pull down of the
+ // notification shade.
+ GestureDetector openGestureDetector = new GestureDetector(context,
+ new OpenGestureListener() {
+ @Override
+ protected void open() {
+ animateExpandPanel();
+ }
+ });
+
+ // Attached to the NavBars to close the notification shade
+ GestureDetector navBarCloseNotificationGestureDetector = new GestureDetector(context,
+ new SystemBarCloseGestureListener() {
+ @Override
+ protected void close() {
+ if (isPanelExpanded()) {
+ animateCollapsePanel();
+ }
+ }
+ });
+
+ mDragOpenTouchListener = (v, event) -> {
+ if (!mCarDeviceProvisionedController.isCurrentUserFullySetup()) {
+ return true;
+ }
+ if (!isInflated()) {
+ getOverlayViewGlobalStateController().inflateView(this);
+ }
+
+ boolean consumed = openGestureDetector.onTouchEvent(event);
+ if (consumed) {
+ return true;
+ }
+ maybeCompleteAnimation(event);
+ return true;
+ };
+
+ mDragCloseTouchListener = (v, event) -> {
+ if (!isInflated()) {
+ return true;
+ }
+ boolean consumed = navBarCloseNotificationGestureDetector.onTouchEvent(event);
+ if (consumed) {
+ return true;
+ }
+ maybeCompleteAnimation(event);
+ return true;
+ };
+ }
+
+ /** Toggles the visibility of the panel. */
+ public void toggle() {
+ if (!isInflated()) {
+ getOverlayViewGlobalStateController().inflateView(this);
+ }
+ if (isPanelExpanded()) {
+ animateCollapsePanel();
+ } else {
+ animateExpandPanel();
+ }
+ }
+
+ /* ***************************************************************************************** *
+ * Panel Animation
+ * ***************************************************************************************** */
+
+ /** Animates the closing of the panel. */
+ protected void animateCollapsePanel() {
+ if (!shouldAnimateCollapsePanel()) {
+ return;
+ }
+
+ if (!isPanelExpanded() || !isPanelVisible()) {
+ return;
+ }
+
+ onAnimateCollapsePanel();
+ getOverlayViewGlobalStateController().setWindowFocusable(false);
+ animatePanel(mClosingVelocity, /* isClosing= */ true);
+ }
+
+ /** Determines whether {@link #animateCollapsePanel()} should collapse the panel. */
+ protected abstract boolean shouldAnimateCollapsePanel();
+
+ /** Called when the panel is beginning to collapse. */
+ protected abstract void onAnimateCollapsePanel();
+
+ /** Animates the expansion of the panel. */
+ protected void animateExpandPanel() {
+ if (!shouldAnimateExpandPanel()) {
+ return;
+ }
+
+ if (!mCarDeviceProvisionedController.isCurrentUserFullySetup()) {
+ return;
+ }
+
+ onAnimateExpandPanel();
+ setPanelVisible(true);
+ animatePanel(mOpeningVelocity, /* isClosing= */ false);
+
+ setPanelExpanded(true);
+ }
+
+ /** Determines whether {@link #animateExpandPanel()}} should expand the panel. */
+ protected abstract boolean shouldAnimateExpandPanel();
+
+ /** Called when the panel is beginning to expand. */
+ protected abstract void onAnimateExpandPanel();
+
+ /**
+ * Depending on certain conditions, determines whether to fully expand or collapse the panel.
+ */
+ protected void maybeCompleteAnimation(MotionEvent event) {
+ if (event.getActionMasked() == MotionEvent.ACTION_UP
+ && isPanelVisible()) {
+ if (mSettleClosePercentage < mPercentageFromBottom) {
+ animatePanel(DEFAULT_FLING_VELOCITY, false);
+ } else {
+ animatePanel(DEFAULT_FLING_VELOCITY, true);
+ }
+ }
+ }
+
+ /**
+ * Animates the panel from one position to other. This is used to either open or
+ * close the panel completely with a velocity. If the animation is to close the
+ * panel this method also makes the view invisible after animation ends.
+ */
+ protected void animatePanel(float velocity, boolean isClosing) {
+ float to = 0;
+ if (!isClosing) {
+ to = getLayout().getHeight();
+ }
+
+ Rect rect = getLayout().getClipBounds();
+ if (rect != null && rect.bottom != to) {
+ float from = rect.bottom;
+ animate(from, to, velocity, isClosing);
+ return;
+ }
+
+ // We will only be here if the shade is being opened programmatically or via button when
+ // height of the layout was not calculated.
+ ViewTreeObserver notificationTreeObserver = getLayout().getViewTreeObserver();
+ notificationTreeObserver.addOnGlobalLayoutListener(
+ new ViewTreeObserver.OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ ViewTreeObserver obs = getLayout().getViewTreeObserver();
+ obs.removeOnGlobalLayoutListener(this);
+ float to = getLayout().getHeight();
+ animate(/* from= */ 0, to, velocity, isClosing);
+ }
+ });
+ }
+
+ private void animate(float from, float to, float velocity, boolean isClosing) {
+ if (mIsAnimating) {
+ return;
+ }
+ mIsAnimating = true;
+ mIsTracking = true;
+ ValueAnimator animator = ValueAnimator.ofFloat(from, to);
+ animator.addUpdateListener(
+ animation -> {
+ float animatedValue = (Float) animation.getAnimatedValue();
+ setViewClipBounds((int) animatedValue);
+ });
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ mIsAnimating = false;
+ mIsTracking = false;
+ mOpeningVelocity = DEFAULT_FLING_VELOCITY;
+ mClosingVelocity = DEFAULT_FLING_VELOCITY;
+ if (isClosing) {
+ setPanelVisible(false);
+ getLayout().setClipBounds(null);
+ onCollapseAnimationEnd();
+ setPanelExpanded(false);
+ } else {
+ onExpandAnimationEnd();
+ setPanelExpanded(true);
+ }
+ }
+ });
+ getFlingAnimationUtils().apply(animator, from, to, Math.abs(velocity));
+ animator.start();
+ }
+
+ /**
+ * Called in {@link Animator.AnimatorListener#onAnimationEnd(Animator)} when the panel is
+ * closing.
+ */
+ protected abstract void onCollapseAnimationEnd();
+
+ /**
+ * Called in {@link Animator.AnimatorListener#onAnimationEnd(Animator)} when the panel is
+ * opening.
+ */
+ protected abstract void onExpandAnimationEnd();
+
+ /* ***************************************************************************************** *
+ * Panel Visibility
+ * ***************************************************************************************** */
+
+ /** Set the panel view to be visible. */
+ protected final void setPanelVisible(boolean visible) {
+ mPanelVisible = visible;
+ onPanelVisible(visible);
+ }
+
+ /** Returns {@code true} if panel is visible. */
+ public final boolean isPanelVisible() {
+ return mPanelVisible;
+ }
+
+ /** Business logic run when panel visibility is set. */
+ @CallSuper
+ protected void onPanelVisible(boolean visible) {
+ if (DEBUG) {
+ Log.e(TAG, "onPanelVisible: " + visible);
+ }
+
+ if (visible && !getOverlayViewGlobalStateController().isWindowVisible()) {
+ getOverlayViewGlobalStateController().setWindowVisible(true);
+ }
+ if (!visible && getOverlayViewGlobalStateController().isWindowVisible()) {
+ getOverlayViewGlobalStateController().setWindowVisible(false);
+ }
+ getLayout().setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
+ getOverlayViewGlobalStateController().setWindowFocusable(visible);
+ }
+
+ /* ***************************************************************************************** *
+ * Panel Expansion
+ * ***************************************************************************************** */
+
+ /**
+ * Set the panel state to expanded. This will expand or collapse the overlay window if
+ * necessary.
+ */
+ protected final void setPanelExpanded(boolean expand) {
+ mPanelExpanded = expand;
+ onPanelExpanded(expand);
+ }
+
+ /** Returns {@code true} if panel is expanded. */
+ public final boolean isPanelExpanded() {
+ return mPanelExpanded;
+ }
+
+ @CallSuper
+ protected void onPanelExpanded(boolean expand) {
+ if (DEBUG) {
+ Log.e(TAG, "onPanelExpanded: " + expand);
+ }
+ }
+
+ /* ***************************************************************************************** *
+ * Misc
+ * ***************************************************************************************** */
+
+ protected void calculatePercentageFromBottom(float height) {
+ if (getLayout().getHeight() > 0) {
+ mPercentageFromBottom = (int) Math.abs(
+ height / getLayout().getHeight() * 100);
+ }
+ }
+
+ protected void setViewClipBounds(int height) {
+ if (height > getLayout().getHeight()) {
+ height = getLayout().getHeight();
+ }
+ Rect clipBounds = new Rect();
+ clipBounds.set(0, 0, getLayout().getWidth(), height);
+ getLayout().setClipBounds(clipBounds);
+ onScroll(height);
+ }
+
+ /** Called while scrolling. */
+ protected abstract void onScroll(int height);
+
+ /* ***************************************************************************************** *
+ * Getters
+ * ***************************************************************************************** */
+
+ /** Returns the open touch listener. */
+ public final View.OnTouchListener getDragOpenTouchListener() {
+ return mDragOpenTouchListener;
+ }
+
+ /** Returns the close touch listener. */
+ public final View.OnTouchListener getDragCloseTouchListener() {
+ return mDragCloseTouchListener;
+ }
+
+ /** Gets the fling animation utils used for animating this panel. */
+ protected final FlingAnimationUtils getFlingAnimationUtils() {
+ return mFlingAnimationUtils;
+ }
+
+ /** Returns {@code true} if the panel is currently tracking. */
+ protected final boolean isTracking() {
+ return mIsTracking;
+ }
+
+ /** Returns {@code true} if the panel is currently animating. */
+ protected final boolean isAnimating() {
+ return mIsAnimating;
+ }
+
+ /** Returns the percentage of the panel that is open from the bottom. */
+ protected final int getPercentageFromBottom() {
+ return mPercentageFromBottom;
+ }
+
+ /** Returns the percentage at which we've determined whether to open or close the panel. */
+ protected final int getSettleClosePercentage() {
+ return mSettleClosePercentage;
+ }
+
+ /* ***************************************************************************************** *
+ * Gesture Listeners
+ * ***************************************************************************************** */
+
+ /** Called when the user is beginning to scroll down the panel. */
+ protected abstract void onOpenScrollStart();
+
+ /**
+ * Only responsible for open hooks. Since once the panel opens it covers all elements
+ * there is no need to merge with close.
+ */
+ protected abstract class OpenGestureListener extends
+ GestureDetector.SimpleOnGestureListener {
+
+ @Override
+ public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX,
+ float distanceY) {
+
+ if (!isPanelVisible()) {
+ onOpenScrollStart();
+ }
+ setPanelVisible(true);
+
+ // clips the view for the notification shade when the user scrolls to open.
+ setViewClipBounds((int) event2.getRawY());
+
+ // Initially the scroll starts with height being zero. This checks protects from divide
+ // by zero error.
+ calculatePercentageFromBottom(event2.getRawY());
+
+ mIsTracking = true;
+ return true;
+ }
+
+
+ @Override
+ public boolean onFling(MotionEvent event1, MotionEvent event2,
+ float velocityX, float velocityY) {
+ if (velocityY > SWIPE_THRESHOLD_VELOCITY) {
+ mOpeningVelocity = velocityY;
+ open();
+ return true;
+ }
+ animatePanel(DEFAULT_FLING_VELOCITY, true);
+
+ return false;
+ }
+
+ protected abstract void open();
+ }
+
+ /** Determines whether the scroll event should allow closing of the panel. */
+ protected abstract boolean shouldAllowClosingScroll();
+
+ protected abstract class CloseGestureListener extends
+ GestureDetector.SimpleOnGestureListener {
+
+ @Override
+ public boolean onSingleTapUp(MotionEvent motionEvent) {
+ if (isPanelExpanded()) {
+ animatePanel(DEFAULT_FLING_VELOCITY, true);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX,
+ float distanceY) {
+ // should not clip while scroll to the bottom of the list.
+ if (!shouldAllowClosingScroll()) {
+ return false;
+ }
+ float actualNotificationHeight =
+ getLayout().getHeight() - (event1.getRawY() - event2.getRawY());
+ if (actualNotificationHeight > getLayout().getHeight()) {
+ actualNotificationHeight = getLayout().getHeight();
+ }
+ if (getLayout().getHeight() > 0) {
+ mPercentageFromBottom = (int) Math.abs(
+ actualNotificationHeight / getLayout().getHeight() * 100);
+ boolean isUp = distanceY > 0;
+
+ // This check is to figure out if onScroll was called while swiping the card at
+ // bottom of the list. At that time we should not allow notification shade to
+ // close. We are also checking for the upwards swipe gesture here because it is
+ // possible if a user is closing the notification shade and while swiping starts
+ // to open again but does not fling. At that time we should allow the
+ // notification shade to close fully or else it would stuck in between.
+ if (Math.abs(getLayout().getHeight() - actualNotificationHeight)
+ > SWIPE_DOWN_MIN_DISTANCE && isUp) {
+ setViewClipBounds((int) actualNotificationHeight);
+ mIsTracking = true;
+ } else if (!isUp) {
+ setViewClipBounds((int) actualNotificationHeight);
+ }
+ }
+ // if we return true the items in RV won't be scrollable.
+ return false;
+ }
+
+
+ @Override
+ public boolean onFling(MotionEvent event1, MotionEvent event2,
+ float velocityX, float velocityY) {
+ // should not fling if the touch does not start when view is at the bottom of the list.
+ if (!shouldAllowClosingScroll()) {
+ return false;
+ }
+ if (Math.abs(event1.getX() - event2.getX()) > SWIPE_MAX_OFF_PATH
+ || Math.abs(velocityY) < SWIPE_THRESHOLD_VELOCITY) {
+ // swipe was not vertical or was not fast enough
+ return false;
+ }
+ boolean isUp = velocityY < 0;
+ if (isUp) {
+ close();
+ return true;
+ } else {
+ // we should close the shade
+ animatePanel(velocityY, false);
+ }
+ return false;
+ }
+
+ protected abstract void close();
+ }
+
+ protected abstract class SystemBarCloseGestureListener extends CloseGestureListener {
+ @Override
+ public boolean onSingleTapUp(MotionEvent e) {
+ mClosingVelocity = DEFAULT_FLING_VELOCITY;
+ if (isPanelExpanded()) {
+ close();
+ }
+ return super.onSingleTapUp(e);
+ }
+
+ @Override
+ public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX,
+ float distanceY) {
+ calculatePercentageFromBottom(event2.getRawY());
+ setViewClipBounds((int) event2.getRawY());
+ return true;
+ }
+ }
+}
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 f2748b8..76557fd 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
@@ -20,13 +20,14 @@
import static org.mockito.Mockito.when;
import android.os.Handler;
-import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableResources;
import android.view.LayoutInflater;
import android.view.WindowManager;
+import androidx.test.filters.SmallTest;
+
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.car.CarDeviceProvisionedController;
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/window/OverlayPanelViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/window/OverlayPanelViewControllerTest.java
new file mode 100644
index 0000000..04f2d06
--- /dev/null
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/window/OverlayPanelViewControllerTest.java
@@ -0,0 +1,431 @@
+/*
+ * 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.window;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.car.CarDeviceProvisionedController;
+import com.android.systemui.statusbar.FlingAnimationUtils;
+import com.android.systemui.tests.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+@SmallTest
+public class OverlayPanelViewControllerTest extends SysuiTestCase {
+ private TestOverlayPanelViewController mOverlayPanelViewController;
+ private ViewGroup mBaseLayout;
+
+ @Mock
+ private OverlayViewGlobalStateController mOverlayViewGlobalStateController;
+ @Mock
+ private FlingAnimationUtils.Builder mFlingAnimationUtilsBuilder;
+ @Mock
+ private FlingAnimationUtils mFlingAnimationUtils;
+ @Mock
+ private CarDeviceProvisionedController mCarDeviceProvisionedController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mBaseLayout = (ViewGroup) LayoutInflater.from(mContext).inflate(
+ R.layout.overlay_view_controller_test, /* root= */ null);
+
+ when(mFlingAnimationUtilsBuilder.setMaxLengthSeconds(anyFloat())).thenReturn(
+ mFlingAnimationUtilsBuilder);
+ when(mFlingAnimationUtilsBuilder.setSpeedUpFactor(anyFloat())).thenReturn(
+ mFlingAnimationUtilsBuilder);
+ when(mFlingAnimationUtilsBuilder.build()).thenReturn(mFlingAnimationUtils);
+ mOverlayPanelViewController = new TestOverlayPanelViewController(
+ getContext(),
+ getContext().getOrCreateTestableResources().getResources(),
+ R.id.overlay_view_controller_stub,
+ mOverlayViewGlobalStateController,
+ mFlingAnimationUtilsBuilder,
+ mCarDeviceProvisionedController);
+ }
+
+ @Test
+ public void toggle_notInflated_inflates() {
+ assertThat(mOverlayPanelViewController.isInflated()).isFalse();
+
+ mOverlayPanelViewController.toggle();
+
+ verify(mOverlayViewGlobalStateController).inflateView(mOverlayPanelViewController);
+ }
+
+ @Test
+ public void toggle_inflated_doesNotInflate() {
+ mOverlayPanelViewController.inflate(mBaseLayout);
+ assertThat(mOverlayPanelViewController.isInflated()).isTrue();
+
+ mOverlayPanelViewController.toggle();
+
+ verify(mOverlayViewGlobalStateController, never()).inflateView(mOverlayPanelViewController);
+ }
+
+ @Test
+ public void toggle_notExpanded_panelExpands() {
+ mOverlayPanelViewController.inflate(mBaseLayout);
+ mOverlayPanelViewController.setPanelExpanded(false);
+
+ mOverlayPanelViewController.toggle();
+
+ assertThat(mOverlayPanelViewController.mAnimateExpandPanelCalled).isTrue();
+ }
+
+ @Test
+ public void toggle_expanded_panelCollapses() {
+ mOverlayPanelViewController.inflate(mBaseLayout);
+ mOverlayPanelViewController.setPanelExpanded(true);
+
+ mOverlayPanelViewController.toggle();
+
+ assertThat(mOverlayPanelViewController.mAnimateCollapsePanelCalled).isTrue();
+ }
+
+ @Test
+ public void animateCollapsePanel_shouldNotAnimateCollapsePanel_doesNotCollapse() {
+ mOverlayPanelViewController.inflate(mBaseLayout);
+ mOverlayPanelViewController.setShouldAnimateCollapsePanel(false);
+
+ mOverlayPanelViewController.animateCollapsePanel();
+
+ assertThat(mOverlayPanelViewController.mAnimateCollapsePanelCalled).isTrue();
+ assertThat(mOverlayPanelViewController.mOnAnimateCollapsePanelCalled).isFalse();
+ }
+
+ @Test
+ public void animateCollapsePanel_isNotExpanded_doesNotCollapse() {
+ mOverlayPanelViewController.inflate(mBaseLayout);
+ mOverlayPanelViewController.setShouldAnimateCollapsePanel(true);
+ mOverlayPanelViewController.setPanelExpanded(false);
+
+ mOverlayPanelViewController.animateCollapsePanel();
+
+ assertThat(mOverlayPanelViewController.mAnimateCollapsePanelCalled).isTrue();
+ assertThat(mOverlayPanelViewController.mOnAnimateCollapsePanelCalled).isFalse();
+ }
+
+ @Test
+ public void animateCollapsePanel_isNotVisible_doesNotCollapse() {
+ mOverlayPanelViewController.inflate(mBaseLayout);
+ mOverlayPanelViewController.setShouldAnimateCollapsePanel(true);
+ mOverlayPanelViewController.setPanelExpanded(true);
+ mOverlayPanelViewController.setPanelVisible(false);
+
+ mOverlayPanelViewController.animateCollapsePanel();
+
+ assertThat(mOverlayPanelViewController.mAnimateCollapsePanelCalled).isTrue();
+ assertThat(mOverlayPanelViewController.mOnAnimateCollapsePanelCalled).isFalse();
+ }
+
+ @Test
+ public void animateCollapsePanel_collapses() {
+ mOverlayPanelViewController.inflate(mBaseLayout);
+ mOverlayPanelViewController.setShouldAnimateCollapsePanel(true);
+ mOverlayPanelViewController.setPanelExpanded(true);
+ mOverlayPanelViewController.setPanelVisible(true);
+
+ mOverlayPanelViewController.animateCollapsePanel();
+
+ assertThat(mOverlayPanelViewController.mOnAnimateCollapsePanelCalled).isTrue();
+ }
+
+ @Test
+ public void animateCollapsePanel_removesWindowFocus() {
+ mOverlayPanelViewController.inflate(mBaseLayout);
+ mOverlayPanelViewController.setShouldAnimateCollapsePanel(true);
+ mOverlayPanelViewController.setPanelExpanded(true);
+ mOverlayPanelViewController.setPanelVisible(true);
+
+ mOverlayPanelViewController.animateCollapsePanel();
+
+ verify(mOverlayViewGlobalStateController).setWindowFocusable(false);
+ }
+
+ @Test
+ public void animateExpandPanel_shouldNotAnimateExpandPanel_doesNotExpand() {
+ mOverlayPanelViewController.inflate(mBaseLayout);
+ mOverlayPanelViewController.setShouldAnimateExpandPanel(false);
+
+ mOverlayPanelViewController.animateExpandPanel();
+
+ assertThat(mOverlayPanelViewController.mAnimateExpandPanelCalled).isTrue();
+ assertThat(mOverlayPanelViewController.mOnAnimateExpandPanelCalled).isFalse();
+ }
+
+ @Test
+ public void animateExpandPanel_userNotSetup_doesNotExpand() {
+ mOverlayPanelViewController.inflate(mBaseLayout);
+ mOverlayPanelViewController.setShouldAnimateExpandPanel(true);
+ when(mCarDeviceProvisionedController.isCurrentUserFullySetup()).thenReturn(false);
+
+ mOverlayPanelViewController.animateExpandPanel();
+
+ assertThat(mOverlayPanelViewController.mAnimateExpandPanelCalled).isTrue();
+ assertThat(mOverlayPanelViewController.mOnAnimateExpandPanelCalled).isFalse();
+ }
+
+ @Test
+ public void animateExpandPanel_expands() {
+ mOverlayPanelViewController.inflate(mBaseLayout);
+ mOverlayPanelViewController.setShouldAnimateExpandPanel(true);
+ when(mCarDeviceProvisionedController.isCurrentUserFullySetup()).thenReturn(true);
+
+ mOverlayPanelViewController.animateExpandPanel();
+
+ assertThat(mOverlayPanelViewController.mOnAnimateExpandPanelCalled).isTrue();
+ }
+
+ @Test
+ public void animateExpandPanel_setsPanelVisible() {
+ mOverlayPanelViewController.inflate(mBaseLayout);
+ mOverlayPanelViewController.setShouldAnimateExpandPanel(true);
+ when(mCarDeviceProvisionedController.isCurrentUserFullySetup()).thenReturn(true);
+
+ mOverlayPanelViewController.animateExpandPanel();
+
+ assertThat(mOverlayPanelViewController.isPanelVisible()).isTrue();
+ }
+
+ @Test
+ public void animateExpandPanel_setsPanelExpanded() {
+ mOverlayPanelViewController.inflate(mBaseLayout);
+ mOverlayPanelViewController.setShouldAnimateExpandPanel(true);
+ when(mCarDeviceProvisionedController.isCurrentUserFullySetup()).thenReturn(true);
+
+ mOverlayPanelViewController.animateExpandPanel();
+
+ assertThat(mOverlayPanelViewController.isPanelExpanded()).isTrue();
+ }
+
+ @Test
+ public void setPanelVisible_setTrue_windowNotVisible_setsWindowVisible() {
+ mOverlayPanelViewController.inflate(mBaseLayout);
+ when(mOverlayViewGlobalStateController.isWindowVisible()).thenReturn(false);
+
+ mOverlayPanelViewController.setPanelVisible(true);
+
+ verify(mOverlayViewGlobalStateController).setWindowVisible(true);
+ }
+
+ @Test
+ public void setPanelVisible_setTrue_windowVisible_doesNotSetWindowVisible() {
+ mOverlayPanelViewController.inflate(mBaseLayout);
+ when(mOverlayViewGlobalStateController.isWindowVisible()).thenReturn(true);
+
+ mOverlayPanelViewController.setPanelVisible(true);
+
+ verify(mOverlayViewGlobalStateController, never()).setWindowVisible(true);
+ }
+
+ @Test
+ public void setPanelVisible_setTrue_setLayoutVisible() {
+ mOverlayPanelViewController.inflate(mBaseLayout);
+ mOverlayPanelViewController.getLayout().setVisibility(View.INVISIBLE);
+
+ mOverlayPanelViewController.setPanelVisible(true);
+
+ assertThat(mOverlayPanelViewController.getLayout().getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void setPanelVisible_setTrue_setWindowFocusable() {
+ mOverlayPanelViewController.inflate(mBaseLayout);
+ mOverlayPanelViewController.setPanelVisible(true);
+
+ verify(mOverlayViewGlobalStateController).setWindowFocusable(true);
+ }
+
+ @Test
+ public void setPanelVisible_setFalse_windowVisible_setsWindowNotVisible() {
+ mOverlayPanelViewController.inflate(mBaseLayout);
+ when(mOverlayViewGlobalStateController.isWindowVisible()).thenReturn(true);
+
+ mOverlayPanelViewController.setPanelVisible(false);
+
+ verify(mOverlayViewGlobalStateController).setWindowVisible(false);
+ }
+
+ @Test
+ public void setPanelVisible_setFalse_windowNotVisible_doesNotSetWindowNotVisible() {
+ mOverlayPanelViewController.inflate(mBaseLayout);
+ when(mOverlayViewGlobalStateController.isWindowVisible()).thenReturn(false);
+
+ mOverlayPanelViewController.setPanelVisible(false);
+
+ verify(mOverlayViewGlobalStateController, never()).setWindowVisible(false);
+ }
+
+ @Test
+ public void setPanelVisible_setFalse_setLayoutInvisible() {
+ mOverlayPanelViewController.inflate(mBaseLayout);
+ mOverlayPanelViewController.getLayout().setVisibility(View.VISIBLE);
+
+ mOverlayPanelViewController.setPanelVisible(false);
+
+ assertThat(mOverlayPanelViewController.getLayout().getVisibility()).isEqualTo(
+ View.INVISIBLE);
+ }
+
+ @Test
+ public void setPanelVisible_setFalse_setWindowNotFocusable() {
+ mOverlayPanelViewController.inflate(mBaseLayout);
+
+ mOverlayPanelViewController.setPanelVisible(false);
+
+ verify(mOverlayViewGlobalStateController).setWindowFocusable(false);
+ }
+
+ @Test
+ public void dragOpenTouchListener_isNotInflated_inflatesView() {
+ when(mCarDeviceProvisionedController.isCurrentUserFullySetup()).thenReturn(true);
+ assertThat(mOverlayPanelViewController.isInflated()).isFalse();
+
+ mOverlayPanelViewController.getDragOpenTouchListener().onTouch(/* v= */ null,
+ MotionEvent.obtain(/* downTime= */ 200, /* eventTime= */ 300,
+ MotionEvent.ACTION_MOVE, /* x= */ 0, /* y= */ 0, /* metaState= */ 0));
+
+ verify(mOverlayViewGlobalStateController).inflateView(mOverlayPanelViewController);
+ }
+
+ private static class TestOverlayPanelViewController extends OverlayPanelViewController {
+
+ private boolean mShouldAnimateCollapsePanel;
+ private boolean mShouldAnimateExpandPanel;
+ private boolean mShouldAllowClosingScroll;
+
+ boolean mOnAnimateCollapsePanelCalled;
+ boolean mAnimateCollapsePanelCalled;
+ boolean mOnAnimateExpandPanelCalled;
+ boolean mAnimateExpandPanelCalled;
+ boolean mOnCollapseAnimationEndCalled;
+ boolean mOnExpandAnimationEndCalled;
+ boolean mOnOpenScrollStartEnd;
+ List<Integer> mOnScrollHeights;
+
+ TestOverlayPanelViewController(
+ Context context,
+ Resources resources,
+ int stubId,
+ OverlayViewGlobalStateController overlayViewGlobalStateController,
+ FlingAnimationUtils.Builder flingAnimationUtilsBuilder,
+ CarDeviceProvisionedController carDeviceProvisionedController) {
+ super(context, resources, stubId, overlayViewGlobalStateController,
+ flingAnimationUtilsBuilder,
+ carDeviceProvisionedController);
+
+ mOnScrollHeights = new ArrayList<>();
+ }
+
+ public void setShouldAnimateCollapsePanel(boolean shouldAnimate) {
+ mShouldAnimateCollapsePanel = shouldAnimate;
+ }
+
+ @Override
+ protected boolean shouldAnimateCollapsePanel() {
+ return mShouldAnimateCollapsePanel;
+ }
+
+ @Override
+ protected void animateCollapsePanel() {
+ super.animateCollapsePanel();
+ mAnimateCollapsePanelCalled = true;
+ }
+
+ @Override
+ protected void onAnimateCollapsePanel() {
+ mOnAnimateCollapsePanelCalled = true;
+ }
+
+ public void setShouldAnimateExpandPanel(boolean shouldAnimate) {
+ mShouldAnimateExpandPanel = shouldAnimate;
+ }
+
+ @Override
+ protected boolean shouldAnimateExpandPanel() {
+ return mShouldAnimateExpandPanel;
+ }
+
+ @Override
+ protected void animateExpandPanel() {
+ super.animateExpandPanel();
+ mAnimateExpandPanelCalled = true;
+ }
+
+ @Override
+ protected void onAnimateExpandPanel() {
+ mOnAnimateExpandPanelCalled = true;
+ }
+
+ @Override
+ protected void onCollapseAnimationEnd() {
+ mOnCollapseAnimationEndCalled = true;
+ }
+
+ @Override
+ protected void onExpandAnimationEnd() {
+ mOnExpandAnimationEndCalled = true;
+ }
+
+ @Override
+ protected void onScroll(int height) {
+ mOnScrollHeights.add(height);
+ }
+
+ @Override
+ protected void onOpenScrollStart() {
+ mOnOpenScrollStartEnd = true;
+ }
+
+ public void setShouldAllowClosingScroll(boolean shouldAllow) {
+ mShouldAllowClosingScroll = shouldAllow;
+ }
+
+ @Override
+ protected boolean shouldAllowClosingScroll() {
+ return mShouldAllowClosingScroll;
+ }
+ }
+}
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/window/OverlayViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/window/OverlayViewControllerTest.java
index 3be9626..3313261 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/window/OverlayViewControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/window/OverlayViewControllerTest.java
@@ -43,7 +43,7 @@
@TestableLooper.RunWithLooper
@SmallTest
public class OverlayViewControllerTest extends SysuiTestCase {
- private MockOverlayViewController mOverlayViewController;
+ private TestOverlayViewController mOverlayViewController;
private ViewGroup mBaseLayout;
@Mock
@@ -56,7 +56,7 @@
public void setUp() {
MockitoAnnotations.initMocks(/* testClass= */ this);
- mOverlayViewController = new MockOverlayViewController(R.id.overlay_view_controller_stub,
+ mOverlayViewController = new TestOverlayViewController(R.id.overlay_view_controller_stub,
mOverlayViewGlobalStateController);
mBaseLayout = (ViewGroup) LayoutInflater.from(mContext).inflate(
@@ -130,12 +130,12 @@
assertThat(mOverlayViewController.mHideInternalCalled).isFalse();
}
- private static class MockOverlayViewController extends OverlayViewController {
+ private static class TestOverlayViewController extends OverlayViewController {
boolean mOnFinishInflateCalled = false;
boolean mShowInternalCalled = false;
boolean mHideInternalCalled = false;
- MockOverlayViewController(int stubId,
+ TestOverlayViewController(int stubId,
OverlayViewGlobalStateController overlayViewGlobalStateController) {
super(stubId, overlayViewGlobalStateController);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
index 8aa0aec..a53bc9f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
@@ -102,12 +102,11 @@
// Turn off divider
view.findViewById(R.id.two_target_divider).setVisibility(View.INVISIBLE);
- // Enable the icon button when this Entry is a canManageSubscription entry.
+ // Enable the icon button when the help string in this WifiEntry is not null.
final ImageButton imageButton = (ImageButton) view.findViewById(R.id.icon_button);
final ImageView frictionImageView = (ImageView) view.findViewById(
R.id.friction_icon);
- if (mWifiEntry.canManageSubscription() && !mWifiEntry.isSaved()
- && !mWifiEntry.isSubscription()
+ if (mWifiEntry.getHelpUriString() != null
&& mWifiEntry.getConnectedState() == WifiEntry.CONNECTED_STATE_DISCONNECTED) {
final Drawable drawablehelp = getDrawable(R.drawable.ic_help);
drawablehelp.setTintList(
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java
index a9f31ce..46e699d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java
@@ -64,7 +64,7 @@
private static final String MOCK_TITLE = "title";
private static final String MOCK_SUMMARY = "summary";
-
+ private static final String FAKE_URI_STRING = "fakeuri";
@Before
public void setUp() {
@@ -155,22 +155,23 @@
}
@Test
- public void canManageSubscription_shouldSetImageButtonVisible() {
- when(mMockWifiEntry.canManageSubscription()).thenReturn(true);
+ public void notNull_whenGetHelpUriString_shouldSetImageButtonVisible() {
+ when(mMockWifiEntry.getHelpUriString()).thenReturn(FAKE_URI_STRING);
final WifiEntryPreference pref =
new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
final LayoutInflater inflater = LayoutInflater.from(mContext);
final View view = inflater.inflate(pref.getLayoutResource(), new LinearLayout(mContext),
false);
final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(view);
+
pref.onBindViewHolder(holder);
assertThat(view.findViewById(R.id.icon_button).getVisibility()).isEqualTo(View.VISIBLE);
}
@Test
- public void helpButton_whenCanManageSubscription_shouldSetCorrectContentDescription() {
- when(mMockWifiEntry.canManageSubscription()).thenReturn(true);
+ public void helpButton_whenGetHelpUriStringNotNull_shouldSetCorrectContentDescription() {
+ when(mMockWifiEntry.getHelpUriString()).thenReturn(FAKE_URI_STRING);
final WifiEntryPreference pref =
new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
final LayoutInflater inflater = LayoutInflater.from(mContext);
diff --git a/packages/SystemUI/res/layout/qs_media_panel.xml b/packages/SystemUI/res/layout/qs_media_panel.xml
index fc3bf94..e5ac5f8 100644
--- a/packages/SystemUI/res/layout/qs_media_panel.xml
+++ b/packages/SystemUI/res/layout/qs_media_panel.xml
@@ -23,7 +23,8 @@
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center_horizontal|fill_vertical"
- android:padding="16dp"
+ android:paddingTop="@dimen/qs_media_panel_outer_padding"
+ android:paddingBottom="@dimen/qs_media_panel_outer_padding"
android:background="@drawable/qs_media_background"
>
@@ -42,7 +43,9 @@
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="16dp"
+ android:layout_marginBottom="@dimen/qs_media_panel_outer_padding"
+ android:paddingStart="@dimen/qs_media_panel_outer_padding"
+ android:paddingEnd="16dp"
>
<ImageView
@@ -139,6 +142,7 @@
<!-- Seek Bar -->
<SeekBar
android:id="@+id/media_progress_bar"
+ style="@android:style/Widget.ProgressBar.Horizontal"
android:clickable="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -154,6 +158,9 @@
android:id="@+id/notification_media_progress_time"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:paddingStart="@dimen/qs_media_panel_outer_padding"
+ android:paddingEnd="@dimen/qs_media_panel_outer_padding"
+ android:layout_marginBottom="10dp"
android:layout_gravity="center"
>
<!-- width is set to "match_parent" to avoid extra layout calls -->
@@ -184,6 +191,8 @@
android:layoutDirection="ltr"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:paddingStart="@dimen/qs_media_panel_outer_padding"
+ android:paddingEnd="@dimen/qs_media_panel_outer_padding"
android:gravity="center"
>
<ImageButton
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index bce5fac..344479f 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1211,6 +1211,7 @@
<!-- Size of media cards in the QSPanel carousel -->
<dimen name="qs_media_width">350dp</dimen>
<dimen name="qs_media_padding">8dp</dimen>
+ <dimen name="qs_media_panel_outer_padding">16dp</dimen>
<dimen name="qs_media_corner_radius">10dp</dimen>
<dimen name="qs_media_album_size">72dp</dimen>
<dimen name="qs_seamless_icon_size">20dp</dimen>
diff --git a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
index 7eb5a8f..e99245f 100644
--- a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
+++ b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
@@ -34,6 +34,8 @@
import com.android.internal.annotations.VisibleForTesting;
+import java.util.NoSuchElementException;
+
/**
* Encapsulates all logic for secondary lockscreen state management.
*/
@@ -79,7 +81,9 @@
private final IKeyguardCallback mCallback = new IKeyguardCallback.Stub() {
@Override
public void onDismiss() {
- dismiss(UserHandle.getCallingUserId());
+ mHandler.post(() -> {
+ dismiss(UserHandle.getCallingUserId());
+ });
}
@Override
@@ -91,7 +95,9 @@
if (surfacePackage != null) {
mView.setChildSurfacePackage(surfacePackage);
} else {
- dismiss(KeyguardUpdateMonitor.getCurrentUser());
+ mHandler.post(() -> {
+ dismiss(KeyguardUpdateMonitor.getCurrentUser());
+ });
}
}
};
@@ -122,6 +128,7 @@
// If the remote content is not readied within the timeout period,
// move on without the secondary lockscreen.
dismiss(userId);
+ Log.w(TAG, "Timed out waiting for secondary lockscreen content.");
},
REMOTE_CONTENT_READY_TIMEOUT_MILLIS);
}
@@ -150,8 +157,12 @@
* Displays the Admin security Surface view.
*/
public void show(Intent serviceIntent) {
- mContext.bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE);
- mParent.addView(mView);
+ if (mClient == null) {
+ mContext.bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE);
+ }
+ if (!mView.isAttachedToWindow()) {
+ mParent.addView(mView);
+ }
}
/**
@@ -162,7 +173,11 @@
mParent.removeView(mView);
}
if (mClient != null) {
- mClient.asBinder().unlinkToDeath(mKeyguardClientDeathRecipient, 0);
+ try {
+ mClient.asBinder().unlinkToDeath(mKeyguardClientDeathRecipient, 0);
+ } catch (NoSuchElementException e) {
+ Log.w(TAG, "IKeyguardClient death recipient already released");
+ }
mContext.unbindService(mConnection);
mClient = null;
}
@@ -185,10 +200,12 @@
private void dismiss(int userId) {
mHandler.removeCallbacksAndMessages(null);
- if (mView != null && mView.isAttachedToWindow()
- && userId == KeyguardUpdateMonitor.getCurrentUser()) {
+ if (mView.isAttachedToWindow() && userId == KeyguardUpdateMonitor.getCurrentUser()) {
hide();
- mKeyguardCallback.dismiss(true, userId);
+ if (mKeyguardCallback != null) {
+ mKeyguardCallback.dismiss(/* securityVerified= */ true, userId,
+ /* bypassSecondaryLockScreen= */true);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java
index d5a08dd..aa2fe3c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java
@@ -85,7 +85,8 @@
// the user proved presence via some other way to the trust agent.
Log.i(TAG, "TrustAgent dismissed Keyguard.");
}
- dismiss(false /* authenticated */, userId);
+ dismiss(false /* authenticated */, userId,
+ /* bypassSecondaryLockScreen */ false);
} else {
mViewMediatorCallback.playTrustedSound();
}
@@ -190,7 +191,7 @@
* @return True if the keyguard is done.
*/
public boolean dismiss(int targetUserId) {
- return dismiss(false, targetUserId);
+ return dismiss(false, targetUserId, false);
}
public boolean handleBackKey() {
@@ -206,8 +207,10 @@
}
@Override
- public boolean dismiss(boolean authenticated, int targetUserId) {
- return mSecurityContainer.showNextSecurityScreenOrFinish(authenticated, targetUserId);
+ public boolean dismiss(boolean authenticated, int targetUserId,
+ boolean bypassSecondaryLockScreen) {
+ return mSecurityContainer.showNextSecurityScreenOrFinish(authenticated, targetUserId,
+ bypassSecondaryLockScreen);
}
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java
index 49dcfff..e384727 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java
@@ -25,6 +25,15 @@
void dismiss(boolean securityVerified, int targetUserId);
/**
+ * Dismiss the given security screen.
+ * @param securityVerified true if the user correctly entered credentials for the given screen.
+ * @param targetUserId a user that needs to be the foreground user at the dismissal completion.
+ * @param bypassSecondaryLockScreen true if the user can bypass the secondary lock screen,
+ * if any, during this dismissal.
+ */
+ void dismiss(boolean securityVerified, int targetUserId, boolean bypassSecondaryLockScreen);
+
+ /**
* Manually report user activity to keep the device awake.
*/
void userActivity();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index ba8a1a9..1e1ce4e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -115,7 +115,8 @@
// Used to notify the container when something interesting happens.
public interface SecurityCallback {
- public boolean dismiss(boolean authenticated, int targetUserId);
+ public boolean dismiss(boolean authenticated, int targetUserId,
+ boolean bypassSecondaryLockScreen);
public void userActivity();
public void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput);
@@ -504,9 +505,12 @@
* @param authenticated true if the user entered the correct authentication
* @param targetUserId a user that needs to be the foreground user at the finish (if called)
* completion.
+ * @param bypassSecondaryLockScreen true if the user is allowed to bypass the secondary
+ * secondary lock screen requirement, if any.
* @return true if keyguard is done
*/
- boolean showNextSecurityScreenOrFinish(boolean authenticated, int targetUserId) {
+ boolean showNextSecurityScreenOrFinish(boolean authenticated, int targetUserId,
+ boolean bypassSecondaryLockScreen) {
if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")");
boolean finish = false;
boolean strongAuth = false;
@@ -555,7 +559,7 @@
}
}
// Check for device admin specified additional security measures.
- if (finish) {
+ if (finish && !bypassSecondaryLockScreen) {
Intent secondaryLockscreenIntent =
mUpdateMonitor.getSecondaryLockscreenRequirement(targetUserId);
if (secondaryLockscreenIntent != null) {
@@ -636,8 +640,15 @@
mUpdateMonitor.cancelFaceAuth();
}
+ @Override
public void dismiss(boolean authenticated, int targetId) {
- mSecurityCallback.dismiss(authenticated, targetId);
+ dismiss(authenticated, targetId, /* bypassSecondaryLockScreen */ false);
+ }
+
+ @Override
+ public void dismiss(boolean authenticated, int targetId,
+ boolean bypassSecondaryLockScreen) {
+ mSecurityCallback.dismiss(authenticated, targetId, bypassSecondaryLockScreen);
}
public boolean isVerifyUnlockOnly() {
@@ -689,6 +700,9 @@
@Override
public void dismiss(boolean securityVerified, int targetUserId) { }
@Override
+ public void dismiss(boolean authenticated, int targetId,
+ boolean bypassSecondaryLockScreen) { }
+ @Override
public void onUserInput() { }
@Override
public void reset() {}
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceLifetimeExtender.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceLifetimeExtender.java
index 362014f..e17d4e6 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceLifetimeExtender.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceLifetimeExtender.java
@@ -25,6 +25,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.statusbar.NotificationLifetimeExtender;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.util.time.SystemClock;
/**
* Extends the lifetime of foreground notification services such that they show for at least
@@ -39,8 +40,10 @@
private NotificationSafeToRemoveCallback mNotificationSafeToRemoveCallback;
private ArraySet<NotificationEntry> mManagedEntries = new ArraySet<>();
private Handler mHandler = new Handler(Looper.getMainLooper());
+ private final SystemClock mSystemClock;
- public ForegroundServiceLifetimeExtender() {
+ public ForegroundServiceLifetimeExtender(SystemClock systemClock) {
+ mSystemClock = systemClock;
}
@Override
@@ -55,8 +58,8 @@
return false;
}
- long currentTime = System.currentTimeMillis();
- return currentTime - entry.getSbn().getPostTime() < MIN_FGS_TIME_MS;
+ long currentTime = mSystemClock.uptimeMillis();
+ return currentTime - entry.getCreationTime() < MIN_FGS_TIME_MS;
}
@Override
@@ -84,7 +87,7 @@
}
};
long delayAmt = MIN_FGS_TIME_MS
- - (System.currentTimeMillis() - entry.getSbn().getPostTime());
+ - (mSystemClock.uptimeMillis() - entry.getCreationTime());
mHandler.postDelayed(r, delayAmt);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
index f01fa81..ef1f4e0 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
@@ -30,6 +30,7 @@
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.util.time.SystemClock;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -49,7 +50,8 @@
public ForegroundServiceNotificationListener(Context context,
ForegroundServiceController foregroundServiceController,
NotificationEntryManager notificationEntryManager,
- NotifPipeline notifPipeline) {
+ NotifPipeline notifPipeline,
+ SystemClock systemClock) {
mContext = context;
mForegroundServiceController = foregroundServiceController;
@@ -76,7 +78,8 @@
removeNotification(entry.getSbn());
}
});
- mEntryManager.addNotificationLifetimeExtender(new ForegroundServiceLifetimeExtender());
+ mEntryManager.addNotificationLifetimeExtender(
+ new ForegroundServiceLifetimeExtender(systemClock));
notifPipeline.addCollectionListener(new NotifCollectionListener() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 9d885fd..99e5eb6 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -719,13 +719,6 @@
}
/**
- * Tell the stack of bubbles to expand.
- */
- public void expandStack() {
- mBubbleData.setExpanded(true);
- }
-
- /**
* Tell the stack of bubbles to collapse.
*/
public void collapseStack() {
@@ -753,12 +746,6 @@
return (isSummary && isSuppressedSummary) || isBubbleAndSuppressed;
}
- @VisibleForTesting
- void selectBubble(String key) {
- Bubble bubble = mBubbleData.getBubbleWithKey(key);
- mBubbleData.setSelectedBubble(bubble);
- }
-
void promoteBubbleFromOverflow(Bubble bubble) {
bubble.setInflateSynchronously(mInflateSynchronously);
mBubbleData.promoteBubbleFromOverflow(bubble, mStackView, mBubbleIconFactory);
@@ -778,13 +765,6 @@
}
/**
- * Tell the stack of bubbles to be dismissed, this will remove all of the bubbles in the stack.
- */
- void dismissStack(@DismissReason int reason) {
- mBubbleData.dismissAll(reason);
- }
-
- /**
* Directs a back gesture at the bubble stack. When opened, the current expanded bubble
* is forwarded a back key down/up pair.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index be9cd5f..4c149dd 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -28,6 +28,7 @@
import android.service.notification.NotificationListenerService;
import android.util.Log;
import android.util.Pair;
+import android.view.View;
import androidx.annotation.Nullable;
@@ -751,6 +752,7 @@
}
@VisibleForTesting(visibility = PRIVATE)
+ @Nullable
Bubble getBubbleWithKey(String key) {
for (int i = 0; i < mBubbles.size(); i++) {
Bubble bubble = mBubbles.get(i);
@@ -761,6 +763,17 @@
return null;
}
+ @Nullable
+ Bubble getBubbleWithView(View view) {
+ for (int i = 0; i < mBubbles.size(); i++) {
+ Bubble bubble = mBubbles.get(i);
+ if (bubble.getIconView() != null && bubble.getIconView().equals(view)) {
+ return bubble;
+ }
+ }
+ return null;
+ }
+
@VisibleForTesting(visibility = PRIVATE)
Bubble getOverflowBubbleWithKey(String key) {
for (int i = 0; i < mOverflowBubbles.size(); i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 496456d..93fb697 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -16,6 +16,7 @@
package com.android.systemui.bubbles;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.view.Display.INVALID_DISPLAY;
@@ -126,6 +127,7 @@
ActivityOptions options = ActivityOptions.makeCustomAnimation(getContext(),
0 /* enterResId */, 0 /* exitResId */);
options.setTaskAlwaysOnTop(true);
+ options.setLaunchWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
// Post to keep the lifecycle normal
post(() -> {
if (DEBUG_BUBBLE_EXPANDED_VIEW) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 044feaa..644e54f 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -30,6 +30,7 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
+import android.annotation.SuppressLint;
import android.app.Notification;
import android.content.Context;
import android.content.res.Configuration;
@@ -43,6 +44,7 @@
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.graphics.Region;
import android.os.Bundle;
import android.os.Vibrator;
import android.util.Log;
@@ -83,6 +85,7 @@
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.util.DismissCircleView;
import com.android.systemui.util.FloatingContentCoordinator;
+import com.android.systemui.util.RelativeTouchListener;
import com.android.systemui.util.animation.PhysicsAnimator;
import com.android.systemui.util.magnetictarget.MagnetizedObject;
@@ -239,7 +242,6 @@
mExpandedAnimationController.dump(fd, pw, args);
}
- private BubbleTouchHandler mTouchHandler;
private BubbleController.BubbleExpandListener mExpandListener;
private SysUiState mSysUiState;
@@ -296,7 +298,7 @@
@Override
public void setValue(Object o, float v) {
- onFlyoutDragged(v);
+ setFlyoutStateForDragLength(v);
}
};
@@ -337,13 +339,6 @@
private MagnetizedObject<?> mMagnetizedObject;
/**
- * The action to run when the magnetized object is released in the dismiss target.
- *
- * This will actually perform the dismissal of either the stack or an individual bubble.
- */
- private Runnable mReleasedInDismissTargetAction;
-
- /**
* The MagneticTarget instance for our circular dismiss view. This is added to the
* MagnetizedObject instances for the stack and any dragged-out bubbles.
*/
@@ -377,7 +372,7 @@
public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) {
mExpandedAnimationController.dismissDraggedOutBubble(
mExpandedAnimationController.getDraggedOutBubble(),
- mReleasedInDismissTargetAction);
+ BubbleStackView.this::dismissMagnetizedObject);
hideDismissTarget();
}
};
@@ -410,7 +405,7 @@
mStackAnimationController.implodeStack(
() -> {
resetDesaturationAndDarken();
- mReleasedInDismissTargetAction.run();
+ dismissMagnetizedObject();
}
);
@@ -418,6 +413,197 @@
}
};
+ /**
+ * Click listener set on each bubble view. When collapsed, clicking a bubble expands the stack.
+ * When expanded, clicking a bubble either expands that bubble, or collapses the stack.
+ */
+ private OnClickListener mBubbleClickListener = new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ final Bubble clickedBubble = mBubbleData.getBubbleWithView(view);
+
+ // If the bubble has since left us, ignore the click.
+ if (clickedBubble == null) {
+ return;
+ }
+
+ final boolean clickedBubbleIsCurrentlyExpandedBubble =
+ clickedBubble.getKey().equals(mExpandedBubble.getKey());
+
+ if (isExpanded() && !clickedBubbleIsCurrentlyExpandedBubble) {
+ if (clickedBubble != mBubbleData.getSelectedBubble()) {
+ // Select the clicked bubble.
+ mBubbleData.setSelectedBubble(clickedBubble);
+ } else {
+ // If the clicked bubble is the selected bubble (but not the expanded bubble),
+ // that means overflow was previously expanded. Set the selected bubble
+ // internally without going through BubbleData (which would ignore it since it's
+ // already selected).
+ setSelectedBubble(clickedBubble);
+
+ }
+ } else {
+ // Otherwise, we either tapped the stack (which means we're collapsed
+ // and should expand) or the currently selected bubble (we're expanded
+ // and should collapse).
+ if (!maybeShowStackUserEducation()) {
+ mBubbleData.setExpanded(!mBubbleData.isExpanded());
+ }
+ }
+ }
+ };
+
+ /**
+ * Touch listener set on each bubble view. This enables dragging and dismissing the stack (when
+ * collapsed), or individual bubbles (when expanded).
+ */
+ private RelativeTouchListener mBubbleTouchListener = new RelativeTouchListener() {
+
+ @Override
+ public boolean onDown(@NonNull View v, @NonNull MotionEvent ev) {
+ // If we're expanding or collapsing, consume but ignore all touch events.
+ if (mIsExpansionAnimating) {
+ return true;
+ }
+
+ if (mBubbleData.isExpanded()) {
+ maybeShowManageEducation(false /* show */);
+
+ // If we're expanded, tell the animation controller to prepare to drag this bubble,
+ // dispatching to the individual bubble magnet listener.
+ mExpandedAnimationController.prepareForBubbleDrag(
+ v /* bubble */,
+ mMagneticTarget,
+ mIndividualBubbleMagnetListener);
+
+ // Save the magnetized individual bubble so we can dispatch touch events to it.
+ mMagnetizedObject = mExpandedAnimationController.getMagnetizedBubbleDraggingOut();
+ } else {
+ // If we're collapsed, prepare to drag the stack. Cancel active animations, set the
+ // animation controller, and hide the flyout.
+ mStackAnimationController.cancelStackPositionAnimations();
+ mBubbleContainer.setActiveController(mStackAnimationController);
+ hideFlyoutImmediate();
+
+ // Also, save the magnetized stack so we can dispatch touch events to it.
+ mMagnetizedObject = mStackAnimationController.getMagnetizedStack(mMagneticTarget);
+ mMagnetizedObject.setMagnetListener(mStackMagnetListener);
+ }
+
+ passEventToMagnetizedObject(ev);
+
+ // Bubbles are always interested in all touch events!
+ return true;
+ }
+
+ @Override
+ public void onMove(@NonNull View v, @NonNull MotionEvent ev, float viewInitialX,
+ float viewInitialY, float dx, float dy) {
+ // If we're expanding or collapsing, ignore all touch events.
+ if (mIsExpansionAnimating) {
+ return;
+ }
+
+ // Show the dismiss target, if we haven't already.
+ springInDismissTargetMaybe();
+
+ // First, see if the magnetized object consumes the event - if so, we shouldn't move the
+ // bubble since it's stuck to the target.
+ if (!passEventToMagnetizedObject(ev)) {
+ if (mBubbleData.isExpanded()) {
+ mExpandedAnimationController.dragBubbleOut(
+ v, viewInitialX + dx, viewInitialY + dy);
+ } else {
+ hideStackUserEducation(false /* fromExpansion */);
+ mStackAnimationController.moveStackFromTouch(
+ viewInitialX + dx, viewInitialY + dy);
+ }
+ }
+ }
+
+ @Override
+ public void onUp(@NonNull View v, @NonNull MotionEvent ev, float viewInitialX,
+ float viewInitialY, float dx, float dy, float velX, float velY) {
+ // If we're expanding or collapsing, ignore all touch events.
+ if (mIsExpansionAnimating) {
+ return;
+ }
+
+ // First, see if the magnetized object consumes the event - if so, the bubble was
+ // released in the target or flung out of it, and we should ignore the event.
+ if (!passEventToMagnetizedObject(ev)) {
+ if (mBubbleData.isExpanded()) {
+ mExpandedAnimationController.snapBubbleBack(v, velX, velY);
+ } else {
+ // Fling the stack to the edge, and save whether or not it's going to end up on
+ // the left side of the screen.
+ mStackOnLeftOrWillBe =
+ mStackAnimationController.flingStackThenSpringToEdge(
+ viewInitialX + dx, velX, velY) <= 0;
+
+ updateBubbleZOrdersAndDotPosition(true /* animate */);
+
+ logBubbleEvent(null /* no bubble associated with bubble stack move */,
+ SysUiStatsLog.BUBBLE_UICHANGED__ACTION__STACK_MOVED);
+ }
+
+ hideDismissTarget();
+ }
+ }
+ };
+
+ /** Click listener set on the flyout, which expands the stack when the flyout is tapped. */
+ private OnClickListener mFlyoutClickListener = new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ if (maybeShowStackUserEducation()) {
+ // If we're showing user education, don't open the bubble show the education first
+ mBubbleToExpandAfterFlyoutCollapse = null;
+ } else {
+ mBubbleToExpandAfterFlyoutCollapse = mBubbleData.getSelectedBubble();
+ }
+
+ mFlyout.removeCallbacks(mHideFlyout);
+ mHideFlyout.run();
+ }
+ };
+
+ /** Touch listener for the flyout. This enables the drag-to-dismiss gesture on the flyout. */
+ private RelativeTouchListener mFlyoutTouchListener = new RelativeTouchListener() {
+
+ @Override
+ public boolean onDown(@NonNull View v, @NonNull MotionEvent ev) {
+ mFlyout.removeCallbacks(mHideFlyout);
+ return true;
+ }
+
+ @Override
+ public void onMove(@NonNull View v, @NonNull MotionEvent ev, float viewInitialX,
+ float viewInitialY, float dx, float dy) {
+ setFlyoutStateForDragLength(dx);
+ }
+
+ @Override
+ public void onUp(@NonNull View v, @NonNull MotionEvent ev, float viewInitialX,
+ float viewInitialY, float dx, float dy, float velX, float velY) {
+ final boolean onLeft = mStackAnimationController.isStackOnLeftSide();
+ final boolean metRequiredVelocity =
+ onLeft ? velX < -FLYOUT_DISMISS_VELOCITY : velX > FLYOUT_DISMISS_VELOCITY;
+ final boolean metRequiredDeltaX =
+ onLeft
+ ? dx < -mFlyout.getWidth() * FLYOUT_DRAG_PERCENT_DISMISS
+ : dx > mFlyout.getWidth() * FLYOUT_DRAG_PERCENT_DISMISS;
+ final boolean isCancelFling = onLeft ? velX > 0 : velX < 0;
+ final boolean shouldDismiss = metRequiredVelocity
+ || (metRequiredDeltaX && !isCancelFling);
+
+ mFlyout.removeCallbacks(mHideFlyout);
+ animateFlyoutCollapsed(shouldDismiss, velX);
+
+ maybeShowStackUserEducation();
+ }
+ };
+
private ViewGroup mDismissTargetContainer;
private PhysicsAnimator<View> mDismissTargetAnimator;
private PhysicsAnimator.SpringConfig mDismissTargetSpring = new PhysicsAnimator.SpringConfig(
@@ -436,6 +622,7 @@
private BubbleManageEducationView mManageEducationView;
private boolean mAnimatingManageEducationAway;
+ @SuppressLint("ClickableViewAccessibility")
public BubbleStackView(Context context, BubbleData data,
@Nullable SurfaceSynchronizer synchronizer,
FloatingContentCoordinator floatingContentCoordinator,
@@ -444,8 +631,6 @@
mBubbleData = data;
mInflater = LayoutInflater.from(context);
- mTouchHandler = new BubbleTouchHandler(this, data, context);
- setOnTouchListener(mTouchHandler);
mSysUiState = sysUiState;
@@ -641,6 +826,18 @@
mDesaturateAndDarkenPaint.setColorFilter(new ColorMatrixColorFilter(animatedMatrix));
mDesaturateAndDarkenTargetView.setLayerPaint(mDesaturateAndDarkenPaint);
});
+
+ // If the stack itself is touched, it means none of its touchable views (bubbles, flyouts,
+ // ActivityViews, etc.) were touched. Collapse the stack if it's expanded.
+ setOnTouchListener((view, ev) -> {
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ if (mBubbleData.isExpanded()) {
+ mBubbleData.setExpanded(false);
+ }
+ }
+
+ return false;
+ });
}
private void setUpUserEducation() {
@@ -690,6 +887,7 @@
}
}
+ @SuppressLint("ClickableViewAccessibility")
private void setUpFlyout() {
if (mFlyout != null) {
removeView(mFlyout);
@@ -699,6 +897,8 @@
mFlyout.animate()
.setDuration(FLYOUT_ALPHA_ANIMATION_DURATION)
.setInterpolator(new AccelerateDecelerateInterpolator());
+ mFlyout.setOnClickListener(mFlyoutClickListener);
+ mFlyout.setOnTouchListener(mFlyoutTouchListener);
addView(mFlyout, new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
}
@@ -718,6 +918,7 @@
mBubbleContainer.addView(mBubbleOverflow.getBtn(), overflowBtnIndex,
new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
+ mBubbleOverflow.getBtn().setOnClickListener(v -> setSelectedBubble(mBubbleOverflow));
}
/**
* Handle theme changes.
@@ -920,6 +1121,7 @@
}
// via BubbleData.Listener
+ @SuppressLint("ClickableViewAccessibility")
void addBubble(Bubble bubble) {
if (DEBUG_BUBBLE_STACK_VIEW) {
Log.d(TAG, "addBubble: " + bubble);
@@ -944,6 +1146,9 @@
bubble.getIconView().setDotPositionOnLeft(
!mStackOnLeftOrWillBe /* onLeft */, false /* animate */);
+ bubble.getIconView().setOnClickListener(mBubbleClickListener);
+ bubble.getIconView().setOnTouchListener(mBubbleTouchListener);
+
mBubbleContainer.addView(bubble.getIconView(), 0,
new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
ViewClippingUtil.setClippingDeactivated(bubble.getIconView(), true, mClippingParameters);
@@ -1009,10 +1214,6 @@
updatePointerPosition();
}
- void showOverflow() {
- setSelectedBubble(mBubbleOverflow);
- }
-
/**
* Changes the currently selected bubble. If the stack is already expanded, the newly selected
* bubble will be shown immediately. This does not change the expanded state or change the
@@ -1177,14 +1378,6 @@
}
}
- /*
- * Sets the action to run to dismiss the currently dragging object (either the stack or an
- * individual bubble).
- */
- public void setReleasedInDismissTargetAction(Runnable action) {
- mReleasedInDismissTargetAction = action;
- }
-
/**
* Dismiss the stack of bubbles.
*
@@ -1201,54 +1394,6 @@
}
/**
- * @return the view the touch event is on
- */
- @Nullable
- public View getTargetView(MotionEvent event) {
- float x = event.getRawX();
- float y = event.getRawY();
- if (mIsExpanded) {
- if (isIntersecting(mBubbleContainer, x, y)) {
- if (BubbleExperimentConfig.allowBubbleOverflow(mContext)
- && isIntersecting(mBubbleOverflow.getBtn(), x, y)) {
- return mBubbleOverflow.getBtn();
- }
- // Could be tapping or dragging a bubble while expanded
- for (int i = 0; i < getBubbleCount(); i++) {
- BadgedImageView view = (BadgedImageView) mBubbleContainer.getChildAt(i);
- if (isIntersecting(view, x, y)) {
- return view;
- }
- }
- }
- BubbleExpandedView bev = (BubbleExpandedView) mExpandedViewContainer.getChildAt(0);
- if (bev.intersectingTouchableContent((int) x, (int) y)) {
- return bev;
- }
- // Outside of the parts we care about.
- return null;
- } else if (mFlyout.getVisibility() == VISIBLE && isIntersecting(mFlyout, x, y)) {
- return mFlyout;
- } else if (mUserEducationView != null && mUserEducationView.getVisibility() == VISIBLE) {
- View bubbleChild = mBubbleContainer.getChildAt(0);
- if (isIntersecting(bubbleChild, x, y)) {
- return this;
- } else if (isIntersecting(mUserEducationView, x, y)) {
- return mUserEducationView;
- } else {
- return null;
- }
- }
-
- // If it wasn't an individual bubble in the expanded state, or the flyout, it's the stack.
- return this;
- }
-
- View getFlyoutView() {
- return mFlyout;
- }
-
- /**
* @deprecated use {@link #setExpanded(boolean)} and
* {@link BubbleData#setSelectedBubble(Bubble)}
*/
@@ -1385,124 +1530,70 @@
}
}
- /** Called when the collapsed stack is tapped on. */
- void onStackTapped() {
- if (!maybeShowStackUserEducation()) {
- mBubbleData.setExpanded(true);
- }
+ /**
+ * This method is called by {@link android.app.ActivityView} because the BubbleStackView has a
+ * higher Z-index than the ActivityView (so that dragged-out bubbles are visible over the AV).
+ * ActivityView is asking BubbleStackView to subtract the stack's bounds from the provided
+ * touchable region, so that the ActivityView doesn't consume events meant for the stack. Due to
+ * the special nature of ActivityView, it does not respect the standard
+ * {@link #dispatchTouchEvent} and {@link #onInterceptTouchEvent} methods typically used for
+ * this purpose.
+ *
+ * BubbleStackView is MATCH_PARENT, so that bubbles can be positioned via their translation
+ * properties for performance reasons. This means that the default implementation of this method
+ * subtracts the entirety of the screen from the ActivityView's touchable region, resulting in
+ * it not receiving any touch events. This was previously addressed by returning false in the
+ * stack's {@link View#canReceivePointerEvents()} method, but this precluded the use of any
+ * touch handlers in the stack or its child views.
+ *
+ * To support touch handlers, we're overriding this method to leave the ActivityView's touchable
+ * region alone. The only touchable part of the stack that can ever overlap the AV is a
+ * dragged-out bubble that is animating back into the row of bubbles. It's not worth continually
+ * updating the touchable region to allow users to grab a bubble while it completes its ~50ms
+ * animation back to the bubble row.
+ *
+ * NOTE: Any future additions to the stack that obscure the ActivityView region will need their
+ * bounds subtracted here in order to receive touch events.
+ */
+ @Override
+ public void subtractObscuredTouchableRegion(Region touchableRegion, View view) {
+
}
- /** Called when a drag operation on an individual bubble has started. */
- public void onBubbleDragStart(View bubble) {
- if (DEBUG_BUBBLE_STACK_VIEW) {
- Log.d(TAG, "onBubbleDragStart: bubble=" + ((BadgedImageView) bubble).getKey());
- }
-
- if (mBubbleOverflow != null && bubble.equals(mBubbleOverflow.getIconView())) {
- return;
- }
-
- mExpandedAnimationController.prepareForBubbleDrag(bubble, mMagneticTarget);
-
- // We're dragging an individual bubble, so set the magnetized object to the magnetized
- // bubble.
- mMagnetizedObject = mExpandedAnimationController.getMagnetizedBubbleDraggingOut();
- mMagnetizedObject.setMagnetListener(mIndividualBubbleMagnetListener);
-
- maybeShowManageEducation(false);
+ /**
+ * If you're here because you're not receiving touch events on a view that is a descendant of
+ * BubbleStackView, and you think BSV is intercepting them - it's not! You need to subtract the
+ * bounds of the view in question in {@link #subtractObscuredTouchableRegion}. The ActivityView
+ * consumes all touch events within its bounds, even for views like the BubbleStackView that are
+ * above it. It ignores typical view touch handling methods like this one and
+ * dispatchTouchEvent.
+ */
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ return super.onInterceptTouchEvent(ev);
}
- /** Called with the coordinates to which an individual bubble has been dragged. */
- public void onBubbleDragged(View bubble, float x, float y) {
- if (!mIsExpanded || mIsExpansionAnimating
- || (mBubbleOverflow != null && bubble.equals(mBubbleOverflow.getIconView()))) {
- return;
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ boolean dispatched = super.dispatchTouchEvent(ev);
+
+ // If a new bubble arrives while the collapsed stack is being dragged, it will be positioned
+ // at the front of the stack (under the touch position). Subsequent ACTION_MOVE events will
+ // then be passed to the new bubble, which will not consume them since it hasn't received an
+ // ACTION_DOWN yet. Work around this by passing MotionEvents directly to the touch handler
+ // until the current gesture ends with an ACTION_UP event.
+ if (!dispatched && !mIsExpanded && mIsGestureInProgress) {
+ dispatched = mBubbleTouchListener.onTouch(this /* view */, ev);
}
- mExpandedAnimationController.dragBubbleOut(bubble, x, y);
- springInDismissTarget();
+ mIsGestureInProgress =
+ ev.getAction() != MotionEvent.ACTION_UP
+ && ev.getAction() != MotionEvent.ACTION_CANCEL;
+
+ return dispatched;
}
- /** Called when a drag operation on an individual bubble has finished. */
- public void onBubbleDragFinish(
- View bubble, float x, float y, float velX, float velY) {
- if (DEBUG_BUBBLE_STACK_VIEW) {
- Log.d(TAG, "onBubbleDragFinish: bubble=" + bubble);
- }
-
- if (!mIsExpanded || mIsExpansionAnimating
- || (mBubbleOverflow != null && bubble.equals(mBubbleOverflow.getIconView()))) {
- return;
- }
-
- mExpandedAnimationController.snapBubbleBack(bubble, velX, velY);
- hideDismissTarget();
- }
-
- /** Expands the clicked bubble. */
- public void expandBubble(Bubble bubble) {
- if (bubble != null && bubble.equals(mBubbleData.getSelectedBubble())) {
- // If the bubble we're supposed to expand is the selected bubble, that means the
- // overflow bubble is currently expanded. Don't tell BubbleData to set this bubble as
- // selected, since it already is. Just call the stack's setSelectedBubble to expand it.
- setSelectedBubble(bubble);
- } else {
- mBubbleData.setSelectedBubble(bubble);
- }
- }
-
- void onDragStart() {
- if (DEBUG_BUBBLE_STACK_VIEW) {
- Log.d(TAG, "onDragStart()");
- }
- if (mIsExpanded || mIsExpansionAnimating) {
- if (DEBUG_BUBBLE_STACK_VIEW) {
- Log.d(TAG, "mIsExpanded or mIsExpansionAnimating");
- }
- return;
- }
- mStackAnimationController.cancelStackPositionAnimations();
- mBubbleContainer.setActiveController(mStackAnimationController);
- hideFlyoutImmediate();
-
- // Since we're dragging the stack, set the magnetized object to the magnetized stack.
- mMagnetizedObject = mStackAnimationController.getMagnetizedStack(mMagneticTarget);
- mMagnetizedObject.setMagnetListener(mStackMagnetListener);
- }
-
- void onDragged(float x, float y) {
- if (mIsExpanded || mIsExpansionAnimating) {
- return;
- }
-
- hideStackUserEducation(false /* fromExpansion */);
- springInDismissTarget();
- mStackAnimationController.moveStackFromTouch(x, y);
- }
-
- void onDragFinish(float x, float y, float velX, float velY) {
- if (DEBUG_BUBBLE_STACK_VIEW) {
- Log.d(TAG, "onDragFinish");
- }
-
- if (mIsExpanded || mIsExpansionAnimating) {
- return;
- }
-
- final float newStackX = mStackAnimationController.flingStackThenSpringToEdge(x, velX, velY);
- logBubbleEvent(null /* no bubble associated with bubble stack move */,
- SysUiStatsLog.BUBBLE_UICHANGED__ACTION__STACK_MOVED);
-
- mStackOnLeftOrWillBe = newStackX <= 0;
- updateBubbleZOrdersAndDotPosition(true /* animate */);
- hideDismissTarget();
- }
-
- void onFlyoutDragStart() {
- mFlyout.removeCallbacks(mHideFlyout);
- }
-
- void onFlyoutDragged(float deltaX) {
+ void setFlyoutStateForDragLength(float deltaX) {
// This shouldn't happen, but if it does, just wait until the flyout lays out. This method
// is continually called.
if (mFlyout.getWidth() <= 0) {
@@ -1538,61 +1629,29 @@
mFlyout.setTranslationX(mFlyout.getRestingTranslationX() + overscrollTranslation);
}
- void onFlyoutTapped() {
- if (maybeShowStackUserEducation()) {
- // If we're showing user education, don't open the bubble show the education first
- mBubbleToExpandAfterFlyoutCollapse = null;
- } else {
- mBubbleToExpandAfterFlyoutCollapse = mBubbleData.getSelectedBubble();
- }
-
- mFlyout.removeCallbacks(mHideFlyout);
- mHideFlyout.run();
- }
-
- /**
- * Called when the flyout drag has finished, and returns true if the gesture successfully
- * dismissed the flyout.
- */
- void onFlyoutDragFinished(float deltaX, float velX) {
- final boolean onLeft = mStackAnimationController.isStackOnLeftSide();
- final boolean metRequiredVelocity =
- onLeft ? velX < -FLYOUT_DISMISS_VELOCITY : velX > FLYOUT_DISMISS_VELOCITY;
- final boolean metRequiredDeltaX =
- onLeft
- ? deltaX < -mFlyout.getWidth() * FLYOUT_DRAG_PERCENT_DISMISS
- : deltaX > mFlyout.getWidth() * FLYOUT_DRAG_PERCENT_DISMISS;
- final boolean isCancelFling = onLeft ? velX > 0 : velX < 0;
- final boolean shouldDismiss = metRequiredVelocity || (metRequiredDeltaX && !isCancelFling);
-
- mFlyout.removeCallbacks(mHideFlyout);
- animateFlyoutCollapsed(shouldDismiss, velX);
-
- maybeShowStackUserEducation();
- }
-
- /**
- * Called when the first touch event of a gesture (stack drag, bubble drag, flyout drag, etc.)
- * is received.
- */
- void onGestureStart() {
- mIsGestureInProgress = true;
- }
-
- /** Called when a gesture is completed or cancelled. */
- void onGestureFinished() {
- mIsGestureInProgress = false;
-
- if (mIsExpanded) {
- mExpandedAnimationController.onGestureFinished();
- }
- }
-
/** Passes the MotionEvent to the magnetized object and returns true if it was consumed. */
- boolean passEventToMagnetizedObject(MotionEvent event) {
+ private boolean passEventToMagnetizedObject(MotionEvent event) {
return mMagnetizedObject != null && mMagnetizedObject.maybeConsumeMotionEvent(event);
}
+ /**
+ * Dismisses the magnetized object - either an individual bubble, if we're expanded, or the
+ * stack, if we're collapsed.
+ */
+ private void dismissMagnetizedObject() {
+ if (mIsExpanded) {
+ final View draggedOutBubbleView = (View) mMagnetizedObject.getUnderlyingObject();
+ final Bubble draggedOutBubble = mBubbleData.getBubbleWithView(draggedOutBubbleView);
+
+ if (mBubbleData.hasBubbleWithKey(draggedOutBubble.getKey())) {
+ mBubbleData.notificationEntryRemoved(
+ draggedOutBubble.getEntry(), BubbleController.DISMISS_USER_GESTURE);
+ }
+ } else {
+ mBubbleData.dismissAll(BubbleController.DISMISS_USER_GESTURE);
+ }
+ }
+
/** Prepares and starts the desaturate/darken animation on the bubble stack. */
private void animateDesaturateAndDarken(View targetView, boolean desaturateAndDarken) {
mDesaturateAndDarkenTargetView = targetView;
@@ -1624,7 +1683,7 @@
}
/** Animates in the dismiss target. */
- private void springInDismissTarget() {
+ private void springInDismissTargetMaybe() {
if (mShowingDismiss) {
return;
}
@@ -1827,13 +1886,6 @@
return 0;
}
- private boolean isIntersecting(View view, float x, float y) {
- mTempLoc = view.getLocationOnScreen();
- mTempRect.set(mTempLoc[0], mTempLoc[1], mTempLoc[0] + view.getWidth(),
- mTempLoc[1] + view.getHeight());
- return mTempRect.contains(x, y);
- }
-
private void requestUpdate() {
if (mViewUpdatedRequested || mIsExpansionAnimating) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
deleted file mode 100644
index 132c45f..0000000
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.bubbles;
-
-import android.content.Context;
-import android.graphics.PointF;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.ViewConfiguration;
-
-import com.android.systemui.Dependency;
-
-/**
- * Handles interpreting touches on a {@link BubbleStackView}. This includes expanding, collapsing,
- * dismissing, and flings.
- */
-class BubbleTouchHandler implements View.OnTouchListener {
-
- private final PointF mTouchDown = new PointF();
- private final PointF mViewPositionOnTouchDown = new PointF();
- private final BubbleStackView mStack;
- private final BubbleData mBubbleData;
-
- private BubbleController mController = Dependency.get(BubbleController.class);
-
- private boolean mMovedEnough;
- private int mTouchSlopSquared;
- private VelocityTracker mVelocityTracker;
-
- /** View that was initially touched, when we received the first ACTION_DOWN event. */
- private View mTouchedView;
-
- BubbleTouchHandler(BubbleStackView stackView,
- BubbleData bubbleData, Context context) {
- final int touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
- mTouchSlopSquared = touchSlop * touchSlop;
- mBubbleData = bubbleData;
- mStack = stackView;
- }
-
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- final int action = event.getActionMasked();
-
- // If we aren't currently in the process of touching a view, figure out what we're touching.
- // It'll be the stack, an individual bubble, or nothing.
- if (mTouchedView == null) {
- mTouchedView = mStack.getTargetView(event);
- }
-
- // If this is an ACTION_OUTSIDE event, or the stack reported that we aren't touching
- // anything, collapse the stack.
- if (action == MotionEvent.ACTION_OUTSIDE || mTouchedView == null) {
- mBubbleData.setExpanded(false);
- mStack.hideStackUserEducation(false /* fromExpansion */);
- resetForNextGesture();
- return false;
- }
-
- if (!(mTouchedView instanceof BadgedImageView)
- && !(mTouchedView instanceof BubbleStackView)
- && !(mTouchedView instanceof BubbleFlyoutView)) {
-
- // Not touching anything touchable, but we shouldn't collapse (e.g. touching edge
- // of expanded view).
- mStack.maybeShowManageEducation(false);
- resetForNextGesture();
- return false;
- }
-
- final boolean isStack = mStack.equals(mTouchedView);
- final boolean isFlyout = mStack.getFlyoutView().equals(mTouchedView);
- final float rawX = event.getRawX();
- final float rawY = event.getRawY();
-
- // The coordinates of the touch event, in terms of the touched view's position.
- final float viewX = mViewPositionOnTouchDown.x + rawX - mTouchDown.x;
- final float viewY = mViewPositionOnTouchDown.y + rawY - mTouchDown.y;
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- trackMovement(event);
-
- mTouchDown.set(rawX, rawY);
- mStack.onGestureStart();
-
- if (isStack) {
- mViewPositionOnTouchDown.set(mStack.getStackPosition());
-
- // Dismiss the entire stack if it's released in the dismiss target.
- mStack.setReleasedInDismissTargetAction(
- () -> mController.dismissStack(BubbleController.DISMISS_USER_GESTURE));
- mStack.onDragStart();
- mStack.passEventToMagnetizedObject(event);
- } else if (isFlyout) {
- mStack.onFlyoutDragStart();
- } else {
- mViewPositionOnTouchDown.set(
- mTouchedView.getTranslationX(), mTouchedView.getTranslationY());
-
- // Dismiss only the dragged-out bubble if it's released in the target.
- final String individualBubbleKey = ((BadgedImageView) mTouchedView).getKey();
- mStack.setReleasedInDismissTargetAction(() -> {
- final Bubble bubble =
- mBubbleData.getBubbleWithKey(individualBubbleKey);
- // bubble can be null if the user is in the middle of
- // dismissing the bubble, but the app also sent a cancel
- if (bubble != null) {
- mController.removeBubble(bubble.getEntry(),
- BubbleController.DISMISS_USER_GESTURE);
- }
- });
-
- mStack.onBubbleDragStart(mTouchedView);
- mStack.passEventToMagnetizedObject(event);
- }
-
- break;
- case MotionEvent.ACTION_MOVE:
- trackMovement(event);
- final float deltaX = rawX - mTouchDown.x;
- final float deltaY = rawY - mTouchDown.y;
-
- if ((deltaX * deltaX) + (deltaY * deltaY) > mTouchSlopSquared && !mMovedEnough) {
- mMovedEnough = true;
- }
-
- if (mMovedEnough) {
- if (isFlyout) {
- mStack.onFlyoutDragged(deltaX);
- } else if (!mStack.passEventToMagnetizedObject(event)) {
- // If the magnetic target doesn't consume the event, drag the stack or
- // bubble.
- if (isStack) {
- mStack.onDragged(viewX, viewY);
- } else {
- mStack.onBubbleDragged(mTouchedView, viewX, viewY);
- }
- }
- }
- break;
-
- case MotionEvent.ACTION_CANCEL:
- resetForNextGesture();
- break;
-
- case MotionEvent.ACTION_UP:
- trackMovement(event);
- mVelocityTracker.computeCurrentVelocity(/* maxVelocity */ 1000);
- final float velX = mVelocityTracker.getXVelocity();
- final float velY = mVelocityTracker.getYVelocity();
-
- if (isFlyout && mMovedEnough) {
- mStack.onFlyoutDragFinished(rawX - mTouchDown.x /* deltaX */, velX);
- } else if (isFlyout) {
- if (!mBubbleData.isExpanded() && !mMovedEnough) {
- mStack.onFlyoutTapped();
- }
- } else if (mMovedEnough) {
- if (!mStack.passEventToMagnetizedObject(event)) {
- // If the magnetic target didn't consume the event, tell the stack to finish
- // the drag.
- if (isStack) {
- mStack.onDragFinish(viewX, viewY, velX, velY);
- } else {
- mStack.onBubbleDragFinish(mTouchedView, viewX, viewY, velX, velY);
- }
- }
- } else if (mTouchedView == mStack.getExpandedBubbleView()) {
- mBubbleData.setExpanded(false);
- } else if (isStack) {
- mStack.onStackTapped();
- } else {
- final String key = ((BadgedImageView) mTouchedView).getKey();
- if (key == BubbleOverflow.KEY) {
- mStack.showOverflow();
- } else {
- mStack.expandBubble(mBubbleData.getBubbleWithKey(key));
- }
- }
- resetForNextGesture();
- break;
- }
-
- return true;
- }
-
- /** Clears all touch-related state. */
- private void resetForNextGesture() {
- if (mVelocityTracker != null) {
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
-
- mTouchedView = null;
- mMovedEnough = false;
-
- mStack.onGestureFinished();
- }
-
- private void trackMovement(MotionEvent event) {
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- }
- mVelocityTracker.addMovement(event);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
index a0b4938..d974adc 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
@@ -252,8 +252,14 @@
mSpringToTouchOnNextMotionEvent = true;
}
- /** Prepares the given bubble to be dragged out. */
- public void prepareForBubbleDrag(View bubble, MagnetizedObject.MagneticTarget target) {
+ /**
+ * Prepares the given bubble view to be dragged out, using the provided magnetic target and
+ * listener.
+ */
+ public void prepareForBubbleDrag(
+ View bubble,
+ MagnetizedObject.MagneticTarget target,
+ MagnetizedObject.MagnetListener listener) {
mLayout.cancelAnimationsOnView(bubble);
bubble.setTranslationZ(Short.MAX_VALUE);
@@ -277,6 +283,7 @@
}
};
mMagnetizedBubbleDraggingOut.addTarget(target);
+ mMagnetizedBubbleDraggingOut.setMagnetListener(listener);
mMagnetizedBubbleDraggingOut.setHapticsEnabled(true);
mMagnetizedBubbleDraggingOut.setFlingToTargetMinVelocity(FLING_TO_DISMISS_MIN_VELOCITY);
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java
index c292769..b1bbafc 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java
@@ -1117,9 +1117,4 @@
mAssociatedController = controller;
}
}
-
- @Override
- protected boolean canReceivePointerEvents() {
- return false;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt
index d33cd94..8fd840e 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt
@@ -56,13 +56,12 @@
enabled: Boolean,
offset: Int = 0
): RenderInfo {
- val (fg, bg) = deviceColorMap.getValue(deviceType)
-
- val iconKey = if (offset > 0) {
+ val key = if (offset > 0) {
deviceType * BUCKET_SIZE + offset
} else deviceType
- val iconState = deviceIconMap.getValue(iconKey)
+ val (fg, bg) = deviceColorMap.getValue(key)
+ val iconState = deviceIconMap.getValue(key)
val resourceId = iconState[enabled]
var icon: Drawable?
if (resourceId == APP_ICON_ID) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
index aa5ebaa..b7658a9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
@@ -46,19 +46,6 @@
/** Updates seek bar views when the data model changes. */
@UiThread
override fun onChanged(data: SeekBarViewModel.Progress) {
- if (data.enabled && seekBarView.visibility == View.GONE) {
- seekBarView.visibility = View.VISIBLE
- elapsedTimeView.visibility = View.VISIBLE
- totalTimeView.visibility = View.VISIBLE
- } else if (!data.enabled && seekBarView.visibility == View.VISIBLE) {
- seekBarView.visibility = View.GONE
- elapsedTimeView.visibility = View.GONE
- totalTimeView.visibility = View.GONE
- return
- }
-
- // TODO: update the style of the disabled progress bar
- seekBarView.setEnabled(data.seekAvailable)
data.color?.let {
var tintList = ColorStateList.valueOf(it)
@@ -71,6 +58,17 @@
totalTimeView.setTextColor(it)
}
+ if (!data.enabled) {
+ seekBarView.setEnabled(false)
+ seekBarView.getThumb().setAlpha(0)
+ elapsedTimeView.setText("")
+ totalTimeView.setText("")
+ return
+ }
+
+ seekBarView.getThumb().setAlpha(if (data.seekAvailable) 255 else 0)
+ seekBarView.setEnabled(data.seekAvailable)
+
data.elapsedTime?.let {
seekBarView.setProgress(it)
elapsedTimeView.setText(DateUtils.formatElapsedTime(
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
index b10dd93..d2994ae 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
@@ -103,6 +103,7 @@
@Override
public void onPipAnimationEnd(SurfaceControl.Transaction tx,
PipAnimationController.PipTransitionAnimator animator) {
+ finishResize(tx, animator.getDestinationBounds(), animator.getTransitionDirection());
mMainHandler.post(() -> {
for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
@@ -110,7 +111,6 @@
animator.getTransitionDirection());
}
});
- finishResize(tx, animator.getDestinationBounds(), animator.getTransitionDirection());
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index a192afc..a8a5d89 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -16,12 +16,10 @@
package com.android.systemui.pip.phone;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.ActivityManager.StackInfo;
import android.app.IActivityTaskManager;
import android.content.Context;
import android.graphics.Rect;
@@ -169,15 +167,7 @@
*/
void synchronizePinnedStackBounds() {
cancelAnimations();
- try {
- StackInfo stackInfo = mActivityTaskManager.getStackInfo(
- WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
- if (stackInfo != null) {
- mBounds.set(stackInfo.bounds);
- }
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to get pinned stack bounds");
- }
+ mBounds.set(mPipTaskOrganizer.getLastReportedBounds());
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index 52c8960..99a01d3 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -289,7 +289,6 @@
mMediaSessionManager =
(MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
- mPipTaskOrganizer.registerPipTransitionCallback(this);
try {
WindowManagerWrapper.getInstance().addPinnedStackListener(mPinnedStackListener);
@@ -334,6 +333,8 @@
* Shows the picture-in-picture menu if an activity is in picture-in-picture mode.
*/
public void showPictureInPictureMenu() {
+ if (DEBUG) Log.d(TAG, "showPictureInPictureMenu(), current state=" + getStateDescription());
+
if (getState() == STATE_PIP) {
resizePinnedStack(STATE_PIP_MENU);
}
@@ -343,10 +344,18 @@
* Closes PIP (PIPed activity and PIP system UI).
*/
public void closePip() {
+ if (DEBUG) Log.d(TAG, "closePip(), current state=" + getStateDescription());
+
closePipInternal(true);
}
private void closePipInternal(boolean removePipStack) {
+ if (DEBUG) {
+ Log.d(TAG,
+ "closePipInternal() removePipStack=" + removePipStack + ", current state="
+ + getStateDescription());
+ }
+
mState = STATE_NO_PIP;
mPipTaskId = TASK_ID_NO_PIP;
mPipMediaController = null;
@@ -371,6 +380,8 @@
* Moves the PIPed activity to the fullscreen and closes PIP system UI.
*/
void movePipToFullscreen() {
+ if (DEBUG) Log.d(TAG, "movePipToFullscreen(), current state=" + getStateDescription());
+
mPipTaskId = TASK_ID_NO_PIP;
for (int i = mListeners.size() - 1; i >= 0; --i) {
mListeners.get(i).onMoveToFullscreen();
@@ -386,6 +397,7 @@
public void suspendPipResizing(int reason) {
if (DEBUG) Log.d(TAG,
"suspendPipResizing() reason=" + reason + " callers=" + Debug.getCallers(2));
+
mSuspendPipResizingReason |= reason;
}
@@ -408,7 +420,11 @@
* @param state In Pip state also used to determine the new size for the Pip.
*/
void resizePinnedStack(int state) {
- if (DEBUG) Log.d(TAG, "resizePinnedStack() state=" + state, new Exception());
+ if (DEBUG) {
+ Log.d(TAG, "resizePinnedStack() state=" + stateToName(state) + ", current state="
+ + getStateDescription(), new Exception());
+ }
+
boolean wasStateNoPip = (mState == STATE_NO_PIP);
for (int i = mListeners.size() - 1; i >= 0; --i) {
mListeners.get(i).onPipResizeAboutToStart();
@@ -418,7 +434,7 @@
if (DEBUG) Log.d(TAG, "resizePinnedStack() deferring"
+ " mSuspendPipResizingReason=" + mSuspendPipResizingReason
+ " mResumeResizePinnedStackRunnableState="
- + mResumeResizePinnedStackRunnableState);
+ + stateToName(mResumeResizePinnedStackRunnableState));
return;
}
mState = state;
@@ -458,7 +474,8 @@
* stack to the centered PIP bound {@link R.config_centeredPictureInPictureBounds}.
*/
private void showPipMenu() {
- if (DEBUG) Log.d(TAG, "showPipMenu()");
+ if (DEBUG) Log.d(TAG, "showPipMenu(), current state=" + getStateDescription());
+
mState = STATE_PIP_MENU;
for (int i = mListeners.size() - 1; i >= 0; --i) {
mListeners.get(i).onShowPipMenu();
@@ -712,6 +729,7 @@
private void onPipTransitionFinishedOrCanceled() {
if (DEBUG) Log.d(TAG, "onPipTransitionFinishedOrCanceled()");
+
if (getState() == STATE_PIP_MENU) {
showPipMenu();
}
@@ -753,4 +771,28 @@
WindowManagerWrapper.getInstance().setPipVisibility(visible);
});
}
+
+ private String getStateDescription() {
+ if (mSuspendPipResizingReason == 0) {
+ return stateToName(mState);
+ }
+ return stateToName(mResumeResizePinnedStackRunnableState) + " (while " + stateToName(mState)
+ + " is suspended)";
+ }
+
+ private static String stateToName(int state) {
+ switch (state) {
+ case STATE_NO_PIP:
+ return "NO_PIP";
+
+ case STATE_PIP:
+ return "PIP";
+
+ case STATE_PIP_MENU:
+ return "PIP_MENU";
+
+ default:
+ return "UNKNOWN(" + state + ")";
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java
index f43f8e7..c7e77cc 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java
@@ -22,6 +22,7 @@
import android.content.Intent;
import android.content.pm.ParceledListSlice;
import android.os.Bundle;
+import android.util.Log;
import com.android.systemui.R;
import com.android.systemui.pip.tv.dagger.TvPipComponent;
@@ -34,6 +35,7 @@
* Activity to show the PIP menu to control PIP.
*/
public class PipMenuActivity extends Activity implements PipManager.Listener {
+ private static final boolean DEBUG = false;
private static final String TAG = "PipMenuActivity";
static final String EXTRA_CUSTOM_ACTIONS = "custom_actions";
@@ -47,7 +49,6 @@
private boolean mRestorePipSizeWhenClose;
private PipControlsViewController mPipControlsViewController;
-
@Inject
public PipMenuActivity(TvPipComponent.Builder pipComponentBuilder, PipManager pipManager) {
super();
@@ -57,6 +58,8 @@
@Override
protected void onCreate(Bundle bundle) {
+ if (DEBUG) Log.d(TAG, "onCreate()");
+
super.onCreate(bundle);
if (!mPipManager.isPipShown()) {
finish();
@@ -81,13 +84,18 @@
@Override
protected void onNewIntent(Intent intent) {
+ if (DEBUG) Log.d(TAG, "onNewIntent(), intent=" + intent);
super.onNewIntent(intent);
onPipMenuActionsChanged(getIntent().getParcelableExtra(EXTRA_CUSTOM_ACTIONS));
}
private void restorePipAndFinish() {
+ if (DEBUG) Log.d(TAG, "restorePipAndFinish()");
+
if (mRestorePipSizeWhenClose) {
+ if (DEBUG) Log.d(TAG, " > restoring to the default position");
+
// When PIP menu activity is closed, restore to the default position.
mPipManager.resizePinnedStack(PipManager.STATE_PIP);
}
@@ -96,12 +104,16 @@
@Override
public void onResume() {
+ if (DEBUG) Log.d(TAG, "onResume()");
+
super.onResume();
mFadeInAnimation.start();
}
@Override
public void onPause() {
+ if (DEBUG) Log.d(TAG, "onPause()");
+
super.onPause();
mFadeOutAnimation.start();
restorePipAndFinish();
@@ -109,6 +121,8 @@
@Override
protected void onDestroy() {
+ if (DEBUG) Log.d(TAG, "onDestroy()");
+
super.onDestroy();
mPipManager.removeListener(this);
mPipManager.resumePipResizing(
@@ -117,29 +131,41 @@
@Override
public void onBackPressed() {
+ if (DEBUG) Log.d(TAG, "onBackPressed()");
+
restorePipAndFinish();
}
@Override
- public void onPipEntered() { }
+ public void onPipEntered() {
+ if (DEBUG) Log.d(TAG, "onPipEntered()");
+ }
@Override
public void onPipActivityClosed() {
+ if (DEBUG) Log.d(TAG, "onPipActivityClosed()");
+
finish();
}
@Override
public void onPipMenuActionsChanged(ParceledListSlice actions) {
+ if (DEBUG) Log.d(TAG, "onPipMenuActionsChanged()");
+
boolean hasCustomActions = actions != null && !actions.getList().isEmpty();
mPipControlsViewController.setActions(
hasCustomActions ? actions.getList() : Collections.EMPTY_LIST);
}
@Override
- public void onShowPipMenu() { }
+ public void onShowPipMenu() {
+ if (DEBUG) Log.d(TAG, "onShowPipMenu()");
+ }
@Override
public void onMoveToFullscreen() {
+ if (DEBUG) Log.d(TAG, "onMoveToFullscreen()");
+
// Moving PIP to fullscreen is implemented by resizing PINNED_STACK with null bounds.
// This conflicts with restoring PIP position, so disable it.
mRestorePipSizeWhenClose = false;
@@ -148,8 +174,17 @@
@Override
public void onPipResizeAboutToStart() {
+ if (DEBUG) Log.d(TAG, "onPipResizeAboutToStart()");
+
finish();
mPipManager.suspendPipResizing(
PipManager.SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH);
}
+
+ @Override
+ public void finish() {
+ if (DEBUG) Log.d(TAG, "finish()", new RuntimeException());
+
+ super.finish();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 295adae..9324b14 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -24,6 +24,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Notification;
+import android.os.SystemClock;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.NotificationListenerService.RankingMap;
@@ -555,8 +556,8 @@
NotificationEntry entry = new NotificationEntry(
notification,
ranking,
- mFgsFeatureController.isForegroundServiceDismissalEnabled());
- mAllNotifications.add(entry);
+ mFgsFeatureController.isForegroundServiceDismissalEnabled(),
+ SystemClock.uptimeMillis());
mLeakDetector.trackInstance(entry);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index 9c2cac71..365862b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -47,6 +47,7 @@
import android.annotation.UserIdInt;
import android.app.Notification;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.Ranking;
@@ -387,7 +388,7 @@
if (entry == null) {
// A new notification!
- entry = new NotificationEntry(sbn, ranking);
+ entry = new NotificationEntry(sbn, ranking, SystemClock.uptimeMillis());
mNotificationSet.put(sbn.getKey(), entry);
mLogger.logNotifPosted(sbn.getKey());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index c1ba26d..68ec34e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -35,6 +35,7 @@
import static java.util.Objects.requireNonNull;
+import android.annotation.CurrentTimeMillisLong;
import android.app.Notification;
import android.app.Notification.MessagingStyle.Message;
import android.app.NotificationChannel;
@@ -93,6 +94,7 @@
private final String mKey;
private StatusBarNotification mSbn;
private Ranking mRanking;
+ private long mCreationTime;
/*
* Bookkeeping members
@@ -171,21 +173,29 @@
private boolean mAllowFgsDismissal;
private int mBucket = BUCKET_ALERTING;
+ /**
+ * @param sbn the StatusBarNotification from system server
+ * @param ranking also from system server
+ * @param creationTime SystemClock.uptimeMillis of when we were created
+ */
public NotificationEntry(
@NonNull StatusBarNotification sbn,
- @NonNull Ranking ranking) {
- this(sbn, ranking, false);
+ @NonNull Ranking ranking,
+ long creationTime) {
+ this(sbn, ranking, false, creationTime);
}
public NotificationEntry(
@NonNull StatusBarNotification sbn,
@NonNull Ranking ranking,
- boolean allowFgsDismissal
+ boolean allowFgsDismissal,
+ long creationTime
) {
super(requireNonNull(Objects.requireNonNull(sbn).getKey()));
requireNonNull(ranking);
+ mCreationTime = creationTime;
mKey = sbn.getKey();
setSbn(sbn);
setRanking(ranking);
@@ -238,6 +248,21 @@
}
/**
+ * A timestamp of SystemClock.uptimeMillis() of when this entry was first created, regardless
+ * of any changes to the data presented. It is set once on creation and will never change, and
+ * allows us to know exactly how long this notification has been alive for in our listener
+ * service. It is entirely unrelated to the information inside of the notification.
+ *
+ * This is different to Notification#when because it persists throughout updates, whereas
+ * system server treats every single call to notify() as a new notification and we handle
+ * updates to NotificationEntry locally.
+ */
+ @CurrentTimeMillisLong
+ public long getCreationTime() {
+ return mCreationTime;
+ }
+
+ /**
* Should only be called by NotificationEntryManager and friends.
* TODO: Make this package-private
*/
diff --git a/packages/SystemUI/src/com/android/systemui/util/RelativeTouchListener.kt b/packages/SystemUI/src/com/android/systemui/util/RelativeTouchListener.kt
new file mode 100644
index 0000000..d65b285
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/RelativeTouchListener.kt
@@ -0,0 +1,162 @@
+/*
+ * 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.util
+
+import android.graphics.PointF
+import android.os.Handler
+import android.view.MotionEvent
+import android.view.VelocityTracker
+import android.view.View
+import android.view.ViewConfiguration
+import kotlin.math.hypot
+
+/**
+ * Listener which receives [onDown], [onMove], and [onUp] events, with relevant information about
+ * the coordinates of the touch and the view relative to the initial ACTION_DOWN event and the
+ * view's initial position.
+ */
+abstract class RelativeTouchListener : View.OnTouchListener {
+
+ /**
+ * Called when an ACTION_DOWN event is received for the given view.
+ *
+ * @return False if the object is not interested in MotionEvents at this time, or true if we
+ * should consume this event and subsequent events, and begin calling [onMove].
+ */
+ abstract fun onDown(v: View, ev: MotionEvent): Boolean
+
+ /**
+ * Called when an ACTION_MOVE event is received for the given view. This signals that the view
+ * is being dragged.
+ *
+ * @param viewInitialX The view's translationX value when this touch gesture started.
+ * @param viewInitialY The view's translationY value when this touch gesture started.
+ * @param dx Horizontal distance covered since the initial ACTION_DOWN event, in pixels.
+ * @param dy Vertical distance covered since the initial ACTION_DOWN event, in pixels.
+ */
+ abstract fun onMove(
+ v: View,
+ ev: MotionEvent,
+ viewInitialX: Float,
+ viewInitialY: Float,
+ dx: Float,
+ dy: Float
+ )
+
+ /**
+ * Called when an ACTION_UP event is received for the given view. This signals that a drag or
+ * fling gesture has completed.
+ *
+ * @param viewInitialX The view's translationX value when this touch gesture started.
+ * @param viewInitialY The view's translationY value when this touch gesture started.
+ * @param dx Horizontal distance covered, in pixels.
+ * @param dy Vertical distance covered, in pixels.
+ * @param velX The final horizontal velocity of the gesture, in pixels/second.
+ * @param velY The final vertical velocity of the gesture, in pixels/second.
+ */
+ abstract fun onUp(
+ v: View,
+ ev: MotionEvent,
+ viewInitialX: Float,
+ viewInitialY: Float,
+ dx: Float,
+ dy: Float,
+ velX: Float,
+ velY: Float
+ )
+
+ /** The raw coordinates of the last ACTION_DOWN event. */
+ private val touchDown = PointF()
+
+ /** The coordinates of the view, at the time of the last ACTION_DOWN event. */
+ private val viewPositionOnTouchDown = PointF()
+
+ private val velocityTracker = VelocityTracker.obtain()
+
+ private var touchSlop: Int = -1
+ private var movedEnough = false
+
+ private val handler = Handler()
+ private var performedLongClick = false
+
+ @Suppress("UNCHECKED_CAST")
+ override fun onTouch(v: View, ev: MotionEvent): Boolean {
+ addMovement(ev)
+
+ val dx = ev.rawX - touchDown.x
+ val dy = ev.rawY - touchDown.y
+
+ when (ev.action) {
+ MotionEvent.ACTION_DOWN -> {
+ if (!onDown(v, ev)) {
+ return false
+ }
+
+ // Grab the touch slop, it might have changed if the config changed since the
+ // last gesture.
+ touchSlop = ViewConfiguration.get(v.context).scaledTouchSlop
+
+ touchDown.set(ev.rawX, ev.rawY)
+ viewPositionOnTouchDown.set(v.translationX, v.translationY)
+
+ performedLongClick = false
+ handler.postDelayed({
+ performedLongClick = v.performLongClick()
+ }, ViewConfiguration.getLongPressTimeout().toLong())
+ }
+
+ MotionEvent.ACTION_MOVE -> {
+ if (!movedEnough && hypot(dx, dy) > touchSlop && !performedLongClick) {
+ movedEnough = true
+ handler.removeCallbacksAndMessages(null)
+ }
+
+ if (movedEnough) {
+ onMove(v, ev, viewPositionOnTouchDown.x, viewPositionOnTouchDown.y, dx, dy)
+ }
+ }
+
+ MotionEvent.ACTION_UP -> {
+ if (movedEnough) {
+ velocityTracker.computeCurrentVelocity(1000 /* units */)
+ onUp(v, ev, viewPositionOnTouchDown.x, viewPositionOnTouchDown.y, dx, dy,
+ velocityTracker.xVelocity, velocityTracker.yVelocity)
+ } else if (!performedLongClick) {
+ v.performClick()
+ } else {
+ handler.removeCallbacksAndMessages(null)
+ }
+
+ velocityTracker.clear()
+ movedEnough = false
+ }
+ }
+
+ return true
+ }
+
+ /**
+ * Adds a movement to the velocity tracker using raw screen coordinates.
+ */
+ private fun addMovement(event: MotionEvent) {
+ val deltaX = event.rawX - event.x
+ val deltaY = event.rawY - event.y
+ event.offsetLocation(deltaX, deltaY)
+ velocityTracker.addMovement(event)
+ event.offsetLocation(-deltaX, -deltaY)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
index cf1299f..ce032c9 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
@@ -115,6 +115,7 @@
@Test
public void testShow_dismissedByCallback() throws Exception {
+ doAnswer(answerVoid(Runnable::run)).when(mHandler).post(any(Runnable.class));
doAnswer(invocation -> {
IKeyguardCallback callback = (IKeyguardCallback) invocation.getArguments()[1];
callback.onDismiss();
@@ -184,7 +185,7 @@
private void verifyViewDismissed(SurfaceView v) throws Exception {
verify(mParent).removeView(v);
- verify(mKeyguardCallback).dismiss(true, TARGET_USER_ID);
+ verify(mKeyguardCallback).dismiss(true, TARGET_USER_ID, true);
assertThat(mContext.isBound(mComponentName)).isFalse();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
index 69e933e..45f9437 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
@@ -52,6 +52,7 @@
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.util.time.FakeSystemClock;
import junit.framework.Assert;
@@ -69,6 +70,7 @@
private ForegroundServiceController mFsc;
private ForegroundServiceNotificationListener mListener;
private NotificationEntryListener mEntryListener;
+ private final FakeSystemClock mClock = new FakeSystemClock();
@Mock private NotificationEntryManager mEntryManager;
@Mock private AppOpsController mAppOpsController;
@Mock private Handler mMainHandler;
@@ -80,9 +82,10 @@
allowTestableLooperAsMainThread();
MockitoAnnotations.initMocks(this);
- mFsc = new ForegroundServiceController(mEntryManager, mAppOpsController, mMainHandler);
+ mFsc = new ForegroundServiceController(
+ mEntryManager, mAppOpsController, mMainHandler);
mListener = new ForegroundServiceNotificationListener(
- mContext, mFsc, mEntryManager, mNotifPipeline);
+ mContext, mFsc, mEntryManager, mNotifPipeline, mClock);
ArgumentCaptor<NotificationEntryListener> entryListenerCaptor =
ArgumentCaptor.forClass(NotificationEntryListener.class);
verify(mEntryManager).addNotificationEntryListener(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceNotificationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceNotificationListenerTest.java
index 46a473b..bca8dee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceNotificationListenerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceNotificationListenerTest.java
@@ -17,7 +17,6 @@
package com.android.systemui;
import static com.android.systemui.ForegroundServiceLifetimeExtender.MIN_FGS_TIME_MS;
-import static com.android.systemui.statusbar.NotificationEntryHelper.modifySbn;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -29,6 +28,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
@@ -37,12 +37,15 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
public class ForegroundServiceNotificationListenerTest extends SysuiTestCase {
- private ForegroundServiceLifetimeExtender mExtender = new ForegroundServiceLifetimeExtender();
+ private ForegroundServiceLifetimeExtender mExtender;
private NotificationEntry mEntry;
private Notification mNotif;
+ private final FakeSystemClock mClock = new FakeSystemClock();
@Before
public void setup() {
+ mExtender = new ForegroundServiceLifetimeExtender(mClock);
+
mNotif = new Notification.Builder(mContext, "")
.setSmallIcon(R.drawable.ic_person)
.setContentTitle("Title")
@@ -50,6 +53,7 @@
.build();
mEntry = new NotificationEntryBuilder()
+ .setCreationTime(mClock.uptimeMillis())
.setNotification(mNotif)
.build();
}
@@ -62,27 +66,26 @@
// Extend the lifetime of a FGS notification iff it has not been visible
// for the minimum time
mNotif.flags |= Notification.FLAG_FOREGROUND_SERVICE;
- modifySbn(mEntry)
- .setPostTime(System.currentTimeMillis())
- .build();
+
+ // No time has elapsed, keep showing
assertTrue(mExtender.shouldExtendLifetime(mEntry));
}
@Test
public void testShouldExtendLifetime_shouldNot_foreground() {
mNotif.flags |= Notification.FLAG_FOREGROUND_SERVICE;
- modifySbn(mEntry)
- .setPostTime(System.currentTimeMillis() - MIN_FGS_TIME_MS - 1)
- .build();
+
+ // Entry was created at mClock.uptimeMillis(), advance it MIN_FGS_TIME_MS + 1
+ mClock.advanceTime(MIN_FGS_TIME_MS + 1);
assertFalse(mExtender.shouldExtendLifetime(mEntry));
}
@Test
public void testShouldExtendLifetime_shouldNot_notForeground() {
mNotif.flags = 0;
- modifySbn(mEntry)
- .setPostTime(System.currentTimeMillis() - MIN_FGS_TIME_MS - 1)
- .build();
+
+ // Entry was created at mClock.uptimeMillis(), advance it MIN_FGS_TIME_MS + 1
+ mClock.advanceTime(MIN_FGS_TIME_MS + 1);
assertFalse(mExtender.shouldExtendLifetime(mEntry));
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 037f04ec..e472de3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -374,7 +374,7 @@
assertNotNull(mBubbleData.getBubbleWithKey(mRow2.getEntry().getKey()));
assertTrue(mBubbleController.hasBubbles());
- mBubbleController.dismissStack(BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.dismissAll(BubbleController.DISMISS_USER_GESTURE);
assertFalse(mNotificationShadeWindowController.getBubblesShowing());
verify(mNotificationEntryManager, times(3)).updateNotifications(any());
assertNull(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
@@ -399,7 +399,7 @@
// Expand the stack
BubbleStackView stackView = mBubbleController.getStackView();
- mBubbleController.expandStack();
+ mBubbleData.setExpanded(true);
assertTrue(mBubbleController.isStackExpanded());
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
assertTrue(mNotificationShadeWindowController.getBubbleExpanded());
@@ -436,7 +436,7 @@
// Expand
BubbleStackView stackView = mBubbleController.getStackView();
- mBubbleController.expandStack();
+ mBubbleData.setExpanded(true);
assertTrue(mBubbleController.isStackExpanded());
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().getKey());
@@ -448,7 +448,7 @@
mRow2.getEntry()));
// Switch which bubble is expanded
- mBubbleController.selectBubble(mRow.getEntry().getKey());
+ mBubbleData.setSelectedBubble(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
mBubbleData.setExpanded(true);
assertEquals(mRow.getEntry(),
mBubbleData.getBubbleWithKey(stackView.getExpandedBubble().getKey()).getEntry());
@@ -482,7 +482,7 @@
assertTrue(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
// Expand
- mBubbleController.expandStack();
+ mBubbleData.setExpanded(true);
assertTrue(mBubbleController.isStackExpanded());
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
@@ -510,7 +510,7 @@
assertTrue(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
// Expand
- mBubbleController.expandStack();
+ mBubbleData.setExpanded(true);
assertTrue(mBubbleController.isStackExpanded());
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
@@ -544,7 +544,7 @@
// Expand
BubbleStackView stackView = mBubbleController.getStackView();
- mBubbleController.expandStack();
+ mBubbleData.setExpanded(true);
assertTrue(mSysUiStateBubblesExpanded);
@@ -726,7 +726,7 @@
public void testDeleteIntent_dismissStack() throws PendingIntent.CanceledException {
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mRow2.getEntry());
- mBubbleController.dismissStack(BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.dismissAll(BubbleController.DISMISS_USER_GESTURE);
verify(mDeleteIntent, times(2)).send();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
index 545de21..5f4f2ef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
@@ -321,7 +321,7 @@
assertNotNull(mBubbleData.getBubbleWithKey(mRow2.getEntry().getKey()));
assertTrue(mBubbleController.hasBubbles());
- mBubbleController.dismissStack(BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.dismissAll(BubbleController.DISMISS_USER_GESTURE);
assertFalse(mNotificationShadeWindowController.getBubblesShowing());
verify(mNotifCallback, times(3)).invalidateNotifications(anyString());
assertNull(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
@@ -344,7 +344,7 @@
// Expand the stack
BubbleStackView stackView = mBubbleController.getStackView();
- mBubbleController.expandStack();
+ mBubbleData.setExpanded(true);
assertTrue(mBubbleController.isStackExpanded());
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
assertTrue(mNotificationShadeWindowController.getBubbleExpanded());
@@ -376,7 +376,7 @@
// Expand
BubbleStackView stackView = mBubbleController.getStackView();
- mBubbleController.expandStack();
+ mBubbleData.setExpanded(true);
assertTrue(mBubbleController.isStackExpanded());
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().getKey());
@@ -385,7 +385,7 @@
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow2.getEntry()));
// Switch which bubble is expanded
- mBubbleController.selectBubble(mRow.getEntry().getKey());
+ mBubbleData.setSelectedBubble(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
mBubbleData.setExpanded(true);
assertEquals(mRow.getEntry(),
mBubbleData.getBubbleWithKey(stackView.getExpandedBubble().getKey()).getEntry());
@@ -416,7 +416,7 @@
assertTrue(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
// Expand
- mBubbleController.expandStack();
+ mBubbleData.setExpanded(true);
assertTrue(mBubbleController.isStackExpanded());
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
@@ -442,7 +442,7 @@
assertTrue(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
// Expand
- mBubbleController.expandStack();
+ mBubbleData.setExpanded(true);
assertTrue(mBubbleController.isStackExpanded());
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
@@ -474,7 +474,7 @@
// Expand
BubbleStackView stackView = mBubbleController.getStackView();
- mBubbleController.expandStack();
+ mBubbleData.setExpanded(true);
assertTrue(mBubbleController.isStackExpanded());
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().getKey());
@@ -628,7 +628,7 @@
public void testDeleteIntent_dismissStack() throws PendingIntent.CanceledException {
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mRow2.getEntry());
- mBubbleController.dismissStack(BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.dismissAll(BubbleController.DISMISS_USER_GESTURE);
verify(mDeleteIntent, times(2)).send();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt
index 260f520..d407b8a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt
@@ -67,10 +67,11 @@
val isEnabled = false
val data = SeekBarViewModel.Progress(isEnabled, false, null, null, null)
observer.onChanged(data)
- // THEN seek bar visibility is set to GONE
- assertThat(seekBarView.getVisibility()).isEqualTo(View.GONE)
- assertThat(elapsedTimeView.getVisibility()).isEqualTo(View.GONE)
- assertThat(totalTimeView.getVisibility()).isEqualTo(View.GONE)
+ // THEN seek bar shows just a line with no text
+ assertThat(seekBarView.isEnabled()).isFalse()
+ assertThat(seekBarView.getThumb().getAlpha()).isEqualTo(0)
+ assertThat(elapsedTimeView.getText()).isEqualTo("")
+ assertThat(totalTimeView.getText()).isEqualTo("")
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
index 43cf83f..81f9546 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
@@ -29,6 +29,7 @@
import com.android.internal.logging.InstanceId;
import com.android.systemui.statusbar.RankingBuilder;
import com.android.systemui.statusbar.SbnBuilder;
+import com.android.systemui.util.time.FakeSystemClock;
import java.util.ArrayList;
@@ -44,16 +45,22 @@
public class NotificationEntryBuilder {
private final SbnBuilder mSbnBuilder = new SbnBuilder();
private final RankingBuilder mRankingBuilder = new RankingBuilder();
+ private final FakeSystemClock mClock = new FakeSystemClock();
private StatusBarNotification mSbn = null;
/* ListEntry properties */
private GroupEntry mParent;
private int mSection = -1;
+ /* If set, use this creation time instead of mClock.uptimeMillis */
+ private long mCreationTime = -1;
+
public NotificationEntry build() {
StatusBarNotification sbn = mSbn != null ? mSbn : mSbnBuilder.build();
mRankingBuilder.setKey(sbn.getKey());
- final NotificationEntry entry = new NotificationEntry(sbn, mRankingBuilder.build());
+ long creationTime = mCreationTime != -1 ? mCreationTime : mClock.uptimeMillis();
+ final NotificationEntry entry = new NotificationEntry(
+ sbn, mRankingBuilder.build(), mClock.uptimeMillis());
/* ListEntry properties */
entry.setParent(mParent);
@@ -86,6 +93,14 @@
return this;
}
+ /**
+ * Set the creation time
+ */
+ public NotificationEntryBuilder setCreationTime(long creationTime) {
+ mCreationTime = creationTime;
+ return this;
+ }
+
/* Delegated to SbnBuilder */
public NotificationEntryBuilder setPkg(String pkg) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
index 5b0b668..1a022ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
@@ -52,6 +52,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.RankingBuilder;
import com.android.systemui.statusbar.SbnBuilder;
+import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
@@ -71,6 +72,7 @@
private int mId;
private NotificationEntry mEntry;
+ private final FakeSystemClock mClock = new FakeSystemClock();
@Before
public void setup() {
@@ -187,7 +189,7 @@
.build();
NotificationEntry entry =
- new NotificationEntry(sbn, ranking);
+ new NotificationEntry(sbn, ranking, mClock.uptimeMillis());
assertEquals(systemGeneratedSmartActions, entry.getSmartActions());
assertEquals(NOTIFICATION_CHANNEL, entry.getChannel());
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java
index b3a30ab..6695eed 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java
@@ -33,7 +33,6 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
@@ -72,8 +71,6 @@
import org.mockito.quality.Strictness;
import java.util.ArrayList;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
@RunWith(AndroidJUnit4.class)
@SmallTest
@@ -255,19 +252,16 @@
@Test
public void testRequestLastEntitlementCacheValue() throws Exception {
- final CountDownLatch mCallbacklatch = new CountDownLatch(1);
// 1. Entitlement check is not required.
mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
ResultReceiver receiver = new ResultReceiver(null) {
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
assertEquals(TETHER_ERROR_NO_ERROR, resultCode);
- mCallbacklatch.countDown();
}
};
mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
mLooper.dispatchAll();
- callbackTimeoutHelper(mCallbacklatch);
assertEquals(0, mEnMgr.uiProvisionCount);
mEnMgr.reset();
@@ -277,12 +271,10 @@
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
assertEquals(TETHER_ERROR_ENTITLEMENT_UNKNOWN, resultCode);
- mCallbacklatch.countDown();
}
};
mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, false);
mLooper.dispatchAll();
- callbackTimeoutHelper(mCallbacklatch);
assertEquals(0, mEnMgr.uiProvisionCount);
mEnMgr.reset();
// 3. No cache value and ui entitlement check is needed.
@@ -291,12 +283,10 @@
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
assertEquals(TETHER_ERROR_PROVISIONING_FAILED, resultCode);
- mCallbacklatch.countDown();
}
};
mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
mLooper.dispatchAll();
- callbackTimeoutHelper(mCallbacklatch);
assertEquals(1, mEnMgr.uiProvisionCount);
mEnMgr.reset();
// 4. Cache value is TETHER_ERROR_PROVISIONING_FAILED and don't need to run entitlement
@@ -306,12 +296,10 @@
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
assertEquals(TETHER_ERROR_PROVISIONING_FAILED, resultCode);
- mCallbacklatch.countDown();
}
};
mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, false);
mLooper.dispatchAll();
- callbackTimeoutHelper(mCallbacklatch);
assertEquals(0, mEnMgr.uiProvisionCount);
mEnMgr.reset();
// 5. Cache value is TETHER_ERROR_PROVISIONING_FAILED and ui entitlement check is needed.
@@ -320,12 +308,10 @@
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
assertEquals(TETHER_ERROR_NO_ERROR, resultCode);
- mCallbacklatch.countDown();
}
};
mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
mLooper.dispatchAll();
- callbackTimeoutHelper(mCallbacklatch);
assertEquals(1, mEnMgr.uiProvisionCount);
mEnMgr.reset();
// 6. Cache value is TETHER_ERROR_NO_ERROR.
@@ -334,12 +320,10 @@
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
assertEquals(TETHER_ERROR_NO_ERROR, resultCode);
- mCallbacklatch.countDown();
}
};
mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
mLooper.dispatchAll();
- callbackTimeoutHelper(mCallbacklatch);
assertEquals(0, mEnMgr.uiProvisionCount);
mEnMgr.reset();
// 7. Test get value for other downstream type.
@@ -347,12 +331,10 @@
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
assertEquals(TETHER_ERROR_ENTITLEMENT_UNKNOWN, resultCode);
- mCallbacklatch.countDown();
}
};
mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_USB, receiver, false);
mLooper.dispatchAll();
- callbackTimeoutHelper(mCallbacklatch);
assertEquals(0, mEnMgr.uiProvisionCount);
mEnMgr.reset();
// 8. Test get value for invalid downstream type.
@@ -361,22 +343,14 @@
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
assertEquals(TETHER_ERROR_ENTITLEMENT_UNKNOWN, resultCode);
- mCallbacklatch.countDown();
}
};
mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI_P2P, receiver, true);
mLooper.dispatchAll();
- callbackTimeoutHelper(mCallbacklatch);
assertEquals(0, mEnMgr.uiProvisionCount);
mEnMgr.reset();
}
- void callbackTimeoutHelper(final CountDownLatch latch) throws Exception {
- if (!latch.await(1, TimeUnit.SECONDS)) {
- fail("Timout, fail to receive callback");
- }
- }
-
@Test
public void verifyPermissionResult() {
setupForRequiredProvisioning();
diff --git a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
index 93bffe9..52a82dd 100644
--- a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
+++ b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
@@ -36,10 +36,12 @@
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.provider.Settings;
import android.util.Slog;
import android.util.Xml;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.content.PackageMonitor;
import libcore.io.IoUtils;
@@ -261,22 +263,7 @@
// And reset to the wallpaper service we should be using
ComponentName wpService = parseWallpaperComponent(infoStage, "wp");
- if (servicePackageExists(wpService)) {
- Slog.i(TAG, "Using wallpaper service " + wpService);
- mWm.setWallpaperComponent(wpService, UserHandle.USER_SYSTEM);
- if (!lockImageStage.exists()) {
- // We have a live wallpaper and no static lock image,
- // allow live wallpaper to show "through" on lock screen.
- mWm.clear(FLAG_LOCK);
- }
- } else {
- // If we've restored a live wallpaper, but the component doesn't exist,
- // we should log it as an error so we can easily identify the problem
- // in reports from users
- if (wpService != null) {
- Slog.e(TAG, "Wallpaper service " + wpService + " isn't available.");
- }
- }
+ updateWallpaperComponent(wpService, !lockImageStage.exists());
} catch (Exception e) {
Slog.e(TAG, "Unable to restore wallpaper: " + e.getMessage());
} finally {
@@ -293,6 +280,28 @@
}
}
+ @VisibleForTesting
+ void updateWallpaperComponent(ComponentName wpService, boolean applyToLock) throws IOException {
+ if (servicePackageExists(wpService)) {
+ Slog.i(TAG, "Using wallpaper service " + wpService);
+ mWm.setWallpaperComponent(wpService, UserHandle.USER_SYSTEM);
+ if (applyToLock) {
+ // We have a live wallpaper and no static lock image,
+ // allow live wallpaper to show "through" on lock screen.
+ mWm.clear(FLAG_LOCK);
+ }
+ } else {
+ // If we've restored a live wallpaper, but the component doesn't exist,
+ // we should log it as an error so we can easily identify the problem
+ // in reports from users
+ if (wpService != null) {
+ applyComponentAtInstall(wpService, applyToLock);
+ Slog.w(TAG, "Wallpaper service " + wpService + " isn't available. "
+ + " Will try to apply later");
+ }
+ }
+ }
+
private void restoreFromStage(File stage, File info, String hintTag, int which)
throws IOException {
if (stage.exists()) {
@@ -372,7 +381,8 @@
return (value == null) ? defValue : Integer.parseInt(value);
}
- private boolean servicePackageExists(ComponentName comp) {
+ @VisibleForTesting
+ boolean servicePackageExists(ComponentName comp) {
try {
if (comp != null) {
final IPackageManager pm = AppGlobals.getPackageManager();
@@ -401,4 +411,53 @@
throws IOException {
// Intentionally blank
}
+
+ private void applyComponentAtInstall(ComponentName componentName, boolean applyToLock) {
+ PackageMonitor packageMonitor = getWallpaperPackageMonitor(componentName, applyToLock);
+ packageMonitor.register(getBaseContext(), null, UserHandle.ALL, true);
+ }
+
+ @VisibleForTesting
+ PackageMonitor getWallpaperPackageMonitor(ComponentName componentName, boolean applyToLock) {
+ return new PackageMonitor() {
+ @Override
+ public void onPackageAdded(String packageName, int uid) {
+ if (!isDeviceInRestore()) {
+ // We don't want to reapply the wallpaper outside a restore.
+ unregister();
+ return;
+ }
+
+ if (componentName.getPackageName().equals(packageName)) {
+ Slog.d(TAG, "Applying component " + componentName);
+ mWm.setWallpaperComponent(componentName);
+ if (applyToLock) {
+ try {
+ mWm.clear(FLAG_LOCK);
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to apply live wallpaper to lock screen: " + e);
+ }
+ }
+ // We're only expecting to restore the wallpaper component once.
+ unregister();
+ }
+ }
+ };
+ }
+
+ @VisibleForTesting
+ boolean isDeviceInRestore() {
+ try {
+ boolean isInSetup = Settings.Secure.getInt(getBaseContext().getContentResolver(),
+ Settings.Secure.USER_SETUP_COMPLETE) == 0;
+ boolean isInDeferredSetup = Settings.Secure.getInt(getBaseContext()
+ .getContentResolver(),
+ Settings.Secure.USER_SETUP_PERSONALIZATION_STATE) ==
+ Settings.Secure.USER_SETUP_PERSONALIZATION_STARTED;
+ return isInSetup || isInDeferredSetup;
+ } catch (Settings.SettingNotFoundException e) {
+ Slog.w(TAG, "Failed to check if the user is in restore: " + e);
+ return false;
+ }
+ }
}
\ No newline at end of file
diff --git a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/tests/WallpaperBackupAgentTest.java b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
similarity index 65%
rename from packages/WallpaperBackup/test/src/com/android/wallpaperbackup/tests/WallpaperBackupAgentTest.java
rename to packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
index 255fdef..4367075 100644
--- a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/tests/WallpaperBackupAgentTest.java
+++ b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wallpaperbackup.tests;
+package com.android.wallpaperbackup;
import static android.app.WallpaperManager.FLAG_LOCK;
import static android.app.WallpaperManager.FLAG_SYSTEM;
@@ -26,17 +26,22 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.WallpaperManager;
import android.app.backup.FullBackupDataOutput;
+import android.content.ComponentName;
+import android.content.Context;
import android.content.SharedPreferences;
import android.os.UserHandle;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.content.PackageMonitor;
import com.android.wallpaperbackup.WallpaperBackupAgent;
import com.android.wallpaperbackup.utils.ContextWithServiceOverrides;
@@ -58,6 +63,7 @@
public class WallpaperBackupAgentTest {
private static final String SYSTEM_GENERATION = "system_gen";
private static final String LOCK_GENERATION = "lock_gen";
+ private static final String TEST_WALLPAPER_PACKAGE = "wallpaper_package";
private static final int TEST_SYSTEM_WALLPAPER_ID = 1;
private static final int TEST_LOCK_WALLPAPER_ID = 2;
@@ -66,11 +72,13 @@
@Mock private WallpaperManager mWallpaperManager;
@Mock private SharedPreferences mSharedPreferences;
@Mock private SharedPreferences.Editor mSharedPreferenceEditor;
+ @Mock private Context mMockContext;
@Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
private ContextWithServiceOverrides mContext;
private IsolatedWallpaperBackupAgent mWallpaperBackupAgent;
+ private ComponentName mWallpaperComponent;
@Before
public void setUp() {
@@ -88,6 +96,8 @@
mWallpaperBackupAgent = new IsolatedWallpaperBackupAgent(mTemporaryFolder.getRoot());
mWallpaperBackupAgent.attach(mContext);
mWallpaperBackupAgent.onCreate();
+
+ mWallpaperComponent = new ComponentName(TEST_WALLPAPER_PACKAGE, "");
}
@Test
@@ -130,6 +140,69 @@
inOrder.verify(mSharedPreferenceEditor).apply();
}
+ @Test
+ public void updateWallpaperComponent_doesApplyLater() throws IOException {
+ mWallpaperBackupAgent.mIsDeviceInRestore = true;
+
+ mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent,
+ /* applyToLock */ true);
+
+ // Imitate wallpaper component installation.
+ mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(TEST_WALLPAPER_PACKAGE,
+ /* uid */0);
+
+ verify(mWallpaperManager, times(1)).setWallpaperComponent(mWallpaperComponent);
+ verify(mWallpaperManager, times(1)).clear(eq(FLAG_LOCK));
+ }
+
+ @Test
+ public void updateWallpaperComponent_applyToLockFalse_doesApplyLaterOnlyToMainScreen()
+ throws IOException {
+ mWallpaperBackupAgent.mIsDeviceInRestore = true;
+
+ mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent,
+ /* applyToLock */ false);
+
+ // Imitate wallpaper component installation.
+ mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(TEST_WALLPAPER_PACKAGE,
+ /* uid */0);
+
+ verify(mWallpaperManager, times(1)).setWallpaperComponent(mWallpaperComponent);
+ verify(mWallpaperManager, never()).clear(eq(FLAG_LOCK));
+ }
+
+ @Test
+ public void updateWallpaperComponent_deviceNotInRestore_doesNotApply()
+ throws IOException {
+ mWallpaperBackupAgent.mIsDeviceInRestore = false;
+
+ mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent,
+ /* applyToLock */ true);
+
+ // Imitate wallpaper component installation.
+ mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(TEST_WALLPAPER_PACKAGE,
+ /* uid */0);
+
+ verify(mWallpaperManager, never()).setWallpaperComponent(mWallpaperComponent);
+ verify(mWallpaperManager, never()).clear(eq(FLAG_LOCK));
+ }
+
+ @Test
+ public void updateWallpaperComponent_differentPackageInstalled_doesNotApply()
+ throws IOException {
+ mWallpaperBackupAgent.mIsDeviceInRestore = false;
+
+ mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent,
+ /* applyToLock */ true);
+
+ // Imitate "wrong" wallpaper component installation.
+ mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(/* packageName */"",
+ /* uid */0);
+
+ verify(mWallpaperManager, never()).setWallpaperComponent(mWallpaperComponent);
+ verify(mWallpaperManager, never()).clear(eq(FLAG_LOCK));
+ }
+
private void mockUnbackedUpState() {
mockCurrentWallpapers(TEST_SYSTEM_WALLPAPER_ID, TEST_LOCK_WALLPAPER_ID);
when(mSharedPreferences.getInt(eq(SYSTEM_GENERATION), eq(-1))).thenReturn(-1);
@@ -162,6 +235,8 @@
private class IsolatedWallpaperBackupAgent extends WallpaperBackupAgent {
File mWallpaperBaseDirectory;
List<File> mBackedUpFiles = new ArrayList<>();
+ PackageMonitor mWallpaperPackageMonitor;
+ boolean mIsDeviceInRestore = false;
IsolatedWallpaperBackupAgent(File wallpaperBaseDirectory) {
mWallpaperBaseDirectory = wallpaperBaseDirectory;
@@ -181,5 +256,27 @@
public SharedPreferences getSharedPreferences(File file, int mode) {
return mSharedPreferences;
}
+
+ @Override
+ boolean servicePackageExists(ComponentName comp) {
+ return false;
+ }
+
+ @Override
+ boolean isDeviceInRestore() {
+ return mIsDeviceInRestore;
+ }
+
+ @Override
+ PackageMonitor getWallpaperPackageMonitor(ComponentName componentName,
+ boolean applyToLock) {
+ mWallpaperPackageMonitor = super.getWallpaperPackageMonitor(componentName, applyToLock);
+ return mWallpaperPackageMonitor;
+ }
+
+ @Override
+ public Context getBaseContext() {
+ return mMockContext;
+ }
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java
index ff59c24..20a11bd 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java
@@ -17,6 +17,7 @@
package com.android.server.accessibility;
import android.annotation.NonNull;
+import android.app.ActivityManager;
import android.os.ShellCommand;
import android.os.UserHandle;
@@ -83,7 +84,7 @@
return null;
}
}
- return UserHandle.USER_SYSTEM;
+ return ActivityManager.getCurrentUser();
}
@Override
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 192ea72..3c0d880 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -119,8 +119,6 @@
private static final int MESSAGE_DISABLE = 2;
private static final int MESSAGE_HANDLE_ENABLE_DELAYED = 3;
private static final int MESSAGE_HANDLE_DISABLE_DELAYED = 4;
- private static final int MESSAGE_REGISTER_ADAPTER = 20;
- private static final int MESSAGE_UNREGISTER_ADAPTER = 21;
private static final int MESSAGE_REGISTER_STATE_CHANGE_CALLBACK = 30;
private static final int MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK = 31;
private static final int MESSAGE_BLUETOOTH_SERVICE_CONNECTED = 40;
@@ -642,10 +640,9 @@
Slog.w(TAG, "Callback is null in registerAdapter");
return null;
}
- Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_ADAPTER);
- msg.obj = callback;
- mHandler.sendMessage(msg);
-
+ synchronized (mCallbacks) {
+ mCallbacks.register(callback);
+ }
return mBluetooth;
}
@@ -655,9 +652,9 @@
return;
}
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_ADAPTER);
- msg.obj = callback;
- mHandler.sendMessage(msg);
+ synchronized (mCallbacks) {
+ mCallbacks.unregister(callback);
+ }
}
public void registerStateChangeCallback(IBluetoothStateChangeCallback callback) {
@@ -1559,18 +1556,20 @@
* Inform BluetoothAdapter instances that Adapter service is up
*/
private void sendBluetoothServiceUpCallback() {
- try {
- int n = mCallbacks.beginBroadcast();
- Slog.d(TAG, "Broadcasting onBluetoothServiceUp() to " + n + " receivers.");
- for (int i = 0; i < n; i++) {
- try {
- mCallbacks.getBroadcastItem(i).onBluetoothServiceUp(mBluetooth);
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to call onBluetoothServiceUp() on callback #" + i, e);
+ synchronized (mCallbacks) {
+ try {
+ int n = mCallbacks.beginBroadcast();
+ Slog.d(TAG, "Broadcasting onBluetoothServiceUp() to " + n + " receivers.");
+ for (int i = 0; i < n; i++) {
+ try {
+ mCallbacks.getBroadcastItem(i).onBluetoothServiceUp(mBluetooth);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to call onBluetoothServiceUp() on callback #" + i, e);
+ }
}
+ } finally {
+ mCallbacks.finishBroadcast();
}
- } finally {
- mCallbacks.finishBroadcast();
}
}
@@ -1578,18 +1577,20 @@
* Inform BluetoothAdapter instances that Adapter service is down
*/
private void sendBluetoothServiceDownCallback() {
- try {
- int n = mCallbacks.beginBroadcast();
- Slog.d(TAG, "Broadcasting onBluetoothServiceDown() to " + n + " receivers.");
- for (int i = 0; i < n; i++) {
- try {
- mCallbacks.getBroadcastItem(i).onBluetoothServiceDown();
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to call onBluetoothServiceDown() on callback #" + i, e);
+ synchronized (mCallbacks) {
+ try {
+ int n = mCallbacks.beginBroadcast();
+ Slog.d(TAG, "Broadcasting onBluetoothServiceDown() to " + n + " receivers.");
+ for (int i = 0; i < n; i++) {
+ try {
+ mCallbacks.getBroadcastItem(i).onBluetoothServiceDown();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to call onBluetoothServiceDown() on callback #" + i, e);
+ }
}
+ } finally {
+ mCallbacks.finishBroadcast();
}
- } finally {
- mCallbacks.finishBroadcast();
}
}
@@ -1917,17 +1918,6 @@
mContext.getPackageName());
}
break;
-
- case MESSAGE_REGISTER_ADAPTER: {
- IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj;
- mCallbacks.register(callback);
- break;
- }
- case MESSAGE_UNREGISTER_ADAPTER: {
- IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj;
- mCallbacks.unregister(callback);
- break;
- }
case MESSAGE_REGISTER_STATE_CHANGE_CALLBACK: {
IBluetoothStateChangeCallback callback =
(IBluetoothStateChangeCallback) msg.obj;
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index a9c3079..7f25de6b 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -1836,12 +1836,13 @@
@Override
public void requestLocationUpdates(LocationRequest request, ILocationListener listener,
- PendingIntent intent, String packageName, String featureId) {
+ PendingIntent intent, String packageName, String featureId, String listenerId) {
if (request == null) {
request = DEFAULT_LOCATION_REQUEST;
}
- CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, featureId);
+ CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, featureId,
+ listenerId);
identity.enforceLocationPermission();
WorkSource workSource = request.getWorkSource();
@@ -2027,7 +2028,7 @@
@Override
public boolean getCurrentLocation(LocationRequest locationRequest,
ICancellationSignal remoteCancellationSignal, ILocationListener listener,
- String packageName, String featureId) {
+ String packageName, String featureId, String listenerId) {
// side effect of validating locationRequest and packageName
Location lastLocation = getLastLocation(locationRequest, packageName, featureId);
if (lastLocation != null) {
@@ -2052,7 +2053,7 @@
}
}
- requestLocationUpdates(locationRequest, listener, null, packageName, featureId);
+ requestLocationUpdates(locationRequest, listener, null, packageName, featureId, listenerId);
CancellationSignal cancellationSignal = CancellationSignal.fromTransport(
remoteCancellationSignal);
if (cancellationSignal != null) {
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 0671477..9aefc8d 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -4468,9 +4468,8 @@
String.format("/storage/emulated/%d/Android/data/%s/",
userId, pkg);
- int appUid =
- UserHandle.getUid(userId, mPmInternal.getPackage(pkg).getUid());
// Create package obb and data dir if it doesn't exist.
+ int appUid = UserHandle.getUid(userId, mPmInternal.getPackage(pkg).getUid());
File file = new File(packageObbDir);
if (!file.exists()) {
vold.setupAppDir(packageObbDir, appUid);
diff --git a/services/core/java/com/android/server/am/AnrHelper.java b/services/core/java/com/android/server/am/AnrHelper.java
index bc45533..8f8990f 100644
--- a/services/core/java/com/android/server/am/AnrHelper.java
+++ b/services/core/java/com/android/server/am/AnrHelper.java
@@ -98,7 +98,7 @@
final long endTime = SystemClock.uptimeMillis();
Slog.d(TAG, "Completed ANR of " + r.mApp.processName + " in "
+ (endTime - startTime) + "ms, latency " + reportLatency
- + (onlyDumpSelf ? "ms" : "ms (expired, only dump ANR app)"));
+ + (onlyDumpSelf ? "ms (expired, only dump ANR app)" : "ms"));
}
mRunning.set(false);
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index dbcb3da..2d6ef81 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -231,7 +231,8 @@
final ServiceThread adjusterThread =
new ServiceThread(TAG, TOP_APP_PRIORITY_BOOST, false /* allowIo */);
adjusterThread.start();
- Process.setThreadGroupAndCpuset(adjusterThread.getThreadId(), THREAD_GROUP_TOP_APP);
+ adjusterThread.getThreadHandler().post(() -> Process.setThreadGroupAndCpuset(
+ adjusterThread.getThreadId(), THREAD_GROUP_TOP_APP));
return adjusterThread;
}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index cb6990a..786e9cf 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -97,6 +97,7 @@
import android.system.Os;
import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.EventLog;
import android.util.LongSparseArray;
import android.util.Pair;
@@ -137,6 +138,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
* Activity manager code dealing with processes.
@@ -2129,18 +2131,11 @@
for (String packageName : packages) {
String volumeUuid = pmInt.getPackage(packageName).getVolumeUuid();
long inode = pmInt.getCeDataInode(packageName, userId);
- if (inode != 0) {
- result.put(packageName, Pair.create(volumeUuid, inode));
+ if (inode == 0) {
+ Slog.w(TAG, packageName + " inode == 0 (b/152760674)");
+ return null;
}
- }
- if (mAppDataIsolationWhitelistedApps != null) {
- for (String packageName : mAppDataIsolationWhitelistedApps) {
- String volumeUuid = pmInt.getPackage(packageName).getVolumeUuid();
- long inode = pmInt.getCeDataInode(packageName, userId);
- if (inode != 0) {
- result.put(packageName, Pair.create(volumeUuid, inode));
- }
- }
+ result.put(packageName, Pair.create(volumeUuid, inode));
}
return result;
@@ -2162,35 +2157,56 @@
app.setHasForegroundActivities(true);
}
+ final Map<String, Pair<String, Long>> pkgDataInfoMap;
+ final Map<String, Pair<String, Long>> whitelistedAppDataInfoMap;
+ boolean bindMountAppStorageDirs = false;
+ boolean bindMountAppsData = mAppDataIsolationEnabled
+ && UserHandle.isApp(app.uid)
+ && mPlatformCompat.isChangeEnabled(APP_DATA_DIRECTORY_ISOLATION, app.info);
+
+ // Get all packages belongs to the same shared uid. sharedPackages is empty array
+ // if it doesn't have shared uid.
+ final PackageManagerInternal pmInt = mService.getPackageManagerInternalLocked();
+ final String[] sharedPackages = pmInt.getSharedUserPackagesForPackage(
+ app.info.packageName, app.userId);
+ final String[] targetPackagesList = sharedPackages.length == 0
+ ? new String[]{app.info.packageName} : sharedPackages;
+
+ pkgDataInfoMap = getPackageAppDataInfoMap(pmInt, targetPackagesList, uid);
+ if (pkgDataInfoMap == null) {
+ // TODO(b/152760674): Handle inode == 0 case properly, now we just give it a
+ // tmp free pass.
+ bindMountAppsData = false;
+ }
+
+ // Remove all packages in pkgDataInfoMap from mAppDataIsolationWhitelistedApps, so
+ // it won't be mounted twice.
+ final Set<String> whitelistedApps = new ArraySet<>(mAppDataIsolationWhitelistedApps);
+ for (String pkg : targetPackagesList) {
+ whitelistedApps.remove(pkg);
+ }
+
+ whitelistedAppDataInfoMap = getPackageAppDataInfoMap(pmInt,
+ whitelistedApps.toArray(new String[0]), uid);
+ if (whitelistedAppDataInfoMap == null) {
+ // TODO(b/152760674): Handle inode == 0 case properly, now we just give it a
+ // tmp free pass.
+ bindMountAppsData = false;
+ }
+
+ int userId = UserHandle.getUserId(uid);
StorageManagerInternal storageManagerInternal = LocalServices.getService(
StorageManagerInternal.class);
- final Map<String, Pair<String, Long>> pkgDataInfoMap;
- boolean bindMountAppStorageDirs = false;
-
- if (mAppDataIsolationEnabled && UserHandle.isApp(app.uid)
- && mPlatformCompat.isChangeEnabled(APP_DATA_DIRECTORY_ISOLATION, app.info)) {
- // Get all packages belongs to the same shared uid. sharedPackages is empty array
- // if it doesn't have shared uid.
- final PackageManagerInternal pmInt = mService.getPackageManagerInternalLocked();
- final String[] sharedPackages = pmInt.getSharedUserPackagesForPackage(
- app.info.packageName, app.userId);
- pkgDataInfoMap = getPackageAppDataInfoMap(pmInt, sharedPackages.length == 0
- ? new String[]{app.info.packageName} : sharedPackages, uid);
-
- int userId = UserHandle.getUserId(uid);
- if (mVoldAppDataIsolationEnabled
- && !storageManagerInternal.isExternalStorageService(uid)) {
- bindMountAppStorageDirs = true;
- if (!storageManagerInternal.prepareStorageDirs(userId, pkgDataInfoMap.keySet(),
- app.processName)) {
- // Cannot prepare Android/app and Android/obb directory,
- // so we won't mount it in zygote.
- app.bindMountPending = true;
- bindMountAppStorageDirs = false;
- }
+ if (mVoldAppDataIsolationEnabled && UserHandle.isApp(app.uid)
+ && !storageManagerInternal.isExternalStorageService(uid)) {
+ bindMountAppStorageDirs = true;
+ if (!storageManagerInternal.prepareStorageDirs(userId, pkgDataInfoMap.keySet(),
+ app.processName)) {
+ // Cannot prepare Android/app and Android/obb directory,
+ // so we won't mount it in zygote.
+ app.bindMountPending = true;
+ bindMountAppStorageDirs = false;
}
- } else {
- pkgDataInfoMap = null;
}
final Process.ProcessStartResult startResult;
@@ -2208,7 +2224,8 @@
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, null, app.info.packageName,
/*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, isTopApp,
- app.mDisabledCompatChanges, pkgDataInfoMap, bindMountAppStorageDirs,
+ app.mDisabledCompatChanges, pkgDataInfoMap, whitelistedAppDataInfoMap,
+ bindMountAppsData, bindMountAppStorageDirs,
new String[]{PROC_START_SEQ_IDENT + app.startSeq});
} else {
startResult = Process.start(entryPoint,
@@ -2216,7 +2233,7 @@
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, invokeWith, app.info.packageName, zygotePolicyFlags,
isTopApp, app.mDisabledCompatChanges, pkgDataInfoMap,
- bindMountAppStorageDirs,
+ whitelistedAppDataInfoMap, bindMountAppsData, bindMountAppStorageDirs,
new String[]{PROC_START_SEQ_IDENT + app.startSeq});
}
checkSlow(startTime, "startProcess: returned from zygote!");
diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
index 4431abe..808f8c21 100644
--- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
+++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
@@ -558,8 +558,10 @@
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
statsModality(), BiometricsProtoEnums.ISSUE_CANCEL_TIMED_OUT);
+ ClientMonitor newClient = mPendingClient;
mCurrentClient = null;
- startClient(mPendingClient, false);
+ mPendingClient = null;
+ startClient(newClient, false);
}
}
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index 962f337..9a910bf 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -569,9 +569,10 @@
// Immediately dispatch notifications to foreground apps that
// are important to the user; all other background observers are
// delayed to avoid stampeding
+ final boolean noDelay = (key.flags & ContentResolver.NOTIFY_NO_DELAY) != 0;
final int procState = LocalServices.getService(ActivityManagerInternal.class)
.getUidProcessState(key.uid);
- if (procState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
+ if (procState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND || noDelay) {
task.run();
} else {
BackgroundThread.getHandler().postDelayed(task, BACKGROUND_OBSERVER_DELAY);
diff --git a/services/core/java/com/android/server/display/color/AppSaturationController.java b/services/core/java/com/android/server/display/color/AppSaturationController.java
index e42be02..6a685bf 100644
--- a/services/core/java/com/android/server/display/color/AppSaturationController.java
+++ b/services/core/java/com/android/server/display/color/AppSaturationController.java
@@ -17,6 +17,7 @@
package com.android.server.display.color;
import android.annotation.UserIdInt;
+import android.util.ArrayMap;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
@@ -61,11 +62,12 @@
* Set the saturation level ({@code ColorDisplayManager#SaturationLevel} constant for a given
* package name and userId.
*/
- public boolean setSaturationLevel(String packageName, @UserIdInt int userId,
+ public boolean setSaturationLevel(String callingPackageName, String affectedPackageName,
+ @UserIdInt int userId,
int saturationLevel) {
synchronized (mLock) {
- return getSaturationControllerLocked(packageName, userId)
- .setSaturationLevel(saturationLevel);
+ return getSaturationControllerLocked(affectedPackageName, userId)
+ .setSaturationLevel(callingPackageName, saturationLevel);
}
}
@@ -148,13 +150,19 @@
private static class SaturationController {
+ private static final int FULL_SATURATION = 100;
+
private final List<WeakReference<ColorTransformController>> mControllerRefs =
new ArrayList<>();
- private int mSaturationLevel = 100;
+ private final ArrayMap<String, Integer> mSaturationLevels = new ArrayMap<>();
private float[] mTransformMatrix = new float[9];
- private boolean setSaturationLevel(int saturationLevel) {
- mSaturationLevel = saturationLevel;
+ private boolean setSaturationLevel(String callingPackageName, int saturationLevel) {
+ if (saturationLevel == FULL_SATURATION) {
+ mSaturationLevels.remove(callingPackageName);
+ } else {
+ mSaturationLevels.put(callingPackageName, saturationLevel);
+ }
if (!mControllerRefs.isEmpty()) {
return updateState();
}
@@ -163,17 +171,27 @@
private boolean addColorTransformController(
WeakReference<ColorTransformController> controller) {
+ clearExpiredReferences();
mControllerRefs.add(controller);
- if (mSaturationLevel != 100) {
+ if (!mSaturationLevels.isEmpty()) {
return updateState();
- } else {
- clearExpiredReferences();
}
return false;
}
+ private int calculateSaturationLevel() {
+ int saturationLevel = FULL_SATURATION;
+ for (int i = 0; i < mSaturationLevels.size(); i++) {
+ final int level = mSaturationLevels.valueAt(i);
+ if (level < saturationLevel) {
+ saturationLevel = level;
+ }
+ }
+ return saturationLevel;
+ }
+
private boolean updateState() {
- computeGrayscaleTransformMatrix(mSaturationLevel / 100f, mTransformMatrix);
+ computeGrayscaleTransformMatrix(calculateSaturationLevel() / 100f, mTransformMatrix);
boolean updated = false;
final Iterator<WeakReference<ColorTransformController>> iterator = mControllerRefs
@@ -190,7 +208,6 @@
}
}
return updated;
-
}
private void clearExpiredReferences() {
@@ -206,7 +223,7 @@
}
private void dump(PrintWriter pw) {
- pw.println(" mSaturationLevel: " + mSaturationLevel);
+ pw.println(" mSaturationLevels: " + mSaturationLevels);
pw.println(" mControllerRefs count: " + mControllerRefs.size());
}
}
diff --git a/services/core/java/com/android/server/display/color/ColorDisplayService.java b/services/core/java/com/android/server/display/color/ColorDisplayService.java
index 2dc2cf0..95a98f1 100644
--- a/services/core/java/com/android/server/display/color/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/color/ColorDisplayService.java
@@ -44,6 +44,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.hardware.display.ColorDisplayManager;
@@ -73,6 +74,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
import com.android.server.DisplayThread;
+import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.twilight.TwilightListener;
import com.android.server.twilight.TwilightManager;
@@ -817,9 +819,11 @@
return LocalDateTime.MIN;
}
- private boolean setAppSaturationLevelInternal(String packageName, int saturationLevel) {
+ private boolean setAppSaturationLevelInternal(String callingPackageName,
+ String affectedPackageName, int saturationLevel) {
return mAppSaturationController
- .setSaturationLevel(packageName, mCurrentUser, saturationLevel);
+ .setSaturationLevel(callingPackageName, affectedPackageName, mCurrentUser,
+ saturationLevel);
}
private void setColorModeInternal(@ColorMode int colorMode) {
@@ -1533,9 +1537,11 @@
getContext().enforceCallingPermission(
Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
"Permission required to set display saturation level");
+ final String callingPackageName = LocalServices.getService(PackageManagerInternal.class)
+ .getNameForUid(Binder.getCallingUid());
final long token = Binder.clearCallingIdentity();
try {
- return setAppSaturationLevelInternal(packageName, level);
+ return setAppSaturationLevelInternal(callingPackageName, packageName, level);
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/services/core/java/com/android/server/location/AppOpsHelper.java b/services/core/java/com/android/server/location/AppOpsHelper.java
index 9c27916..cb64c50 100644
--- a/services/core/java/com/android/server/location/AppOpsHelper.java
+++ b/services/core/java/com/android/server/location/AppOpsHelper.java
@@ -191,7 +191,7 @@
callerIdentity.uid,
callerIdentity.packageName,
callerIdentity.featureId,
- null) == AppOpsManager.MODE_ALLOWED;
+ callerIdentity.listenerId) == AppOpsManager.MODE_ALLOWED;
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -210,7 +210,7 @@
callerIdentity.packageName,
false,
callerIdentity.featureId,
- null) == AppOpsManager.MODE_ALLOWED;
+ callerIdentity.listenerId) == AppOpsManager.MODE_ALLOWED;
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -245,7 +245,7 @@
callerIdentity.uid,
callerIdentity.packageName,
callerIdentity.featureId,
- null) == AppOpsManager.MODE_ALLOWED;
+ callerIdentity.listenerId) == AppOpsManager.MODE_ALLOWED;
} finally {
Binder.restoreCallingIdentity(identity);
}
diff --git a/services/core/java/com/android/server/location/CallerIdentity.java b/services/core/java/com/android/server/location/CallerIdentity.java
index b84fd13..8d508bb 100644
--- a/services/core/java/com/android/server/location/CallerIdentity.java
+++ b/services/core/java/com/android/server/location/CallerIdentity.java
@@ -83,12 +83,22 @@
*/
public static CallerIdentity fromBinder(Context context, String packageName,
@Nullable String featureId) {
+ return fromBinder(context, packageName, featureId, null);
+ }
+
+ /**
+ * Creates a CallerIdentity from the current binder identity, using the given package, feature
+ * id, and listener id. The package will be checked to enforce it belongs to the calling uid,
+ * and a security exception will be thrown if it is invalid.
+ */
+ public static CallerIdentity fromBinder(Context context, String packageName,
+ @Nullable String featureId, @Nullable String listenerId) {
int uid = Binder.getCallingUid();
if (!ArrayUtils.contains(context.getPackageManager().getPackagesForUid(uid), packageName)) {
throw new SecurityException("invalid package \"" + packageName + "\" for uid " + uid);
}
- return fromBinderUnsafe(context, packageName, featureId);
+ return fromBinderUnsafe(context, packageName, featureId, listenerId);
}
/**
@@ -99,8 +109,19 @@
*/
public static CallerIdentity fromBinderUnsafe(Context context, String packageName,
@Nullable String featureId) {
+ return fromBinderUnsafe(context, packageName, featureId, null);
+ }
+
+ /**
+ * Creates a CallerIdentity from the current binder identity, using the given package, feature
+ * id, and listener id. The package will not be checked to enforce that it belongs to the
+ * calling uid - this method should only be used if the package will be validated by some other
+ * means, such as an appops call.
+ */
+ public static CallerIdentity fromBinderUnsafe(Context context, String packageName,
+ @Nullable String featureId, @Nullable String listenerId) {
return new CallerIdentity(Binder.getCallingUid(), Binder.getCallingPid(),
- UserHandle.getCallingUserId(), packageName, featureId,
+ UserHandle.getCallingUserId(), packageName, featureId, listenerId,
getBinderPermissionLevel(context));
}
@@ -157,6 +178,9 @@
/** The calling feature id. */
public final @Nullable String featureId;
+ /** The calling listener id. */
+ public final @Nullable String listenerId;
+
/**
* The calling location permission level. This field should only be used for validating
* permissions for API access. It should not be used for validating permissions for location
@@ -167,11 +191,18 @@
@VisibleForTesting
public CallerIdentity(int uid, int pid, int userId, String packageName,
@Nullable String featureId, @PermissionLevel int permissionLevel) {
+ this(uid, pid, userId, packageName, featureId, null, permissionLevel);
+ }
+
+ private CallerIdentity(int uid, int pid, int userId, String packageName,
+ @Nullable String featureId, @Nullable String listenerId,
+ @PermissionLevel int permissionLevel) {
this.uid = uid;
this.pid = pid;
this.userId = userId;
this.packageName = Objects.requireNonNull(packageName);
this.featureId = featureId;
+ this.listenerId = listenerId;
this.permissionLevel = Preconditions.checkArgumentInRange(permissionLevel, PERMISSION_NONE,
PERMISSION_FINE, "permissionLevel");
}
@@ -216,7 +247,8 @@
return uid == that.uid
&& pid == that.pid
&& packageName.equals(that.packageName)
- && Objects.equals(featureId, that.featureId);
+ && Objects.equals(featureId, that.featureId)
+ && Objects.equals(listenerId, that.listenerId);
}
@Override
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 1931079..c3413e8 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -98,18 +98,26 @@
public List<MediaRoute2Info> getSystemRoutes() {
final int uid = Binder.getCallingUid();
final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
+ final boolean hasModifyAudioRoutingPermission = mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ == PackageManager.PERMISSION_GRANTED;
final long token = Binder.clearCallingIdentity();
try {
Collection<MediaRoute2Info> systemRoutes;
synchronized (mLock) {
UserRecord userRecord = getOrCreateUserRecordLocked(userId);
- MediaRoute2ProviderInfo providerInfo =
- userRecord.mHandler.mSystemProvider.getProviderInfo();
- if (providerInfo != null) {
- systemRoutes = providerInfo.getRoutes();
+ if (hasModifyAudioRoutingPermission) {
+ MediaRoute2ProviderInfo providerInfo =
+ userRecord.mHandler.mSystemProvider.getProviderInfo();
+ if (providerInfo != null) {
+ systemRoutes = providerInfo.getRoutes();
+ } else {
+ systemRoutes = Collections.emptyList();
+ }
} else {
- systemRoutes = Collections.emptyList();
+ systemRoutes = new ArrayList<>();
+ systemRoutes.add(userRecord.mHandler.mSystemProvider.getDefaultRoute());
}
}
return new ArrayList<>(systemRoutes);
@@ -122,18 +130,25 @@
public RoutingSessionInfo getSystemSessionInfo() {
final int uid = Binder.getCallingUid();
final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
+ final boolean hasModifyAudioRoutingPermission = mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ == PackageManager.PERMISSION_GRANTED;
final long token = Binder.clearCallingIdentity();
try {
RoutingSessionInfo systemSessionInfo = null;
synchronized (mLock) {
UserRecord userRecord = getOrCreateUserRecordLocked(userId);
- List<RoutingSessionInfo> sessionInfos =
- userRecord.mHandler.mSystemProvider.getSessionInfos();
- if (sessionInfos != null && !sessionInfos.isEmpty()) {
- systemSessionInfo = sessionInfos.get(0);
+ List<RoutingSessionInfo> sessionInfos;
+ if (hasModifyAudioRoutingPermission) {
+ sessionInfos = userRecord.mHandler.mSystemProvider.getSessionInfos();
+ if (sessionInfos != null && !sessionInfos.isEmpty()) {
+ systemSessionInfo = sessionInfos.get(0);
+ } else {
+ Slog.w(TAG, "System provider does not have any session info.");
+ }
} else {
- Slog.w(TAG, "System provider does not have any session info.");
+ systemSessionInfo = userRecord.mHandler.mSystemProvider.getDefaultSessionInfo();
}
}
return systemSessionInfo;
@@ -654,10 +669,20 @@
return;
}
- routerRecord.mUserRecord.mHandler.sendMessage(
- obtainMessage(UserHandler::transferToRouteOnHandler,
- routerRecord.mUserRecord.mHandler,
- DUMMY_REQUEST_ID, routerRecord, uniqueSessionId, route));
+ String defaultRouteId =
+ routerRecord.mUserRecord.mHandler.mSystemProvider.getDefaultRoute().getId();
+ if (route.isSystemRoute() && !routerRecord.mHasModifyAudioRoutingPermission
+ && !TextUtils.equals(route.getId(), defaultRouteId)) {
+ routerRecord.mUserRecord.mHandler.sendMessage(
+ obtainMessage(UserHandler::notifySessionCreationFailedToRouter,
+ routerRecord.mUserRecord.mHandler,
+ routerRecord, toOriginalRequestId(DUMMY_REQUEST_ID)));
+ } else {
+ routerRecord.mUserRecord.mHandler.sendMessage(
+ obtainMessage(UserHandler::transferToRouteOnHandler,
+ routerRecord.mUserRecord.mHandler,
+ DUMMY_REQUEST_ID, routerRecord, uniqueSessionId, route));
+ }
}
private void setSessionVolumeWithRouter2Locked(@NonNull IMediaRouter2 router,
@@ -1185,18 +1210,42 @@
}
}
- List<IMediaRouter2> routers = getRouters();
+ List<IMediaRouter2> routersWithModifyAudioRoutingPermission = getRouters(true);
+ List<IMediaRouter2> routersWithoutModifyAudioRoutingPermission = getRouters(false);
List<IMediaRouter2Manager> managers = getManagers();
+ List<MediaRoute2Info> defaultRoute = new ArrayList<>();
+ defaultRoute.add(mSystemProvider.getDefaultRoute());
+
if (addedRoutes.size() > 0) {
- notifyRoutesAddedToRouters(routers, addedRoutes);
+ notifyRoutesAddedToRouters(routersWithModifyAudioRoutingPermission, addedRoutes);
+ if (!provider.mIsSystemRouteProvider) {
+ notifyRoutesAddedToRouters(routersWithoutModifyAudioRoutingPermission,
+ addedRoutes);
+ } else if (prevInfo == null) {
+ notifyRoutesAddedToRouters(routersWithoutModifyAudioRoutingPermission,
+ defaultRoute);
+ } // 'else' is handled as changed routes
notifyRoutesAddedToManagers(managers, addedRoutes);
}
if (removedRoutes.size() > 0) {
- notifyRoutesRemovedToRouters(routers, removedRoutes);
+ notifyRoutesRemovedToRouters(routersWithModifyAudioRoutingPermission,
+ removedRoutes);
+ if (!provider.mIsSystemRouteProvider) {
+ notifyRoutesRemovedToRouters(routersWithoutModifyAudioRoutingPermission,
+ removedRoutes);
+ }
notifyRoutesRemovedToManagers(managers, removedRoutes);
}
if (changedRoutes.size() > 0) {
- notifyRoutesChangedToRouters(routers, changedRoutes);
+ notifyRoutesChangedToRouters(routersWithModifyAudioRoutingPermission,
+ changedRoutes);
+ if (!provider.mIsSystemRouteProvider) {
+ notifyRoutesChangedToRouters(routersWithoutModifyAudioRoutingPermission,
+ changedRoutes);
+ } else if (prevInfo != null) {
+ notifyRoutesChangedToRouters(routersWithoutModifyAudioRoutingPermission,
+ defaultRoute);
+ } // 'else' is handled as added routes
notifyRoutesChangedToManagers(managers, changedRoutes);
}
}
@@ -1223,6 +1272,15 @@
toOriginalRequestId(uniqueRequestId));
return;
}
+ if (route.isSystemRoute() && !routerRecord.mHasModifyAudioRoutingPermission
+ && !TextUtils.equals(route.getId(),
+ mSystemProvider.getDefaultRoute().getId())) {
+ Slog.w(TAG, "MODIFY_AUDIO_ROUTING permission is required to transfer to"
+ + route);
+ notifySessionCreationFailedToRouter(routerRecord,
+ toOriginalRequestId(uniqueRequestId));
+ return;
+ }
SessionCreationRequest request =
new SessionCreationRequest(routerRecord, uniqueRequestId, route, managerRecord);
@@ -1444,7 +1502,9 @@
if (service == null) {
return;
}
- notifySessionInfoChangedToRouters(getRouters(), sessionInfo);
+ notifySessionInfoChangedToRouters(getRouters(true), sessionInfo);
+ notifySessionInfoChangedToRouters(getRouters(false),
+ mSystemProvider.getDefaultSessionInfo());
return;
}
@@ -1565,7 +1625,7 @@
}
}
- private List<IMediaRouter2> getRouters() {
+ private List<IMediaRouter2> getAllRouters() {
final List<IMediaRouter2> routers = new ArrayList<>();
MediaRouter2ServiceImpl service = mServiceRef.get();
if (service == null) {
@@ -1579,6 +1639,23 @@
return routers;
}
+ private List<IMediaRouter2> getRouters(boolean hasModifyAudioRoutingPermission) {
+ final List<IMediaRouter2> routers = new ArrayList<>();
+ MediaRouter2ServiceImpl service = mServiceRef.get();
+ if (service == null) {
+ return routers;
+ }
+ synchronized (service.mLock) {
+ for (RouterRecord routerRecord : mUserRecord.mRouterRecords) {
+ if (hasModifyAudioRoutingPermission
+ == routerRecord.mHasModifyAudioRoutingPermission) {
+ routers.add(routerRecord.mRouter);
+ }
+ }
+ }
+ return routers;
+ }
+
private List<IMediaRouter2Manager> getManagers() {
final List<IMediaRouter2Manager> managers = new ArrayList<>();
MediaRouter2ServiceImpl service = mServiceRef.get();
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 5b16d68..6e2feeb 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -72,6 +72,7 @@
// This should be the currently selected route.
MediaRoute2Info mDefaultRoute;
MediaRoute2Info mDeviceRoute;
+ RoutingSessionInfo mDefaultSessionInfo;
final AudioRoutesInfo mCurAudioRoutesInfo = new AudioRoutesInfo();
final IAudioRoutesObserver.Stub mAudioRoutesObserver = new IAudioRoutesObserver.Stub() {
@@ -114,6 +115,7 @@
}
});
updateSessionInfosIfNeeded();
+
mContext.registerReceiver(new VolumeChangeReceiver(),
new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION));
@@ -156,6 +158,10 @@
@Override
public void transferToRoute(long requestId, String sessionId, String routeId) {
+ if (TextUtils.equals(routeId, DEFAULT_ROUTE_ID)) {
+ // The currently selected route is the default route.
+ return;
+ }
if (mBtRouteProvider != null) {
if (TextUtils.equals(routeId, mDeviceRoute.getId())) {
mBtRouteProvider.transferTo(null);
@@ -182,6 +188,10 @@
return mDefaultRoute;
}
+ public RoutingSessionInfo getDefaultSessionInfo() {
+ return mDefaultSessionInfo;
+ }
+
private void updateDeviceRoute(AudioRoutesInfo newRoutes) {
int name = R.string.default_audio_route_name;
if (newRoutes != null) {
@@ -229,8 +239,6 @@
*/
boolean updateSessionInfosIfNeeded() {
synchronized (mLock) {
- // Prevent to execute this method before mBtRouteProvider is created.
- if (mBtRouteProvider == null) return false;
RoutingSessionInfo oldSessionInfo = mSessionInfos.isEmpty() ? null : mSessionInfos.get(
0);
@@ -238,14 +246,19 @@
SYSTEM_SESSION_ID, "" /* clientPackageName */)
.setSystemSession(true);
- MediaRoute2Info selectedRoute = mBtRouteProvider.getSelectedRoute();
- if (selectedRoute == null) {
- selectedRoute = mDeviceRoute;
- } else {
- builder.addTransferableRoute(mDeviceRoute.getId());
+ MediaRoute2Info selectedRoute = mDeviceRoute;
+ if (mBtRouteProvider != null) {
+ MediaRoute2Info selectedBtRoute = mBtRouteProvider.getSelectedRoute();
+ if (selectedBtRoute != null) {
+ selectedRoute = selectedBtRoute;
+ builder.addTransferableRoute(mDeviceRoute.getId());
+ }
}
mSelectedRouteId = selectedRoute.getId();
- mDefaultRoute = new MediaRoute2Info.Builder(DEFAULT_ROUTE_ID, selectedRoute).build();
+ mDefaultRoute = new MediaRoute2Info.Builder(DEFAULT_ROUTE_ID, selectedRoute)
+ .setSystemRoute(true)
+ .setProviderId(mUniqueId)
+ .build();
builder.addSelectedRoute(mSelectedRouteId);
for (MediaRoute2Info route : mBtRouteProvider.getTransferableRoutes()) {
@@ -258,6 +271,12 @@
} else {
mSessionInfos.clear();
mSessionInfos.add(newSessionInfo);
+ mDefaultSessionInfo = new RoutingSessionInfo.Builder(
+ SYSTEM_SESSION_ID, "" /* clientPackageName */)
+ .setProviderId(mUniqueId)
+ .setSystemSession(true)
+ .addSelectedRoute(DEFAULT_ROUTE_ID)
+ .build();
return true;
}
}
@@ -302,6 +321,9 @@
} else if (mBtRouteProvider != null) {
mBtRouteProvider.setSelectedRouteVolume(newVolume);
}
+ mDefaultRoute = new MediaRoute2Info.Builder(mDefaultRoute)
+ .setVolume(newVolume)
+ .build();
publishProviderState();
}
}
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 1e4dc7b..daf4bf2 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -399,7 +399,9 @@
@VisibleForTesting
protected IApexService waitForApexService() {
try {
- return IApexService.Stub.asInterface(Binder.waitForService("apexservice"));
+ // Since apexd is a trusted platform component, synchronized calls are allowable
+ return IApexService.Stub.asInterface(
+ Binder.allowBlocking(Binder.waitForService("apexservice")));
} catch (RemoteException e) {
throw new IllegalStateException("Required service apexservice not available");
}
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index ba7583f..dab4bfd 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -46,6 +46,7 @@
import java.io.File;
import java.nio.file.Paths;
+import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@@ -106,6 +107,8 @@
private static final long mDowngradeUnusedAppsThresholdInMillis =
getDowngradeUnusedAppsThresholdInMillis();
+ private static List<PackagesUpdatedListener> sPackagesUpdatedListeners = new ArrayList<>();
+
public static void schedule(Context context) {
if (isBackgroundDexoptDisabled()) {
return;
@@ -244,6 +247,7 @@
}
}
notifyPinService(updatedPackages);
+ notifyPackagesUpdated(updatedPackages);
// Ran to completion, so we abandon our timeslice and do not reschedule.
jobFinished(jobParams, /* reschedule */ false);
}
@@ -391,6 +395,7 @@
} finally {
// Always let the pinner service know about changes.
notifyPinService(updatedPackages);
+ notifyPackagesUpdated(updatedPackages);
}
}
@@ -642,6 +647,32 @@
}
}
+ public static interface PackagesUpdatedListener {
+ /** Callback when packages have been updated by the bg-dexopt service. */
+ public void onPackagesUpdated(ArraySet<String> updatedPackages);
+ }
+
+ public static void addPackagesUpdatedListener(PackagesUpdatedListener listener) {
+ synchronized (sPackagesUpdatedListeners) {
+ sPackagesUpdatedListeners.add(listener);
+ }
+ }
+
+ public static void removePackagesUpdatedListener(PackagesUpdatedListener listener) {
+ synchronized (sPackagesUpdatedListeners) {
+ sPackagesUpdatedListeners.remove(listener);
+ }
+ }
+
+ /** Notify all listeners (#addPackagesUpdatedListener) that packages have been updated. */
+ private void notifyPackagesUpdated(ArraySet<String> updatedPackages) {
+ synchronized (sPackagesUpdatedListeners) {
+ for (PackagesUpdatedListener listener : sPackagesUpdatedListeners) {
+ listener.onPackagesUpdated(updatedPackages);
+ }
+ }
+ }
+
private static long getDowngradeUnusedAppsThresholdInMillis() {
final String sysPropKey = "pm.dexopt.downgrade_after_inactive_days";
String sysPropValue = SystemProperties.get(sysPropKey);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index c47d215..c4d0342 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -6484,11 +6484,18 @@
final Rect containingBounds = mTmpBounds;
mCompatDisplayInsets.getContainerBounds(containingAppBounds, containingBounds, rotation,
orientation, orientationRequested, canChangeOrientation);
- resolvedBounds.set(containingAppBounds);
+ resolvedBounds.set(containingBounds);
// The size of floating task is fixed (only swap), so the aspect ratio is already correct.
if (!mCompatDisplayInsets.mIsFloating) {
applyAspectRatio(resolvedBounds, containingAppBounds, containingBounds);
}
+ // If the bounds are restricted by fixed aspect ratio, the resolved bounds should be put in
+ // the container app bounds. Otherwise the entire container bounds are available.
+ final boolean fillContainer = resolvedBounds.equals(containingBounds);
+ if (!fillContainer) {
+ // The horizontal position should not cover insets.
+ resolvedBounds.left = containingAppBounds.left;
+ }
// Use resolvedBounds to compute other override configurations such as appBounds. The bounds
// are calculated in compat container space. The actual position on screen will be applied
@@ -6557,7 +6564,7 @@
final int offsetX = getHorizontalCenterOffset(
(int) viewportW, (int) (contentW * mSizeCompatScale));
// Above coordinates are in "@" space, now place "*" and "#" to screen space.
- final int screenPosX = parentAppBounds.left + offsetX;
+ final int screenPosX = (fillContainer ? parentBounds.left : parentAppBounds.left) + offsetX;
final int screenPosY = parentBounds.top;
if (screenPosX != 0 || screenPosY != 0) {
if (mSizeCompatBounds != null) {
@@ -7654,8 +7661,6 @@
// Ensure the app bounds won't overlap with insets.
Task.intersectWithInsetsIfFits(outAppBounds, outBounds, mNonDecorInsets[rotation]);
}
- // The horizontal position is centered and it should not cover insets.
- outBounds.left = outAppBounds.left;
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 1e5a924..0470ffa 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -1469,7 +1469,7 @@
if (toDisplay.getDisplayId() != stack.getDisplayId()) {
stack.reparent(toDisplay.getDefaultTaskDisplayArea(), false /* onTop */);
} else {
- toDisplay.mTaskContainers.positionStackAtBottom(stack);
+ toDisplay.getDefaultTaskDisplayArea().positionStackAtBottom(stack);
}
mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index edd14b7..a512337 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -54,7 +54,6 @@
* - BELOW_TASKS: Can only contain BELOW_TASK DisplayAreas and WindowTokens that go below tasks.
* - ABOVE_TASKS: Can only contain ABOVE_TASK DisplayAreas and WindowTokens that go above tasks.
* - ANY: Can contain any kind of DisplayArea, and any kind of WindowToken or the Task container.
- * Cannot have a sibling that is of type ANY.
*
* @param <T> type of the children of the DisplayArea.
*/
@@ -274,7 +273,6 @@
ANY;
static void checkSiblings(Type bottom, Type top) {
- checkState(!(bottom == ANY && top == ANY), "ANY cannot be a sibling of ANY");
checkState(!(bottom != BELOW_TASKS && top == BELOW_TASKS),
bottom + " must be above BELOW_TASKS");
checkState(!(bottom == ABOVE_TASKS && top != ABOVE_TASKS),
diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
index 9821573..15b6f973 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
@@ -16,9 +16,14 @@
package com.android.server.wm;
+import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
+
import android.content.res.Resources;
import android.text.TextUtils;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Policy that manages DisplayAreas.
*/
@@ -37,9 +42,9 @@
protected final DisplayArea<? extends WindowContainer> mImeContainer;
/**
- * The Tasks container. Tasks etc. are automatically added to this container.
+ * The task display areas. Tasks etc. are automatically added to these containers.
*/
- protected final DisplayArea<? extends ActivityStack> mTaskContainers;
+ protected final List<TaskDisplayArea> mTaskDisplayAreas;
/**
* Construct a new {@link DisplayAreaPolicy}
@@ -48,19 +53,19 @@
* @param content the display content for which the policy applies
* @param root the root display area under which the policy operates
* @param imeContainer the ime container that the policy must attach
- * @param taskDisplayArea the task container that the policy must attach
+ * @param taskDisplayAreas the task display areas that the policy must attach
*
* @see #attachDisplayAreas()
*/
protected DisplayAreaPolicy(WindowManagerService wmService,
DisplayContent content, DisplayArea.Root root,
DisplayArea<? extends WindowContainer> imeContainer,
- DisplayArea<? extends ActivityStack> taskDisplayArea) {
+ List<TaskDisplayArea> taskDisplayAreas) {
mWmService = wmService;
mContent = content;
mRoot = root;
mImeContainer = imeContainer;
- mTaskContainers = taskDisplayArea;
+ mTaskDisplayAreas = taskDisplayAreas;
}
/**
@@ -80,15 +85,32 @@
*/
public abstract void addWindow(WindowToken token);
+ /**
+ * @return the number of task display areas on the display.
+ */
+ public int getTaskDisplayAreaCount() {
+ return mTaskDisplayAreas.size();
+ }
+
+ /**
+ * @return the task display area at index.
+ */
+ public TaskDisplayArea getTaskDisplayAreaAt(int index) {
+ return mTaskDisplayAreas.get(index);
+ }
+
/** Provider for platform-default display area policy. */
static final class DefaultProvider implements DisplayAreaPolicy.Provider {
@Override
public DisplayAreaPolicy instantiate(WindowManagerService wmService,
DisplayContent content, DisplayArea.Root root,
- DisplayArea<? extends WindowContainer> imeContainer,
- TaskDisplayArea taskDisplayArea) {
+ DisplayArea<? extends WindowContainer> imeContainer) {
+ final TaskDisplayArea defaultTaskDisplayArea = new TaskDisplayArea(content, wmService,
+ "DefaultTaskDisplayArea", FEATURE_DEFAULT_TASK_CONTAINER);
+ final List<TaskDisplayArea> tdaList = new ArrayList<>();
+ tdaList.add(defaultTaskDisplayArea);
return new DisplayAreaPolicyBuilder()
- .build(wmService, content, root, imeContainer, taskDisplayArea);
+ .build(wmService, content, root, imeContainer, tdaList);
}
}
@@ -107,8 +129,7 @@
*/
DisplayAreaPolicy instantiate(WindowManagerService wmService,
DisplayContent content, DisplayArea.Root root,
- DisplayArea<? extends WindowContainer> imeContainer,
- TaskDisplayArea taskDisplayArea);
+ DisplayArea<? extends WindowContainer> imeContainer);
/**
* Instantiate the device-specific {@link Provider}.
diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
index e8becfa..2c2f433 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
@@ -201,8 +201,8 @@
Result(WindowManagerService wmService, DisplayContent content, DisplayArea.Root root,
DisplayArea<? extends WindowContainer> imeContainer,
- DisplayArea<? extends ActivityStack> taskDisplayArea, ArrayList<Feature> features) {
- super(wmService, content, root, imeContainer, taskDisplayArea);
+ List<TaskDisplayArea> taskDisplayAreas, ArrayList<Feature> features) {
+ super(wmService, content, root, imeContainer, taskDisplayAreas);
mFeatures = features;
mAreas = new HashMap<>(features.size());
for (int i = 0; i < mFeatures.size(); i++) {
@@ -267,7 +267,7 @@
areaForLayer[layer].mChildren.add(leafArea);
leafType = type;
if (leafType == LEAF_TYPE_TASK_CONTAINERS) {
- leafArea.mExisting = mTaskContainers;
+ addTaskDisplayAreasToLayer(areaForLayer[layer], layer);
} else if (leafType == LEAF_TYPE_IME_CONTAINERS) {
leafArea.mExisting = mImeContainer;
}
@@ -278,6 +278,17 @@
root.instantiateChildren(mRoot, mAreaForLayer, 0, mAreas);
}
+ /** Adds all task display areas to the specified layer */
+ private void addTaskDisplayAreasToLayer(PendingArea parentPendingArea, int layer) {
+ final int count = mTaskDisplayAreas.size();
+ for (int i = 0; i < count; i++) {
+ PendingArea leafArea = new PendingArea(null, layer, parentPendingArea);
+ leafArea.mExisting = mTaskDisplayAreas.get(i);
+ leafArea.mMaxLayer = layer;
+ parentPendingArea.mChildren.add(leafArea);
+ }
+ }
+
@Override
public void addWindow(WindowToken token) {
DisplayArea.Tokens area = findAreaForToken(token);
@@ -317,12 +328,16 @@
return this;
}
+ protected List<Feature> getFeatures() {
+ return mFeatures;
+ }
+
Result build(WindowManagerService wmService,
DisplayContent content, DisplayArea.Root root,
DisplayArea<? extends WindowContainer> imeContainer,
- DisplayArea<? extends ActivityStack> taskDisplayArea) {
+ List<TaskDisplayArea> taskDisplayAreas) {
- return new Result(wmService, content, root, imeContainer, taskDisplayArea, new ArrayList<>(
+ return new Result(wmService, content, root, imeContainer, taskDisplayAreas, new ArrayList<>(
mFeatures));
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 2816633..40243e8 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -72,6 +72,7 @@
import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_TASK_OPEN;
import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
+import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
@@ -268,8 +269,6 @@
new NonAppWindowContainers("mOverlayContainers", mWmService);
/** The containers below are the only child containers {@link #mWindowContainers} can have. */
- // Contains all window containers that are related to apps (Activities)
- final TaskDisplayArea mTaskContainers = new TaskDisplayArea(this, mWmService);
// Contains all IME window containers. Note that the z-ordering of the IME windows will depend
// on the IME target. We mainly have this container grouping so we can keep track of all the IME
@@ -971,7 +970,7 @@
super.addChild(mOverlayContainers, null);
mDisplayAreaPolicy = mWmService.mDisplayAreaPolicyProvider.instantiate(
- mWmService, this, mRootDisplayArea, mImeWindowsContainers, mTaskContainers);
+ mWmService, this, mRootDisplayArea, mImeWindowsContainers);
mWindowContainers.addChildren();
// Sets the display content for the children.
@@ -2069,13 +2068,11 @@
}
protected int getTaskDisplayAreaCount() {
- // TODO(multi-display-area): Report actual display area count
- return 1;
+ return mDisplayAreaPolicy.getTaskDisplayAreaCount();
}
protected TaskDisplayArea getTaskDisplayAreaAt(int index) {
- // TODO(multi-display-area): Report actual display area values
- return mTaskContainers;
+ return mDisplayAreaPolicy.getTaskDisplayAreaAt(index);
}
ActivityStack getStack(int rootTaskId) {
@@ -2401,7 +2398,7 @@
* or for cases when multi-instance is not supported yet (like Split-screen, PiP or Recents).
*/
TaskDisplayArea getDefaultTaskDisplayArea() {
- return mTaskContainers;
+ return mDisplayAreaPolicy.getTaskDisplayAreaAt(0);
}
@Override
@@ -3424,7 +3421,10 @@
private void setInputMethodTarget(WindowState target, boolean targetWaitingAnim) {
// Always update control target. This is needed to handle rotation.
- updateImeControlTarget(target);
+ // We cannot set target as the control target, because mInputMethodTarget can only help
+ // decide the z-order of IME, but cannot control IME. Only the IME target reported from
+ // updateInputMethodTargetWindow can control IME.
+ updateImeControlTarget(mInputMethodControlTarget);
if (target == mInputMethodTarget && mInputMethodTargetWaitingAnim == targetWaitingAnim) {
return;
}
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index ec3c99b..11cfad2 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -31,7 +31,6 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-import static android.window.DisplayAreaOrganizer.FEATURE_TASK_CONTAINER;
import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE;
@@ -141,8 +140,9 @@
*/
private boolean mRemoved;
- TaskDisplayArea(DisplayContent displayContent, WindowManagerService service) {
- super(service, Type.ANY, "TaskContainers", FEATURE_TASK_CONTAINER);
+ TaskDisplayArea(DisplayContent displayContent, WindowManagerService service, String name,
+ int displayAreaFeature) {
+ super(service, Type.ANY, name, displayAreaFeature);
mDisplayContent = displayContent;
mRootWindowContainer = service.mRoot;
mAtmService = service.mAtmService;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 8d3a4f2..dea2e96 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -32,6 +32,7 @@
import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
import static android.content.pm.PackageManager.FEATURE_PC;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.Process.INVALID_UID;
import static android.os.Process.SYSTEM_UID;
import static android.os.Process.myPid;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
@@ -2573,6 +2574,8 @@
String packageName, boolean fromClientToken) {
final boolean callerCanManageAppTokens =
checkCallingPermission(MANAGE_APP_TOKENS, "addWindowToken()");
+ // WindowContext users usually don't hold MANAGE_APP_TOKEN permission. Check permissions
+ // by checkAddPermission.
if (!callerCanManageAppTokens) {
final int res = mPolicy.checkAddPermission(type, false /* isRoundedCornerOverlay */,
packageName, new int[1]);
@@ -2587,7 +2590,7 @@
synchronized (mGlobalLock) {
if (!callerCanManageAppTokens) {
if (packageName == null || !unprivilegedAppCanCreateTokenWith(
- null /* parentWindow */, callingUid, type, type, null /* tokenForLog */,
+ null /* parentWindow */, callingUid, type, type, binder,
packageName)) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
}
@@ -2612,7 +2615,7 @@
new WallpaperWindowToken(this, binder, true, dc, callerCanManageAppTokens);
} else {
new WindowToken(this, binder, type, true, dc, callerCanManageAppTokens,
- false /* roundedCornerOverlay */, fromClientToken);
+ callingUid, false /* roundedCornerOverlay */, fromClientToken);
}
}
} finally {
@@ -2635,8 +2638,25 @@
@Override
public void removeWindowToken(IBinder binder, int displayId) {
- if (!checkCallingPermission(MANAGE_APP_TOKENS, "removeWindowToken()")) {
- throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
+ final boolean callerCanManageAppTokens =
+ checkCallingPermission(MANAGE_APP_TOKENS, "removeWindowToken()");
+ final WindowToken windowToken;
+ synchronized (mGlobalLock) {
+ windowToken = mRoot.getWindowToken(binder);
+ }
+ if (windowToken == null) {
+ ProtoLog.w(WM_ERROR,
+ "removeWindowToken: Attempted to remove non-existing token: %s", binder);
+ return;
+ }
+ final int callingUid = Binder.getCallingUid();
+
+ // If MANAGE_APP_TOKEN permission is not held(usually from WindowContext), callers can only
+ // remove the window tokens which they added themselves.
+ if (!callerCanManageAppTokens && (windowToken.getOwnerUid() == INVALID_UID
+ || callingUid != windowToken.getOwnerUid())) {
+ throw new SecurityException("removeWindowToken: Requires MANAGE_APP_TOKENS permission"
+ + " to remove token owned by another uid");
}
final long origId = Binder.clearCallingIdentity();
@@ -2649,14 +2669,7 @@
return;
}
- final WindowToken token = dc.removeWindowToken(binder);
- if (token == null) {
- ProtoLog.w(WM_ERROR,
- "removeWindowToken: Attempted to remove non-existing token: %s",
- binder);
- return;
- }
-
+ dc.removeWindowToken(binder);
dc.getInputMonitor().updateInputWindowsLw(true /*force*/);
}
} finally {
@@ -8006,6 +8019,33 @@
}
}
+ /** Set layer tracing flags. */
+ public void setLayerTracingFlags(int flags) {
+ mAtmInternal.enforceCallerIsRecentsOrHasPermission(android.Manifest.permission.DUMP,
+ "setLayerTracingFlags");
+ long token = Binder.clearCallingIdentity();
+ try {
+ Parcel data = null;
+ try {
+ IBinder sf = ServiceManager.getService("SurfaceFlinger");
+ if (sf != null) {
+ data = Parcel.obtain();
+ data.writeInterfaceToken("android.ui.ISurfaceComposer");
+ data.writeInt(flags);
+ sf.transact(1033 /* LAYER_TRACE_FLAGS_CODE */, data, null, 0 /* flags */);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to set layer tracing flags");
+ } finally {
+ if (data != null) {
+ data.recycle();
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
@Override
public boolean mirrorDisplay(int displayId, SurfaceControl outSurfaceControl) {
if (!checkCallingPermission(READ_FRAME_BUFFER, "mirrorDisplay()")) {
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 23091a0..5976b48 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.os.Process.INVALID_UID;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
@@ -44,6 +45,7 @@
import android.os.Debug;
import android.os.IBinder;
import android.os.RemoteException;
+import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.DisplayInfo;
import android.view.InsetsState;
@@ -106,6 +108,11 @@
@VisibleForTesting
final boolean mFromClientToken;
+ private DeathRecipient mDeathRecipient;
+ private boolean mBinderDied = false;
+
+ private final int mOwnerUid;
+
/**
* Used to fix the transform of the token to be rotated to a rotation different than it's
* display. The window frames and surfaces corresponding to this token will be layouted and
@@ -165,6 +172,30 @@
}
}
+ private class DeathRecipient implements IBinder.DeathRecipient {
+ private boolean mHasUnlinkToDeath = false;
+
+ @Override
+ public void binderDied() {
+ synchronized (mWmService.mGlobalLock) {
+ mBinderDied = true;
+ removeImmediately();
+ }
+ }
+
+ void linkToDeath() throws RemoteException {
+ token.linkToDeath(DeathRecipient.this, 0);
+ }
+
+ void unlinkToDeath() {
+ if (mHasUnlinkToDeath) {
+ return;
+ }
+ token.unlinkToDeath(DeathRecipient.this, 0);
+ mHasUnlinkToDeath = true;
+ }
+ }
+
/**
* Compares two child window of this token and returns -1 if the first is lesser than the
* second in terms of z-order and 1 otherwise.
@@ -193,23 +224,35 @@
WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
DisplayContent dc, boolean ownerCanManageAppTokens, boolean roundedCornerOverlay) {
- this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens,
+ this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens, INVALID_UID,
roundedCornerOverlay, false /* fromClientToken */);
}
WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
- DisplayContent dc, boolean ownerCanManageAppTokens, boolean roundedCornerOverlay,
- boolean fromClientToken) {
+ DisplayContent dc, boolean ownerCanManageAppTokens, int ownerUid,
+ boolean roundedCornerOverlay, boolean fromClientToken) {
super(service);
token = _token;
windowType = type;
mPersistOnEmpty = persistOnEmpty;
mOwnerCanManageAppTokens = ownerCanManageAppTokens;
+ mOwnerUid = ownerUid;
mRoundedCornerOverlay = roundedCornerOverlay;
mFromClientToken = fromClientToken;
if (dc != null) {
dc.addWindowToken(token, this);
}
+ if (shouldReportToClient()) {
+ try {
+ mDeathRecipient = new DeathRecipient();
+ mDeathRecipient.linkToDeath();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to add window token with type " + windowType + " on "
+ + "display " + dc.getDisplayId(), e);
+ mDeathRecipient = null;
+ return;
+ }
+ }
}
void removeAllWindowsIfPossible() {
@@ -222,7 +265,7 @@
}
void setExiting() {
- if (mChildren.size() == 0) {
+ if (isEmpty()) {
super.removeImmediately();
return;
}
@@ -340,6 +383,21 @@
// Needs to occur after the token is removed from the display above to avoid attempt at
// duplicate removal of this window container from it's parent.
super.removeImmediately();
+
+ reportWindowTokenRemovedToClient();
+ }
+
+ private void reportWindowTokenRemovedToClient() {
+ if (!shouldReportToClient()) {
+ return;
+ }
+ mDeathRecipient.unlinkToDeath();
+ IWindowToken windowTokenClient = IWindowToken.Stub.asInterface(token);
+ try {
+ windowTokenClient.onWindowTokenRemoved();
+ } catch (RemoteException e) {
+ ProtoLog.w(WM_ERROR, "Could not report token removal to the window token client.");
+ }
}
@Override
@@ -361,17 +419,9 @@
}
void reportConfigToWindowTokenClient() {
- if (asActivityRecord() != null) {
- // Activities are updated through ATM callbacks.
+ if (!shouldReportToClient()) {
return;
}
-
- // Unfortunately, this WindowToken is not from WindowContext so it cannot handle
- // its own configuration changes.
- if (!mFromClientToken) {
- return;
- }
-
final Configuration config = getConfiguration();
final int displayId = getDisplayContent().getDisplayId();
if (config.equals(mLastReportedConfig) && displayId == mLastReportedDisplay) {
@@ -383,16 +433,26 @@
mLastReportedDisplay = displayId;
IWindowToken windowTokenClient = IWindowToken.Stub.asInterface(token);
- if (windowTokenClient != null) {
- try {
- windowTokenClient.onConfigurationChanged(config, displayId);
- } catch (RemoteException e) {
- ProtoLog.w(WM_ERROR,
- "Could not report config changes to the window token client.");
- }
+ try {
+ windowTokenClient.onConfigurationChanged(config, displayId);
+ } catch (RemoteException e) {
+ ProtoLog.w(WM_ERROR,
+ "Could not report config changes to the window token client.");
}
}
+ /**
+ * @return {@code true} if this {@link WindowToken} is not an {@link ActivityRecord} and
+ * registered from client side.
+ */
+ private boolean shouldReportToClient() {
+ // Only report to client for WindowToken because Activities are updated through ATM
+ // callbacks.
+ return asActivityRecord() == null
+ // Report to {@link android.view.WindowTokenClient} if this token was registered from it.
+ && mFromClientToken && !mBinderDied;
+ }
+
@Override
void assignLayer(SurfaceControl.Transaction t, int layer) {
if (windowType == TYPE_DOCK_DIVIDER) {
@@ -616,4 +676,8 @@
int getWindowLayerFromType() {
return mWmService.mPolicy.getWindowLayerFromTypeLw(windowType, mOwnerCanManageAppTokens);
}
+
+ int getOwnerUid() {
+ return mOwnerUid;
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 67e83ba..d1d353d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1836,8 +1836,9 @@
Bundle addSyntheticRestrictions(Bundle restrictions) {
if (disableCamera) {
restrictions.putBoolean(UserManager.DISALLOW_CAMERA, true);
- } else {
- restrictions.remove(UserManager.DISALLOW_CAMERA);
+ }
+ if (requireAutoTime) {
+ restrictions.putBoolean(UserManager.DISALLOW_CONFIG_DATE_TIME, true);
}
return restrictions;
}
@@ -1864,7 +1865,7 @@
Bundle getEffectiveRestrictions() {
return addSyntheticRestrictions(
- removeDeprecatedRestrictions(ensureUserRestrictions()));
+ removeDeprecatedRestrictions(new Bundle(ensureUserRestrictions())));
}
Bundle getLocalUserRestrictions(int adminType) {
@@ -2747,6 +2748,8 @@
// The following policies weren't available to PO, but will be available after migration.
parentAdmin.disableCamera = doAdmin.disableCamera;
+ parentAdmin.requireAutoTime = doAdmin.requireAutoTime;
+
// TODO(b/143516163): Uncomment once corresponding APIs are available via parent instance.
// parentAdmin.disableScreenCapture = doAdmin.disableScreenCapture;
// parentAdmin.accountTypesWithManagementDisabled.addAll(
@@ -7839,16 +7842,21 @@
}
Objects.requireNonNull(who, "ComponentName is null");
final int userHandle = UserHandle.getCallingUserId();
+ boolean requireAutoTimeChanged = false;
synchronized (getLockObject()) {
ActiveAdmin admin = getActiveAdminForCallerLocked(who,
DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
if (admin.requireAutoTime != required) {
admin.requireAutoTime = required;
saveSettingsLocked(userHandle);
+ requireAutoTimeChanged = true;
}
}
-
- // TODO: (b/145604635) Add upgrade case
+ // requireAutoTime is now backed by DISALLOW_CONFIG_DATE_TIME restriction, so propagate
+ // updated restrictions to the framework.
+ if (requireAutoTimeChanged) {
+ pushUserRestrictions(userHandle);
+ }
// Turn AUTO_TIME on in settings if it is required
if (required) {
mInjector.binderWithCleanCallingIdentity(
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index d1153e6..92366e5 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -51,7 +51,7 @@
namespace fs = std::filesystem;
constexpr const char* kDataUsageStats = "android.permission.LOADER_USAGE_STATS";
-constexpr const char* kOpUsage = "android:get_usage_stats";
+constexpr const char* kOpUsage = "android:loader_usage_stats";
namespace android::incremental {
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index 0635ae1..18ae4b5 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -137,6 +137,7 @@
bool* _aidl_return) {
mId = mountId;
mListener = listener;
+ mServiceConnector = control.service;
*_aidl_return = true;
return binder::Status::ok();
}
@@ -166,9 +167,17 @@
mListener->onStatusChanged(mId, IDataLoaderStatusListener::DATA_LOADER_CREATED);
}
+ int32_t setStorageParams(bool enableReadLogs) {
+ int32_t result = -1;
+ EXPECT_NE(mServiceConnector.get(), nullptr);
+ EXPECT_TRUE(mServiceConnector->setStorageParams(enableReadLogs, &result).isOk());
+ return result;
+ }
+
private:
int mId;
sp<IDataLoaderStatusListener> mListener;
+ sp<IIncrementalServiceConnector> mServiceConnector;
sp<IDataLoader> mDataLoader = sp<IDataLoader>(new FakeDataLoader());
};
@@ -453,7 +462,7 @@
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
IncrementalService::CreateOptions::CreateNew);
ASSERT_GE(storageId, 0);
- ASSERT_GE(mIncrementalService->setStorageParams(storageId, true), 0);
+ ASSERT_GE(mDataLoaderManager->setStorageParams(true), 0);
}
TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccessAndPermissionChanged) {
@@ -480,7 +489,7 @@
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
IncrementalService::CreateOptions::CreateNew);
ASSERT_GE(storageId, 0);
- ASSERT_GE(mIncrementalService->setStorageParams(storageId, true), 0);
+ ASSERT_GE(mDataLoaderManager->setStorageParams(true), 0);
ASSERT_NE(nullptr, mAppOpsManager->mStoredCallback.get());
mAppOpsManager->mStoredCallback->opChanged(0, {});
}
@@ -503,7 +512,7 @@
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
IncrementalService::CreateOptions::CreateNew);
ASSERT_GE(storageId, 0);
- ASSERT_LT(mIncrementalService->setStorageParams(storageId, true), 0);
+ ASSERT_LT(mDataLoaderManager->setStorageParams(true), 0);
}
TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsFails) {
@@ -526,7 +535,7 @@
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
IncrementalService::CreateOptions::CreateNew);
ASSERT_GE(storageId, 0);
- ASSERT_LT(mIncrementalService->setStorageParams(storageId, true), 0);
+ ASSERT_LT(mDataLoaderManager->setStorageParams(true), 0);
}
TEST_F(IncrementalServiceTest, testMakeDirectory) {
diff --git a/services/systemcaptions/java/com/android/server/systemcaptions/RemoteSystemCaptionsManagerService.java b/services/systemcaptions/java/com/android/server/systemcaptions/RemoteSystemCaptionsManagerService.java
index c225d3f..1aab672 100644
--- a/services/systemcaptions/java/com/android/server/systemcaptions/RemoteSystemCaptionsManagerService.java
+++ b/services/systemcaptions/java/com/android/server/systemcaptions/RemoteSystemCaptionsManagerService.java
@@ -16,6 +16,8 @@
package com.android.server.systemcaptions;
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
@@ -37,6 +39,8 @@
private static final String SERVICE_INTERFACE =
"android.service.systemcaptions.SystemCaptionsManagerService";
+ private static final int MSG_BIND = 1;
+
private final Object mLock = new Object();
private final Context mContext;
@@ -71,18 +75,26 @@
if (mVerbose) {
Slog.v(TAG, "initialize()");
}
- ensureBound();
+ scheduleBind();
}
- void destroy() {
+ /**
+ * Destroys this service.
+ */
+ public void destroy() {
+ mHandler.sendMessage(
+ obtainMessage(RemoteSystemCaptionsManagerService::handleDestroy, this));
+ }
+
+ void handleDestroy() {
if (mVerbose) {
- Slog.v(TAG, "destroy()");
+ Slog.v(TAG, "handleDestroy()");
}
synchronized (mLock) {
if (mDestroyed) {
if (mVerbose) {
- Slog.v(TAG, "destroy(): Already destroyed");
+ Slog.v(TAG, "handleDestroy(): Already destroyed");
}
return;
}
@@ -97,14 +109,24 @@
}
}
- private void ensureBound() {
+ private void scheduleBind() {
+ if (mHandler.hasMessages(MSG_BIND)) {
+ if (mVerbose) Slog.v(TAG, "scheduleBind(): already scheduled");
+ return;
+ }
+ mHandler.sendMessage(
+ obtainMessage(RemoteSystemCaptionsManagerService::handleEnsureBound, this)
+ .setWhat(MSG_BIND));
+ }
+
+ private void handleEnsureBound() {
synchronized (mLock) {
if (mService != null || mBinding) {
return;
}
if (mVerbose) {
- Slog.v(TAG, "ensureBound(): binding");
+ Slog.v(TAG, "handleEnsureBound(): binding");
}
mBinding = true;
diff --git a/services/tests/servicestests/res/raw/comp_policies_primary.xml b/services/tests/servicestests/res/raw/comp_policies_primary.xml
index d30f479..8b7709e 100644
--- a/services/tests/servicestests/res/raw/comp_policies_primary.xml
+++ b/services/tests/servicestests/res/raw/comp_policies_primary.xml
@@ -3,6 +3,7 @@
<admin name="com.android.frameworks.servicestests/com.android.server.devicepolicy.DummyDeviceAdmins$Admin1">
<policies flags="991"/>
<password-history-length value="33" />
+ <require_auto_time value="true" />
<user-restrictions no_bluetooth="true" />
</admin>
</policies>
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
index de2addf..c9bd01a 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
@@ -385,6 +385,15 @@
assertFalse("User restriction was put into non-parent PO instance",
dpm.getUserRestrictions(admin1).containsKey(UserManager.DISALLOW_BLUETOOTH));
+ assertTrue("User restriction wasn't migrated to PO parent instance",
+ dpms.getProfileOwnerAdminLocked(COPE_PROFILE_USER_ID)
+ .getParentActiveAdmin()
+ .getEffectiveRestrictions()
+ .containsKey(UserManager.DISALLOW_CONFIG_DATE_TIME));
+ assertFalse("User restriction was put into non-parent PO instance",
+ dpms.getProfileOwnerAdminLocked(COPE_PROFILE_USER_ID)
+ .getEffectiveRestrictions()
+ .containsKey(UserManager.DISALLOW_CONFIG_DATE_TIME));
// TODO(b/143516163): verify more policies.
});
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index d780370..fe224ce 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -2032,13 +2032,17 @@
eq(false));
DpmTestUtils.assertRestrictions(
DpmTestUtils.newRestrictions(UserManager.DISALLOW_CAMERA),
- parentDpm.getUserRestrictions(admin1)
+ dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE)
+ .getParentActiveAdmin()
+ .getEffectiveRestrictions()
);
parentDpm.setCameraDisabled(admin1, false);
DpmTestUtils.assertRestrictions(
DpmTestUtils.newRestrictions(),
- parentDpm.getUserRestrictions(admin1)
+ dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE)
+ .getParentActiveAdmin()
+ .getEffectiveRestrictions()
);
reset(getServices().userManagerInternal);
}
@@ -2053,7 +2057,9 @@
parentDpm.clearUserRestriction(admin1, restriction);
DpmTestUtils.assertRestrictions(
DpmTestUtils.newRestrictions(),
- parentDpm.getUserRestrictions(admin1)
+ dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE)
+ .getParentActiveAdmin()
+ .getEffectiveRestrictions()
);
}
@@ -2088,11 +2094,7 @@
private void assertNoDeviceOwnerRestrictions() {
DpmTestUtils.assertRestrictions(
DpmTestUtils.newRestrictions(),
- getDeviceOwner().ensureUserRestrictions()
- );
- DpmTestUtils.assertRestrictions(
- DpmTestUtils.newRestrictions(),
- dpm.getUserRestrictions(admin1)
+ getDeviceOwner().getEffectiveRestrictions()
);
}
diff --git a/services/tests/servicestests/src/com/android/server/display/color/AppSaturationControllerTest.java b/services/tests/servicestests/src/com/android/server/display/color/AppSaturationControllerTest.java
index 7c9a81d..a525814 100644
--- a/services/tests/servicestests/src/com/android/server/display/color/AppSaturationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/color/AppSaturationControllerTest.java
@@ -43,7 +43,9 @@
@RunWith(AndroidJUnit4.class)
public class AppSaturationControllerTest {
- private static final String TEST_PACKAGE_NAME = "com.android.test";
+ private static final String TEST_CALLER_PACKAGE_NAME = "com.android.test.caller";
+ private static final String TEST_CALLER_PACKAGE_NAME_TWO = "com.android.test.caller.two";
+ private static final String TEST_AFFECTED_PACKAGE_NAME = "com.android.test.affected";
private int mUserId;
private AppSaturationController mAppSaturationController;
@@ -70,8 +72,11 @@
public void addColorTransformController_appliesExistingSaturation() {
final WeakReference<ColorTransformController> ref = new WeakReference<>(
mColorTransformController);
- mAppSaturationController.setSaturationLevel(TEST_PACKAGE_NAME, mUserId, 30);
- mAppSaturationController.addColorTransformController(TEST_PACKAGE_NAME, mUserId, ref);
+ mAppSaturationController
+ .setSaturationLevel(TEST_CALLER_PACKAGE_NAME, TEST_AFFECTED_PACKAGE_NAME, mUserId,
+ 30);
+ mAppSaturationController
+ .addColorTransformController(TEST_AFFECTED_PACKAGE_NAME, mUserId, ref);
AppSaturationController.computeGrayscaleTransformMatrix(.3f, mMatrix);
verify(mColorTransformController).applyAppSaturation(eq(mMatrix), eq(TRANSLATION_VECTOR));
}
@@ -80,14 +85,19 @@
public void setSaturationLevel_resetToDefault() {
final WeakReference<ColorTransformController> ref = new WeakReference<>(
mColorTransformController);
- mAppSaturationController.addColorTransformController(TEST_PACKAGE_NAME, mUserId, ref);
+ mAppSaturationController
+ .addColorTransformController(TEST_AFFECTED_PACKAGE_NAME, mUserId, ref);
verify(mColorTransformController, never())
.applyAppSaturation(any(), eq(TRANSLATION_VECTOR));
- mAppSaturationController.setSaturationLevel(TEST_PACKAGE_NAME, mUserId, 30);
+ mAppSaturationController
+ .setSaturationLevel(TEST_CALLER_PACKAGE_NAME, TEST_AFFECTED_PACKAGE_NAME, mUserId,
+ 30);
AppSaturationController.computeGrayscaleTransformMatrix(.3f, mMatrix);
verify(mColorTransformController, times(1))
.applyAppSaturation(eq(mMatrix), eq(TRANSLATION_VECTOR));
- mAppSaturationController.setSaturationLevel(TEST_PACKAGE_NAME, mUserId, 100);
+ mAppSaturationController
+ .setSaturationLevel(TEST_CALLER_PACKAGE_NAME, TEST_AFFECTED_PACKAGE_NAME, mUserId,
+ 100);
AppSaturationController.computeGrayscaleTransformMatrix(1.0f, mMatrix);
verify(mColorTransformController, times(2))
.applyAppSaturation(eq(mMatrix), eq(TRANSLATION_VECTOR));
@@ -97,19 +107,76 @@
public void setSaturationLevel_updateLevel() {
final WeakReference<ColorTransformController> ref = new WeakReference<>(
mColorTransformController);
- mAppSaturationController.addColorTransformController(TEST_PACKAGE_NAME, mUserId, ref);
+ mAppSaturationController
+ .addColorTransformController(TEST_AFFECTED_PACKAGE_NAME, mUserId, ref);
verify(mColorTransformController, never())
.applyAppSaturation(any(), eq(TRANSLATION_VECTOR));
- mAppSaturationController.setSaturationLevel(TEST_PACKAGE_NAME, mUserId, 30);
+ mAppSaturationController
+ .setSaturationLevel(TEST_CALLER_PACKAGE_NAME, TEST_AFFECTED_PACKAGE_NAME, mUserId,
+ 30);
AppSaturationController.computeGrayscaleTransformMatrix(.3f, mMatrix);
verify(mColorTransformController).applyAppSaturation(eq(mMatrix), eq(TRANSLATION_VECTOR));
- mAppSaturationController.setSaturationLevel(TEST_PACKAGE_NAME, mUserId, 70);
+ mAppSaturationController
+ .setSaturationLevel(TEST_CALLER_PACKAGE_NAME, TEST_AFFECTED_PACKAGE_NAME, mUserId,
+ 70);
AppSaturationController.computeGrayscaleTransformMatrix(.7f, mMatrix);
verify(mColorTransformController, times(2))
.applyAppSaturation(eq(mMatrix), eq(TRANSLATION_VECTOR));
- mAppSaturationController.setSaturationLevel(TEST_PACKAGE_NAME, mUserId, 100);
+ mAppSaturationController
+ .setSaturationLevel(TEST_CALLER_PACKAGE_NAME, TEST_AFFECTED_PACKAGE_NAME, mUserId,
+ 100);
AppSaturationController.computeGrayscaleTransformMatrix(1.0f, mMatrix);
verify(mColorTransformController, times(3))
.applyAppSaturation(eq(mMatrix), eq(TRANSLATION_VECTOR));
}
+
+ @Test
+ public void setSaturationLevel_multipleCallers_appliesStrongest() {
+ final WeakReference<ColorTransformController> ref = new WeakReference<>(
+ mColorTransformController);
+ mAppSaturationController
+ .addColorTransformController(TEST_AFFECTED_PACKAGE_NAME, mUserId, ref);
+ verify(mColorTransformController, never())
+ .applyAppSaturation(any(), eq(TRANSLATION_VECTOR));
+ mAppSaturationController
+ .setSaturationLevel(TEST_CALLER_PACKAGE_NAME, TEST_AFFECTED_PACKAGE_NAME, mUserId,
+ 30);
+ AppSaturationController.computeGrayscaleTransformMatrix(0.3f, mMatrix);
+ verify(mColorTransformController, times(1))
+ .applyAppSaturation(eq(mMatrix), eq(TRANSLATION_VECTOR));
+ mAppSaturationController
+ .setSaturationLevel(TEST_CALLER_PACKAGE_NAME_TWO, TEST_AFFECTED_PACKAGE_NAME,
+ mUserId,
+ 70);
+ verify(mColorTransformController, times(2))
+ .applyAppSaturation(eq(mMatrix), eq(TRANSLATION_VECTOR));
+ }
+
+ @Test
+ public void setSaturationLevel_multipleCallers_removingOneDoesNotAffectTheOther() {
+ final WeakReference<ColorTransformController> ref = new WeakReference<>(
+ mColorTransformController);
+ mAppSaturationController
+ .addColorTransformController(TEST_AFFECTED_PACKAGE_NAME, mUserId, ref);
+ verify(mColorTransformController, never())
+ .applyAppSaturation(any(), eq(TRANSLATION_VECTOR));
+ mAppSaturationController
+ .setSaturationLevel(TEST_CALLER_PACKAGE_NAME, TEST_AFFECTED_PACKAGE_NAME, mUserId,
+ 70);
+ AppSaturationController.computeGrayscaleTransformMatrix(0.7f, mMatrix);
+ verify(mColorTransformController, times(1))
+ .applyAppSaturation(eq(mMatrix), eq(TRANSLATION_VECTOR));
+ mAppSaturationController
+ .setSaturationLevel(TEST_CALLER_PACKAGE_NAME_TWO, TEST_AFFECTED_PACKAGE_NAME,
+ mUserId,
+ 30);
+ AppSaturationController.computeGrayscaleTransformMatrix(0.3f, mMatrix);
+ verify(mColorTransformController, times(2))
+ .applyAppSaturation(eq(mMatrix), eq(TRANSLATION_VECTOR));
+ mAppSaturationController
+ .setSaturationLevel(TEST_CALLER_PACKAGE_NAME_TWO, TEST_AFFECTED_PACKAGE_NAME,
+ mUserId,
+ 100);
+ AppSaturationController.computeGrayscaleTransformMatrix(0.7f, mMatrix);
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
index 12bdec6..0568be8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
@@ -22,6 +22,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER;
+import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS;
import static com.android.server.wm.DisplayArea.Type.ANY;
@@ -72,7 +73,11 @@
WindowManagerService wms = mSystemServices.getWindowManagerService();
DisplayArea.Root root = new SurfacelessDisplayAreaRoot(wms);
DisplayArea<WindowContainer> ime = new DisplayArea<>(wms, ABOVE_TASKS, "Ime");
- DisplayArea<ActivityStack> tasks = new DisplayArea<>(wms, ANY, "Tasks");
+ DisplayContent displayContent = mock(DisplayContent.class);
+ TaskDisplayArea taskDisplayArea = new TaskDisplayArea(displayContent, wms, "Tasks",
+ FEATURE_DEFAULT_TASK_CONTAINER);
+ List<TaskDisplayArea> taskDisplayAreaList = new ArrayList<>();
+ taskDisplayAreaList.add(taskDisplayArea);
final Feature foo;
final Feature bar;
@@ -86,7 +91,7 @@
.all()
.except(TYPE_STATUS_BAR)
.build())
- .build(wms, mock(DisplayContent.class), root, ime, tasks);
+ .build(wms, displayContent, root, ime, taskDisplayAreaList);
policy.attachDisplayAreas();
@@ -98,9 +103,9 @@
assertThat(policy.findAreaForToken(tokenOfType(TYPE_STATUS_BAR)),
is(not(decendantOfOneOf(policy.getDisplayAreas(bar)))));
- assertThat(tasks,
+ assertThat(taskDisplayArea,
is(decendantOfOneOf(policy.getDisplayAreas(foo))));
- assertThat(tasks,
+ assertThat(taskDisplayArea,
is(decendantOfOneOf(policy.getDisplayAreas(bar))));
assertThat(ime,
@@ -109,7 +114,8 @@
is(decendantOfOneOf(policy.getDisplayAreas(bar))));
List<DisplayArea<?>> actualOrder = collectLeafAreas(root);
- Map<DisplayArea<?>, Set<Integer>> zSets = calculateZSets(policy, root, ime, tasks);
+ Map<DisplayArea<?>, Set<Integer>> zSets = calculateZSets(policy, root, ime,
+ taskDisplayArea);
actualOrder = actualOrder.stream().filter(zSets::containsKey).collect(toList());
Map<DisplayArea<?>, Integer> expectedByMinLayer = mapValues(zSets,
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java
index 4e4627b..6834ee5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java
@@ -77,8 +77,7 @@
@Override
public DisplayAreaPolicy instantiate(WindowManagerService wmService, DisplayContent content,
- DisplayArea.Root root, DisplayArea<? extends WindowContainer> imeContainer,
- TaskDisplayArea taskDisplayArea) {
+ DisplayArea.Root root, DisplayArea<? extends WindowContainer> imeContainer) {
throw new RuntimeException("test stub");
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
index 618e608..880c486 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
@@ -54,17 +54,6 @@
}
@Test
- public void testDisplayArea_positionChanged_throwsIfIncompatibleSibling() {
- WindowManagerService wms = mWmsRule.getWindowManagerService();
- DisplayArea<WindowContainer> parent = new SurfacelessDisplayArea<>(wms, ANY, "Parent");
- DisplayArea<WindowContainer> child1 = new DisplayArea<>(wms, ANY, "Child1");
- DisplayArea<WindowContainer> child2 = new DisplayArea<>(wms, ANY, "Child2");
-
- parent.addChild(child1, 0);
- assertThrows(IllegalStateException.class, () -> parent.addChild(child2, 0));
- }
-
- @Test
public void testType_typeOf() {
WindowManagerService wms = mWmsRule.getWindowManagerService();
@@ -87,10 +76,10 @@
checkSiblings(BELOW_TASKS, ABOVE_TASKS);
checkSiblings(ANY, ABOVE_TASKS);
checkSiblings(ABOVE_TASKS, ABOVE_TASKS);
+ checkSiblings(ANY, ANY);
assertThrows(IllegalStateException.class, () -> checkSiblings(ABOVE_TASKS, BELOW_TASKS));
assertThrows(IllegalStateException.class, () -> checkSiblings(ABOVE_TASKS, ANY));
- assertThrows(IllegalStateException.class, () -> checkSiblings(ANY, ANY));
assertThrows(IllegalStateException.class, () -> checkSiblings(ANY, BELOW_TASKS));
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 6734694..e47792f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -243,6 +243,26 @@
}
@Test
+ public void testAspectRatioMatchParentBoundsAndImeAttachable() {
+ setUpApp(new TestDisplayContent.Builder(mService, 1000, 2000)
+ .setSystemDecorations(true).build());
+ prepareUnresizable(2f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED);
+ assertFitted();
+
+ rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
+ mActivity.mDisplayContent.mInputMethodTarget = addWindowToActivity(mActivity);
+ // Because the aspect ratio of display doesn't exceed the max aspect ratio of activity.
+ // The activity should still fill its parent container and IME can attach to the activity.
+ assertTrue(mActivity.matchParentBounds());
+ assertTrue(mActivity.mDisplayContent.isImeAttachedToApp());
+
+ final Rect letterboxInnerBounds = new Rect();
+ mActivity.getLetterboxInnerBounds(letterboxInnerBounds);
+ // The activity should not have letterbox.
+ assertTrue(letterboxInnerBounds.isEmpty());
+ }
+
+ @Test
public void testMoveToDifferentOrientDisplay() {
setUpDisplaySizeWithApp(1000, 2500);
prepareUnresizable(-1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
index 9625ffd..a708533 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -51,10 +51,10 @@
import org.junit.runner.RunWith;
/**
- * Tests for the {@link DisplayContent.TaskStackContainers} container in {@link DisplayContent}.
+ * Tests for the {@link TaskDisplayArea} container.
*
* Build/Install/Run:
- * atest WmTests:TaskStackContainersTests
+ * atest WmTests:TaskDisplayAreaTests
*/
@SmallTest
@Presubmit
@@ -154,8 +154,9 @@
ACTIVITY_TYPE_STANDARD, mDisplayContent);
final Task newStack = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_STANDARD, mDisplayContent);
- doReturn(newStack).when(mDisplayContent.mTaskContainers).createStack(anyInt(),
- anyInt(), anyBoolean(), any(), any(), anyBoolean());
+ final TaskDisplayArea taskDisplayArea = candidateTask.getDisplayArea();
+ doReturn(newStack).when(taskDisplayArea).createStack(anyInt(), anyInt(), anyBoolean(),
+ any(), any(), anyBoolean());
final int type = ACTIVITY_TYPE_STANDARD;
assertGetOrCreateStack(WINDOWING_MODE_FULLSCREEN, type, candidateTask,
@@ -186,7 +187,7 @@
private void assertGetOrCreateStack(int windowingMode, int activityType, Task candidateTask,
boolean reuseCandidate) {
- final TaskDisplayArea taskDisplayArea = (TaskDisplayArea) candidateTask.getParent();
+ final TaskDisplayArea taskDisplayArea = candidateTask.getDisplayArea();
final ActivityStack stack = taskDisplayArea.getOrCreateStack(windowingMode, activityType,
false /* onTop */, null /* intent */, candidateTask /* candidateTask */);
assertEquals(reuseCandidate, stack == candidateTask);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
index 18737c2..d2a2732 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -43,7 +43,9 @@
// hard-code to FULLSCREEN for tests.
setWindowingMode(WINDOWING_MODE_FULLSCREEN);
spyOn(this);
- spyOn(mTaskContainers);
+ for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) {
+ spyOn(getTaskDisplayAreaAt(i));
+ }
final DisplayRotation displayRotation = getDisplayRotation();
spyOn(displayRotation);
@@ -137,6 +139,7 @@
spyOn(displayPolicy);
if (mSystemDecorations) {
doReturn(true).when(newDisplay).supportsSystemDecorations();
+ doReturn(true).when(displayPolicy).hasNavigationBar();
} else {
doReturn(false).when(displayPolicy).hasNavigationBar();
doReturn(false).when(displayPolicy).hasStatusBar();
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index da4bde5..79b9ae1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -16,13 +16,18 @@
package com.android.server.wm;
+import static android.os.Process.INVALID_UID;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
import android.content.pm.PackageManager;
import android.os.IBinder;
@@ -85,4 +90,19 @@
assertFalse(windowToken.mRoundedCornerOverlay);
assertTrue(windowToken.mFromClientToken);
}
+
+ @Test(expected = SecurityException.class)
+ public void testRemoveWindowToken_ownerUidNotMatch_throwException() {
+ IBinder token = mock(IBinder.class);
+ mWm.addWindowTokenWithOptions(token, TYPE_TOAST, mDisplayContent.getDisplayId(),
+ null /* options */, null /* options */);
+
+ spyOn(mWm);
+ when(mWm.checkCallingPermission(anyString(), anyString())).thenReturn(false);
+ WindowToken windowToken = mWm.mRoot.getWindowToken(token);
+ spyOn(windowToken);
+ when(windowToken.getOwnerUid()).thenReturn(INVALID_UID);
+
+ mWm.removeWindowToken(token, mDisplayContent.getDisplayId());
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
index 7a347cb..76479cb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.os.Process.INVALID_UID;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
@@ -152,7 +153,7 @@
token = new WindowToken(mDisplayContent.mWmService, mock(IBinder.class), TYPE_TOAST,
true /* persistOnEmpty */, mDisplayContent, true /* ownerCanManageAppTokens */,
- true /* roundedCornerOverlay */, true /* fromClientToken */);
+ INVALID_UID, true /* roundedCornerOverlay */, true /* fromClientToken */);
assertTrue(token.mRoundedCornerOverlay);
assertTrue(token.mFromClientToken);
}
@@ -166,7 +167,7 @@
public void testSurfaceCreatedForWindowToken() {
final WindowToken fromClientToken = new WindowToken(mDisplayContent.mWmService,
mock(IBinder.class), TYPE_APPLICATION_OVERLAY, true /* persistOnEmpty */,
- mDisplayContent, true /* ownerCanManageAppTokens */,
+ mDisplayContent, true /* ownerCanManageAppTokens */, INVALID_UID,
true /* roundedCornerOverlay */, true /* fromClientToken */);
assertNull(fromClientToken.mSurfaceControl);
@@ -175,7 +176,7 @@
final WindowToken nonClientToken = new WindowToken(mDisplayContent.mWmService,
mock(IBinder.class), TYPE_TOAST, true /* persistOnEmpty */, mDisplayContent,
- true /* ownerCanManageAppTokens */, true /* roundedCornerOverlay */,
+ true /* ownerCanManageAppTokens */, INVALID_UID, true /* roundedCornerOverlay */,
false /* fromClientToken */);
assertNotNull(nonClientToken.mSurfaceControl);
}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
index d5851d8..0c25cfb 100644
--- a/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
+++ b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
@@ -34,18 +34,21 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
+import android.util.ArraySet;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.IoThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.pm.BackgroundDexOptService;
import com.android.server.wm.ActivityMetricsLaunchObserver;
import com.android.server.wm.ActivityMetricsLaunchObserver.ActivityRecordProto;
import com.android.server.wm.ActivityMetricsLaunchObserver.Temperature;
import com.android.server.wm.ActivityMetricsLaunchObserverRegistry;
import com.android.server.wm.ActivityTaskManagerInternal;
+import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
import java.util.HashMap;
@@ -286,6 +289,7 @@
private final AppLaunchObserver mAppLaunchObserver = new AppLaunchObserver();
private final EventSequenceValidator mEventSequenceValidator = new EventSequenceValidator();
+ private final DexOptPackagesUpdated mDexOptPackagesUpdated = new DexOptPackagesUpdated();
private boolean mRegisteredListeners = false;
private void registerInProcessListenersLocked() {
@@ -308,9 +312,22 @@
launchObserverRegistry.registerLaunchObserver(mAppLaunchObserver);
launchObserverRegistry.registerLaunchObserver(mEventSequenceValidator);
+ BackgroundDexOptService.addPackagesUpdatedListener(mDexOptPackagesUpdated);
+
+
mRegisteredListeners = true;
}
+ private class DexOptPackagesUpdated implements BackgroundDexOptService.PackagesUpdatedListener {
+ @Override
+ public void onPackagesUpdated(ArraySet<String> updatedPackages) {
+ String[] updated = updatedPackages.toArray(new String[0]);
+ for (String packageName : updated) {
+ Log.d(TAG, "onPackagesUpdated: " + packageName);
+ }
+ }
+ }
+
private class AppLaunchObserver implements ActivityMetricsLaunchObserver {
// We add a synthetic sequence ID here to make it easier to differentiate new
// launch sequences on the native side.
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 327e8b3..56f3c3e 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1147,6 +1147,21 @@
"support_ims_conference_event_package_bool";
/**
+ * Determines whether processing of conference event package data received on a device other
+ * than the conference host is supported.
+ * <p>
+ * When a device A merges calls B and C into a conference it is considered the conference host
+ * and B and C are considered the conference peers.
+ * <p>
+ * When {@code true}, the conference peer will display the conference state if it receives
+ * conference event package data from the network. When {@code false}, the conference peer will
+ * ignore conference event package data received from the network.
+ * @hide
+ */
+ public static final String KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_ON_PEER_BOOL =
+ "support_ims_conference_event_package_on_peer_bool";
+
+ /**
* Determines whether High Definition audio property is displayed in the dialer UI.
* If {@code false}, remove the HD audio property from the connection so that HD audio related
* UI is not displayed. If {@code true}, keep HD audio property as it is configured.
@@ -1163,6 +1178,25 @@
"support_ims_conference_call_bool";
/**
+ * Determines whether the device will locally disconnect an IMS conference when the participant
+ * count drops to zero. When {@code true}, it is assumed the carrier does NOT disconnect a
+ * conference when the participant count drops to zero and that the device must do this by
+ * disconnecting the conference locally. When {@code false}, it is assumed that the carrier
+ * is responsible for disconnecting the conference when there are no longer any participants
+ * present.
+ * <p>
+ * Note: both {@link #KEY_SUPPORT_IMS_CONFERENCE_CALL_BOOL} and
+ * {@link #KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_BOOL} must be true for this configuration to
+ * have any effect.
+ * <p>
+ * Defaults to {@code false}, meaning the carrier network is responsible for disconnecting an
+ * empty IMS conference.
+ * @hide
+ */
+ public static final String KEY_LOCAL_DISCONNECT_EMPTY_IMS_CONFERENCE_BOOL =
+ "local_disconnect_empty_ims_conference_bool";
+
+ /**
* Determines whether video conference calls are supported by a carrier. When {@code true},
* video calls can be merged into conference calls, {@code false} otherwiwse.
* <p>
@@ -3778,8 +3812,10 @@
sDefaults.putBoolean(KEY_SUPPORT_ADD_CONFERENCE_PARTICIPANTS_BOOL, false);
sDefaults.putBoolean(KEY_SUPPORT_CONFERENCE_CALL_BOOL, true);
sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_CALL_BOOL, true);
+ sDefaults.putBoolean(KEY_LOCAL_DISCONNECT_EMPTY_IMS_CONFERENCE_BOOL, false);
sDefaults.putBoolean(KEY_SUPPORT_MANAGE_IMS_CONFERENCE_CALL_BOOL, true);
sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_BOOL, true);
+ sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_ON_PEER_BOOL, true);
sDefaults.putBoolean(KEY_SUPPORT_VIDEO_CONFERENCE_CALL_BOOL, false);
sDefaults.putBoolean(KEY_IS_IMS_CONFERENCE_SIZE_ENFORCED_BOOL, false);
sDefaults.putInt(KEY_IMS_CONFERENCE_SIZE_LIMIT_INT, 5);
diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
index 4c2a984..737665f 100644
--- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
@@ -17,6 +17,7 @@
package com.android.framework.permission.tests;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import android.os.Binder;
import android.os.RemoteException;
@@ -27,6 +28,8 @@
import junit.framework.TestCase;
+import org.junit.Test;
+
/**
* TODO: Remove this. This is only a placeholder, need to implement this.
*/
@@ -53,7 +56,7 @@
}
try {
- mWm.addWindowToken(null, 0, DEFAULT_DISPLAY);
+ mWm.addWindowToken(null, TYPE_APPLICATION, DEFAULT_DISPLAY);
fail("IWindowManager.addWindowToken did not throw SecurityException as"
+ " expected");
} catch (SecurityException e) {
@@ -63,16 +66,6 @@
}
try {
- mWm.removeWindowToken(null, DEFAULT_DISPLAY);
- fail("IWindowManager.removeWindowToken did not throw SecurityException as"
- + " expected");
- } catch (SecurityException e) {
- // expected
- } catch (RemoteException e) {
- fail("Unexpected remote exception");
- }
-
- try {
mWm.prepareAppTransition(0, false);
fail("IWindowManager.prepareAppTransition did not throw SecurityException as"
+ " expected");
@@ -182,4 +175,29 @@
fail("Unexpected remote exception");
}
}
+
+ @Test
+ public void testADD_WINDOW_TOKEN_WITH_OPTIONS() {
+ // Verify if addWindowTokenWithOptions throw SecurityException for privileged window type.
+ try {
+ mWm.addWindowTokenWithOptions(null, TYPE_APPLICATION, DEFAULT_DISPLAY, null, "");
+ fail("IWindowManager.addWindowTokenWithOptions did not throw SecurityException as"
+ + " expected");
+ } catch (SecurityException e) {
+ // expected
+ } catch (RemoteException e) {
+ fail("Unexpected remote exception");
+ }
+
+ // Verify if addWindowTokenWithOptions throw SecurityException for null packageName.
+ try {
+ mWm.addWindowTokenWithOptions(null, TYPE_APPLICATION, DEFAULT_DISPLAY, null, null);
+ fail("IWindowManager.addWindowTokenWithOptions did not throw SecurityException as"
+ + " expected");
+ } catch (SecurityException e) {
+ // expected
+ } catch (RemoteException e) {
+ fail("Unexpected remote exception");
+ }
+ }
}
diff --git a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
index 3026e0b..0dd45ba 100644
--- a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
+++ b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
@@ -47,7 +47,8 @@
"android.view.InsetsSourceConsumerTest",
"android.view.InsetsStateTest",
"android.view.WindowMetricsTest",
- "android.view.PendingInsetsControllerTest"
+ "android.view.PendingInsetsControllerTest",
+ "android.app.WindowContextTest"
};
public FrameworksTestsFilter(Bundle testArgs) {
diff --git a/tools/stats_log_api_gen/atoms_info_writer.cpp b/tools/stats_log_api_gen/atoms_info_writer.cpp
index 23a0f72..5fe9498 100644
--- a/tools/stats_log_api_gen/atoms_info_writer.cpp
+++ b/tools/stats_log_api_gen/atoms_info_writer.cpp
@@ -42,7 +42,6 @@
fprintf(out,
" const static std::set<int> "
"kTruncatingTimestampAtomBlackList;\n");
- fprintf(out, " const static std::map<int, int> kAtomsWithUidField;\n");
fprintf(out, " const static std::set<int> kAtomsWithAttributionChain;\n");
fprintf(out,
" const static std::map<int, StateAtomFieldOptions> "
@@ -101,28 +100,6 @@
fprintf(out, "};\n");
fprintf(out, "\n");
- fprintf(out, "static std::map<int, int> getAtomUidField() {\n");
- fprintf(out, " std::map<int, int> uidField;\n");
- for (AtomDeclSet::const_iterator atomIt = atoms.decls.begin(); atomIt != atoms.decls.end();
- atomIt++) {
- if ((*atomIt)->uidField == 0) {
- continue;
- }
- fprintf(out,
- "\n // Adding uid field for atom "
- "(%d)%s\n",
- (*atomIt)->code, (*atomIt)->name.c_str());
- fprintf(out, " uidField[%d /* %s */] = %d;\n", (*atomIt)->code,
- make_constant_name((*atomIt)->name).c_str(), (*atomIt)->uidField);
- }
-
- fprintf(out, " return uidField;\n");
- fprintf(out, "};\n");
-
- fprintf(out,
- "const std::map<int, int> AtomsInfo::kAtomsWithUidField = "
- "getAtomUidField();\n");
-
fprintf(out,
"static std::map<int, StateAtomFieldOptions> "
"getStateAtomFieldOptions() {\n");
diff --git a/wifi/jarjar-rules.txt b/wifi/jarjar-rules.txt
index eeb006e..cf11f43 100644
--- a/wifi/jarjar-rules.txt
+++ b/wifi/jarjar-rules.txt
@@ -53,6 +53,9 @@
rule com.android.internal.messages.SystemMessageProto* com.android.server.x.wifi.messages.SystemMessageProto@1
# Use our statically linked PlatformProperties library
rule android.sysprop.** com.android.server.x.wifi.sysprop.@1
+# Use our statically linked HIDL stubs
+rule android.hardware.** com.android.server.x.wifi.hardware.@1
+rule android.hidl.** com.android.server.x.wifi.hidl.@1
# used by both framework-wifi and wifi-service
rule android.content.pm.BaseParceledListSlice* android.x.net.wifi.util.BaseParceledListSlice@1