Merge "Fix registering PiP transition calback in PipManager multiple times" into rvc-dev
diff --git a/api/test-current.txt b/api/test-current.txt
index 5de6c2a..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";
@@ -4860,7 +4861,9 @@
}
public final class SurfaceControl implements android.os.Parcelable {
+ ctor public SurfaceControl(@NonNull android.view.SurfaceControl);
method public static long acquireFrameRateFlexibilityToken();
+ method public boolean isSameSurface(@NonNull android.view.SurfaceControl);
method public static void releaseFrameRateFlexibilityToken(long);
}
@@ -5213,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/atoms.proto b/cmds/statsd/src/atoms.proto
index bd15264..d1995bc 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -418,7 +418,7 @@
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"];
SdkExtensionStatus sdk_extension_status = 354;
}
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/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..9cdf582 100644
--- a/cmds/statsd/tests/LogEntryMatcher_test.cpp
+++ b/cmds/statsd/tests/LogEntryMatcher_test.cpp
@@ -48,15 +48,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 +58,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,15 +68,9 @@
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,
@@ -99,13 +81,8 @@
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
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..3e1cc5e 100644
--- a/cmds/statsd/tests/external/puller_util_test.cpp
+++ b/cmds/statsd/tests/external/puller_util_test.cpp
@@ -23,6 +23,7 @@
#include "../metrics/metrics_test_helper.h"
#include "stats_event.h"
#include "statslog_statsdtest.h"
+#include "tests/statsd_test_util.h"
#ifdef __ANDROID__
@@ -71,14 +72,9 @@
AStatsEvent_writeInt32(statsEvent, uid);
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 +82,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..009e49a 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/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/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/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/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index 69bab4d..6cb9374 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -91,6 +91,8 @@
if (mSourceControl == control) {
return;
}
+ SurfaceControl oldLeash = mSourceControl != null ? mSourceControl.getLeash() : null;
+
final InsetsSourceControl lastControl = mSourceControl;
mSourceControl = control;
@@ -116,6 +118,12 @@
// However make sure that the leash visibility is still up to date.
if (applyLocalVisibilityOverride()) {
mController.notifyVisibilityChanged();
+ }
+
+ // If we have a new leash, make sure visibility is up-to-date, even though we
+ // didn't want to run an animation above.
+ SurfaceControl newLeash = mSourceControl.getLeash();
+ if (oldLeash == null || newLeash == null || !oldLeash.isSameSurface(newLeash)) {
applyHiddenToControl();
}
}
diff --git a/core/java/android/view/InsetsSourceControl.java b/core/java/android/view/InsetsSourceControl.java
index f3ec65f..e001b66 100644
--- a/core/java/android/view/InsetsSourceControl.java
+++ b/core/java/android/view/InsetsSourceControl.java
@@ -44,8 +44,7 @@
public InsetsSourceControl(InsetsSourceControl other) {
mType = other.mType;
if (other.mLeash != null) {
- mLeash = new SurfaceControl();
- mLeash.copyFrom(other.mLeash);
+ mLeash = new SurfaceControl(other.mLeash);
} else {
mLeash = null;
}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index b5f9df7..ab65e3a 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -216,6 +216,7 @@
private static native void nativeSetFrameRate(
long transactionObj, long nativeObject, float frameRate, int compatibility);
+ private static native long nativeGetHandle(long nativeObject);
private static native long nativeAcquireFrameRateFlexibilityToken();
private static native void nativeReleaseFrameRateFlexibilityToken(long token);
@@ -226,6 +227,7 @@
* @hide
*/
public long mNativeObject;
+ private long mNativeHandle;
// TODO: Move this to native.
private final Object mSizeLock = new Object();
@@ -428,12 +430,13 @@
mCloseGuard.open("release");
}
mNativeObject = nativeObject;
+ mNativeHandle = mNativeObject != 0 ? nativeGetHandle(nativeObject) : 0;
}
/**
* @hide
*/
- public void copyFrom(SurfaceControl other) {
+ public void copyFrom(@NonNull SurfaceControl other) {
mName = other.mName;
mWidth = other.mWidth;
mHeight = other.mHeight;
@@ -853,23 +856,19 @@
throw new OutOfResourcesException(
"Couldn't allocate SurfaceControl native object");
}
-
+ mNativeHandle = nativeGetHandle(mNativeObject);
mCloseGuard.open("release");
}
- /** This is a transfer constructor, useful for transferring a live SurfaceControl native
- * object to another Java wrapper which could have some different behavior, e.g.
- * event logging.
+ /**
+ * Copy constructor. Creates a new native object pointing to the same surface as {@code other}.
+ *
+ * @param other The object to copy the surface from.
* @hide
*/
- public SurfaceControl(SurfaceControl other) {
- mName = other.mName;
- mWidth = other.mWidth;
- mHeight = other.mHeight;
- mNativeObject = other.mNativeObject;
- other.mCloseGuard.close();
- other.mNativeObject = 0;
- mCloseGuard.open("release");
+ @TestApi
+ public SurfaceControl(@NonNull SurfaceControl other) {
+ copyFrom(other);
}
private SurfaceControl(Parcel in) {
@@ -921,6 +920,18 @@
}
/**
+ * Checks whether two {@link SurfaceControl} objects represent the same surface.
+ *
+ * @param other The other object to check
+ * @return {@code true} if these two {@link SurfaceControl} objects represent the same surface.
+ * @hide
+ */
+ @TestApi
+ public boolean isSameSurface(@NonNull SurfaceControl other) {
+ return other.mNativeHandle == mNativeHandle;
+ }
+
+ /**
* Write to a protocol buffer output stream. Protocol buffer message definition is at {@link
* android.view.SurfaceControlProto}.
*
@@ -977,6 +988,7 @@
if (mNativeObject != 0) {
nativeRelease(mNativeObject);
mNativeObject = 0;
+ mNativeHandle = 0;
mCloseGuard.close();
}
}
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/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index ff03f1a..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);
@@ -1060,4 +1077,11 @@
*/
@FastNative
public static native int nativeParseSigChld(byte[] in, int length, int[] out);
+
+ /**
+ * Returns whether the kernel supports tagged pointers. Present in the
+ * Android Common Kernel from 4.14 and up. By default, you should prefer
+ * fully-feature Memory Tagging, rather than the static Tagged Pointers.
+ */
+ public static native boolean nativeSupportsTaggedPointers();
}
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/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index ec1f516..c2b13c9 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -757,9 +757,11 @@
Zygote.applyDebuggerSystemProperty(parsedArgs);
Zygote.applyInvokeWithSystemProperty(parsedArgs);
- /* Enable pointer tagging in the system server unconditionally. Hardware support for
- * this is present in all ARMv8 CPUs; this flag has no effect on other platforms. */
- parsedArgs.mRuntimeFlags |= Zygote.MEMORY_TAG_LEVEL_TBI;
+ if (Zygote.nativeSupportsTaggedPointers()) {
+ /* Enable pointer tagging in the system server. Hardware support for this is present
+ * in all ARMv8 CPUs. */
+ parsedArgs.mRuntimeFlags |= Zygote.MEMORY_TAG_LEVEL_TBI;
+ }
/* Enable gwp-asan on the system server with a small probability. This is the same
* policy as applied to native processes and system apps. */
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index b11c4c9..1cfa12d 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -1430,6 +1430,12 @@
client->setGlobalShadowSettings(ambientColor, spotColor, lightPosY, lightPosZ, lightRadius);
}
+
+static jlong nativeGetHandle(JNIEnv* env, jclass clazz, jlong nativeObject) {
+ SurfaceControl *surfaceControl = reinterpret_cast<SurfaceControl*>(nativeObject);
+ return reinterpret_cast<jlong>(surfaceControl->getHandle().get());
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod sSurfaceControlMethods[] = {
@@ -1606,6 +1612,8 @@
(void*)nativeMirrorSurface },
{"nativeSetGlobalShadowSettings", "([F[FFFF)V",
(void*)nativeSetGlobalShadowSettings },
+ {"nativeGetHandle", "(J)J",
+ (void*)nativeGetHandle },
};
int register_android_view_SurfaceControl(JNIEnv* env)
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index ea3c0fa..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);
}
/**
@@ -2405,10 +2430,19 @@
return -1;
}
+static jboolean com_android_internal_os_Zygote_nativeSupportsTaggedPointers(JNIEnv* env, jclass) {
+#ifdef __aarch64__
+ int res = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
+ return res >= 0 && res & PR_TAGGED_ADDR_ENABLE;
+#else
+ return false;
+#endif
+}
+
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},
@@ -2421,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},
@@ -2440,6 +2474,8 @@
(void*)com_android_internal_os_Zygote_nativeBoostUsapPriority},
{"nativeParseSigChld", "([BI[I)I",
(void*)com_android_internal_os_Zygote_nativeParseSigChld},
+ {"nativeSupportsTaggedPointers", "()Z",
+ (void*)com_android_internal_os_Zygote_nativeSupportsTaggedPointers},
};
int register_com_android_internal_os_Zygote(JNIEnv* env) {
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index cbb379b..d432dda 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -696,8 +696,7 @@
// Simulate binder behavior by copying SurfaceControl. Otherwise, InsetsController will
// attempt to release mLeash directly.
- SurfaceControl copy = new SurfaceControl();
- copy.copyFrom(mLeash);
+ SurfaceControl copy = new SurfaceControl(mLeash);
return new InsetsSourceControl(type, copy, new Point());
}
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/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/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/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 23fa645..5442299 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -120,7 +120,7 @@
private void init(DozeParameters dozeParameters) {
mIsHighEndGfx = ActivityManager.isHighEndGfx();
mDisplayNeedsBlanking = dozeParameters.getDisplayNeedsBlanking();
- mNeedTransition = mIsHighEndGfx && !mDisplayNeedsBlanking;
+ mNeedTransition = false;
// We will preserve EGL context when we are in lock screen or aod
// to avoid janking in following transition, we need to release when back to home.
@@ -137,7 +137,7 @@
mRenderer = getRendererInstance();
getDisplayContext().getDisplay().getDisplayInfo(mDisplayInfo);
setFixedSizeAllowed(true);
- setOffsetNotificationsEnabled(true);
+ setOffsetNotificationsEnabled(mNeedTransition);
updateSurfaceSize();
}
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/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/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index 55d3e83..729f934 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -38,6 +38,7 @@
import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK
import com.android.systemui.statusbar.phone.NotificationShadeWindowController
import com.android.systemui.statusbar.phone.PanelExpansionListener
+import com.android.systemui.statusbar.phone.ScrimController
import com.android.systemui.statusbar.policy.KeyguardStateController
import java.io.FileDescriptor
import java.io.PrintWriter
@@ -107,6 +108,16 @@
}
/**
+ * Force stop blur effect when necessary.
+ */
+ private var scrimsVisible: Boolean = false
+ set(value) {
+ if (field == value) return
+ field = value
+ scheduleUpdate()
+ }
+
+ /**
* Blur radius of the wake-up animation on this frame.
*/
private var wakeAndUnlockBlurRadius = 0
@@ -142,7 +153,13 @@
if (showingHomeControls) {
globalActionsRadius = 0
}
- val blur = max(shadeRadius.toInt(), globalActionsRadius)
+ var blur = max(shadeRadius.toInt(), globalActionsRadius)
+
+ // Make blur be 0 if it is necessary to stop blur effect.
+ if (scrimsVisible) {
+ blur = 0
+ }
+
blurUtils.applyBlur(blurRoot?.viewRootImpl ?: root.viewRootImpl, blur)
try {
wallpaperManager.setWallpaperZoomOut(root.windowToken,
@@ -202,6 +219,10 @@
brightnessMirrorSpring.finishIfRunning()
}
}
+
+ override fun onDozeAmountChanged(linear: Float, eased: Float) {
+ wakeAndUnlockBlurRadius = blurUtils.blurRadiusOfRatio(eased)
+ }
}
init {
@@ -210,6 +231,10 @@
keyguardStateController.addCallback(keyguardStateCallback)
}
statusBarStateController.addCallback(statusBarStateCallback)
+ notificationShadeWindowController.setScrimsVisibilityListener {
+ // Stop blur effect when scrims is opaque to avoid unnecessary GPU composition.
+ visibility -> scrimsVisible = visibility == ScrimController.OPAQUE
+ }
}
/**
@@ -225,7 +250,8 @@
private fun updateShadeBlur() {
var newBlur = 0
- if (statusBarStateController.state == StatusBarState.SHADE) {
+ val state = statusBarStateController.state
+ if (state == StatusBarState.SHADE || state == StatusBarState.SHADE_LOCKED) {
val animatedBlur =
Interpolators.SHADE_ANIMATION.getInterpolation(
MathUtils.constrain(shadeExpansion / 0.15f, 0f, 1f))
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
index e1e679f..462b42a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
@@ -61,6 +61,7 @@
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.function.Consumer;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -92,6 +93,7 @@
private final State mCurrentState = new State();
private OtherwisedCollapsedListener mListener;
private ForcePluginOpenListener mForcePluginOpenListener;
+ private Consumer<Integer> mScrimsVisibilityListener;
private final ArrayList<WeakReference<StatusBarWindowCallback>>
mCallbacks = Lists.newArrayList();
@@ -150,6 +152,16 @@
mCallbacks.add(new WeakReference<StatusBarWindowCallback>(callback));
}
+ /**
+ * Register a listener to monitor scrims visibility
+ * @param listener A listener to monitor scrims visibility
+ */
+ public void setScrimsVisibilityListener(Consumer<Integer> listener) {
+ if (listener != null && mScrimsVisibilityListener != listener) {
+ mScrimsVisibilityListener = listener;
+ }
+ }
+
private boolean shouldEnableKeyguardScreenRotation() {
Resources res = mContext.getResources();
return SystemProperties.getBoolean("lockscreen.rot_override", false)
@@ -477,6 +489,7 @@
public void setScrimsVisibility(int scrimsVisibility) {
mCurrentState.mScrimsVisibility = scrimsVisibility;
apply(mCurrentState);
+ mScrimsVisibilityListener.accept(scrimsVisibility);
}
/**
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/systemui/ImageWallpaperTest.java b/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java
index 475023e..5227aaf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java
@@ -160,7 +160,7 @@
LOW_BMP_HEIGHT /* bmpHeight */,
LOW_BMP_WIDTH /* surfaceWidth */,
LOW_BMP_HEIGHT /* surfaceHeight */,
- true /* assertion */);
+ false /* assertion */);
}
@Test
@@ -172,7 +172,7 @@
INVALID_BMP_HEIGHT /* bmpHeight */,
ImageWallpaper.GLEngine.MIN_SURFACE_WIDTH /* surfaceWidth */,
ImageWallpaper.GLEngine.MIN_SURFACE_HEIGHT /* surfaceHeight */,
- true /* assertion */);
+ false /* assertion */);
}
private void verifySurfaceSizeAndAssertTransition(int bmpWidth, int bmpHeight,
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/Tethering/tests/integration/Android.bp b/packages/Tethering/tests/integration/Android.bp
index 1a1c30d..620261b 100644
--- a/packages/Tethering/tests/integration/Android.bp
+++ b/packages/Tethering/tests/integration/Android.bp
@@ -39,4 +39,9 @@
"android.test.base",
"android.test.mock",
],
+ jni_libs: [
+ // For mockito extended
+ "libdexmakerjvmtiagent",
+ "libstaticjvmtiagent",
+ ],
}
diff --git a/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
index dbd68ef..b02bb23 100644
--- a/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
+++ b/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
@@ -205,7 +205,7 @@
requestWithStaticIpv4(localAddr, clientAddr));
mTetheringEventCallback.awaitInterfaceTethered();
- assertInterfaceHasIpAddress(iface, clientAddr);
+ assertInterfaceHasIpAddress(iface, localAddr);
byte[] client1 = MacAddress.fromString("1:2:3:4:5:6").toByteArray();
byte[] client2 = MacAddress.fromString("a:b:c:d:e:f").toByteArray();
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/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java b/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java
new file mode 100644
index 0000000..3612e09
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.autofill;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.os.Bundle;
+import android.os.Handler;
+import android.view.autofill.AutofillId;
+import android.view.inputmethod.InlineSuggestionsRequest;
+import android.view.inputmethod.InlineSuggestionsResponse;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.inputmethod.InputMethodManagerInternal;
+
+import java.util.Collections;
+import java.util.Optional;
+import java.util.function.Consumer;
+
+
+/**
+ * Controls the interaction with the IME for the inline suggestion sessions.
+ */
+final class AutofillInlineSessionController {
+ @NonNull
+ private final InputMethodManagerInternal mInputMethodManagerInternal;
+ private final int mUserId;
+ @NonNull
+ private final ComponentName mComponentName;
+ @NonNull
+ private final Object mLock;
+ @NonNull
+ private final Handler mHandler;
+
+ @GuardedBy("mLock")
+ private AutofillInlineSuggestionsRequestSession mSession;
+
+ AutofillInlineSessionController(InputMethodManagerInternal inputMethodManagerInternal,
+ int userId, ComponentName componentName, Handler handler, Object lock) {
+ mInputMethodManagerInternal = inputMethodManagerInternal;
+ mUserId = userId;
+ mComponentName = componentName;
+ mHandler = handler;
+ mLock = lock;
+ }
+
+
+ /**
+ * Requests the IME to create an {@link InlineSuggestionsRequest} for {@code autofillId}.
+ *
+ * @param autofillId the Id of the field for which the request is for.
+ * @param requestConsumer the callback which will be invoked when IME responded or if it times
+ * out waiting for IME response.
+ */
+ @GuardedBy("mLock")
+ void onCreateInlineSuggestionsRequestLocked(@NonNull AutofillId autofillId,
+ @NonNull Consumer<InlineSuggestionsRequest> requestConsumer, @NonNull Bundle uiExtras) {
+ // TODO(b/151123764): rename the method to better reflect what it does.
+ if (mSession != null) {
+ // Send an empty response to IME and destroy the existing session.
+ mSession.onInlineSuggestionsResponseLocked(mSession.getAutofillIdLocked(),
+ new InlineSuggestionsResponse(Collections.EMPTY_LIST));
+ mSession.destroySessionLocked();
+ }
+ // TODO(b/151123764): consider reusing the same AutofillInlineSession object for the
+ // same field.
+ mSession = new AutofillInlineSuggestionsRequestSession(mInputMethodManagerInternal, mUserId,
+ mComponentName, mHandler, mLock, autofillId, requestConsumer, uiExtras);
+ mSession.onCreateInlineSuggestionsRequestLocked();
+
+ }
+
+ /**
+ * Returns the {@link InlineSuggestionsRequest} provided by IME for the last request.
+ *
+ * <p> The caller is responsible for making sure Autofill hears back from IME before calling
+ * this method, using the {@code requestConsumer} provided when calling {@link
+ * #onCreateInlineSuggestionsRequestLocked(AutofillId, Consumer, Bundle)}.
+ */
+ @GuardedBy("mLock")
+ Optional<InlineSuggestionsRequest> getInlineSuggestionsRequestLocked() {
+ if (mSession != null) {
+ return mSession.getInlineSuggestionsRequestLocked();
+ }
+ return Optional.empty();
+ }
+
+ /**
+ * Requests the IME to hide the current suggestions, if any. Returns true if the message is sent
+ * to the IME.
+ */
+ @GuardedBy("mLock")
+ boolean hideInlineSuggestionsUiLocked(@NonNull AutofillId autofillId) {
+ if (mSession != null) {
+ return mSession.onInlineSuggestionsResponseLocked(autofillId,
+ new InlineSuggestionsResponse(Collections.EMPTY_LIST));
+ }
+ return false;
+ }
+
+ /**
+ * Requests showing the inline suggestion in the IME when the IME becomes visible and is focused
+ * on the {@code autofillId}.
+ *
+ * @return false if there is no session, or if the IME callback is not available in the session.
+ */
+ @GuardedBy("mLock")
+ boolean onInlineSuggestionsResponseLocked(@NonNull AutofillId autofillId,
+ @NonNull InlineSuggestionsResponse inlineSuggestionsResponse) {
+ // TODO(b/151123764): rename the method to better reflect what it does.
+ if (mSession != null) {
+ return mSession.onInlineSuggestionsResponseLocked(autofillId,
+ inlineSuggestionsResponse);
+ }
+ return false;
+ }
+}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java b/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
new file mode 100644
index 0000000..ca230b6
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
@@ -0,0 +1,419 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.autofill;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+import static com.android.server.autofill.Helper.sDebug;
+
+import android.annotation.BinderThread;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Slog;
+import android.view.autofill.AutofillId;
+import android.view.inputmethod.InlineSuggestionsRequest;
+import android.view.inputmethod.InlineSuggestionsResponse;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.view.IInlineSuggestionsRequestCallback;
+import com.android.internal.view.IInlineSuggestionsResponseCallback;
+import com.android.internal.view.InlineSuggestionsRequestInfo;
+import com.android.server.inputmethod.InputMethodManagerInternal;
+
+import java.lang.ref.WeakReference;
+import java.util.Optional;
+import java.util.function.Consumer;
+
+/**
+ * Maintains an inline suggestion session with the IME.
+ *
+ * <p> Each session corresponds to one request from the Autofill manager service to create an
+ * {@link InlineSuggestionsRequest}. It's responsible for receiving callbacks from the IME and
+ * sending {@link android.view.inputmethod.InlineSuggestionsResponse} to IME.
+ */
+final class AutofillInlineSuggestionsRequestSession {
+
+ private static final String TAG = AutofillInlineSuggestionsRequestSession.class.getSimpleName();
+ private static final int INLINE_REQUEST_TIMEOUT_MS = 200;
+
+ @NonNull
+ private final InputMethodManagerInternal mInputMethodManagerInternal;
+ private final int mUserId;
+ @NonNull
+ private final ComponentName mComponentName;
+ @NonNull
+ private final Object mLock;
+ @NonNull
+ private final Handler mHandler;
+ @NonNull
+ private final Bundle mUiExtras;
+
+ @GuardedBy("mLock")
+ @NonNull
+ private AutofillId mAutofillId;
+ @GuardedBy("mLock")
+ @Nullable
+ private Consumer<InlineSuggestionsRequest> mImeRequestConsumer;
+
+ @GuardedBy("mLock")
+ private boolean mImeRequestReceived;
+ @GuardedBy("mLock")
+ @Nullable
+ private InlineSuggestionsRequest mImeRequest;
+ @GuardedBy("mLock")
+ @Nullable
+ private IInlineSuggestionsResponseCallback mResponseCallback;
+ @GuardedBy("mLock")
+ @Nullable
+ private Runnable mTimeoutCallback;
+
+ @GuardedBy("mLock")
+ @Nullable
+ private AutofillId mImeCurrentFieldId;
+ @GuardedBy("mLock")
+ private boolean mImeInputStarted;
+ @GuardedBy("mLock")
+ private boolean mImeInputViewStarted;
+ @GuardedBy("mLock")
+ @Nullable
+ private InlineSuggestionsResponse mInlineSuggestionsResponse;
+ @GuardedBy("mLock")
+ private boolean mPreviousResponseIsNotEmpty;
+
+ @GuardedBy("mLock")
+ private boolean mDestroyed = false;
+
+ AutofillInlineSuggestionsRequestSession(
+ @NonNull InputMethodManagerInternal inputMethodManagerInternal, int userId,
+ @NonNull ComponentName componentName, @NonNull Handler handler, @NonNull Object lock,
+ @NonNull AutofillId autofillId,
+ @NonNull Consumer<InlineSuggestionsRequest> requestConsumer, @NonNull Bundle uiExtras) {
+ mInputMethodManagerInternal = inputMethodManagerInternal;
+ mUserId = userId;
+ mComponentName = componentName;
+ mHandler = handler;
+ mLock = lock;
+ mUiExtras = uiExtras;
+
+ mAutofillId = autofillId;
+ mImeRequestConsumer = requestConsumer;
+ }
+
+ @GuardedBy("mLock")
+ @NonNull
+ AutofillId getAutofillIdLocked() {
+ return mAutofillId;
+ }
+
+ /**
+ * Returns the {@link InlineSuggestionsRequest} provided by IME.
+ *
+ * <p> The caller is responsible for making sure Autofill hears back from IME before calling
+ * this method, using the {@link #mImeRequestConsumer}.
+ */
+ @GuardedBy("mLock")
+ Optional<InlineSuggestionsRequest> getInlineSuggestionsRequestLocked() {
+ if (mDestroyed) {
+ return Optional.empty();
+ }
+ return Optional.ofNullable(mImeRequest);
+ }
+
+ /**
+ * Requests showing the inline suggestion in the IME when the IME becomes visible and is focused
+ * on the {@code autofillId}.
+ *
+ * @return false if the IME callback is not available.
+ */
+ @GuardedBy("mLock")
+ boolean onInlineSuggestionsResponseLocked(@NonNull AutofillId autofillId,
+ @NonNull InlineSuggestionsResponse inlineSuggestionsResponse) {
+ if (mDestroyed) {
+ return false;
+ }
+ if (sDebug) Log.d(TAG, "onInlineSuggestionsResponseLocked called for:" + autofillId);
+ if (mImeRequest == null || mResponseCallback == null) {
+ return false;
+ }
+ // TODO(b/151123764): each session should only correspond to one field.
+ mAutofillId = autofillId;
+ mInlineSuggestionsResponse = inlineSuggestionsResponse;
+ maybeUpdateResponseToImeLocked();
+ return true;
+ }
+
+ /**
+ * This method must be called when the session is destroyed, to avoid further callbacks from/to
+ * the IME.
+ */
+ @GuardedBy("mLock")
+ void destroySessionLocked() {
+ mDestroyed = true;
+ }
+
+ /**
+ * Requests the IME to create an {@link InlineSuggestionsRequest}.
+ *
+ * <p> This method should only be called once per session.
+ */
+ @GuardedBy("mLock")
+ void onCreateInlineSuggestionsRequestLocked() {
+ if (sDebug) Log.d(TAG, "onCreateInlineSuggestionsRequestLocked called: " + mAutofillId);
+ if (mDestroyed) {
+ return;
+ }
+ mInputMethodManagerInternal.onCreateInlineSuggestionsRequest(mUserId,
+ new InlineSuggestionsRequestInfo(mComponentName, mAutofillId, mUiExtras),
+ new InlineSuggestionsRequestCallbackImpl(this));
+ mTimeoutCallback = () -> {
+ Log.w(TAG, "Timed out waiting for IME callback InlineSuggestionsRequest.");
+ handleOnReceiveImeRequest(null, null);
+ };
+ mHandler.postDelayed(mTimeoutCallback, INLINE_REQUEST_TIMEOUT_MS);
+ }
+
+ /**
+ * Optionally sends inline response to the IME, depending on the current state.
+ */
+ @GuardedBy("mLock")
+ private void maybeUpdateResponseToImeLocked() {
+ if (sDebug) Log.d(TAG, "maybeUpdateResponseToImeLocked called");
+ if (mDestroyed || mResponseCallback == null) {
+ return;
+ }
+ if (!mImeInputViewStarted && mPreviousResponseIsNotEmpty) {
+ // 1. if previous response is not empty, and IME just become invisible, then send
+ // empty response to make sure existing responses don't stick around on the IME.
+ // Although the inline suggestions should disappear when IME hides which removes them
+ // from the view hierarchy, but we still send an empty response to be extra safe.
+
+ // TODO(b/149945531): clear the existing suggestions when IME is hide, once the bug is
+ // fixed.
+ //if (sDebug) Log.d(TAG, "Send empty inline response");
+ //updateResponseToImeUncheckLocked(new InlineSuggestionsResponse(Collections
+ // .EMPTY_LIST));
+ //mPreviousResponseIsNotEmpty = false;
+ } else if (mImeInputViewStarted && mInlineSuggestionsResponse != null && match(mAutofillId,
+ mImeCurrentFieldId)) {
+ // 2. if IME is visible, and response is not null, send the response
+ boolean isEmptyResponse = mInlineSuggestionsResponse.getInlineSuggestions().isEmpty();
+ if (isEmptyResponse && !mPreviousResponseIsNotEmpty) {
+ // No-op if both the previous response and current response are empty.
+ return;
+ }
+ if (sDebug) {
+ Log.d(TAG, "Send inline response: "
+ + mInlineSuggestionsResponse.getInlineSuggestions().size());
+ }
+ updateResponseToImeUncheckLocked(mInlineSuggestionsResponse);
+ // TODO(b/149945531): don't set the response to null so it's cached, once the bug is
+ // fixed.
+ mInlineSuggestionsResponse = null;
+ mPreviousResponseIsNotEmpty = !isEmptyResponse;
+ }
+ }
+
+ /**
+ * Sends the {@code response} to the IME, assuming all the relevant checks are already done.
+ */
+ @GuardedBy("mLock")
+ private void updateResponseToImeUncheckLocked(InlineSuggestionsResponse response) {
+ if (mDestroyed) {
+ return;
+ }
+ try {
+ mResponseCallback.onInlineSuggestionsResponse(mAutofillId, response);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "RemoteException sending InlineSuggestionsResponse to IME");
+ }
+ }
+
+ /**
+ * Handles the {@code request} and {@code callback} received from the IME.
+ *
+ * <p> Should only invoked in the {@link #mHandler} thread.
+ */
+ private void handleOnReceiveImeRequest(@Nullable InlineSuggestionsRequest request,
+ @Nullable IInlineSuggestionsResponseCallback callback) {
+ synchronized (mLock) {
+ if (mDestroyed || mImeRequestReceived) {
+ return;
+ }
+ mImeRequestReceived = true;
+
+ if (mTimeoutCallback != null) {
+ if (sDebug) Log.d(TAG, "removing timeout callback");
+ mHandler.removeCallbacks(mTimeoutCallback);
+ mTimeoutCallback = null;
+ }
+ if (request != null && callback != null) {
+ mImeRequest = request;
+ mResponseCallback = callback;
+ handleOnReceiveImeStatusUpdated(mAutofillId, true, false);
+ }
+ if (mImeRequestConsumer != null) {
+ // Note that mImeRequest is only set if both request and callback are non-null.
+ mImeRequestConsumer.accept(mImeRequest);
+ mImeRequestConsumer = null;
+ }
+ }
+ }
+
+ /**
+ * Handles the IME status updates received from the IME.
+ *
+ * <p> Should only be invoked in the {@link #mHandler} thread.
+ */
+ private void handleOnReceiveImeStatusUpdated(boolean imeInputStarted,
+ boolean imeInputViewStarted) {
+ synchronized (mLock) {
+ if (mDestroyed) {
+ return;
+ }
+ if (mImeCurrentFieldId != null) {
+ boolean imeInputStartedChanged = (mImeInputStarted != imeInputStarted);
+ boolean imeInputViewStartedChanged = (mImeInputViewStarted != imeInputViewStarted);
+ mImeInputStarted = imeInputStarted;
+ mImeInputViewStarted = imeInputViewStarted;
+ if (imeInputStartedChanged || imeInputViewStartedChanged) {
+ maybeUpdateResponseToImeLocked();
+ }
+ }
+ }
+ }
+
+ /**
+ * Handles the IME status updates received from the IME.
+ *
+ * <p> Should only be invoked in the {@link #mHandler} thread.
+ */
+ private void handleOnReceiveImeStatusUpdated(@Nullable AutofillId imeFieldId,
+ boolean imeInputStarted, boolean imeInputViewStarted) {
+ synchronized (mLock) {
+ if (mDestroyed) {
+ return;
+ }
+ if (imeFieldId != null) {
+ mImeCurrentFieldId = imeFieldId;
+ }
+ handleOnReceiveImeStatusUpdated(imeInputStarted, imeInputViewStarted);
+ }
+ }
+
+ private static final class InlineSuggestionsRequestCallbackImpl extends
+ IInlineSuggestionsRequestCallback.Stub {
+
+ private final WeakReference<AutofillInlineSuggestionsRequestSession> mSession;
+
+ private InlineSuggestionsRequestCallbackImpl(
+ AutofillInlineSuggestionsRequestSession session) {
+ mSession = new WeakReference<>(session);
+ }
+
+ @BinderThread
+ @Override
+ public void onInlineSuggestionsUnsupported() throws RemoteException {
+ if (sDebug) Log.d(TAG, "onInlineSuggestionsUnsupported() called.");
+ final AutofillInlineSuggestionsRequestSession session = mSession.get();
+ if (session != null) {
+ session.mHandler.sendMessage(obtainMessage(
+ AutofillInlineSuggestionsRequestSession::handleOnReceiveImeRequest, session,
+ null, null));
+ }
+ }
+
+ @BinderThread
+ @Override
+ public void onInlineSuggestionsRequest(InlineSuggestionsRequest request,
+ IInlineSuggestionsResponseCallback callback) {
+ if (sDebug) Log.d(TAG, "onInlineSuggestionsRequest() received: " + request);
+ final AutofillInlineSuggestionsRequestSession session = mSession.get();
+ if (session != null) {
+ session.mHandler.sendMessage(obtainMessage(
+ AutofillInlineSuggestionsRequestSession::handleOnReceiveImeRequest, session,
+ request, callback));
+ }
+ }
+
+ @Override
+ public void onInputMethodStartInput(AutofillId imeFieldId) throws RemoteException {
+ if (sDebug) Log.d(TAG, "onInputMethodStartInput() received on " + imeFieldId);
+ final AutofillInlineSuggestionsRequestSession session = mSession.get();
+ if (session != null) {
+ session.mHandler.sendMessage(obtainMessage(
+ AutofillInlineSuggestionsRequestSession::handleOnReceiveImeStatusUpdated,
+ session, imeFieldId, true, false));
+ }
+ }
+
+ @Override
+ public void onInputMethodShowInputRequested(boolean requestResult) throws RemoteException {
+ if (sDebug) {
+ Log.d(TAG, "onInputMethodShowInputRequested() received: " + requestResult);
+ }
+ }
+
+ @BinderThread
+ @Override
+ public void onInputMethodStartInputView() {
+ if (sDebug) Log.d(TAG, "onInputMethodStartInputView() received");
+ final AutofillInlineSuggestionsRequestSession session = mSession.get();
+ if (session != null) {
+ session.mHandler.sendMessage(obtainMessage(
+ AutofillInlineSuggestionsRequestSession::handleOnReceiveImeStatusUpdated,
+ session, true, true));
+ }
+ }
+
+ @BinderThread
+ @Override
+ public void onInputMethodFinishInputView() {
+ if (sDebug) Log.d(TAG, "onInputMethodFinishInputView() received");
+ final AutofillInlineSuggestionsRequestSession session = mSession.get();
+ if (session != null) {
+ session.mHandler.sendMessage(obtainMessage(
+ AutofillInlineSuggestionsRequestSession::handleOnReceiveImeStatusUpdated,
+ session, true, false));
+ }
+ }
+
+ @Override
+ public void onInputMethodFinishInput() throws RemoteException {
+ if (sDebug) Log.d(TAG, "onInputMethodFinishInput() received");
+ final AutofillInlineSuggestionsRequestSession session = mSession.get();
+ if (session != null) {
+ session.mHandler.sendMessage(obtainMessage(
+ AutofillInlineSuggestionsRequestSession::handleOnReceiveImeStatusUpdated,
+ session, false, false));
+ }
+ }
+ }
+
+ private static boolean match(@Nullable AutofillId autofillId,
+ @Nullable AutofillId imeClientFieldId) {
+ // The IME doesn't have information about the virtual view id for the child views in the
+ // web view, so we are only comparing the parent view id here. This means that for cases
+ // where there are two input fields in the web view, they will have the same view id
+ // (although different virtual child id), and we will not be able to distinguish them.
+ return autofillId != null && imeClientFieldId != null
+ && autofillId.getViewId() == imeClientFieldId.getViewId();
+ }
+}
diff --git a/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java b/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java
deleted file mode 100644
index e2d5112..0000000
--- a/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java
+++ /dev/null
@@ -1,403 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.autofill;
-
-import static com.android.server.autofill.Helper.sDebug;
-
-import android.annotation.BinderThread;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.RemoteException;
-import android.util.Log;
-import android.util.Slog;
-import android.view.autofill.AutofillId;
-import android.view.inputmethod.InlineSuggestionsRequest;
-import android.view.inputmethod.InlineSuggestionsResponse;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.view.IInlineSuggestionsRequestCallback;
-import com.android.internal.view.IInlineSuggestionsResponseCallback;
-import com.android.internal.view.InlineSuggestionsRequestInfo;
-import com.android.server.inputmethod.InputMethodManagerInternal;
-
-import java.util.Collections;
-import java.util.Optional;
-import java.util.concurrent.CompletableFuture;
-import java.util.function.Consumer;
-
-/**
- * Maintains an autofill inline suggestion session that communicates with the IME.
- *
- * <p>
- * The same session may be reused for multiple input fields involved in the same autofill
- * {@link Session}. Therefore, one {@link InlineSuggestionsRequest} and one
- * {@link IInlineSuggestionsResponseCallback} may be used to generate and callback with inline
- * suggestions for different input fields.
- *
- * <p>
- * This class is the sole place in Autofill responsible for directly communicating with the IME. It
- * receives the IME input view start/finish events, with the associated IME field Id. It uses the
- * information to decide when to send the {@link InlineSuggestionsResponse} to IME. As a result,
- * some of the response will be cached locally and only be sent when the IME is ready to show them.
- *
- * <p>
- * See {@link android.inputmethodservice.InlineSuggestionSession} comments for InputMethodService
- * side flow.
- *
- * <p>
- * This class should hold the same lock as {@link Session} as they call into each other.
- */
-final class InlineSuggestionSession {
-
- private static final String TAG = "AfInlineSuggestionSession";
- private static final int INLINE_REQUEST_TIMEOUT_MS = 200;
-
- @NonNull
- private final InputMethodManagerInternal mInputMethodManagerInternal;
- private final int mUserId;
- @NonNull
- private final ComponentName mComponentName;
- @NonNull
- private final Object mLock;
- @NonNull
- private final ImeStatusListener mImeStatusListener;
- @NonNull
- private final Handler mHandler;
-
- /**
- * To avoid the race condition, one should not access {@code mPendingImeResponse} without
- * holding the {@code mLock}. For consuming the existing value, tt's recommended to use
- * {@link #getPendingImeResponse()} to get a copy of the reference to avoid blocking call.
- */
- @GuardedBy("mLock")
- @Nullable
- private CompletableFuture<ImeResponse> mPendingImeResponse;
-
- @GuardedBy("mLock")
- @Nullable
- private AutofillResponse mPendingAutofillResponse;
-
- @GuardedBy("mLock")
- private boolean mIsLastResponseNonEmpty = false;
-
- @Nullable
- @GuardedBy("mLock")
- private AutofillId mImeFieldId = null;
-
- @GuardedBy("mLock")
- private boolean mImeInputViewStarted = false;
-
- InlineSuggestionSession(InputMethodManagerInternal inputMethodManagerInternal,
- int userId, ComponentName componentName, Handler handler, Object lock) {
- mInputMethodManagerInternal = inputMethodManagerInternal;
- mUserId = userId;
- mComponentName = componentName;
- mHandler = handler;
- mLock = lock;
- mImeStatusListener = new ImeStatusListener() {
- @Override
- public void onInputMethodStartInput(AutofillId imeFieldId) {
- synchronized (mLock) {
- mImeFieldId = imeFieldId;
- mImeInputViewStarted = false;
- }
- }
-
- @Override
- public void onInputMethodStartInputView() {
- synchronized (mLock) {
- mImeInputViewStarted = true;
- AutofillResponse pendingAutofillResponse = mPendingAutofillResponse;
- if (pendingAutofillResponse != null
- && pendingAutofillResponse.mAutofillId.equalsIgnoreSession(
- mImeFieldId)) {
- mPendingAutofillResponse = null;
- onInlineSuggestionsResponseLocked(pendingAutofillResponse.mAutofillId,
- pendingAutofillResponse.mResponse);
- }
- }
- }
-
- @Override
- public void onInputMethodFinishInputView() {
- synchronized (mLock) {
- mImeInputViewStarted = false;
- }
- }
-
- @Override
- public void onInputMethodFinishInput() {
- synchronized (mLock) {
- mImeFieldId = null;
- }
- }
- };
- }
-
- public void onCreateInlineSuggestionsRequest(@NonNull AutofillId autofillId,
- @NonNull Consumer<InlineSuggestionsRequest> requestConsumer) {
- if (sDebug) Log.d(TAG, "onCreateInlineSuggestionsRequest called for " + autofillId);
-
- synchronized (mLock) {
- // Clean up all the state about the previous request.
- hideInlineSuggestionsUi(autofillId);
- mImeFieldId = null;
- mImeInputViewStarted = false;
- if (mPendingImeResponse != null && !mPendingImeResponse.isDone()) {
- mPendingImeResponse.complete(null);
- }
- mPendingImeResponse = new CompletableFuture<>();
- // TODO(b/146454892): pipe the uiExtras from the ExtServices.
- mInputMethodManagerInternal.onCreateInlineSuggestionsRequest(
- mUserId,
- new InlineSuggestionsRequestInfo(mComponentName, autofillId, new Bundle()),
- new InlineSuggestionsRequestCallbackImpl(autofillId, mPendingImeResponse,
- mImeStatusListener, requestConsumer, mHandler, mLock));
- }
- }
-
- public Optional<InlineSuggestionsRequest> getInlineSuggestionsRequest() {
- final CompletableFuture<ImeResponse> pendingImeResponse = getPendingImeResponse();
- if (pendingImeResponse == null || !pendingImeResponse.isDone()) {
- return Optional.empty();
- }
- return Optional.ofNullable(pendingImeResponse.getNow(null)).map(ImeResponse::getRequest);
- }
-
- public boolean hideInlineSuggestionsUi(@NonNull AutofillId autofillId) {
- synchronized (mLock) {
- if (mIsLastResponseNonEmpty) {
- return onInlineSuggestionsResponseLocked(autofillId,
- new InlineSuggestionsResponse(Collections.EMPTY_LIST));
- }
- return false;
- }
- }
-
- public boolean onInlineSuggestionsResponse(@NonNull AutofillId autofillId,
- @NonNull InlineSuggestionsResponse inlineSuggestionsResponse) {
- synchronized (mLock) {
- return onInlineSuggestionsResponseLocked(autofillId, inlineSuggestionsResponse);
- }
- }
-
- private boolean onInlineSuggestionsResponseLocked(@NonNull AutofillId autofillId,
- @NonNull InlineSuggestionsResponse inlineSuggestionsResponse) {
- final CompletableFuture<ImeResponse> completedImsResponse = getPendingImeResponse();
- if (completedImsResponse == null || !completedImsResponse.isDone()) {
- if (sDebug) Log.d(TAG, "onInlineSuggestionsResponseLocked without IMS request");
- return false;
- }
- // There is no need to wait on the CompletableFuture since it should have been completed.
- ImeResponse imeResponse = completedImsResponse.getNow(null);
- if (imeResponse == null) {
- if (sDebug) Log.d(TAG, "onInlineSuggestionsResponseLocked with pending IMS response");
- return false;
- }
-
- // TODO(b/151846600): IME doesn't have access to the virtual id of the webview, so we
- // only compare the view id for now.
- if (!mImeInputViewStarted || mImeFieldId == null
- || autofillId.getViewId() != mImeFieldId.getViewId()) {
- if (sDebug) {
- Log.d(TAG,
- "onInlineSuggestionsResponseLocked not sent because input view is not "
- + "started for " + autofillId);
- }
- mPendingAutofillResponse = new AutofillResponse(autofillId, inlineSuggestionsResponse);
- // TODO(b/149442582): Although we are not sending the response to IME right away, we
- // still return true to indicate that the response may be sent eventually, such that
- // the dropdown UI will not be shown. This may not be the desired behavior in the
- // auto-focus case where IME isn't shown after switching back to an activity. We may
- // revisit this.
- return true;
- }
-
- try {
- imeResponse.mCallback.onInlineSuggestionsResponse(autofillId,
- inlineSuggestionsResponse);
- mIsLastResponseNonEmpty = !inlineSuggestionsResponse.getInlineSuggestions().isEmpty();
- if (sDebug) {
- Log.d(TAG, "Autofill sends inline response to IME: "
- + inlineSuggestionsResponse.getInlineSuggestions().size());
- }
- return true;
- } catch (RemoteException e) {
- Slog.e(TAG, "RemoteException sending InlineSuggestionsResponse to IME");
- return false;
- }
- }
-
- @Nullable
- @GuardedBy("mLock")
- private CompletableFuture<ImeResponse> getPendingImeResponse() {
- synchronized (mLock) {
- return mPendingImeResponse;
- }
- }
-
- private static final class InlineSuggestionsRequestCallbackImpl
- extends IInlineSuggestionsRequestCallback.Stub {
-
- private final Object mLock;
- private final AutofillId mAutofillId;
- @GuardedBy("mLock")
- private final CompletableFuture<ImeResponse> mResponse;
- @GuardedBy("mLock")
- private final Consumer<InlineSuggestionsRequest> mRequestConsumer;
- private final ImeStatusListener mImeStatusListener;
- private final Handler mHandler;
- private final Runnable mTimeoutCallback;
-
- private InlineSuggestionsRequestCallbackImpl(AutofillId autofillId,
- CompletableFuture<ImeResponse> response,
- ImeStatusListener imeStatusListener,
- Consumer<InlineSuggestionsRequest> requestConsumer,
- Handler handler, Object lock) {
- mAutofillId = autofillId;
- mResponse = response;
- mImeStatusListener = imeStatusListener;
- mRequestConsumer = requestConsumer;
- mLock = lock;
-
- mHandler = handler;
- mTimeoutCallback = () -> {
- Log.w(TAG, "Timed out waiting for IME callback InlineSuggestionsRequest.");
- completeIfNot(null);
- };
- mHandler.postDelayed(mTimeoutCallback, INLINE_REQUEST_TIMEOUT_MS);
- }
-
- private void completeIfNot(@Nullable ImeResponse response) {
- synchronized (mLock) {
- if (mResponse.isDone()) {
- return;
- }
- mResponse.complete(response);
- mRequestConsumer.accept(response == null ? null : response.mRequest);
- mHandler.removeCallbacks(mTimeoutCallback);
- }
- }
-
- @BinderThread
- @Override
- public void onInlineSuggestionsUnsupported() throws RemoteException {
- if (sDebug) Log.d(TAG, "onInlineSuggestionsUnsupported() called.");
- completeIfNot(null);
- }
-
- @BinderThread
- @Override
- public void onInlineSuggestionsRequest(InlineSuggestionsRequest request,
- IInlineSuggestionsResponseCallback callback) {
- if (sDebug) Log.d(TAG, "onInlineSuggestionsRequest() received: " + request);
- mImeStatusListener.onInputMethodStartInput(mAutofillId);
- if (request != null && callback != null) {
- completeIfNot(new ImeResponse(request, callback));
- } else {
- completeIfNot(null);
- }
- }
-
- @Override
- public void onInputMethodStartInput(AutofillId imeFieldId) throws RemoteException {
- if (sDebug) Log.d(TAG, "onInputMethodStartInput() received on " + imeFieldId);
- mImeStatusListener.onInputMethodStartInput(imeFieldId);
- }
-
- @Override
- public void onInputMethodShowInputRequested(boolean requestResult) throws RemoteException {
- if (sDebug) {
- Log.d(TAG, "onInputMethodShowInputRequested() received: " + requestResult);
- }
- // TODO(b/151123764): use this signal to adjust the timeout on Autofill side waiting for
- // IME to show.
- }
-
- @BinderThread
- @Override
- public void onInputMethodStartInputView() {
- if (sDebug) Log.d(TAG, "onInputMethodStartInputView() received");
- mImeStatusListener.onInputMethodStartInputView();
- }
-
- @BinderThread
- @Override
- public void onInputMethodFinishInputView() {
- if (sDebug) Log.d(TAG, "onInputMethodFinishInputView() received");
- mImeStatusListener.onInputMethodFinishInputView();
- }
-
- @Override
- public void onInputMethodFinishInput() throws RemoteException {
- if (sDebug) Log.d(TAG, "onInputMethodFinishInput() received");
- mImeStatusListener.onInputMethodFinishInput();
- }
- }
-
- private interface ImeStatusListener {
- void onInputMethodStartInput(AutofillId imeFieldId);
-
- void onInputMethodStartInputView();
-
- void onInputMethodFinishInputView();
-
- void onInputMethodFinishInput();
- }
-
- /**
- * A data class wrapping Autofill responses for the inline suggestion request.
- */
- private static class AutofillResponse {
- @NonNull
- final AutofillId mAutofillId;
-
- @NonNull
- final InlineSuggestionsResponse mResponse;
-
- AutofillResponse(@NonNull AutofillId autofillId,
- @NonNull InlineSuggestionsResponse response) {
- mAutofillId = autofillId;
- mResponse = response;
- }
-
- }
-
- /**
- * A data class wrapping IME responses for the create inline suggestions request.
- */
- private static class ImeResponse {
- @NonNull
- final InlineSuggestionsRequest mRequest;
-
- @NonNull
- final IInlineSuggestionsResponseCallback mCallback;
-
- ImeResponse(@NonNull InlineSuggestionsRequest request,
- @NonNull IInlineSuggestionsResponseCallback callback) {
- mRequest = request;
- mCallback = callback;
- }
-
- InlineSuggestionsRequest getRequest() {
- return mRequest;
- }
- }
-}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 1290569..4ecffd8 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -304,7 +304,7 @@
private boolean mForAugmentedAutofillOnly;
@Nullable
- private final InlineSuggestionSession mInlineSuggestionSession;
+ private final AutofillInlineSessionController mInlineSessionController;
/**
* Receiver of assist data from the app's {@link Activity}.
@@ -720,8 +720,9 @@
Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer =
mAssistReceiver.newAutofillRequestLocked(/*isInlineRequest=*/ true);
if (inlineSuggestionsRequestConsumer != null) {
- mInlineSuggestionSession.onCreateInlineSuggestionsRequest(mCurrentViewId,
- inlineSuggestionsRequestConsumer);
+ // TODO(b/146454892): pipe the uiExtras from the ExtServices.
+ mInlineSessionController.onCreateInlineSuggestionsRequestLocked(mCurrentViewId,
+ inlineSuggestionsRequestConsumer, Bundle.EMPTY);
}
} else {
mAssistReceiver.newAutofillRequestLocked(/*isInlineRequest=*/ false);
@@ -777,8 +778,8 @@
mForAugmentedAutofillOnly = forAugmentedAutofillOnly;
setClientLocked(client);
- mInlineSuggestionSession = new InlineSuggestionSession(inputMethodManagerInternal, userId,
- componentName, handler, mLock);
+ mInlineSessionController = new AutofillInlineSessionController(inputMethodManagerInternal,
+ userId, componentName, handler, mLock);
mMetricsLogger.write(newLogMaker(MetricsEvent.AUTOFILL_SESSION_STARTED)
.addTaggedData(MetricsEvent.FIELD_AUTOFILL_FLAGS, flags));
@@ -2561,7 +2562,7 @@
if (sVerbose) Slog.v(TAG, "Exiting view " + id);
mUi.hideFillUi(this);
hideAugmentedAutofillLocked(viewState);
- mInlineSuggestionSession.hideInlineSuggestionsUi(mCurrentViewId);
+ mInlineSessionController.hideInlineSuggestionsUiLocked(mCurrentViewId);
mCurrentViewId = null;
}
break;
@@ -2779,7 +2780,7 @@
private boolean requestShowInlineSuggestionsLocked(@NonNull FillResponse response,
@Nullable String filterText) {
final Optional<InlineSuggestionsRequest> inlineSuggestionsRequest =
- mInlineSuggestionSession.getInlineSuggestionsRequest();
+ mInlineSessionController.getInlineSuggestionsRequestLocked();
if (!inlineSuggestionsRequest.isPresent()) {
Log.w(TAG, "InlineSuggestionsRequest unavailable");
return false;
@@ -2801,7 +2802,8 @@
inlineSuggestionsRequest.get(), response, filterText, mCurrentViewId,
this, () -> {
synchronized (mLock) {
- mInlineSuggestionSession.hideInlineSuggestionsUi(mCurrentViewId);
+ mInlineSessionController.hideInlineSuggestionsUiLocked(
+ mCurrentViewId);
}
}, remoteRenderService);
if (inlineSuggestionsResponse == null) {
@@ -2809,7 +2811,7 @@
return false;
}
- return mInlineSuggestionSession.onInlineSuggestionsResponse(mCurrentViewId,
+ return mInlineSessionController.onInlineSuggestionsResponseLocked(mCurrentViewId,
inlineSuggestionsResponse);
}
@@ -3106,8 +3108,13 @@
focusedId,
currentValue, inlineSuggestionsRequest,
/*inlineSuggestionsCallback=*/
- response -> mInlineSuggestionSession.onInlineSuggestionsResponse(
- mCurrentViewId, response),
+ response -> {
+ synchronized (mLock) {
+ return mInlineSessionController
+ .onInlineSuggestionsResponseLocked(
+ mCurrentViewId, response);
+ }
+ },
/*onErrorCallback=*/ () -> {
synchronized (mLock) {
cancelAugmentedAutofillLocked();
@@ -3125,11 +3132,12 @@
&& (mForAugmentedAutofillOnly
|| !isInlineSuggestionsEnabledByAutofillProviderLocked())) {
if (sDebug) Slog.d(TAG, "Create inline request for augmented autofill");
- mInlineSuggestionSession.onCreateInlineSuggestionsRequest(mCurrentViewId,
- /*requestConsumer=*/ requestAugmentedAutofill);
+ // TODO(b/146454892): pipe the uiExtras from the ExtServices.
+ mInlineSessionController.onCreateInlineSuggestionsRequestLocked(mCurrentViewId,
+ /*requestConsumer=*/ requestAugmentedAutofill, Bundle.EMPTY);
} else {
requestAugmentedAutofill.accept(
- mInlineSuggestionSession.getInlineSuggestionsRequest().orElse(null));
+ mInlineSessionController.getInlineSuggestionsRequestLocked().orElse(null));
}
if (mAugmentedAutofillDestroyer == null) {
mAugmentedAutofillDestroyer = () -> remoteService.onDestroyAutofillWindowsRequest();
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/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 595275d..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.
@@ -1846,11 +1848,13 @@
runtimeFlags |= Zygote.USE_APP_IMAGE_STARTUP_CACHE;
}
- // Enable heap pointer tagging, unless disabled by the app manifest, target sdk level,
- // or the compat feature.
- if (app.info.allowsNativeHeapPointerTagging()
- && mPlatformCompat.isChangeEnabled(NATIVE_HEAP_POINTER_TAGGING, app.info)) {
- runtimeFlags |= Zygote.MEMORY_TAG_LEVEL_TBI;
+ if (Zygote.nativeSupportsTaggedPointers()) {
+ // Enable heap pointer tagging if supported by the kernel, unless disabled by the
+ // app manifest, target sdk level, or compat feature.
+ if (app.info.allowsNativeHeapPointerTagging()
+ && mPlatformCompat.isChangeEnabled(NATIVE_HEAP_POINTER_TAGGING, app.info)) {
+ runtimeFlags |= Zygote.MEMORY_TAG_LEVEL_TBI;
+ }
}
runtimeFlags |= decideGwpAsanLevel(app);
@@ -2127,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;
@@ -2160,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;
@@ -2206,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,
@@ -2214,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/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/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 846d099..e6cb371 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -3106,14 +3106,10 @@
MSG_SHOW_SOFT_INPUT, getImeShowFlags(), reason, mCurMethod, resultReceiver,
showInputToken));
mInputShown = true;
-
if (mHaveConnection && !mVisibleBound) {
bindCurrentInputMethodServiceLocked(
mCurIntent, mVisibleConnection, IME_VISIBLE_BIND_FLAGS);
mVisibleBound = true;
- } else {
- // Clear the show request after the input shown.
- mShowRequested = false;
}
res = true;
} else if (mHaveConnection && SystemClock.uptimeMillis()
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/net/NetworkStatsSubscriptionsMonitor.java b/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
new file mode 100644
index 0000000..0bdf3f2
--- /dev/null
+++ b/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.net;
+
+import static android.net.NetworkTemplate.getCollapsedRatType;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.telephony.Annotation;
+import android.telephony.PhoneStateListener;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.util.CollectionUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.Executor;
+
+/**
+ * Helper class that watches for events that are triggered per subscription.
+ */
+// TODO (b/152176562): Write tests to verify subscription changes generate corresponding
+// register/unregister calls.
+public class NetworkStatsSubscriptionsMonitor extends
+ SubscriptionManager.OnSubscriptionsChangedListener {
+
+ /**
+ * Interface that this monitor uses to delegate event handling to NetworkStatsService.
+ */
+ public interface Delegate {
+ /**
+ * Notify that the collapsed RAT type has been changed for any subscription. The method
+ * will also be triggered for any existing sub when start and stop monitoring.
+ *
+ * @param subscriberId IMSI of the subscription.
+ * @param collapsedRatType collapsed RAT type.
+ * @see android.net.NetworkTemplate#getCollapsedRatType(int).
+ */
+ void onCollapsedRatTypeChanged(@NonNull String subscriberId,
+ @Annotation.NetworkType int collapsedRatType);
+ }
+ private final Delegate mDelegate;
+
+ /**
+ * Receivers that watches for {@link ServiceState} changes for each subscription, to
+ * monitor the transitioning between Radio Access Technology(RAT) types for each sub.
+ */
+ @NonNull
+ private final CopyOnWriteArrayList<RatTypeListener> mRatListeners =
+ new CopyOnWriteArrayList<>();
+
+ @NonNull
+ private final SubscriptionManager mSubscriptionManager;
+ @NonNull
+ private final TelephonyManager mTeleManager;
+
+ @NonNull
+ private final Executor mExecutor;
+
+ NetworkStatsSubscriptionsMonitor(@NonNull Context context, @NonNull Executor executor,
+ @NonNull Delegate delegate) {
+ super();
+ mSubscriptionManager = (SubscriptionManager) context.getSystemService(
+ Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+ mTeleManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+ mExecutor = executor;
+ mDelegate = delegate;
+ }
+
+ @Override
+ public void onSubscriptionsChanged() {
+ // Collect active subId list, hidden subId such as opportunistic subscriptions are
+ // also needed to track CBRS.
+ final List<Integer> newSubs = getActiveSubIdList(mSubscriptionManager);
+
+ for (final int subId : newSubs) {
+ final RatTypeListener match = CollectionUtils.find(mRatListeners,
+ it -> it.mSubId == subId);
+ if (match != null) continue;
+
+ // Create listener for every newly added sub. Also store subscriberId into it to
+ // prevent binder call to telephony when querying RAT.
+ final String subscriberId = mTeleManager.getSubscriberId(subId);
+ if (TextUtils.isEmpty(subscriberId)) {
+ Log.wtf(NetworkStatsService.TAG,
+ "Empty subscriberId for newly added sub: " + subId);
+ }
+ final RatTypeListener listener =
+ new RatTypeListener(mExecutor, this, subId, subscriberId);
+ mRatListeners.add(listener);
+
+ // Register listener to the telephony manager that associated with specific sub.
+ mTeleManager.createForSubscriptionId(subId)
+ .listen(listener, PhoneStateListener.LISTEN_SERVICE_STATE);
+ }
+
+ for (final RatTypeListener listener : new ArrayList<>(mRatListeners)) {
+ // If the new list contains the subId of the listener, keeps it.
+ final Integer match = CollectionUtils.find(newSubs, it -> it == listener.mSubId);
+ if (match != null) continue;
+
+ handleRemoveRatTypeListener(listener);
+ }
+ }
+
+ @NonNull
+ private List<Integer> getActiveSubIdList(@NonNull SubscriptionManager subscriptionManager) {
+ final ArrayList<Integer> ret = new ArrayList<>();
+ final int[] ids = subscriptionManager.getCompleteActiveSubscriptionIdList();
+ for (int id : ids) ret.add(id);
+ return ret;
+ }
+
+ /**
+ * Get a collapsed RatType for the given subscriberId.
+ *
+ * @param subscriberId the target subscriberId
+ * @return collapsed RatType for the given subscriberId
+ */
+ public int getRatTypeForSubscriberId(@NonNull String subscriberId) {
+ final RatTypeListener match = CollectionUtils.find(mRatListeners,
+ it -> TextUtils.equals(subscriberId, it.mSubscriberId));
+ return match != null ? match.mLastCollapsedRatType : TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ }
+
+ /**
+ * Start monitoring events that triggered per subscription.
+ */
+ public void start() {
+ mSubscriptionManager.addOnSubscriptionsChangedListener(mExecutor, this);
+ }
+
+ /**
+ * Unregister subscription changes and all listeners for each subscription.
+ */
+ public void stop() {
+ mSubscriptionManager.removeOnSubscriptionsChangedListener(this);
+
+ for (final RatTypeListener listener : new ArrayList<>(mRatListeners)) {
+ handleRemoveRatTypeListener(listener);
+ }
+ }
+
+ private void handleRemoveRatTypeListener(@NonNull RatTypeListener listener) {
+ mTeleManager.createForSubscriptionId(listener.mSubId)
+ .listen(listener, PhoneStateListener.LISTEN_NONE);
+ mRatListeners.remove(listener);
+
+ // Removal of subscriptions doesn't generate RAT changed event, fire it for every
+ // RatTypeListener.
+ mDelegate.onCollapsedRatTypeChanged(
+ listener.mSubscriberId, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ }
+
+ static class RatTypeListener extends PhoneStateListener {
+ // Unique id for the subscription. See {@link SubscriptionInfo#getSubscriptionId}.
+ @NonNull
+ private final int mSubId;
+
+ // IMSI to identifying the corresponding network from {@link NetworkState}.
+ // See {@link TelephonyManager#getSubscriberId}.
+ @NonNull
+ private final String mSubscriberId;
+
+ private volatile int mLastCollapsedRatType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ @NonNull
+ private final NetworkStatsSubscriptionsMonitor mMonitor;
+
+ RatTypeListener(@NonNull Executor executor,
+ @NonNull NetworkStatsSubscriptionsMonitor monitor, int subId,
+ @NonNull String subscriberId) {
+ super(executor);
+ mSubId = subId;
+ mSubscriberId = subscriberId;
+ mMonitor = monitor;
+ }
+
+ @Override
+ public void onServiceStateChanged(@NonNull ServiceState ss) {
+ final int networkType = ss.getDataNetworkType();
+ final int collapsedRatType = getCollapsedRatType(networkType);
+ if (collapsedRatType == mLastCollapsedRatType) return;
+
+ if (NetworkStatsService.LOGD) {
+ Log.d(NetworkStatsService.TAG, "subtype changed for sub(" + mSubId + "): "
+ + mLastCollapsedRatType + " -> " + collapsedRatType);
+ }
+ mLastCollapsedRatType = collapsedRatType;
+ mMonitor.mDelegate.onCollapsedRatTypeChanged(mSubscriberId, mLastCollapsedRatType);
+ }
+ }
+}
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/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index f37af3a..9fb468e 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -724,6 +724,30 @@
}
/**
+ * Deletes all snapshots of credential encrypted user data, where the snapshot id is not
+ * included in {@code retainSnapshotIds}.
+ *
+ * @param userId id of the user whose user data snapshots to delete.
+ * @param retainSnapshotIds ids of the snapshots that should not be deleted.
+ *
+ * @return {@code true} if the operation was successful, or {@code false} if a remote call
+ * shouldn't be continued. See {@link #checkBeforeRemote}.
+ *
+ * @throws InstallerException if failed to delete user data snapshot.
+ */
+ public boolean destroyCeSnapshotsNotSpecified(@UserIdInt int userId,
+ int[] retainSnapshotIds) throws InstallerException {
+ if (!checkBeforeRemote()) return false;
+
+ try {
+ mInstalld.destroyCeSnapshotsNotSpecified(null, userId, retainSnapshotIds);
+ return true;
+ } catch (Exception e) {
+ throw InstallerException.from(e);
+ }
+ }
+
+ /**
* Migrates obb data from its legacy location {@code /data/media/obb} to
* {@code /data/media/0/Android/obb}. This call is idempotent and a fast no-op if data has
* already been migrated.
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 5f871ad4..83e99b0 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -505,6 +505,11 @@
rollbackIds[i] = mRollbacks.get(i).info.getRollbackId();
}
ApexManager.getInstance().destroyCeSnapshotsNotSpecified(userId, rollbackIds);
+ try {
+ mInstaller.destroyCeSnapshotsNotSpecified(userId, rollbackIds);
+ } catch (Installer.InstallerException ie) {
+ Slog.e(TAG, "Failed to delete snapshots for user: " + userId, ie);
+ }
}
@WorkerThread
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 8111c0e..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.
@@ -1081,8 +1080,7 @@
return null;
}
mShellRoots.put(windowType, root);
- SurfaceControl out = new SurfaceControl();
- out.copyFrom(rootLeash);
+ SurfaceControl out = new SurfaceControl(rootLeash);
return out;
}
@@ -2070,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) {
@@ -2402,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
@@ -3425,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/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index f9955fa..f6e952c 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -2510,9 +2510,7 @@
// We need to copy the SurfaceControl instead of returning the original
// because the Parcel FLAGS PARCELABLE_WRITE_RETURN_VALUE cause SurfaceControls
// to release themselves.
- SurfaceControl sc = new SurfaceControl();
- sc.copyFrom(wc.getSurfaceControl());
- return sc;
+ return new SurfaceControl(wc.getSurfaceControl());
}
WindowContainerToken toWindowContainerToken() {
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/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/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..57c7504 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);
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/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java
index 2b1d9e5..18e2592 100644
--- a/telephony/java/com/android/internal/telephony/DctConstants.java
+++ b/telephony/java/com/android/internal/telephony/DctConstants.java
@@ -113,6 +113,7 @@
public static final int EVENT_5G_TIMER_HYSTERESIS = BASE + 53;
public static final int EVENT_5G_TIMER_WATCHDOG = BASE + 54;
public static final int EVENT_CARRIER_CONFIG_CHANGED = BASE + 55;
+ public static final int EVENT_SIM_STATE_UPDATED = BASE + 56;
/***** Constants *****/
diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
index 6e9dc8e..3f8261d 100644
--- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
@@ -17,6 +17,8 @@
package android.net;
import static android.net.NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
+import static android.net.NetworkCapabilities.MAX_TRANSPORT;
+import static android.net.NetworkCapabilities.MIN_TRANSPORT;
import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS;
import static android.net.NetworkCapabilities.NET_CAPABILITY_EIMS;
@@ -32,10 +34,12 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_WIFI_P2P;
import static android.net.NetworkCapabilities.RESTRICTED_CAPABILITIES;
+import static android.net.NetworkCapabilities.SIGNAL_STRENGTH_UNSPECIFIED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_TEST;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
import static android.net.NetworkCapabilities.UNRESTRICTED_CAPABILITIES;
import static com.android.testutils.ParcelUtilsKt.assertParcelSane;
@@ -45,10 +49,15 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.net.wifi.aware.DiscoverySession;
+import android.net.wifi.aware.PeerHandle;
+import android.net.wifi.aware.WifiAwareNetworkSpecifier;
import android.os.Build;
+import android.os.Process;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.ArraySet;
@@ -61,6 +70,7 @@
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mockito;
import java.util.Arrays;
import java.util.Set;
@@ -74,6 +84,9 @@
@Rule
public DevSdkIgnoreRule mDevSdkIgnoreRule = new DevSdkIgnoreRule();
+ private DiscoverySession mDiscoverySession = Mockito.mock(DiscoverySession.class);
+ private PeerHandle mPeerHandle = Mockito.mock(PeerHandle.class);
+
private boolean isAtLeastR() {
// BuildCompat.isAtLeastR() is used to check the Android version before releasing Android R.
// Build.VERSION.SDK_INT > Build.VERSION_CODES.Q is used to check the Android version after
@@ -685,4 +698,238 @@
assertEquals(TRANSPORT_VPN, transportTypes[2]);
assertEquals(TRANSPORT_TEST, transportTypes[3]);
}
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ public void testTelephonyNetworkSpecifier() {
+ final TelephonyNetworkSpecifier specifier = new TelephonyNetworkSpecifier(1);
+ final NetworkCapabilities nc1 = new NetworkCapabilities.Builder()
+ .addTransportType(TRANSPORT_WIFI)
+ .setNetworkSpecifier(specifier)
+ .build();
+ assertEquals(specifier, nc1.getNetworkSpecifier());
+ try {
+ final NetworkCapabilities nc2 = new NetworkCapabilities.Builder()
+ .setNetworkSpecifier(specifier)
+ .build();
+ fail("Must have a single transport type. Without transport type or multiple transport"
+ + " types is invalid.");
+ } catch (IllegalStateException expected) { }
+ }
+
+ @Test
+ public void testWifiAwareNetworkSpecifier() {
+ final NetworkCapabilities nc = new NetworkCapabilities()
+ .addTransportType(TRANSPORT_WIFI_AWARE);
+ // If NetworkSpecifier is not set, the default value is null.
+ assertNull(nc.getNetworkSpecifier());
+ final WifiAwareNetworkSpecifier specifier = new WifiAwareNetworkSpecifier.Builder(
+ mDiscoverySession, mPeerHandle).build();
+ nc.setNetworkSpecifier(specifier);
+ assertEquals(specifier, nc.getNetworkSpecifier());
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ public void testAdministratorUidsAndOwnerUid() {
+ // Test default owner uid.
+ // If the owner uid is not set, the default value should be Process.INVALID_UID.
+ final NetworkCapabilities nc1 = new NetworkCapabilities.Builder().build();
+ assertEquals(Process.INVALID_UID, nc1.getOwnerUid());
+ // Test setAdministratorUids and getAdministratorUids.
+ final int[] administratorUids = {1001, 10001};
+ final NetworkCapabilities nc2 = new NetworkCapabilities.Builder()
+ .setAdministratorUids(administratorUids)
+ .build();
+ assertTrue(Arrays.equals(administratorUids, nc2.getAdministratorUids()));
+ // Test setOwnerUid and getOwnerUid.
+ // The owner UID must be included in administrator UIDs, or throw IllegalStateException.
+ try {
+ final NetworkCapabilities nc3 = new NetworkCapabilities.Builder()
+ .setOwnerUid(1001)
+ .build();
+ fail("The owner UID must be included in administrator UIDs.");
+ } catch (IllegalStateException expected) { }
+ final NetworkCapabilities nc4 = new NetworkCapabilities.Builder()
+ .setAdministratorUids(administratorUids)
+ .setOwnerUid(1001)
+ .build();
+ assertEquals(1001, nc4.getOwnerUid());
+ try {
+ final NetworkCapabilities nc5 = new NetworkCapabilities.Builder()
+ .setAdministratorUids(null)
+ .build();
+ fail("Should not set null into setAdministratorUids");
+ } catch (NullPointerException expected) { }
+ }
+
+ @Test
+ public void testLinkBandwidthKbps() {
+ final NetworkCapabilities nc = new NetworkCapabilities();
+ // The default value of LinkDown/UpstreamBandwidthKbps should be LINK_BANDWIDTH_UNSPECIFIED.
+ assertEquals(LINK_BANDWIDTH_UNSPECIFIED, nc.getLinkDownstreamBandwidthKbps());
+ assertEquals(LINK_BANDWIDTH_UNSPECIFIED, nc.getLinkUpstreamBandwidthKbps());
+ nc.setLinkDownstreamBandwidthKbps(512);
+ nc.setLinkUpstreamBandwidthKbps(128);
+ assertEquals(512, nc.getLinkDownstreamBandwidthKbps());
+ assertNotEquals(128, nc.getLinkDownstreamBandwidthKbps());
+ assertEquals(128, nc.getLinkUpstreamBandwidthKbps());
+ assertNotEquals(512, nc.getLinkUpstreamBandwidthKbps());
+ }
+
+ @Test
+ public void testSignalStrength() {
+ final NetworkCapabilities nc = new NetworkCapabilities();
+ // The default value of signal strength should be SIGNAL_STRENGTH_UNSPECIFIED.
+ assertEquals(SIGNAL_STRENGTH_UNSPECIFIED, nc.getSignalStrength());
+ nc.setSignalStrength(-80);
+ assertEquals(-80, nc.getSignalStrength());
+ assertNotEquals(-50, nc.getSignalStrength());
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ public void testDeduceRestrictedCapability() {
+ final NetworkCapabilities nc = new NetworkCapabilities();
+ // Default capabilities don't have restricted capability.
+ assertFalse(nc.deduceRestrictedCapability());
+ // If there is a force restricted capability, then the network capabilities is restricted.
+ nc.addCapability(NET_CAPABILITY_OEM_PAID);
+ nc.addCapability(NET_CAPABILITY_INTERNET);
+ assertTrue(nc.deduceRestrictedCapability());
+ // Except for the force restricted capability, if there is any unrestricted capability in
+ // capabilities, then the network capabilities is not restricted.
+ nc.removeCapability(NET_CAPABILITY_OEM_PAID);
+ nc.addCapability(NET_CAPABILITY_CBS);
+ assertFalse(nc.deduceRestrictedCapability());
+ // Except for the force restricted capability, the network capabilities will only be treated
+ // as restricted when there is no any unrestricted capability.
+ nc.removeCapability(NET_CAPABILITY_INTERNET);
+ assertTrue(nc.deduceRestrictedCapability());
+ }
+
+ private void assertNoTransport(NetworkCapabilities nc) {
+ for (int i = MIN_TRANSPORT; i <= MAX_TRANSPORT; i++) {
+ assertFalse(nc.hasTransport(i));
+ }
+ }
+
+ // Checks that all transport types from MIN_TRANSPORT to maxTransportType are set and all
+ // transport types from maxTransportType + 1 to MAX_TRANSPORT are not set when positiveSequence
+ // is true. If positiveSequence is false, then the check sequence is opposite.
+ private void checkCurrentTransportTypes(NetworkCapabilities nc, int maxTransportType,
+ boolean positiveSequence) {
+ for (int i = MIN_TRANSPORT; i <= maxTransportType; i++) {
+ if (positiveSequence) {
+ assertTrue(nc.hasTransport(i));
+ } else {
+ assertFalse(nc.hasTransport(i));
+ }
+ }
+ for (int i = MAX_TRANSPORT; i > maxTransportType; i--) {
+ if (positiveSequence) {
+ assertFalse(nc.hasTransport(i));
+ } else {
+ assertTrue(nc.hasTransport(i));
+ }
+ }
+ }
+
+ @Test
+ public void testMultipleTransportTypes() {
+ final NetworkCapabilities nc = new NetworkCapabilities();
+ assertNoTransport(nc);
+ // Test adding multiple transport types.
+ for (int i = MIN_TRANSPORT; i <= MAX_TRANSPORT; i++) {
+ nc.addTransportType(i);
+ checkCurrentTransportTypes(nc, i, true /* positiveSequence */);
+ }
+ // Test removing multiple transport types.
+ for (int i = MIN_TRANSPORT; i <= MAX_TRANSPORT; i++) {
+ nc.removeTransportType(i);
+ checkCurrentTransportTypes(nc, i, false /* positiveSequence */);
+ }
+ assertNoTransport(nc);
+ nc.addTransportType(TRANSPORT_WIFI);
+ assertTrue(nc.hasTransport(TRANSPORT_WIFI));
+ assertFalse(nc.hasTransport(TRANSPORT_VPN));
+ nc.addTransportType(TRANSPORT_VPN);
+ assertTrue(nc.hasTransport(TRANSPORT_WIFI));
+ assertTrue(nc.hasTransport(TRANSPORT_VPN));
+ nc.removeTransportType(TRANSPORT_WIFI);
+ assertFalse(nc.hasTransport(TRANSPORT_WIFI));
+ assertTrue(nc.hasTransport(TRANSPORT_VPN));
+ nc.removeTransportType(TRANSPORT_VPN);
+ assertFalse(nc.hasTransport(TRANSPORT_WIFI));
+ assertFalse(nc.hasTransport(TRANSPORT_VPN));
+ assertNoTransport(nc);
+ }
+
+ @Test
+ public void testAddAndRemoveTransportType() {
+ final NetworkCapabilities nc = new NetworkCapabilities();
+ try {
+ nc.addTransportType(-1);
+ fail("Should not set invalid transport type into addTransportType");
+ } catch (IllegalArgumentException expected) { }
+ try {
+ nc.removeTransportType(-1);
+ fail("Should not set invalid transport type into removeTransportType");
+ } catch (IllegalArgumentException e) { }
+ }
+
+ private class TestTransportInfo implements TransportInfo {
+ TestTransportInfo() {
+ }
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ public void testBuilder() {
+ final int ownerUid = 1001;
+ final int signalStrength = -80;
+ final int requestUid = 10100;
+ final int[] administratorUids = {ownerUid, 10001};
+ final TelephonyNetworkSpecifier specifier = new TelephonyNetworkSpecifier(1);
+ final TestTransportInfo transportInfo = new TestTransportInfo();
+ final String ssid = "TEST_SSID";
+ final String packageName = "com.google.test.networkcapabilities";
+ final NetworkCapabilities nc = new NetworkCapabilities.Builder()
+ .addTransportType(TRANSPORT_WIFI)
+ .addTransportType(TRANSPORT_CELLULAR)
+ .removeTransportType(TRANSPORT_CELLULAR)
+ .addCapability(NET_CAPABILITY_EIMS)
+ .addCapability(NET_CAPABILITY_CBS)
+ .removeCapability(NET_CAPABILITY_CBS)
+ .setAdministratorUids(administratorUids)
+ .setOwnerUid(ownerUid)
+ .setLinkDownstreamBandwidthKbps(512)
+ .setLinkUpstreamBandwidthKbps(128)
+ .setNetworkSpecifier(specifier)
+ .setTransportInfo(transportInfo)
+ .setSignalStrength(signalStrength)
+ .setSsid(ssid)
+ .setRequestorUid(requestUid)
+ .setRequestorPackageName(packageName)
+ .build();
+ assertEquals(1, nc.getTransportTypes().length);
+ assertEquals(TRANSPORT_WIFI, nc.getTransportTypes()[0]);
+ assertTrue(nc.hasCapability(NET_CAPABILITY_EIMS));
+ assertFalse(nc.hasCapability(NET_CAPABILITY_CBS));
+ assertTrue(Arrays.equals(administratorUids, nc.getAdministratorUids()));
+ assertEquals(ownerUid, nc.getOwnerUid());
+ assertEquals(512, nc.getLinkDownstreamBandwidthKbps());
+ assertNotEquals(128, nc.getLinkDownstreamBandwidthKbps());
+ assertEquals(128, nc.getLinkUpstreamBandwidthKbps());
+ assertNotEquals(512, nc.getLinkUpstreamBandwidthKbps());
+ assertEquals(specifier, nc.getNetworkSpecifier());
+ assertEquals(transportInfo, nc.getTransportInfo());
+ assertEquals(signalStrength, nc.getSignalStrength());
+ assertNotEquals(-50, nc.getSignalStrength());
+ assertEquals(ssid, nc.getSsid());
+ assertEquals(requestUid, nc.getRequestorUid());
+ assertEquals(packageName, nc.getRequestorPackageName());
+ // Cannot assign null into NetworkCapabilities.Builder
+ try {
+ final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder(null);
+ fail("Should not set null into NetworkCapabilities.Builder");
+ } catch (NullPointerException expected) { }
+ assertEquals(nc, new NetworkCapabilities.Builder(nc).build());
+ }
}