diff options
596 files changed, 16368 insertions, 6750 deletions
diff --git a/Android.bp b/Android.bp index 0fcc0d501a1d..abf95a8ebc58 100644 --- a/Android.bp +++ b/Android.bp @@ -687,6 +687,7 @@ java_defaults { "core/java/com/android/server/DropboxLogTags.logtags", "core/java/org/chromium/arc/EventLogTags.logtags", + ":apex-properties", ":platform-properties", ":framework-statslog-gen", @@ -1834,4 +1835,4 @@ aidl_mapping { name: "framework-aidl-mappings", srcs: [":framework-defaults"], output: "framework-aidl-mappings.txt" -}
\ No newline at end of file +} diff --git a/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java b/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java index c1491956c798..bd3b6737f505 100644 --- a/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java +++ b/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java @@ -52,7 +52,7 @@ public class TextClassificationManagerPerfTest { BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { textClassificationManager.getTextClassifier(); - textClassificationManager.invalidate(); + textClassificationManager.invalidateForTesting(); } } @@ -68,7 +68,7 @@ public class TextClassificationManagerPerfTest { BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { textClassificationManager.getTextClassifier(); - textClassificationManager.invalidate(); + textClassificationManager.invalidateForTesting(); } } } diff --git a/api/current.txt b/api/current.txt index ec7df66b0e62..8b24826a267b 100644 --- a/api/current.txt +++ b/api/current.txt @@ -38820,8 +38820,7 @@ package android.provider { field @Deprecated public static final String LOCATION_MODE = "location_mode"; field @Deprecated public static final int LOCATION_MODE_BATTERY_SAVING = 2; // 0x2 field @Deprecated public static final int LOCATION_MODE_HIGH_ACCURACY = 3; // 0x3 - field @Deprecated public static final int LOCATION_MODE_OFF = 0; // 0x0 - field @Deprecated public static final int LOCATION_MODE_ON = 3; // 0x3 + field public static final int LOCATION_MODE_OFF = 0; // 0x0 field @Deprecated public static final int LOCATION_MODE_SENSORS_ONLY = 1; // 0x1 field @Deprecated public static final String LOCATION_PROVIDERS_ALLOWED = "location_providers_allowed"; field @Deprecated public static final String LOCK_PATTERN_ENABLED = "lock_pattern_autolock"; diff --git a/api/system-current.txt b/api/system-current.txt index 2ce1ee15d98d..36680a171f35 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -1635,7 +1635,7 @@ package android.content.pm { method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryIntentServicesAsUser(@NonNull android.content.Intent, int, @NonNull android.os.UserHandle); method public abstract void registerDexModule(@NonNull String, @Nullable android.content.pm.PackageManager.DexModuleRegisterCallback); method @RequiresPermission("android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS") public abstract void removeOnPermissionsChangeListener(@NonNull android.content.pm.PackageManager.OnPermissionsChangedListener); - method @Deprecated public void replacePreferredActivity(@NonNull android.content.IntentFilter, int, @NonNull java.util.List<android.content.ComponentName>, @NonNull android.content.ComponentName); + method public void replacePreferredActivity(@NonNull android.content.IntentFilter, int, @NonNull java.util.List<android.content.ComponentName>, @NonNull android.content.ComponentName); method @RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) public abstract void revokeRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle); method public void sendDeviceCustomizationReadyBroadcast(); method @RequiresPermission(allOf={android.Manifest.permission.SET_PREFERRED_APPLICATIONS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public abstract boolean setDefaultBrowserPackageNameAsUser(@Nullable String, int); @@ -6046,6 +6046,7 @@ package android.provider { field public static final String LAST_SETUP_SHOWN = "last_setup_shown"; field public static final String LOCATION_ACCESS_CHECK_DELAY_MILLIS = "location_access_check_delay_millis"; field public static final String LOCATION_ACCESS_CHECK_INTERVAL_MILLIS = "location_access_check_interval_millis"; + field public static final int LOCATION_MODE_ON = 3; // 0x3 field public static final String LOCATION_PERMISSIONS_UPGRADE_TO_Q_MODE = "location_permissions_upgrade_to_q_mode"; field public static final String LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS = "lock_screen_allow_private_notifications"; field public static final String LOCK_SCREEN_SHOW_NOTIFICATIONS = "lock_screen_show_notifications"; diff --git a/api/test-current.txt b/api/test-current.txt index 77c3a94f1d10..62d8b206511e 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -3205,6 +3205,7 @@ package android.view { method public default void setShouldShowIme(int, boolean); method public default void setShouldShowSystemDecors(int, boolean); method public default void setShouldShowWithInsecureKeyguard(int, boolean); + method public default boolean shouldShowIme(int); method public default boolean shouldShowSystemDecors(int); } @@ -3261,6 +3262,7 @@ package android.view.autofill { ctor public AutofillId(@NonNull android.view.autofill.AutofillId, int); ctor public AutofillId(int, int); ctor public AutofillId(@NonNull android.view.autofill.AutofillId, long, int); + method public boolean equalsIgnoreSession(@Nullable android.view.autofill.AutofillId); } public final class AutofillManager { diff --git a/cmds/am/proto/instrumentation_data.proto b/cmds/am/proto/instrumentation_data.proto index 8e29f9645568..23e951992305 100644 --- a/cmds/am/proto/instrumentation_data.proto +++ b/cmds/am/proto/instrumentation_data.proto @@ -38,6 +38,7 @@ message ResultsBundle { message TestStatus { optional sint32 result_code = 3; optional ResultsBundle results = 4; + optional string logcat = 5; } enum SessionStatusCode { diff --git a/cmds/am/src/com/android/commands/am/Instrument.java b/cmds/am/src/com/android/commands/am/Instrument.java index 70baa8702ba9..4d7b5a79b4f7 100644 --- a/cmds/am/src/com/android/commands/am/Instrument.java +++ b/cmds/am/src/com/android/commands/am/Instrument.java @@ -38,6 +38,7 @@ import android.view.IWindowManager; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStreamReader; import java.io.OutputStream; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -62,8 +63,15 @@ import java.util.Locale; * other: Failure */ public class Instrument { + private static final String TAG = "am"; + public static final String DEFAULT_LOG_DIR = "instrument-logs"; + private static final int STATUS_TEST_PASSED = 0; + private static final int STATUS_TEST_STARTED = 1; + private static final int STATUS_TEST_FAILED_ASSERTION = -1; + private static final int STATUS_TEST_FAILED_OTHER = -2; + private final IActivityManager mAm; private final IPackageManager mPm; private final IWindowManager mWm; @@ -207,6 +215,8 @@ public class Instrument { private File mLog; + private long mTestStartMs; + ProtoStatusReporter() { if (protoFile) { if (logPath == null) { @@ -241,10 +251,22 @@ public class Instrument { Bundle results) { final ProtoOutputStream proto = new ProtoOutputStream(); - final long token = proto.start(InstrumentationData.Session.TEST_STATUS); + final long testStatusToken = proto.start(InstrumentationData.Session.TEST_STATUS); + proto.write(InstrumentationData.TestStatus.RESULT_CODE, resultCode); writeBundle(proto, InstrumentationData.TestStatus.RESULTS, results); - proto.end(token); + + if (resultCode == STATUS_TEST_STARTED) { + // Logcat -T takes wall clock time (!?) + mTestStartMs = System.currentTimeMillis(); + } else { + if (mTestStartMs > 0) { + proto.write(InstrumentationData.TestStatus.LOGCAT, readLogcat(mTestStartMs)); + } + mTestStartMs = 0; + } + + proto.end(testStatusToken); outputProto(proto); } @@ -254,12 +276,12 @@ public class Instrument { Bundle results) { final ProtoOutputStream proto = new ProtoOutputStream(); - final long token = proto.start(InstrumentationData.Session.SESSION_STATUS); + final long sessionStatusToken = proto.start(InstrumentationData.Session.SESSION_STATUS); proto.write(InstrumentationData.SessionStatus.STATUS_CODE, InstrumentationData.SESSION_FINISHED); proto.write(InstrumentationData.SessionStatus.RESULT_CODE, resultCode); writeBundle(proto, InstrumentationData.SessionStatus.RESULTS, results); - proto.end(token); + proto.end(sessionStatusToken); outputProto(proto); } @@ -268,11 +290,11 @@ public class Instrument { public void onError(String errorText, boolean commandError) { final ProtoOutputStream proto = new ProtoOutputStream(); - final long token = proto.start(InstrumentationData.Session.SESSION_STATUS); + final long sessionStatusToken = proto.start(InstrumentationData.Session.SESSION_STATUS); proto.write(InstrumentationData.SessionStatus.STATUS_CODE, InstrumentationData.SESSION_ABORTED); proto.write(InstrumentationData.SessionStatus.ERROR_TEXT, errorText); - proto.end(token); + proto.end(sessionStatusToken); outputProto(proto); } @@ -514,5 +536,43 @@ public class Instrument { } } } + + private static String readLogcat(long startTimeMs) { + try { + // Figure out the timestamp arg for logcat. + final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + final String timestamp = format.format(new Date(startTimeMs)); + + // Start the process + final Process process = new ProcessBuilder() + .command("logcat", "-d", "-v threadtime,uid", "-T", timestamp) + .start(); + + // Nothing to write. Don't let the command accidentally block. + process.getOutputStream().close(); + + // Read the output + final StringBuilder str = new StringBuilder(); + final InputStreamReader reader = new InputStreamReader(process.getInputStream()); + char[] buffer = new char[4096]; + int amt; + while ((amt = reader.read(buffer, 0, buffer.length)) >= 0) { + if (amt > 0) { + str.append(buffer, 0, amt); + } + } + + try { + process.waitFor(); + } catch (InterruptedException ex) { + // We already have the text, drop the exception. + } + + return str.toString(); + + } catch (IOException ex) { + return "Error reading logcat command:\n" + ex.toString(); + } + } } diff --git a/cmds/incidentd/OWNERS b/cmds/incidentd/OWNERS index cede4eae50ad..bcdcfc3cafed 100644 --- a/cmds/incidentd/OWNERS +++ b/cmds/incidentd/OWNERS @@ -1,3 +1,4 @@ joeo@google.com -kwekua@google.com +yaochen@google.com yanmin@google.com +zhouwenjie@google.com diff --git a/cmds/statsd/OWNERS b/cmds/statsd/OWNERS index 13157505fc28..380e499a5abf 100644 --- a/cmds/statsd/OWNERS +++ b/cmds/statsd/OWNERS @@ -1,11 +1,7 @@ -bookatz@google.com -cjyu@google.com -dwchen@google.com -jinyithu@google.com +jianjin@google.com joeo@google.com -kwekua@google.com +jtnguyen@google.com +muhammadq@google.com singhtejinder@google.com -stlafon@google.com yaochen@google.com -yanglu@google.com yro@google.com diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index a9f5208ef812..ec02b121d0dd 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -68,12 +68,8 @@ const int FIELD_ID_CURRENT_REPORT_WALL_CLOCK_NANOS = 6; const int FIELD_ID_DUMP_REPORT_REASON = 8; const int FIELD_ID_STRINGS = 9; -const int FIELD_ID_ACTIVE_CONFIG_LIST = 1; -const int FIELD_ID_CONFIG_ID = 1; -const int FIELD_ID_CONFIG_UID = 2; -const int FIELD_ID_ACTIVE_METRIC = 3; -const int FIELD_ID_METRIC_ID = 1; -const int FIELD_ID_TIME_TO_LIVE_NANOS = 2; +// for ActiveConfigList +const int FIELD_ID_ACTIVE_CONFIG_LIST_CONFIG = 1; #define NS_PER_HOUR 3600 * NS_PER_SEC @@ -523,7 +519,7 @@ void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) { std::lock_guard<std::mutex> lock(mMetricsMutex); auto it = mMetricsManagers.find(key); if (it != mMetricsManagers.end()) { - WriteDataToDiskLocked(key, getElapsedRealtimeNs(), CONFIG_REMOVED, + WriteDataToDiskLocked(key, getElapsedRealtimeNs(), CONFIG_REMOVED, NO_TIME_CONSTRAINTS); mMetricsManagers.erase(it); mUidMap->OnConfigRemoved(key); @@ -613,7 +609,7 @@ void StatsLogProcessor::WriteDataToDiskLocked(const ConfigKey& key, mOnDiskDataConfigs.insert(key); } -void StatsLogProcessor::WriteMetricsActivationToDisk(int64_t currentTimeNs) { +void StatsLogProcessor::SaveActiveConfigsToDisk(int64_t currentTimeNs) { std::lock_guard<std::mutex> lock(mMetricsMutex); const int64_t timeNs = getElapsedRealtimeNs(); @@ -629,28 +625,12 @@ void StatsLogProcessor::WriteMetricsActivationToDisk(int64_t currentTimeNs) { mLastActiveMetricsWriteNs = timeNs; ProtoOutputStream proto; - for (const auto& pair : mMetricsManagers) { - uint64_t activeConfigListToken = proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - FIELD_ID_ACTIVE_CONFIG_LIST); - proto.write(FIELD_TYPE_INT64 | FIELD_ID_CONFIG_ID, (long long)pair.first.GetId()); - proto.write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_UID, pair.first.GetUid()); - - vector<MetricProducer*> activeMetrics; - pair.second->prepForShutDown(currentTimeNs); - pair.second->getActiveMetrics(activeMetrics); - for (MetricProducer* metric : activeMetrics) { - if (metric->isActive()) { - uint64_t metricToken = proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | - FIELD_ID_ACTIVE_METRIC); - proto.write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_ID, - (long long)metric->getMetricId()); - proto.write(FIELD_TYPE_INT64 | FIELD_ID_TIME_TO_LIVE_NANOS, - (long long)metric->getRemainingTtlNs(currentTimeNs)); - proto.end(metricToken); - } - } - proto.end(activeConfigListToken); + const sp<MetricsManager>& metricsManager = pair.second; + uint64_t configToken = proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | + FIELD_ID_ACTIVE_CONFIG_LIST_CONFIG); + metricsManager->writeActiveConfigToProtoOutputStream(currentTimeNs, &proto); + proto.end(configToken); } string file_name = StringPrintf("%s/active_metrics", STATS_ACTIVE_METRIC_DIR); @@ -664,30 +644,45 @@ void StatsLogProcessor::WriteMetricsActivationToDisk(int64_t currentTimeNs) { proto.flush(fd.get()); } -void StatsLogProcessor::LoadMetricsActivationFromDisk() { +void StatsLogProcessor::LoadActiveConfigsFromDisk() { + std::lock_guard<std::mutex> lock(mMetricsMutex); + string file_name = StringPrintf("%s/active_metrics", STATS_ACTIVE_METRIC_DIR); int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC); - if (fd != -1) { - string content; - if (android::base::ReadFdToString(fd, &content)) { - ActiveConfigList activeConfigList; - if (activeConfigList.ParseFromString(content)) { - for (int i = 0; i < activeConfigList.active_config_size(); i++) { - const auto& config = activeConfigList.active_config(i); - ConfigKey key(config.uid(), config.config_id()); - auto it = mMetricsManagers.find(key); - if (it == mMetricsManagers.end()) { - ALOGE("No config found for config %s", key.ToString().c_str()); - continue; - } - VLOG("Setting active config %s", key.ToString().c_str()); - it->second->setActiveMetrics(config, mTimeBaseNs); - } - } - VLOG("Successfully loaded %d active configs.", activeConfigList.active_config_size()); - } + if (-1 == fd) { + VLOG("Attempt to read %s but failed", file_name.c_str()); + StorageManager::deleteFile(file_name.c_str()); + return; + } + string content; + if (!android::base::ReadFdToString(fd, &content)) { + ALOGE("Attempt to read %s but failed", file_name.c_str()); close(fd); + StorageManager::deleteFile(file_name.c_str()); + return; + } + + close(fd); + + ActiveConfigList activeConfigList; + if (!activeConfigList.ParseFromString(content)) { + ALOGE("Attempt to read %s but failed; failed to load active configs", file_name.c_str()); + StorageManager::deleteFile(file_name.c_str()); + return; + } + for (int i = 0; i < activeConfigList.config_size(); i++) { + const auto& config = activeConfigList.config(i); + ConfigKey key(config.uid(), config.id()); + auto it = mMetricsManagers.find(key); + if (it == mMetricsManagers.end()) { + ALOGE("No config found for config %s", key.ToString().c_str()); + continue; + } + VLOG("Setting active config %s", key.ToString().c_str()); + it->second->loadActiveConfig(config, mTimeBaseNs); } + VLOG("Successfully loaded %d active configs.", activeConfigList.config_size()); + StorageManager::deleteFile(file_name.c_str()); } @@ -709,7 +704,7 @@ void StatsLogProcessor::WriteDataToDiskLocked(const DumpReportReason dumpReportR } } -void StatsLogProcessor::WriteDataToDisk(const DumpReportReason dumpReportReason, +void StatsLogProcessor::WriteDataToDisk(const DumpReportReason dumpReportReason, const DumpLatency dumpLatency) { std::lock_guard<std::mutex> lock(mMetricsMutex); WriteDataToDiskLocked(dumpReportReason, dumpLatency); diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h index 6178a4bdf136..0dc597b4cb02 100644 --- a/cmds/statsd/src/StatsLogProcessor.h +++ b/cmds/statsd/src/StatsLogProcessor.h @@ -89,11 +89,11 @@ public: void WriteDataToDisk(const DumpReportReason dumpReportReason, const DumpLatency dumpLatency); - /* Persist metric activation status onto disk. */ - void WriteMetricsActivationToDisk(int64_t currentTimeNs); + /* Persist configs containing metrics with active activations to disk. */ + void SaveActiveConfigsToDisk(int64_t currentTimeNs); - /* Load metric activation status from disk. */ - void LoadMetricsActivationFromDisk(); + /* Load configs containing metrics with active activations from disk. */ + void LoadActiveConfigsFromDisk(); // Reset all configs. void resetConfigs(); @@ -221,6 +221,9 @@ private: FRIEND_TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge); FRIEND_TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead); FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBoot); + FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations); + FRIEND_TEST(StatsLogProcessorTest, + TestActivationOnBootMultipleActivationsDifferentActivationTypes); FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration1); FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration2); diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index 4c97c3454e82..623a1f2cdafb 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -28,6 +28,7 @@ #include <android-base/file.h> #include <android-base/stringprintf.h> +#include <android-base/strings.h> #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> #include <binder/PermissionController.h> @@ -394,6 +395,10 @@ status_t StatsService::command(int in, int out, int err, Vector<String8>& args, return cmd_log_app_breadcrumb(out, args); } + if (!args[0].compare(String8("log-binary-push"))) { + return cmd_log_binary_push(out, args); + } + if (!args[0].compare(String8("clear-puller-cache"))) { return cmd_clear_puller_cache(out); } @@ -461,6 +466,21 @@ void StatsService::print_cmd_help(int out) { dprintf(out, " STATE Integer in [0, 3], as per atoms.proto.\n"); dprintf(out, "\n"); dprintf(out, "\n"); + dprintf(out, + "usage: adb shell cmd stats log-binary-push NAME VERSION STAGING ROLLBACK_ENABLED " + "LOW_LATENCY STATE EXPERIMENT_IDS\n"); + dprintf(out, " Log a binary push state changed event.\n"); + dprintf(out, " NAME The train name.\n"); + dprintf(out, " VERSION The train version code.\n"); + dprintf(out, " STAGING If this train requires a restart.\n"); + dprintf(out, " ROLLBACK_ENABLED If rollback should be enabled for this install.\n"); + dprintf(out, " LOW_LATENCY If the train requires low latency monitoring.\n"); + dprintf(out, " STATE The status of the train push.\n"); + dprintf(out, " Integer value of the enum in atoms.proto.\n"); + dprintf(out, " EXPERIMENT_IDS Comma separated list of experiment ids.\n"); + dprintf(out, " Leave blank for none.\n"); + dprintf(out, "\n"); + dprintf(out, "\n"); dprintf(out, "usage: adb shell cmd stats config remove [UID] [NAME]\n"); dprintf(out, "usage: adb shell cmd stats config update [UID] NAME\n"); dprintf(out, "\n"); @@ -506,7 +526,6 @@ void StatsService::print_cmd_help(int out) { dprintf(out, " --configs Send the list of configs in the name list instead of\n"); dprintf(out, " the currently active configs\n"); dprintf(out, " NAME LIST List of configuration names to be included in the broadcast.\n"); - dprintf(out, "\n"); dprintf(out, "\n"); dprintf(out, "usage: adb shell cmd stats print-stats\n"); @@ -821,6 +840,39 @@ status_t StatsService::cmd_log_app_breadcrumb(int out, const Vector<String8>& ar return NO_ERROR; } +status_t StatsService::cmd_log_binary_push(int out, const Vector<String8>& args) { + // Security checks are done in the sendBinaryPushStateChanged atom. + const int argCount = args.size(); + if (argCount != 7 && argCount != 8) { + dprintf(out, "Incorrect number of argument supplied\n"); + return UNKNOWN_ERROR; + } + android::String16 trainName = android::String16(args[1].c_str()); + int64_t trainVersion = strtoll(args[2].c_str(), nullptr, 10); + int options = 0; + if (args[3] == "1") { + options = options | IStatsManager::FLAG_REQUIRE_STAGING; + } + if (args[4] == "1") { + options = options | IStatsManager::FLAG_ROLLBACK_ENABLED; + } + if (args[5] == "1") { + options = options | IStatsManager::FLAG_REQUIRE_LOW_LATENCY_MONITOR; + } + int32_t state = atoi(args[6].c_str()); + vector<int64_t> experimentIds; + if (argCount == 8) { + vector<string> experimentIdsString = android::base::Split(string(args[7].c_str()), ","); + for (string experimentIdString : experimentIdsString) { + int64_t experimentId = strtoll(experimentIdString.c_str(), nullptr, 10); + experimentIds.push_back(experimentId); + } + } + dprintf(out, "Logging BinaryPushStateChanged\n"); + sendBinaryPushStateChangedAtom(trainName, trainVersion, options, state, experimentIds); + return NO_ERROR; +} + status_t StatsService::cmd_print_pulled_metrics(int out, const Vector<String8>& args) { int s = atoi(args[1].c_str()); vector<shared_ptr<LogEvent> > stats; @@ -986,7 +1038,7 @@ Status StatsService::informDeviceShutdown() { ENFORCE_UID(AID_SYSTEM); VLOG("StatsService::informDeviceShutdown"); mProcessor->WriteDataToDisk(DEVICE_SHUTDOWN, FAST); - mProcessor->WriteMetricsActivationToDisk(getElapsedRealtimeNs()); + mProcessor->SaveActiveConfigsToDisk(getElapsedRealtimeNs()); return Status::ok(); } @@ -1021,14 +1073,14 @@ Status StatsService::statsCompanionReady() { void StatsService::Startup() { mConfigManager->Startup(); - mProcessor->LoadMetricsActivationFromDisk(); + mProcessor->LoadActiveConfigsFromDisk(); } void StatsService::Terminate() { ALOGI("StatsService::Terminating"); if (mProcessor != nullptr) { mProcessor->WriteDataToDisk(TERMINATION_SIGNAL_RECEIVED, FAST); - mProcessor->WriteMetricsActivationToDisk(getElapsedRealtimeNs()); + mProcessor->SaveActiveConfigsToDisk(getElapsedRealtimeNs()); } } @@ -1207,22 +1259,25 @@ Status StatsService::sendBinaryPushStateChangedAtom(const android::String16& tra const int options, const int32_t state, const std::vector<int64_t>& experimentIdsIn) { + // Note: We skip the usage stats op check here since we do not have a package name. + // This is ok since we are overloading the usage_stats permission. + // This method only sends data, it does not receive it. + pid_t pid = IPCThreadState::self()->getCallingPid(); uid_t uid = IPCThreadState::self()->getCallingUid(); - // For testing - if (uid == AID_ROOT || uid == AID_SYSTEM || uid == AID_SHELL) { - return ok(); - } - - // Caller must be granted these permissions - if (!checkCallingPermission(String16(kPermissionDump))) { - return exception(binder::Status::EX_SECURITY, - StringPrintf("UID %d lacks permission %s", uid, kPermissionDump)); - } - if (!checkCallingPermission(String16(kPermissionUsage))) { - return exception(binder::Status::EX_SECURITY, - StringPrintf("UID %d lacks permission %s", uid, kPermissionUsage)); + // Root, system, and shell always have access + if (uid != AID_ROOT && uid != AID_SYSTEM && uid != AID_SHELL) { + // Caller must be granted these permissions + if (!checkCallingPermission(String16(kPermissionDump))) { + return exception(binder::Status::EX_SECURITY, + StringPrintf("UID %d / PID %d lacks permission %s", uid, pid, + kPermissionDump)); + } + if (!checkCallingPermission(String16(kPermissionUsage))) { + return exception(binder::Status::EX_SECURITY, + StringPrintf("UID %d / PID %d lacks permission %s", uid, pid, + kPermissionUsage)); + } } - // TODO: add verifier permission bool readTrainInfoSuccess = false; InstallTrainInfo trainInfoOnDisk; diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index 0b6df8b0997c..936f7db52c38 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -331,6 +331,11 @@ private: status_t cmd_log_app_breadcrumb(int outFd, const Vector<String8>& args); /** + * Write an BinaryPushStateChanged event, as if calling StatsLog.logBinaryPushStateChanged(). + */ + status_t cmd_log_binary_push(int outFd, const Vector<String8>& args); + + /** * Print contents of a pulled metrics source. */ status_t cmd_print_pulled_metrics(int outFd, const Vector<String8>& args); diff --git a/cmds/statsd/src/active_config_list.proto b/cmds/statsd/src/active_config_list.proto index 0e9ee03dd2cb..ef8e50bb2596 100644 --- a/cmds/statsd/src/active_config_list.proto +++ b/cmds/statsd/src/active_config_list.proto @@ -21,23 +21,26 @@ option java_package = "com.android.os"; option java_multiple_files = true; option java_outer_classname = "ActiveConfigProto"; +message ActiveEventActivation { + optional int32 atom_matcher_index = 1; + + // Time left in activation. When this proto is loaded after device boot, + // the activation should be set to active for this duration. + optional int64 remaining_ttl_nanos = 2; +} + message ActiveMetric { - // metric id - optional int64 metric_id = 1; - // Remaining time to live in nano seconds. -1 for infinity. - optional int64 time_to_live_nanos = 2; + optional int64 id = 1; + repeated ActiveEventActivation activation = 2; } message ActiveConfig { - // config id - optional int64 config_id = 1; - // config uid + optional int64 id = 1; optional int32 uid = 2; - // metrics - repeated ActiveMetric active_metric = 3; + repeated ActiveMetric metric = 3; } // all configs and their metrics on device. message ActiveConfigList { - repeated ActiveConfig active_config = 1; -}
\ No newline at end of file + repeated ActiveConfig config = 1; +} diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 2bd429976d4c..3a84b79fc681 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -48,6 +48,7 @@ import "frameworks/base/core/proto/android/stats/docsui/docsui_enums.proto"; import "frameworks/base/core/proto/android/stats/enums.proto"; import "frameworks/base/core/proto/android/stats/intelligence/enums.proto"; import "frameworks/base/core/proto/android/stats/launcher/launcher.proto"; +import "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.proto"; import "frameworks/base/core/proto/android/stats/storage/storage_enums.proto"; import "frameworks/base/core/proto/android/stats/style/style_enums.proto"; import "frameworks/base/core/proto/android/telecomm/enums.proto"; @@ -283,6 +284,16 @@ message Atom { ThermalThrottlingSeverityStateChanged thermal_throttling_severity_state_changed = 189; RoleRequestResultReported role_request_result_reported = 190 [(log_from_module) = "permissioncontroller"]; + MediametricsAudiopolicyReported mediametrics_audiopolicy_reported = 191; + MediametricsAudiorecordReported mediametrics_audiorecord_reported = 192; + MediametricsAudiothreadReported mediametrics_audiothread_reported = 193; + MediametricsAudiotrackReported mediametrics_audiotrack_reported = 194; + MediametricsCodecReported mediametrics_codec_reported = 195; + MediametricsDrmWidevineReported mediametrics_drm_widevine_reported = 196; + MediametricsExtractorReported mediametrics_extractor_reported = 197; + MediametricsMediadrmReported mediametrics_mediadrm_reported = 198; + MediametricsNuPlayerReported mediametrics_nuplayer_reported = 199; + MediametricsRecorderReported mediametrics_recorder_reported = 200; } // Pulled events will start at field 10000. @@ -3296,7 +3307,10 @@ message BinaryPushStateChanged { INSTALL_STAGED_NOT_READY = 3; INSTALL_STAGED_READY = 4; INSTALL_SUCCESS = 5; - INSTALL_FAILURE = 6; + // Replaced by INSTALL_FAILURE_DOWNLOAD, INSTALL_FAILURE_STATE_MISMATCH, + // and INSTALL_FAILURE_COMMIT. + INSTALL_FAILURE = 6 [deprecated = true]; + // This enum is for installs that are manually cancelled via the Manual Update UI. INSTALL_CANCELLED = 7; INSTALLER_ROLLBACK_REQUESTED = 8; INSTALLER_ROLLBACK_INITIATED = 9; @@ -3313,6 +3327,9 @@ message BinaryPushStateChanged { INSTALL_STAGED_CANCEL_REQUESTED = 20; INSTALL_STAGED_CANCEL_SUCCESS = 21; INSTALL_STAGED_CANCEL_FAILURE = 22; + INSTALL_FAILURE_DOWNLOAD = 23; + INSTALL_FAILURE_STATE_MISMATCH = 24; + INSTALL_FAILURE_COMMIT = 25; } optional State state = 6; // Possible experiment ids for monitoring this push. @@ -5809,6 +5826,160 @@ message ProcessStartTime { } /** + * Track Media Codec usage + * Logged from: + * frameworks/av/media/libstagefright/MediaCodec.cpp + * frameworks/av/services/mediaanalytics/statsd_codec.cpp + */ +message MediametricsCodecReported { + optional int64 timestamp_nanos = 1; + optional string package_name = 2; + optional int64 package_version_code = 3; + optional int64 media_apex_version = 4; + + optional android.stats.mediametrics.CodecData codec_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES]; +} + +/** + * Track Media Extractor (pulling video/audio streams out of containers) usage + * Logged from: + * frameworks/av/media/libstagefright/RemoteMediaExtractor.cpp + * frameworks/av/services/mediaanalytics/statsd_extractor.cpp + */ +message MediametricsExtractorReported { + optional int64 timestamp_nanos = 1; + optional string package_name = 2; + optional int64 package_version_code = 3; + optional int64 media_apex_version = 4; + + optional android.stats.mediametrics.ExtractorData extractor_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES]; +} + +/** + * Track how we arbitrate between microphone/input requests. + * Logged from + * frameworks/av/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp + * frameworks/av/services/mediaanalytics/statsd_audiopolicy.cpp + */ +message MediametricsAudiopolicyReported { + optional int64 timestamp_nanos = 1; + optional string package_name = 2; + optional int64 package_version_code = 3; + optional int64 media_apex_version = 4; + + optional android.stats.mediametrics.AudioPolicyData audiopolicy_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES]; +} + +/** + * Track how we arbitrate between microphone requests. + * Logged from + * frameworks/av/media/libaudioclient/AudioRecord.cpp + * frameworks/av/services/mediaanalytics/statsd_audiorecord.cpp + */ +message MediametricsAudiorecordReported { + optional int64 timestamp_nanos = 1; + optional string package_name = 2; + optional int64 package_version_code = 3; + optional int64 media_apex_version = 4; + + optional android.stats.mediametrics.AudioRecordData audiorecord_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES]; +} + +/** + * Track how we arbitrate between microphone/input requests. + * Logged from + * frameworks/av/media/libnblog/ReportPerformance.cpp + * frameworks/av/services/mediaanalytics/statsd_audiothread.cpp + */ +message MediametricsAudiothreadReported { + optional int64 timestamp_nanos = 1; + optional string package_name = 2; + optional int64 package_version_code = 3; + optional int64 media_apex_version = 4; + + optional android.stats.mediametrics.AudioThreadData audiothread_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES]; +} + +/** + * Track how we arbitrate between microphone/input requests. + * Logged from + * frameworks/av/media/libaudioclient/AudioTrack.cpp + * frameworks/av/services/mediaanalytics/statsd_audiotrack.cpp + */ +message MediametricsAudiotrackReported { + optional int64 timestamp_nanos = 1; + optional string package_name = 2; + optional int64 package_version_code = 3; + optional int64 media_apex_version = 4; + + optional android.stats.mediametrics.AudioTrackData audiotrack_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES]; +} + +/** + * Track information about DRM framework performance + * Logged from + * frameworks/av/drm/libmediadrm/DrmHal.cpp + * frameworks/av/services/mediaanalytics/statsd_drm.cpp + */ +message MediametricsMediadrmReported { + optional int64 timestamp_nanos = 1; + optional string package_name = 2; + optional int64 package_version_code = 3; + optional int64 media_apex_version = 4; + + // vendor+description tell about which DRM plugin is in use on this device + optional string vendor = 5; + optional string description = 6; + // from frameworks/av/drm/libmediadrm/protos/metrics.proto + optional bytes framework_stats = 7 [(android.os.statsd.log_mode) = MODE_BYTES]; +} + +/** + * Track information about the widevine DRM plugin performance + * Logged from + * vendor/widevine/libwvdrmengine/cdm/metrics + * frameworks/av/services/mediaanalytics/statsd_drm.cpp + */ +message MediametricsDrmWidevineReported { + optional int64 timestamp_nanos = 1; + optional string package_name = 2; + optional int64 package_version_code = 3; + optional int64 media_apex_version = 4; + + optional bytes vendor_specific_stats = 5 [(android.os.statsd.log_mode) = MODE_BYTES]; +} + +/** + * Track information about recordings (e.g. camcorder) + * Logged from + * frameworks/av/media/libmediaplayerservice/StagefrightRecorder.cpp + * frameworks/av/services/mediaanalytics/statsd_recorder.cpp + */ +message MediametricsRecorderReported { + optional int64 timestamp_nanos = 1; + optional string package_name = 2; + optional int64 package_version_code = 3; + optional int64 media_apex_version = 4; + + optional android.stats.mediametrics.RecorderData recorder_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES]; +} + +/** + * Track Media Player usage + * Logged from: + * frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp + * frameworks/av/services/mediaanalytics/statsd_nuplayer.cpp + */ +message MediametricsNuPlayerReported { + optional int64 timestamp_nanos = 1; + optional string package_name = 2; + optional int64 package_version_code = 3; + optional int64 media_apex_version = 4; + + optional android.stats.mediametrics.NuPlayerData nuplayer_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES]; +} + +/** * State of a dangerous permission requested by a package */ message DangerousPermissionState { @@ -5863,7 +6034,10 @@ message TrainInfo { INSTALL_STAGED_NOT_READY = 3; INSTALL_STAGED_READY = 4; INSTALL_SUCCESS = 5; - INSTALL_FAILURE = 6; + // Replaced by INSTALL_FAILURE_DOWNLOAD, INSTALL_FAILURE_STATE_MISMATCH, + // and INSTALL_FAILURE_COMMIT. + INSTALL_FAILURE = 6 [deprecated = true]; + // This enum is for installs that are manually cancelled via the Manual Update UI. INSTALL_CANCELLED = 7; INSTALLER_ROLLBACK_REQUESTED = 8; INSTALLER_ROLLBACK_INITIATED = 9; @@ -5880,6 +6054,9 @@ message TrainInfo { INSTALL_STAGED_CANCEL_REQUESTED = 20; INSTALL_STAGED_CANCEL_SUCCESS = 21; INSTALL_STAGED_CANCEL_FAILURE = 22; + INSTALL_FAILURE_DOWNLOAD = 23; + INSTALL_FAILURE_STATE_MISMATCH = 24; + INSTALL_FAILURE_COMMIT = 25; } optional Status status = 4; } diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp index e22b85370052..9ad7f09ab512 100644 --- a/cmds/statsd/src/metrics/MetricProducer.cpp +++ b/cmds/statsd/src/metrics/MetricProducer.cpp @@ -18,12 +18,26 @@ #include "Log.h" #include "MetricProducer.h" +using android::util::FIELD_COUNT_REPEATED; +using android::util::FIELD_TYPE_INT32; +using android::util::FIELD_TYPE_INT64; +using android::util::FIELD_TYPE_MESSAGE; +using android::util::ProtoOutputStream; + namespace android { namespace os { namespace statsd { using std::map; +// for ActiveMetric +const int FIELD_ID_ACTIVE_METRIC_ID = 1; +const int FIELD_ID_ACTIVE_METRIC_ACTIVATION = 2; + +// for ActiveEventActivation +const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_ATOM_MATCHER_INDEX = 1; +const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS = 2; + void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) { if (!mIsActive) { return; @@ -74,7 +88,7 @@ bool MetricProducer::evaluateActiveStateLocked(int64_t elapsedTimestampNs) { bool isActive = mEventActivationMap.empty(); for (auto& it : mEventActivationMap) { if (it.second->state == ActivationState::kActive && - elapsedTimestampNs > it.second->ttl_ns + it.second->activation_ns) { + elapsedTimestampNs > it.second->ttl_ns + it.second->start_ns) { it.second->state = ActivationState::kNotActive; } if (it.second->state == ActivationState::kActive) { @@ -95,8 +109,8 @@ void MetricProducer::flushIfExpire(int64_t elapsedTimestampNs) { } } -void MetricProducer::addActivation(int activationTrackerIndex, int64_t ttl_seconds, - int deactivationTrackerIndex) { +void MetricProducer::addActivation(int activationTrackerIndex, const ActivationType& activationType, + int64_t ttl_seconds, int deactivationTrackerIndex) { std::lock_guard<std::mutex> lock(mMutex); // When a metric producer does not depend on any activation, its mIsActive is true. // Therefore, if this is the 1st activation, mIsActive will turn to false. Otherwise it does not @@ -104,8 +118,8 @@ void MetricProducer::addActivation(int activationTrackerIndex, int64_t ttl_secon if (mEventActivationMap.empty()) { mIsActive = false; } - std::shared_ptr<Activation> activation = std::make_shared<Activation>(); - activation->ttl_ns = ttl_seconds * NS_PER_SEC; + std::shared_ptr<Activation> activation = + std::make_shared<Activation>(activationType, ttl_seconds * NS_PER_SEC); mEventActivationMap.emplace(activationTrackerIndex, activation); if (-1 != deactivationTrackerIndex) { mEventDeactivationMap.emplace(deactivationTrackerIndex, activation); @@ -117,13 +131,16 @@ void MetricProducer::activateLocked(int activationTrackerIndex, int64_t elapsedT if (it == mEventActivationMap.end()) { return; } - if (mActivationType == MetricActivation::ACTIVATE_ON_BOOT && - it->second->state == ActivationState::kNotActive) { - it->second->state = ActivationState::kActiveOnBoot; + auto& activation = it->second; + if (ACTIVATE_ON_BOOT == activation->activationType) { + if (ActivationState::kNotActive == activation->state) { + activation->state = ActivationState::kActiveOnBoot; + } + // If the Activation is already active or set to kActiveOnBoot, do nothing. return; } - it->second->activation_ns = elapsedTimestampNs; - it->second->state = ActivationState::kActive; + activation->start_ns = elapsedTimestampNs; + activation->state = ActivationState::kActive; mIsActive = true; } @@ -135,46 +152,55 @@ void MetricProducer::cancelEventActivationLocked(int deactivationTrackerIndex) { it->second->state = ActivationState::kNotActive; } -void MetricProducer::setActiveLocked(int64_t currentTimeNs, int64_t remainingTtlNs) { +void MetricProducer::loadActiveMetricLocked(const ActiveMetric& activeMetric, + int64_t currentTimeNs) { if (mEventActivationMap.size() == 0) { return; } - for (auto& pair : mEventActivationMap) { - auto& activation = pair.second; - if (activation->ttl_ns >= remainingTtlNs) { - activation->activation_ns = currentTimeNs + remainingTtlNs - activation->ttl_ns; - activation->state = kActive; - mIsActive = true; - VLOG("setting new activation->time to %lld, %lld, %lld", - (long long)activation->activation_ns, (long long)currentTimeNs, - (long long)remainingTtlNs); - return; + for (int i = 0; i < activeMetric.activation_size(); i++) { + const auto& activeEventActivation = activeMetric.activation(i); + auto it = mEventActivationMap.find(activeEventActivation.atom_matcher_index()); + if (it == mEventActivationMap.end()) { + ALOGE("Saved event activation not found"); + continue; } + auto& activation = it->second; + // We don't want to change the ttl for future activations, so we set the start_ns + // such that start_ns + ttl_ns == currentTimeNs + remaining_ttl_nanos + activation->start_ns = + currentTimeNs + activeEventActivation.remaining_ttl_nanos() - activation->ttl_ns; + activation->state = ActivationState::kActive; + mIsActive = true; } - ALOGE("Required ttl is longer than all possible activations."); } -int64_t MetricProducer::getRemainingTtlNsLocked(int64_t currentTimeNs) const { - int64_t maxTtl = 0; - for (const auto& activation : mEventActivationMap) { - if (activation.second->state == kActive) { - maxTtl = std::max(maxTtl, activation.second->ttl_ns + activation.second->activation_ns - - currentTimeNs); +void MetricProducer::writeActiveMetricToProtoOutputStream( + int64_t currentTimeNs, ProtoOutputStream* proto) { + proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_METRIC_ID, (long long)mMetricId); + for (auto& it : mEventActivationMap) { + const int atom_matcher_index = it.first; + const std::shared_ptr<Activation>& activation = it.second; + + if (ActivationState::kNotActive == activation->state || + (ActivationState::kActive == activation->state && + activation->start_ns + activation->ttl_ns < currentTimeNs)) { + continue; } - } - return maxTtl; -} -void MetricProducer::prepActiveForBootIfNecessaryLocked(int64_t currentTimeNs) { - if (mActivationType != MetricActivation::ACTIVATE_ON_BOOT) { - return; - } - for (auto& activation : mEventActivationMap) { - if (activation.second->state == kActiveOnBoot) { - activation.second->state = kActive; - activation.second->activation_ns = currentTimeNs; - mIsActive = true; + const uint64_t activationToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | + FIELD_ID_ACTIVE_METRIC_ACTIVATION); + proto->write(FIELD_TYPE_INT32 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_ATOM_MATCHER_INDEX, + atom_matcher_index); + if (ActivationState::kActive == activation->state) { + const int64_t remainingTtlNs = + activation->start_ns + activation->ttl_ns - currentTimeNs; + proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS, + (long long)remainingTtlNs); + } else if (ActivationState::kActiveOnBoot == activation->state) { + proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS, + (long long)activation->ttl_ns); } + proto->end(activationToken); } } diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h index 750566dd8e64..7676f598f749 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -19,6 +19,7 @@ #include <shared_mutex> +#include <frameworks/base/cmds/statsd/src/active_config_list.pb.h> #include "HashableDimensionKey.h" #include "anomaly/AnomalyTracker.h" #include "condition/ConditionWizard.h" @@ -198,15 +199,9 @@ public: return mMetricId; } - int64_t getRemainingTtlNs(int64_t currentTimeNs) const { + void loadActiveMetric(const ActiveMetric& activeMetric, int64_t currentTimeNs) { std::lock_guard<std::mutex> lock(mMutex); - return getRemainingTtlNsLocked(currentTimeNs); - } - - // Set metric to active for ttlNs. - void setActive(int64_t currentTimeNs, int64_t remainingTtlNs) { - std::lock_guard<std::mutex> lock(mMutex); - setActiveLocked(currentTimeNs, remainingTtlNs); + loadActiveMetricLocked(activeMetric, currentTimeNs); } // Let MetricProducer drop in-memory data to save memory. @@ -238,17 +233,8 @@ public: return isActiveLocked(); } - void prepActiveForBootIfNecessary(int64_t currentTimeNs) { - std::lock_guard<std::mutex> lock(mMutex); - prepActiveForBootIfNecessaryLocked(currentTimeNs); - } - - void addActivation(int activationTrackerIndex, int64_t ttl_seconds, - int deactivationTrackerIndex = -1); - - inline void setActivationType(const MetricActivation::ActivationType& activationType) { - mActivationType = activationType; - } + void addActivation(int activationTrackerIndex, const ActivationType& activationType, + int64_t ttl_seconds, int deactivationTrackerIndex = -1); void prepareFistBucket() { std::lock_guard<std::mutex> lock(mMutex); @@ -257,6 +243,8 @@ public: void flushIfExpire(int64_t elapsedTimestampNs); + void writeActiveMetricToProtoOutputStream( + int64_t currentTimeNs, ProtoOutputStream* proto); protected: virtual void onConditionChangedLocked(const bool condition, const int64_t eventTime) = 0; virtual void onSlicedConditionMayChangeLocked(bool overallCondition, @@ -282,9 +270,7 @@ protected: void prepActiveForBootIfNecessaryLocked(int64_t currentTimeNs); - int64_t getRemainingTtlNsLocked(int64_t currentTimeNs) const; - - void setActiveLocked(int64_t currentTimeNs, int64_t remainingTtlNs); + void loadActiveMetricLocked(const ActiveMetric& activeMetric, int64_t currentTimeNs); virtual void prepareFistBucketLocked() {}; /** @@ -396,11 +382,16 @@ protected: mutable std::mutex mMutex; struct Activation { - Activation() : ttl_ns(0), activation_ns(0), state(ActivationState::kNotActive) {} - - int64_t ttl_ns; - int64_t activation_ns; + Activation(const ActivationType& activationType, const int64_t ttlNs) + : ttl_ns(ttlNs), + start_ns(0), + state(ActivationState::kNotActive), + activationType(activationType) {} + + const int64_t ttl_ns; + int64_t start_ns; ActivationState state; + const ActivationType activationType; }; // When the metric producer has multiple activations, these activations are ORed to determine // whether the metric producer is ready to generate metrics. @@ -411,8 +402,6 @@ protected: bool mIsActive; - MetricActivation::ActivationType mActivationType; - FRIEND_TEST(MetricActivationE2eTest, TestCountMetric); FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation); FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations); @@ -420,6 +409,9 @@ protected: FRIEND_TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead); FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBoot); + FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations); + FRIEND_TEST(StatsLogProcessorTest, + TestActivationOnBootMultipleActivationsDifferentActivationTypes); }; } // namespace statsd diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp index 6a55289bc8a2..947f37782fcc 100644 --- a/cmds/statsd/src/metrics/MetricsManager.cpp +++ b/cmds/statsd/src/metrics/MetricsManager.cpp @@ -54,6 +54,11 @@ const int FIELD_ID_ANNOTATIONS = 7; const int FIELD_ID_ANNOTATIONS_INT64 = 1; const int FIELD_ID_ANNOTATIONS_INT32 = 2; +// for ActiveConfig +const int FIELD_ID_ACTIVE_CONFIG_ID = 1; +const int FIELD_ID_ACTIVE_CONFIG_UID = 2; +const int FIELD_ID_ACTIVE_CONFIG_METRIC = 3; + MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs, const int64_t currentTimeNs, const sp<UidMap>& uidMap, @@ -503,25 +508,41 @@ size_t MetricsManager::byteSize() { return totalSize; } -void MetricsManager::setActiveMetrics(ActiveConfig config, int64_t currentTimeNs) { - if (config.active_metric_size() == 0) { +void MetricsManager::loadActiveConfig(const ActiveConfig& config, int64_t currentTimeNs) { + if (config.metric_size() == 0) { ALOGW("No active metric for config %s", mConfigKey.ToString().c_str()); return; } - for (int i = 0; i < config.active_metric_size(); i++) { - for (int metric : mMetricIndexesWithActivation) { - if (mAllMetricProducers[metric]->getMetricId() == config.active_metric(i).metric_id()) { - VLOG("Setting active metric: %lld", - (long long)mAllMetricProducers[metric]->getMetricId()); - mAllMetricProducers[metric]->setActive( - currentTimeNs, config.active_metric(i).time_to_live_nanos()); - mIsActive = true; + for (int i = 0; i < config.metric_size(); i++) { + const auto& activeMetric = config.metric(i); + for (int metricIndex : mMetricIndexesWithActivation) { + const auto& metric = mAllMetricProducers[metricIndex]; + if (metric->getMetricId() == activeMetric.id()) { + VLOG("Setting active metric: %lld", (long long)metric->getMetricId()); + metric->loadActiveMetric(activeMetric, currentTimeNs); + mIsActive |= metric->isActive(); } } } } +void MetricsManager::writeActiveConfigToProtoOutputStream( + int64_t currentTimeNs, ProtoOutputStream* proto) { + proto->write(FIELD_TYPE_INT64 | FIELD_ID_ACTIVE_CONFIG_ID, (long long)mConfigKey.GetId()); + proto->write(FIELD_TYPE_INT32 | FIELD_ID_ACTIVE_CONFIG_UID, mConfigKey.GetUid()); + for (int metricIndex : mMetricIndexesWithActivation) { + const auto& metric = mAllMetricProducers[metricIndex]; + const uint64_t metricToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | + FIELD_ID_ACTIVE_CONFIG_METRIC); + metric->writeActiveMetricToProtoOutputStream(currentTimeNs, proto); + proto->end(metricToken); + } +} + + + + } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h index d05bb8b164e8..818131efe138 100644 --- a/cmds/statsd/src/metrics/MetricsManager.h +++ b/cmds/statsd/src/metrics/MetricsManager.h @@ -16,7 +16,6 @@ #pragma once -#include <frameworks/base/cmds/statsd/src/active_config_list.pb.h> #include "anomaly/AlarmMonitor.h" #include "anomaly/AlarmTracker.h" #include "anomaly/AnomalyTracker.h" @@ -139,21 +138,10 @@ public: return mIsActive; } - inline void getActiveMetrics(std::vector<MetricProducer*>& metrics) const { - for (const auto& metric : mAllMetricProducers) { - if (metric->isActive()) { - metrics.push_back(metric.get()); - } - } - } - - inline void prepForShutDown(int64_t currentTimeNs) { - for (const auto& metric : mAllMetricProducers) { - metric->prepActiveForBootIfNecessary(currentTimeNs); - } - } + void loadActiveConfig(const ActiveConfig& config, int64_t currentTimeNs); - void setActiveMetrics(ActiveConfig config, int64_t currentTimeNs); + void writeActiveConfigToProtoOutputStream( + int64_t currentTimeNs, ProtoOutputStream* proto); private: // For test only. @@ -299,6 +287,9 @@ private: FRIEND_TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead); FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBoot); + FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations); + FRIEND_TEST(StatsLogProcessorTest, + TestActivationOnBootMultipleActivationsDifferentActivationTypes); }; } // namespace statsd diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp index 31b424e8792e..b027fa0d13e8 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.cpp +++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp @@ -727,7 +727,6 @@ bool initMetricActivations(const ConfigKey& key, const StatsdConfig& config, return false; } const sp<MetricProducer>& metric = allMetricProducers[metricTrackerIndex]; - metric->setActivationType(metric_activation.activation_type()); metricsWithActivation.push_back(metricTrackerIndex); for (int j = 0; j < metric_activation.event_activation_size(); ++j) { const EventActivation& activation = metric_activation.event_activation(j); @@ -740,6 +739,13 @@ bool initMetricActivations(const ConfigKey& key, const StatsdConfig& config, activationAtomTrackerToMetricMap[atomMatcherIndex].push_back( metricTrackerIndex); + ActivationType activationType; + if (activation.has_activation_type()) { + activationType = activation.activation_type(); + } else { + activationType = metric_activation.activation_type(); + } + if (activation.has_deactivation_atom_matcher_id()) { auto deactivationAtomMatcherIt = logEventTrackerMap.find(activation.deactivation_atom_matcher_id()); @@ -750,10 +756,10 @@ bool initMetricActivations(const ConfigKey& key, const StatsdConfig& config, const int deactivationMatcherIndex = deactivationAtomMatcherIt->second; deactivationAtomTrackerToMetricMap[deactivationMatcherIndex] .push_back(metricTrackerIndex); - metric->addActivation(atomMatcherIndex, activation.ttl_seconds(), + metric->addActivation(atomMatcherIndex, activationType, activation.ttl_seconds(), deactivationMatcherIndex); } else { - metric->addActivation(atomMatcherIndex, activation.ttl_seconds()); + metric->addActivation(atomMatcherIndex, activationType, activation.ttl_seconds()); } } } diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto index 2260b9b56d0b..4e419b6acddc 100644 --- a/cmds/statsd/src/statsd_config.proto +++ b/cmds/statsd/src/statsd_config.proto @@ -377,21 +377,23 @@ message Subscription { optional float probability_of_informing = 7 [default = 1.1]; } +enum ActivationType { + ACTIVATION_TYPE_UNKNOWN = 0; + ACTIVATE_IMMEDIATELY = 1; + ACTIVATE_ON_BOOT = 2; +} + message EventActivation { optional int64 atom_matcher_id = 1; optional int64 ttl_seconds = 2; optional int64 deactivation_atom_matcher_id = 3; + optional ActivationType activation_type = 4; } message MetricActivation { optional int64 metric_id = 1; - enum ActivationType { - UNKNOWN = 0; - ACTIVATE_IMMEDIATELY = 1; - ACTIVATE_ON_BOOT = 2; - } - optional ActivationType activation_type = 3; + optional ActivationType activation_type = 3 [deprecated = true]; repeated EventActivation event_activation = 2; } diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp index 91e282a7f796..49b4e904d93c 100644 --- a/cmds/statsd/tests/StatsLogProcessor_test.cpp +++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp @@ -339,6 +339,7 @@ TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { auto metric3Activation = config2.add_metric_activation(); metric3Activation->set_metric_id(metricId3); + metric3Activation->set_activation_type(ACTIVATE_IMMEDIATELY); auto metric3ActivationTrigger = metric3Activation->add_event_activation(); metric3ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id()); metric3ActivationTrigger->set_ttl_seconds(100); @@ -366,12 +367,14 @@ TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { auto metric5Activation = config3.add_metric_activation(); metric5Activation->set_metric_id(metricId5); + metric5Activation->set_activation_type(ACTIVATE_IMMEDIATELY); auto metric5ActivationTrigger = metric5Activation->add_event_activation(); metric5ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id()); metric5ActivationTrigger->set_ttl_seconds(100); auto metric6Activation = config3.add_metric_activation(); metric6Activation->set_metric_id(metricId6); + metric6Activation->set_activation_type(ACTIVATE_IMMEDIATELY); auto metric6ActivationTrigger = metric6Activation->add_event_activation(); metric6ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id()); metric6ActivationTrigger->set_ttl_seconds(200); @@ -507,17 +510,13 @@ TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { // When we shut down, metrics 3 & 5 have 100ns remaining, metric 6 has 100s + 100ns. int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC; - EXPECT_TRUE(metricProducer3->isActive()); - int64_t ttl3 = metricProducer3->getRemainingTtlNs(shutDownTime); - EXPECT_EQ(100, ttl3); - EXPECT_TRUE(metricProducer5->isActive()); - int64_t ttl5 = metricProducer5->getRemainingTtlNs(shutDownTime); - EXPECT_EQ(100, ttl5); - EXPECT_TRUE(metricProducer6->isActive()); - int64_t ttl6 = metricProducer6->getRemainingTtlNs(shutDownTime); - EXPECT_EQ(100 + 100 * NS_PER_SEC, ttl6); - - processor.WriteMetricsActivationToDisk(shutDownTime); + processor.SaveActiveConfigsToDisk(shutDownTime); + const int64_t ttl3 = event->GetElapsedTimestampNs() + + metric3ActivationTrigger->ttl_seconds() * NS_PER_SEC - shutDownTime; + const int64_t ttl5 = event->GetElapsedTimestampNs() + + metric5ActivationTrigger->ttl_seconds() * NS_PER_SEC - shutDownTime; + const int64_t ttl6 = event->GetElapsedTimestampNs() + + metric6ActivationTrigger->ttl_seconds() * NS_PER_SEC - shutDownTime; // Create a second StatsLogProcessor and push the same 3 configs. long timeBase2 = 1000; @@ -611,25 +610,25 @@ TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { EXPECT_FALSE(metricProducer1003->isActive()); const auto& activation1003 = metricProducer1003->mEventActivationMap.begin()->second; EXPECT_EQ(100 * NS_PER_SEC, activation1003->ttl_ns); - EXPECT_EQ(0, activation1003->activation_ns); + EXPECT_EQ(0, activation1003->start_ns); EXPECT_FALSE(metricProducer1005->isActive()); const auto& activation1005 = metricProducer1005->mEventActivationMap.begin()->second; EXPECT_EQ(100 * NS_PER_SEC, activation1005->ttl_ns); - EXPECT_EQ(0, activation1005->activation_ns); + EXPECT_EQ(0, activation1005->start_ns); EXPECT_FALSE(metricProducer1006->isActive()); const auto& activation1006 = metricProducer1006->mEventActivationMap.begin()->second; EXPECT_EQ(200 * NS_PER_SEC, activation1006->ttl_ns); - EXPECT_EQ(0, activation1006->activation_ns); + EXPECT_EQ(0, activation1006->start_ns); - processor2->LoadMetricsActivationFromDisk(); + processor2->LoadActiveConfigsFromDisk(); // After loading activations from disk, assert that all 3 metrics are active. EXPECT_TRUE(metricProducer1003->isActive()); - EXPECT_EQ(timeBase2 + ttl3 - activation1003->ttl_ns, activation1003->activation_ns); + EXPECT_EQ(timeBase2 + ttl3 - activation1003->ttl_ns, activation1003->start_ns); EXPECT_TRUE(metricProducer1005->isActive()); - EXPECT_EQ(timeBase2 + ttl5 - activation1005->ttl_ns, activation1005->activation_ns); + EXPECT_EQ(timeBase2 + ttl5 - activation1005->ttl_ns, activation1005->start_ns); EXPECT_TRUE(metricProducer1006->isActive()); - EXPECT_EQ(timeBase2 + ttl6 - activation1006->ttl_ns, activation1003->activation_ns); + EXPECT_EQ(timeBase2 + ttl6 - activation1006->ttl_ns, activation1003->start_ns); // Make sure no more broadcasts have happened. EXPECT_EQ(broadcastCount, 1); @@ -638,7 +637,6 @@ TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { TEST(StatsLogProcessorTest, TestActivationOnBoot) { int uid = 1111; - // Setup a simple config, no activation StatsdConfig config1; config1.set_id(12341); config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. @@ -659,7 +657,7 @@ TEST(StatsLogProcessorTest, TestActivationOnBoot) { auto metric1Activation = config1.add_metric_activation(); metric1Activation->set_metric_id(metricId1); - metric1Activation->set_activation_type(MetricActivation::ACTIVATE_ON_BOOT); + metric1Activation->set_activation_type(ACTIVATE_ON_BOOT); auto metric1ActivationTrigger = metric1Activation->add_event_activation(); metric1ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id()); metric1ActivationTrigger->set_ttl_seconds(100); @@ -697,7 +695,7 @@ TEST(StatsLogProcessorTest, TestActivationOnBoot) { const auto& activation1 = metricProducer1->mEventActivationMap.begin()->second; EXPECT_EQ(100 * NS_PER_SEC, activation1->ttl_ns); - EXPECT_EQ(0, activation1->activation_ns); + EXPECT_EQ(0, activation1->start_ns); EXPECT_EQ(kNotActive, activation1->state); std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")}; @@ -705,15 +703,13 @@ TEST(StatsLogProcessorTest, TestActivationOnBoot) { processor->OnLogEvent(event.get()); EXPECT_FALSE(metricProducer1->isActive()); - EXPECT_EQ(0, activation1->activation_ns); + EXPECT_EQ(0, activation1->start_ns); EXPECT_EQ(kActiveOnBoot, activation1->state); int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC; - - processor->WriteMetricsActivationToDisk(shutDownTime); - EXPECT_TRUE(metricProducer1->isActive()); - int64_t ttl1 = metricProducer1->getRemainingTtlNs(shutDownTime); - EXPECT_EQ(100 * NS_PER_SEC, ttl1); + processor->SaveActiveConfigsToDisk(shutDownTime); + EXPECT_FALSE(metricProducer1->isActive()); + const int64_t ttl1 = metric1ActivationTrigger->ttl_seconds() * NS_PER_SEC; long timeBase2 = 1000; sp<StatsLogProcessor> processor2 = @@ -747,13 +743,743 @@ TEST(StatsLogProcessorTest, TestActivationOnBoot) { const auto& activation1001 = metricProducer1001->mEventActivationMap.begin()->second; EXPECT_EQ(100 * NS_PER_SEC, activation1001->ttl_ns); - EXPECT_EQ(0, activation1001->activation_ns); + EXPECT_EQ(0, activation1001->start_ns); EXPECT_EQ(kNotActive, activation1001->state); - processor2->LoadMetricsActivationFromDisk(); + processor2->LoadActiveConfigsFromDisk(); + + EXPECT_TRUE(metricProducer1001->isActive()); + EXPECT_EQ(timeBase2 + ttl1 - activation1001->ttl_ns, activation1001->start_ns); + EXPECT_EQ(kActive, activation1001->state); +} + +TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations) { + int uid = 1111; + + // Create config with 2 metrics: + // Metric 1: Activate on boot with 2 activations + // Metric 2: Always active + StatsdConfig config1; + config1.set_id(12341); + config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); + auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); + *config1.add_atom_matcher() = wakelockAcquireMatcher; + *config1.add_atom_matcher() = screenOnMatcher; + + long metricId1 = 1234561; + long metricId2 = 1234562; + + auto countMetric1 = config1.add_count_metric(); + countMetric1->set_id(metricId1); + countMetric1->set_what(wakelockAcquireMatcher.id()); + countMetric1->set_bucket(FIVE_MINUTES); + + auto countMetric2 = config1.add_count_metric(); + countMetric2->set_id(metricId2); + countMetric2->set_what(wakelockAcquireMatcher.id()); + countMetric2->set_bucket(FIVE_MINUTES); + + auto metric1Activation = config1.add_metric_activation(); + metric1Activation->set_metric_id(metricId1); + metric1Activation->set_activation_type(ACTIVATE_ON_BOOT); + auto metric1ActivationTrigger1 = metric1Activation->add_event_activation(); + metric1ActivationTrigger1->set_atom_matcher_id(wakelockAcquireMatcher.id()); + metric1ActivationTrigger1->set_ttl_seconds(100); + auto metric1ActivationTrigger2 = metric1Activation->add_event_activation(); + metric1ActivationTrigger2->set_atom_matcher_id(screenOnMatcher.id()); + metric1ActivationTrigger2->set_ttl_seconds(200); + + ConfigKey cfgKey1(uid, 12341); + long timeBase1 = 1; + sp<StatsLogProcessor> processor = + CreateStatsLogProcessor(timeBase1, timeBase1, config1, cfgKey1); + + // Metric 1 is not active. + // Metric 2 is active. + // {{{--------------------------------------------------------------------------- + EXPECT_EQ(1, processor->mMetricsManagers.size()); + auto it = processor->mMetricsManagers.find(cfgKey1); + EXPECT_TRUE(it != processor->mMetricsManagers.end()); + auto& metricsManager1 = it->second; + EXPECT_TRUE(metricsManager1->isActive()); + + auto metricIt = metricsManager1->mAllMetricProducers.begin(); + for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId1) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end()); + auto& metricProducer1 = *metricIt; + EXPECT_FALSE(metricProducer1->isActive()); + + metricIt = metricsManager1->mAllMetricProducers.begin(); + for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId2) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end()); + auto& metricProducer2 = *metricIt; + EXPECT_TRUE(metricProducer2->isActive()); + + int i = 0; + for (; i < metricsManager1->mAllAtomMatchers.size(); i++) { + if (metricsManager1->mAllAtomMatchers[i]->getId() == + metric1ActivationTrigger1->atom_matcher_id()) { + break; + } + } + const auto& activation1 = metricProducer1->mEventActivationMap.at(i); + EXPECT_EQ(100 * NS_PER_SEC, activation1->ttl_ns); + EXPECT_EQ(0, activation1->start_ns); + EXPECT_EQ(kNotActive, activation1->state); + + i = 0; + for (; i < metricsManager1->mAllAtomMatchers.size(); i++) { + if (metricsManager1->mAllAtomMatchers[i]->getId() == + metric1ActivationTrigger2->atom_matcher_id()) { + break; + } + } + const auto& activation2 = metricProducer1->mEventActivationMap.at(i); + EXPECT_EQ(200 * NS_PER_SEC, activation2->ttl_ns); + EXPECT_EQ(0, activation2->start_ns); + EXPECT_EQ(kNotActive, activation2->state); + // }}}------------------------------------------------------------------------------ + + // Trigger Activation 1 for Metric 1 + std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")}; + auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 100 + timeBase1); + processor->OnLogEvent(event.get()); + + // Metric 1 is not active; Activation 1 set to kActiveOnBoot + // Metric 2 is active. + // {{{--------------------------------------------------------------------------- + EXPECT_FALSE(metricProducer1->isActive()); + EXPECT_EQ(0, activation1->start_ns); + EXPECT_EQ(kActiveOnBoot, activation1->state); + EXPECT_EQ(0, activation2->start_ns); + EXPECT_EQ(kNotActive, activation2->state); + + EXPECT_TRUE(metricProducer2->isActive()); + // }}}----------------------------------------------------------------------------- + + // Simulate shutdown by saving state to disk + int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC; + processor->SaveActiveConfigsToDisk(shutDownTime); + EXPECT_FALSE(metricProducer1->isActive()); + int64_t ttl1 = metric1ActivationTrigger1->ttl_seconds() * NS_PER_SEC; + + // Simulate device restarted state by creating new instance of StatsLogProcessor with the + // same config. + long timeBase2 = 1000; + sp<StatsLogProcessor> processor2 = + CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1); + + // Metric 1 is not active. + // Metric 2 is active. + // {{{--------------------------------------------------------------------------- + EXPECT_EQ(1, processor2->mMetricsManagers.size()); + it = processor2->mMetricsManagers.find(cfgKey1); + EXPECT_TRUE(it != processor2->mMetricsManagers.end()); + auto& metricsManager1001 = it->second; + EXPECT_TRUE(metricsManager1001->isActive()); + + metricIt = metricsManager1001->mAllMetricProducers.begin(); + for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId1) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end()); + auto& metricProducer1001 = *metricIt; + EXPECT_FALSE(metricProducer1001->isActive()); + + metricIt = metricsManager1001->mAllMetricProducers.begin(); + for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId2) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end()); + auto& metricProducer1002 = *metricIt; + EXPECT_TRUE(metricProducer1002->isActive()); + + i = 0; + for (; i < metricsManager1001->mAllAtomMatchers.size(); i++) { + if (metricsManager1001->mAllAtomMatchers[i]->getId() == + metric1ActivationTrigger1->atom_matcher_id()) { + break; + } + } + const auto& activation1001_1 = metricProducer1001->mEventActivationMap.at(i); + EXPECT_EQ(100 * NS_PER_SEC, activation1001_1->ttl_ns); + EXPECT_EQ(0, activation1001_1->start_ns); + EXPECT_EQ(kNotActive, activation1001_1->state); + + i = 0; + for (; i < metricsManager1001->mAllAtomMatchers.size(); i++) { + if (metricsManager1001->mAllAtomMatchers[i]->getId() == + metric1ActivationTrigger2->atom_matcher_id()) { + break; + } + } + + const auto& activation1001_2 = metricProducer1001->mEventActivationMap.at(i); + EXPECT_EQ(200 * NS_PER_SEC, activation1001_2->ttl_ns); + EXPECT_EQ(0, activation1001_2->start_ns); + EXPECT_EQ(kNotActive, activation1001_2->state); + // }}}----------------------------------------------------------------------------------- + + // Load saved state from disk. + processor2->LoadActiveConfigsFromDisk(); + + // Metric 1 active; Activation 1 is active, Activation 2 is not active + // Metric 2 is active. + // {{{--------------------------------------------------------------------------- + EXPECT_TRUE(metricProducer1001->isActive()); + EXPECT_EQ(timeBase2 + ttl1 - activation1001_1->ttl_ns, activation1001_1->start_ns); + EXPECT_EQ(kActive, activation1001_1->state); + EXPECT_EQ(0, activation1001_2->start_ns); + EXPECT_EQ(kNotActive, activation1001_2->state); + + EXPECT_TRUE(metricProducer1002->isActive()); + // }}}-------------------------------------------------------------------------------- + + // Trigger Activation 2 for Metric 1. + auto screenOnEvent = CreateScreenStateChangedEvent( + android::view::DISPLAY_STATE_ON, + timeBase2 + 200 + ); + processor2->OnLogEvent(screenOnEvent.get()); + + // Metric 1 active; Activation 1 is active, Activation 2 is set to kActiveOnBoot + // Metric 2 is active. + // {{{--------------------------------------------------------------------------- + EXPECT_TRUE(metricProducer1001->isActive()); + EXPECT_EQ(timeBase2 + ttl1 - activation1001_1->ttl_ns, activation1001_1->start_ns); + EXPECT_EQ(kActive, activation1001_1->state); + EXPECT_EQ(0, activation1001_2->start_ns); + EXPECT_EQ(kActiveOnBoot, activation1001_2->state); + + EXPECT_TRUE(metricProducer1002->isActive()); + // }}}--------------------------------------------------------------------------- + // Simulate shutdown by saving state to disk + shutDownTime = timeBase2 + 50 * NS_PER_SEC; + processor2->SaveActiveConfigsToDisk(shutDownTime); EXPECT_TRUE(metricProducer1001->isActive()); - EXPECT_EQ(timeBase2 + ttl1 - activation1001->ttl_ns, activation1001->activation_ns); + EXPECT_TRUE(metricProducer1002->isActive()); + ttl1 = timeBase2 + metric1ActivationTrigger1->ttl_seconds() * NS_PER_SEC - shutDownTime; + int64_t ttl2 = metric1ActivationTrigger2->ttl_seconds() * NS_PER_SEC; + + // Simulate device restarted state by creating new instance of StatsLogProcessor with the + // same config. + long timeBase3 = timeBase2 + 120 * NS_PER_SEC; + sp<StatsLogProcessor> processor3 = + CreateStatsLogProcessor(timeBase3, timeBase3, config1, cfgKey1); + + // Metric 1 is not active. + // Metric 2 is active. + // {{{--------------------------------------------------------------------------- + EXPECT_EQ(1, processor3->mMetricsManagers.size()); + it = processor3->mMetricsManagers.find(cfgKey1); + EXPECT_TRUE(it != processor3->mMetricsManagers.end()); + auto& metricsManagerTimeBase3 = it->second; + EXPECT_TRUE(metricsManagerTimeBase3->isActive()); + + metricIt = metricsManagerTimeBase3->mAllMetricProducers.begin(); + for (; metricIt != metricsManagerTimeBase3->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId1) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManagerTimeBase3->mAllMetricProducers.end()); + auto& metricProducerTimeBase3_1 = *metricIt; + EXPECT_FALSE(metricProducerTimeBase3_1->isActive()); + + metricIt = metricsManagerTimeBase3->mAllMetricProducers.begin(); + for (; metricIt != metricsManagerTimeBase3->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId2) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManagerTimeBase3->mAllMetricProducers.end()); + auto& metricProducerTimeBase3_2 = *metricIt; + EXPECT_TRUE(metricProducerTimeBase3_2->isActive()); + + i = 0; + for (; i < metricsManagerTimeBase3->mAllAtomMatchers.size(); i++) { + if (metricsManagerTimeBase3->mAllAtomMatchers[i]->getId() == + metric1ActivationTrigger1->atom_matcher_id()) { + break; + } + } + const auto& activationTimeBase3_1 = metricProducerTimeBase3_1->mEventActivationMap.at(i); + EXPECT_EQ(100 * NS_PER_SEC, activationTimeBase3_1->ttl_ns); + EXPECT_EQ(0, activationTimeBase3_1->start_ns); + EXPECT_EQ(kNotActive, activationTimeBase3_1->state); + + i = 0; + for (; i < metricsManagerTimeBase3->mAllAtomMatchers.size(); i++) { + if (metricsManagerTimeBase3->mAllAtomMatchers[i]->getId() == + metric1ActivationTrigger2->atom_matcher_id()) { + break; + } + } + + const auto& activationTimeBase3_2 = metricProducerTimeBase3_1->mEventActivationMap.at(i); + EXPECT_EQ(200 * NS_PER_SEC, activationTimeBase3_2->ttl_ns); + EXPECT_EQ(0, activationTimeBase3_2->start_ns); + EXPECT_EQ(kNotActive, activationTimeBase3_2->state); + + EXPECT_TRUE(metricProducerTimeBase3_2->isActive()); + // }}}---------------------------------------------------------------------------------- + + // Load saved state from disk. + processor3->LoadActiveConfigsFromDisk(); + + // Metric 1 active: Activation 1 is active, Activation 2 is active + // Metric 2 is active. + // {{{--------------------------------------------------------------------------- + EXPECT_TRUE(metricProducerTimeBase3_1->isActive()); + EXPECT_EQ(timeBase3 + ttl1 - activationTimeBase3_1->ttl_ns, activationTimeBase3_1->start_ns); + EXPECT_EQ(kActive, activationTimeBase3_1->state); + EXPECT_EQ(timeBase3 + ttl2 - activationTimeBase3_2->ttl_ns, activationTimeBase3_2->start_ns); + EXPECT_EQ(kActive, activationTimeBase3_2->state); + + EXPECT_TRUE(metricProducerTimeBase3_2->isActive()); + // }}}------------------------------------------------------------------------------- + + // Trigger Activation 2 for Metric 1 again. + screenOnEvent = CreateScreenStateChangedEvent( + android::view::DISPLAY_STATE_ON, + timeBase3 + 100 * NS_PER_SEC + ); + processor3->OnLogEvent(screenOnEvent.get()); + + // Metric 1 active; Activation 1 is not active, Activation 2 is set to active + // Metric 2 is active. + // {{{--------------------------------------------------------------------------- + EXPECT_TRUE(metricProducerTimeBase3_1->isActive()); + EXPECT_EQ(kNotActive, activationTimeBase3_1->state); + EXPECT_EQ(timeBase3 + ttl2 - activationTimeBase3_2->ttl_ns, activationTimeBase3_2->start_ns); + EXPECT_EQ(kActive, activationTimeBase3_2->state); + + EXPECT_TRUE(metricProducerTimeBase3_2->isActive()); + // }}}--------------------------------------------------------------------------- + + // Simulate shutdown by saving state to disk. + shutDownTime = timeBase3 + 500 * NS_PER_SEC; + processor3->SaveActiveConfigsToDisk(shutDownTime); + EXPECT_TRUE(metricProducer1001->isActive()); + EXPECT_TRUE(metricProducer1002->isActive()); + ttl1 = timeBase3 + ttl1 - shutDownTime; + ttl2 = timeBase3 + metric1ActivationTrigger2->ttl_seconds() * NS_PER_SEC - shutDownTime; + + // Simulate device restarted state by creating new instance of StatsLogProcessor with the + // same config. + long timeBase4 = timeBase3 + 600 * NS_PER_SEC; + sp<StatsLogProcessor> processor4 = + CreateStatsLogProcessor(timeBase4, timeBase4, config1, cfgKey1); + + // Metric 1 is not active. + // Metric 2 is active. + // {{{--------------------------------------------------------------------------- + EXPECT_EQ(1, processor4->mMetricsManagers.size()); + it = processor4->mMetricsManagers.find(cfgKey1); + EXPECT_TRUE(it != processor4->mMetricsManagers.end()); + auto& metricsManagerTimeBase4 = it->second; + EXPECT_TRUE(metricsManagerTimeBase4->isActive()); + + metricIt = metricsManagerTimeBase4->mAllMetricProducers.begin(); + for (; metricIt != metricsManagerTimeBase4->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId1) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManagerTimeBase4->mAllMetricProducers.end()); + auto& metricProducerTimeBase4_1 = *metricIt; + EXPECT_FALSE(metricProducerTimeBase4_1->isActive()); + + metricIt = metricsManagerTimeBase4->mAllMetricProducers.begin(); + for (; metricIt != metricsManagerTimeBase4->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId2) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManagerTimeBase4->mAllMetricProducers.end()); + auto& metricProducerTimeBase4_2 = *metricIt; + EXPECT_TRUE(metricProducerTimeBase4_2->isActive()); + + i = 0; + for (; i < metricsManagerTimeBase4->mAllAtomMatchers.size(); i++) { + if (metricsManagerTimeBase4->mAllAtomMatchers[i]->getId() == + metric1ActivationTrigger1->atom_matcher_id()) { + break; + } + } + const auto& activationTimeBase4_1 = metricProducerTimeBase4_1->mEventActivationMap.at(i); + EXPECT_EQ(100 * NS_PER_SEC, activationTimeBase4_1->ttl_ns); + EXPECT_EQ(0, activationTimeBase4_1->start_ns); + EXPECT_EQ(kNotActive, activationTimeBase4_1->state); + + i = 0; + for (; i < metricsManagerTimeBase4->mAllAtomMatchers.size(); i++) { + if (metricsManagerTimeBase4->mAllAtomMatchers[i]->getId() == + metric1ActivationTrigger2->atom_matcher_id()) { + break; + } + } + + const auto& activationTimeBase4_2 = metricProducerTimeBase4_1->mEventActivationMap.at(i); + EXPECT_EQ(200 * NS_PER_SEC, activationTimeBase4_2->ttl_ns); + EXPECT_EQ(0, activationTimeBase4_2->start_ns); + EXPECT_EQ(kNotActive, activationTimeBase4_2->state); + + EXPECT_TRUE(metricProducerTimeBase4_2->isActive()); + // }}}---------------------------------------------------------------------------------- + + // Load saved state from disk. + processor4->LoadActiveConfigsFromDisk(); + + // Metric 1 active: Activation 1 is not active, Activation 2 is not active + // Metric 2 is active. + // {{{--------------------------------------------------------------------------- + EXPECT_FALSE(metricProducerTimeBase4_1->isActive()); + EXPECT_EQ(kNotActive, activationTimeBase4_1->state); + EXPECT_EQ(kNotActive, activationTimeBase4_2->state); + + EXPECT_TRUE(metricProducerTimeBase4_2->isActive()); + // }}}------------------------------------------------------------------------------- +} + +TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivationsDifferentActivationTypes) { + int uid = 1111; + + // Create config with 2 metrics: + // Metric 1: Activate on boot with 2 activations + // Metric 2: Always active + StatsdConfig config1; + config1.set_id(12341); + config1.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); + auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher(); + *config1.add_atom_matcher() = wakelockAcquireMatcher; + *config1.add_atom_matcher() = screenOnMatcher; + + long metricId1 = 1234561; + long metricId2 = 1234562; + + auto countMetric1 = config1.add_count_metric(); + countMetric1->set_id(metricId1); + countMetric1->set_what(wakelockAcquireMatcher.id()); + countMetric1->set_bucket(FIVE_MINUTES); + + auto countMetric2 = config1.add_count_metric(); + countMetric2->set_id(metricId2); + countMetric2->set_what(wakelockAcquireMatcher.id()); + countMetric2->set_bucket(FIVE_MINUTES); + + auto metric1Activation = config1.add_metric_activation(); + metric1Activation->set_metric_id(metricId1); + metric1Activation->set_activation_type(ACTIVATE_ON_BOOT); + auto metric1ActivationTrigger1 = metric1Activation->add_event_activation(); + metric1ActivationTrigger1->set_atom_matcher_id(wakelockAcquireMatcher.id()); + metric1ActivationTrigger1->set_ttl_seconds(100); + auto metric1ActivationTrigger2 = metric1Activation->add_event_activation(); + metric1ActivationTrigger2->set_atom_matcher_id(screenOnMatcher.id()); + metric1ActivationTrigger2->set_ttl_seconds(200); + metric1ActivationTrigger2->set_activation_type(ACTIVATE_IMMEDIATELY); + + ConfigKey cfgKey1(uid, 12341); + long timeBase1 = 1; + sp<StatsLogProcessor> processor = + CreateStatsLogProcessor(timeBase1, timeBase1, config1, cfgKey1); + + // Metric 1 is not active. + // Metric 2 is active. + // {{{--------------------------------------------------------------------------- + EXPECT_EQ(1, processor->mMetricsManagers.size()); + auto it = processor->mMetricsManagers.find(cfgKey1); + EXPECT_TRUE(it != processor->mMetricsManagers.end()); + auto& metricsManager1 = it->second; + EXPECT_TRUE(metricsManager1->isActive()); + + auto metricIt = metricsManager1->mAllMetricProducers.begin(); + for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId1) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end()); + auto& metricProducer1 = *metricIt; + EXPECT_FALSE(metricProducer1->isActive()); + + metricIt = metricsManager1->mAllMetricProducers.begin(); + for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId2) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end()); + auto& metricProducer2 = *metricIt; + EXPECT_TRUE(metricProducer2->isActive()); + + int i = 0; + for (; i < metricsManager1->mAllAtomMatchers.size(); i++) { + if (metricsManager1->mAllAtomMatchers[i]->getId() == + metric1ActivationTrigger1->atom_matcher_id()) { + break; + } + } + const auto& activation1 = metricProducer1->mEventActivationMap.at(i); + EXPECT_EQ(100 * NS_PER_SEC, activation1->ttl_ns); + EXPECT_EQ(0, activation1->start_ns); + EXPECT_EQ(kNotActive, activation1->state); + EXPECT_EQ(ACTIVATE_ON_BOOT, activation1->activationType); + + i = 0; + for (; i < metricsManager1->mAllAtomMatchers.size(); i++) { + if (metricsManager1->mAllAtomMatchers[i]->getId() == + metric1ActivationTrigger2->atom_matcher_id()) { + break; + } + } + const auto& activation2 = metricProducer1->mEventActivationMap.at(i); + EXPECT_EQ(200 * NS_PER_SEC, activation2->ttl_ns); + EXPECT_EQ(0, activation2->start_ns); + EXPECT_EQ(kNotActive, activation2->state); + EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation2->activationType); + // }}}------------------------------------------------------------------------------ + + // Trigger Activation 1 for Metric 1 + std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")}; + auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 100 + timeBase1); + processor->OnLogEvent(event.get()); + + // Metric 1 is not active; Activation 1 set to kActiveOnBoot + // Metric 2 is active. + // {{{--------------------------------------------------------------------------- + EXPECT_FALSE(metricProducer1->isActive()); + EXPECT_EQ(0, activation1->start_ns); + EXPECT_EQ(kActiveOnBoot, activation1->state); + EXPECT_EQ(0, activation2->start_ns); + EXPECT_EQ(kNotActive, activation2->state); + + EXPECT_TRUE(metricProducer2->isActive()); + // }}}----------------------------------------------------------------------------- + + // Simulate shutdown by saving state to disk + int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC; + processor->SaveActiveConfigsToDisk(shutDownTime); + EXPECT_FALSE(metricProducer1->isActive()); + int64_t ttl1 = metric1ActivationTrigger1->ttl_seconds() * NS_PER_SEC; + + // Simulate device restarted state by creating new instance of StatsLogProcessor with the + // same config. + long timeBase2 = 1000; + sp<StatsLogProcessor> processor2 = + CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1); + + // Metric 1 is not active. + // Metric 2 is active. + // {{{--------------------------------------------------------------------------- + EXPECT_EQ(1, processor2->mMetricsManagers.size()); + it = processor2->mMetricsManagers.find(cfgKey1); + EXPECT_TRUE(it != processor2->mMetricsManagers.end()); + auto& metricsManager1001 = it->second; + EXPECT_TRUE(metricsManager1001->isActive()); + + metricIt = metricsManager1001->mAllMetricProducers.begin(); + for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId1) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end()); + auto& metricProducer1001 = *metricIt; + EXPECT_FALSE(metricProducer1001->isActive()); + + metricIt = metricsManager1001->mAllMetricProducers.begin(); + for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId2) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end()); + auto& metricProducer1002 = *metricIt; + EXPECT_TRUE(metricProducer1002->isActive()); + + i = 0; + for (; i < metricsManager1001->mAllAtomMatchers.size(); i++) { + if (metricsManager1001->mAllAtomMatchers[i]->getId() == + metric1ActivationTrigger1->atom_matcher_id()) { + break; + } + } + const auto& activation1001_1 = metricProducer1001->mEventActivationMap.at(i); + EXPECT_EQ(100 * NS_PER_SEC, activation1001_1->ttl_ns); + EXPECT_EQ(0, activation1001_1->start_ns); + EXPECT_EQ(kNotActive, activation1001_1->state); + EXPECT_EQ(ACTIVATE_ON_BOOT, activation1001_1->activationType); + + i = 0; + for (; i < metricsManager1001->mAllAtomMatchers.size(); i++) { + if (metricsManager1001->mAllAtomMatchers[i]->getId() == + metric1ActivationTrigger2->atom_matcher_id()) { + break; + } + } + + const auto& activation1001_2 = metricProducer1001->mEventActivationMap.at(i); + EXPECT_EQ(200 * NS_PER_SEC, activation1001_2->ttl_ns); + EXPECT_EQ(0, activation1001_2->start_ns); + EXPECT_EQ(kNotActive, activation1001_2->state); + EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1001_2->activationType); + // }}}----------------------------------------------------------------------------------- + + // Load saved state from disk. + processor2->LoadActiveConfigsFromDisk(); + + // Metric 1 active; Activation 1 is active, Activation 2 is not active + // Metric 2 is active. + // {{{--------------------------------------------------------------------------- + EXPECT_TRUE(metricProducer1001->isActive()); + EXPECT_EQ(timeBase2 + ttl1 - activation1001_1->ttl_ns, activation1001_1->start_ns); + EXPECT_EQ(kActive, activation1001_1->state); + EXPECT_EQ(0, activation1001_2->start_ns); + EXPECT_EQ(kNotActive, activation1001_2->state); + + EXPECT_TRUE(metricProducer1002->isActive()); + // }}}-------------------------------------------------------------------------------- + + // Trigger Activation 2 for Metric 1. + auto screenOnEvent = CreateScreenStateChangedEvent( + android::view::DISPLAY_STATE_ON, + timeBase2 + 200 + ); + processor2->OnLogEvent(screenOnEvent.get()); + + // Metric 1 active; Activation 1 is active, Activation 2 is active + // Metric 2 is active. + // {{{--------------------------------------------------------------------------- + EXPECT_TRUE(metricProducer1001->isActive()); + EXPECT_EQ(timeBase2 + ttl1 - activation1001_1->ttl_ns, activation1001_1->start_ns); + EXPECT_EQ(kActive, activation1001_1->state); + EXPECT_EQ(screenOnEvent->GetElapsedTimestampNs(), activation1001_2->start_ns); + EXPECT_EQ(kActive, activation1001_2->state); + + EXPECT_TRUE(metricProducer1002->isActive()); + // }}}--------------------------------------------------------------------------- + + // Simulate shutdown by saving state to disk + shutDownTime = timeBase2 + 50 * NS_PER_SEC; + processor2->SaveActiveConfigsToDisk(shutDownTime); + EXPECT_TRUE(metricProducer1001->isActive()); + EXPECT_TRUE(metricProducer1002->isActive()); + ttl1 = timeBase2 + metric1ActivationTrigger1->ttl_seconds() * NS_PER_SEC - shutDownTime; + int64_t ttl2 = screenOnEvent->GetElapsedTimestampNs() + + metric1ActivationTrigger2->ttl_seconds() * NS_PER_SEC - shutDownTime; + + // Simulate device restarted state by creating new instance of StatsLogProcessor with the + // same config. + long timeBase3 = timeBase2 + 120 * NS_PER_SEC; + sp<StatsLogProcessor> processor3 = + CreateStatsLogProcessor(timeBase3, timeBase3, config1, cfgKey1); + + // Metric 1 is not active. + // Metric 2 is active. + // {{{--------------------------------------------------------------------------- + EXPECT_EQ(1, processor3->mMetricsManagers.size()); + it = processor3->mMetricsManagers.find(cfgKey1); + EXPECT_TRUE(it != processor3->mMetricsManagers.end()); + auto& metricsManagerTimeBase3 = it->second; + EXPECT_TRUE(metricsManagerTimeBase3->isActive()); + + metricIt = metricsManagerTimeBase3->mAllMetricProducers.begin(); + for (; metricIt != metricsManagerTimeBase3->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId1) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManagerTimeBase3->mAllMetricProducers.end()); + auto& metricProducerTimeBase3_1 = *metricIt; + EXPECT_FALSE(metricProducerTimeBase3_1->isActive()); + + metricIt = metricsManagerTimeBase3->mAllMetricProducers.begin(); + for (; metricIt != metricsManagerTimeBase3->mAllMetricProducers.end(); metricIt++) { + if ((*metricIt)->getMetricId() == metricId2) { + break; + } + } + EXPECT_TRUE(metricIt != metricsManagerTimeBase3->mAllMetricProducers.end()); + auto& metricProducerTimeBase3_2 = *metricIt; + EXPECT_TRUE(metricProducerTimeBase3_2->isActive()); + + i = 0; + for (; i < metricsManagerTimeBase3->mAllAtomMatchers.size(); i++) { + if (metricsManagerTimeBase3->mAllAtomMatchers[i]->getId() == + metric1ActivationTrigger1->atom_matcher_id()) { + break; + } + } + const auto& activationTimeBase3_1 = metricProducerTimeBase3_1->mEventActivationMap.at(i); + EXPECT_EQ(100 * NS_PER_SEC, activationTimeBase3_1->ttl_ns); + EXPECT_EQ(0, activationTimeBase3_1->start_ns); + EXPECT_EQ(kNotActive, activationTimeBase3_1->state); + EXPECT_EQ(ACTIVATE_ON_BOOT, activationTimeBase3_1->activationType); + + i = 0; + for (; i < metricsManagerTimeBase3->mAllAtomMatchers.size(); i++) { + if (metricsManagerTimeBase3->mAllAtomMatchers[i]->getId() == + metric1ActivationTrigger2->atom_matcher_id()) { + break; + } + } + + const auto& activationTimeBase3_2 = metricProducerTimeBase3_1->mEventActivationMap.at(i); + EXPECT_EQ(200 * NS_PER_SEC, activationTimeBase3_2->ttl_ns); + EXPECT_EQ(0, activationTimeBase3_2->start_ns); + EXPECT_EQ(kNotActive, activationTimeBase3_2->state); + EXPECT_EQ(ACTIVATE_IMMEDIATELY, activationTimeBase3_2->activationType); + // }}}---------------------------------------------------------------------------------- + + // Load saved state from disk. + processor3->LoadActiveConfigsFromDisk(); + + // Metric 1 active: Activation 1 is active, Activation 2 is active + // Metric 2 is active. + // {{{--------------------------------------------------------------------------- + EXPECT_TRUE(metricProducerTimeBase3_1->isActive()); + EXPECT_EQ(timeBase3 + ttl1 - activationTimeBase3_1->ttl_ns, activationTimeBase3_1->start_ns); + EXPECT_EQ(kActive, activationTimeBase3_1->state); + EXPECT_EQ(timeBase3 + ttl2 - activationTimeBase3_2->ttl_ns, activationTimeBase3_2->start_ns); + EXPECT_EQ(kActive, activationTimeBase3_2->state); + + EXPECT_TRUE(metricProducerTimeBase3_2->isActive()); + // }}}------------------------------------------------------------------------------- + + + // Trigger Activation 2 for Metric 1 again. + screenOnEvent = CreateScreenStateChangedEvent( + android::view::DISPLAY_STATE_ON, + timeBase3 + 100 * NS_PER_SEC + ); + processor3->OnLogEvent(screenOnEvent.get()); + + // Metric 1 active; Activation 1 is not active, Activation 2 is set to active + // Metric 2 is active. + // {{{--------------------------------------------------------------------------- + EXPECT_TRUE(metricProducerTimeBase3_1->isActive()); + EXPECT_EQ(kNotActive, activationTimeBase3_1->state); + EXPECT_EQ(screenOnEvent->GetElapsedTimestampNs(), activationTimeBase3_2->start_ns); + EXPECT_EQ(kActive, activationTimeBase3_2->state); + + EXPECT_TRUE(metricProducerTimeBase3_2->isActive()); + // }}}--------------------------------------------------------------------------- } #else diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp index f01ad0609295..6ec0a114b3ed 100644 --- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp +++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp @@ -418,7 +418,7 @@ TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsWithActivation) { const int64_t ttlNs = 2 * bucketSizeNs; // Two buckets. auto metric_activation = config.add_metric_activation(); metric_activation->set_metric_id(metricId); - metric_activation->set_activation_type(MetricActivation::ACTIVATE_IMMEDIATELY); + metric_activation->set_activation_type(ACTIVATE_IMMEDIATELY); auto event_activation = metric_activation->add_event_activation(); event_activation->set_atom_matcher_id(batterySaverStartMatcher.id()); event_activation->set_ttl_seconds(ttlNs / 1000000000); diff --git a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp index bf52bb04e71d..d99d281afc68 100644 --- a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp @@ -245,10 +245,10 @@ TEST(MetricActivationE2eTest, TestCountMetric) { EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end()); EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end()); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[0]->activation_ns, 0); + EXPECT_EQ(eventActivationMap[0]->start_ns, 0); EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->activation_ns, 0); + EXPECT_EQ(eventActivationMap[2]->start_ns, 0); EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); std::unique_ptr<LogEvent> event; @@ -268,10 +268,10 @@ TEST(MetricActivationE2eTest, TestCountMetric) { EXPECT_EQ(activeConfigsBroadcast.size(), 1); EXPECT_EQ(activeConfigsBroadcast[0], cfgId); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->activation_ns, 0); + EXPECT_EQ(eventActivationMap[2]->start_ns, 0); EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); // First processed event. @@ -285,10 +285,10 @@ TEST(MetricActivationE2eTest, TestCountMetric) { EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); // 2nd processed event. @@ -298,10 +298,10 @@ TEST(MetricActivationE2eTest, TestCountMetric) { EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); // No new broadcast since the config should still be active. EXPECT_EQ(broadcastCount, 1); @@ -319,10 +319,10 @@ TEST(MetricActivationE2eTest, TestCountMetric) { EXPECT_EQ(broadcastCount, 2); EXPECT_EQ(activeConfigsBroadcast.size(), 0); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); // Re-activate metric via screen on. @@ -335,10 +335,10 @@ TEST(MetricActivationE2eTest, TestCountMetric) { EXPECT_EQ(activeConfigsBroadcast.size(), 1); EXPECT_EQ(activeConfigsBroadcast[0], cfgId); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); // 4th processed event. @@ -460,10 +460,10 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) { EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end()); EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end()); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[0]->activation_ns, 0); + EXPECT_EQ(eventActivationMap[0]->start_ns, 0); EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->activation_ns, 0); + EXPECT_EQ(eventActivationMap[2]->start_ns, 0); EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); EXPECT_EQ(eventDeactivationMap.size(), 1u); EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end()); @@ -486,10 +486,10 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) { EXPECT_EQ(activeConfigsBroadcast.size(), 1); EXPECT_EQ(activeConfigsBroadcast[0], cfgId); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->activation_ns, 0); + EXPECT_EQ(eventActivationMap[2]->start_ns, 0); EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]); @@ -504,10 +504,10 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) { EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]); @@ -518,10 +518,10 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) { EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]); // No new broadcast since the config should still be active. @@ -540,10 +540,10 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) { EXPECT_EQ(broadcastCount, 2); EXPECT_EQ(activeConfigsBroadcast.size(), 0); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]); @@ -557,10 +557,10 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) { EXPECT_EQ(activeConfigsBroadcast.size(), 1); EXPECT_EQ(activeConfigsBroadcast[0], cfgId); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]); @@ -577,10 +577,10 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) { EXPECT_EQ(activeConfigsBroadcast.size(), 1); EXPECT_EQ(activeConfigsBroadcast[0], cfgId); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]); @@ -597,10 +597,10 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) { EXPECT_EQ(activeConfigsBroadcast.size(), 1); EXPECT_EQ(activeConfigsBroadcast[0], cfgId); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]); @@ -613,10 +613,10 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) { EXPECT_EQ(broadcastCount, 4); EXPECT_EQ(activeConfigsBroadcast.size(), 0); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]); @@ -632,10 +632,10 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) { EXPECT_EQ(activeConfigsBroadcast.size(), 1); EXPECT_EQ(activeConfigsBroadcast[0], cfgId); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]); @@ -647,10 +647,10 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) { EXPECT_EQ(broadcastCount, 6); EXPECT_EQ(activeConfigsBroadcast.size(), 0); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]); @@ -782,10 +782,10 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) { EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end()); EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end()); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[0]->activation_ns, 0); + EXPECT_EQ(eventActivationMap[0]->start_ns, 0); EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->activation_ns, 0); + EXPECT_EQ(eventActivationMap[2]->start_ns, 0); EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); EXPECT_EQ(eventDeactivationMap.size(), 2u); EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end()); @@ -810,10 +810,10 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) { EXPECT_EQ(activeConfigsBroadcast.size(), 1); EXPECT_EQ(activeConfigsBroadcast[0], cfgId); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->activation_ns, 0); + EXPECT_EQ(eventActivationMap[2]->start_ns, 0); EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]); EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]); @@ -829,10 +829,10 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) { EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]); EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]); @@ -844,10 +844,10 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) { EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]); EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]); @@ -867,10 +867,10 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) { EXPECT_EQ(broadcastCount, 2); EXPECT_EQ(activeConfigsBroadcast.size(), 0); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]); EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]); @@ -885,10 +885,10 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) { EXPECT_EQ(activeConfigsBroadcast.size(), 1); EXPECT_EQ(activeConfigsBroadcast[0], cfgId); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]); EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]); @@ -906,10 +906,10 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) { EXPECT_EQ(activeConfigsBroadcast.size(), 1); EXPECT_EQ(activeConfigsBroadcast[0], cfgId); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]); EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]); @@ -927,10 +927,10 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) { EXPECT_EQ(broadcastCount, 4); EXPECT_EQ(activeConfigsBroadcast.size(), 0); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]); EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]); @@ -943,10 +943,10 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) { EXPECT_EQ(broadcastCount, 4); EXPECT_EQ(activeConfigsBroadcast.size(), 0); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]); EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]); @@ -963,10 +963,10 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) { EXPECT_EQ(activeConfigsBroadcast.size(), 1); EXPECT_EQ(activeConfigsBroadcast[0], cfgId); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]); EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]); @@ -979,10 +979,10 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) { EXPECT_EQ(broadcastCount, 6); EXPECT_EQ(activeConfigsBroadcast.size(), 0); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]); EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]); @@ -1119,10 +1119,10 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end()); EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end()); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[0]->activation_ns, 0); + EXPECT_EQ(eventActivationMap[0]->start_ns, 0); EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->activation_ns, 0); + EXPECT_EQ(eventActivationMap[2]->start_ns, 0); EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); EXPECT_EQ(eventDeactivationMap.size(), 2u); EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end()); @@ -1134,10 +1134,10 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { EXPECT_TRUE(eventActivationMap2.find(0) != eventActivationMap2.end()); EXPECT_TRUE(eventActivationMap2.find(2) != eventActivationMap2.end()); EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap2[0]->activation_ns, 0); + EXPECT_EQ(eventActivationMap2[0]->start_ns, 0); EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap2[2]->activation_ns, 0); + EXPECT_EQ(eventActivationMap2[2]->start_ns, 0); EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); EXPECT_EQ(eventDeactivationMap2.size(), 2u); EXPECT_TRUE(eventDeactivationMap2.find(3) != eventDeactivationMap2.end()); @@ -1165,19 +1165,19 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { EXPECT_EQ(activeConfigsBroadcast[0], cfgId); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->activation_ns, 0); + EXPECT_EQ(eventActivationMap[2]->start_ns, 0); EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]); EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]); EXPECT_TRUE(metricProducer2->mIsActive); EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap2[0]->activation_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10); EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap2[2]->activation_ns, 0); + EXPECT_EQ(eventActivationMap2[2]->start_ns, 0); EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); EXPECT_EQ(eventDeactivationMap2[3], eventActivationMap2[0]); EXPECT_EQ(eventDeactivationMap2[4], eventActivationMap2[2]); @@ -1195,19 +1195,19 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]); EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]); EXPECT_TRUE(metricProducer2->mIsActive); EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap2[0]->activation_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10); EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap2[2]->activation_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + 20); EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); EXPECT_EQ(eventDeactivationMap2[3], eventActivationMap2[0]); EXPECT_EQ(eventDeactivationMap2[4], eventActivationMap2[2]); @@ -1221,19 +1221,19 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]); EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]); EXPECT_TRUE(metricProducer2->mIsActive); EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap2[0]->activation_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10); EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap2[2]->activation_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + 20); EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); EXPECT_EQ(eventDeactivationMap2[3], eventActivationMap2[0]); EXPECT_EQ(eventDeactivationMap2[4], eventActivationMap2[2]); @@ -1257,19 +1257,19 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { EXPECT_EQ(activeConfigsBroadcast.size(), 0); EXPECT_FALSE(metricProducer->mIsActive); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 20); EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]); EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]); EXPECT_FALSE(metricProducer2->mIsActive); EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap2[0]->activation_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10); EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap2[2]->activation_ns, bucketStartTimeNs + 20); + EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + 20); EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); EXPECT_EQ(eventDeactivationMap2[3], eventActivationMap2[0]); EXPECT_EQ(eventDeactivationMap2[4], eventActivationMap2[2]); @@ -1284,19 +1284,19 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { EXPECT_EQ(activeConfigsBroadcast[0], cfgId); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10); EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]); EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]); EXPECT_TRUE(metricProducer2->mIsActive); EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap2[0]->activation_ns, bucketStartTimeNs + 10); + EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + 10); EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap2[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); EXPECT_EQ(eventDeactivationMap2[3], eventActivationMap2[0]); EXPECT_EQ(eventDeactivationMap2[4], eventActivationMap2[2]); @@ -1316,19 +1316,19 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { EXPECT_EQ(activeConfigsBroadcast[0], cfgId); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]); EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]); EXPECT_TRUE(metricProducer2->mIsActive); EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap2[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap2[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); EXPECT_EQ(eventDeactivationMap2[3], eventActivationMap2[0]); EXPECT_EQ(eventDeactivationMap2[4], eventActivationMap2[2]); @@ -1348,19 +1348,19 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { EXPECT_EQ(activeConfigsBroadcast.size(), 0); EXPECT_FALSE(metricProducer->mIsActive); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]); EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]); EXPECT_FALSE(metricProducer2->mIsActive); EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap2[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap2[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); EXPECT_EQ(eventDeactivationMap2[3], eventActivationMap2[0]); EXPECT_EQ(eventDeactivationMap2[4], eventActivationMap2[2]); @@ -1375,19 +1375,19 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { EXPECT_EQ(activeConfigsBroadcast.size(), 0); EXPECT_FALSE(metricProducer->mIsActive); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]); EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]); EXPECT_FALSE(metricProducer2->mIsActive); EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap2[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); + EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15); EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap2[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); EXPECT_EQ(eventDeactivationMap2[3], eventActivationMap2[0]); EXPECT_EQ(eventDeactivationMap2[4], eventActivationMap2[2]); @@ -1406,19 +1406,19 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { EXPECT_EQ(activeConfigsBroadcast[0], cfgId); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]); EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]); EXPECT_TRUE(metricProducer2->mIsActive); EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kActive); - EXPECT_EQ(eventActivationMap2[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); + EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap2[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); EXPECT_EQ(eventDeactivationMap2[3], eventActivationMap2[0]); EXPECT_EQ(eventDeactivationMap2[4], eventActivationMap2[2]); @@ -1431,19 +1431,19 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { EXPECT_EQ(activeConfigsBroadcast.size(), 0); EXPECT_FALSE(metricProducer->mIsActive); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); + EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC); EXPECT_EQ(eventDeactivationMap[3], eventActivationMap[0]); EXPECT_EQ(eventDeactivationMap[4], eventActivationMap[2]); EXPECT_FALSE(metricProducer2->mIsActive); EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap2[0]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); + EXPECT_EQ(eventActivationMap2[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15); EXPECT_EQ(eventActivationMap2[0]->ttl_ns, 60 * 6 * NS_PER_SEC); EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive); - EXPECT_EQ(eventActivationMap2[2]->activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); + EXPECT_EQ(eventActivationMap2[2]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC); EXPECT_EQ(eventDeactivationMap2[3], eventActivationMap2[0]); EXPECT_EQ(eventDeactivationMap2[4], eventActivationMap2[2]); diff --git a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp index e967eb34ab2e..ff6af38bd351 100644 --- a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp @@ -279,7 +279,7 @@ TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation) { const int64_t ttlNs = 2 * bucketSizeNs; // Two buckets. auto metric_activation = config.add_metric_activation(); metric_activation->set_metric_id(metricId); - metric_activation->set_activation_type(MetricActivation::ACTIVATE_IMMEDIATELY); + metric_activation->set_activation_type(ACTIVATE_IMMEDIATELY); auto event_activation = metric_activation->add_event_activation(); event_activation->set_atom_matcher_id(batterySaverStartMatcher.id()); event_activation->set_ttl_seconds(ttlNs / 1000000000); diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java index fc7b778b7a86..3e705fdf8a12 100644 --- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java +++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java @@ -57,6 +57,7 @@ public class TestDrive { "AID_BLUETOOTH", "AID_LMKD", "com.android.managedprovisioning", + "AID_MEDIA", "AID_NETWORK_STACK" }; private static final Logger LOGGER = Logger.getLogger(TestDrive.class.getName()); diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java index 1b7fbfe0e32c..052445095afe 100644 --- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java +++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java @@ -95,7 +95,8 @@ public final class Telecom extends BaseCommand { + "telecom set-phone-account-disabled: Disables the given phone account, if it \n" + " has already been registered with telecom.\n" + "\n" - + "telecom set-default-dialer: Sets the default dialer to the given component. \n" + + "telecom set-default-dialer: Sets the override default dialer to the given " + + "component; this will override whatever the dialer role is set to. \n" + "\n" + "telecom get-default-dialer: Displays the current default dialer. \n" + "\n" @@ -254,13 +255,8 @@ public final class Telecom extends BaseCommand { private void runSetDefaultDialer() throws RemoteException { final String packageName = nextArgRequired(); - final boolean success = mTelecomService.setDefaultDialer(packageName); - if (success) { - System.out.println("Success - " + packageName + " set as default dialer."); - } else { - System.out.println("Error - " + packageName + " is not an installed Dialer app, \n" - + " or is already the default dialer."); - } + mTelecomService.setTestDefaultDialer(packageName); + System.out.println("Success - " + packageName + " set as override default dialer."); } private void runGetDefaultDialer() throws RemoteException { diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 43531eddf638..2914f6c5abf2 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -126,7 +126,6 @@ import android.view.autofill.AutofillManager; import android.view.autofill.AutofillManager.AutofillClient; import android.view.autofill.AutofillPopupWindow; import android.view.autofill.IAutofillWindowPresenter; -import android.view.contentcapture.ContentCaptureContext; import android.view.contentcapture.ContentCaptureManager; import android.view.contentcapture.ContentCaptureManager.ContentCaptureClient; import android.widget.AdapterView; @@ -840,7 +839,7 @@ public class Activity extends ContextThemeWrapper /** The autofill manager. Always access via {@link #getAutofillManager()}. */ @Nullable private AutofillManager mAutofillManager; - /** The content capture manager. Always access via {@link #getContentCaptureManager()}. */ + /** The content capture manager. Access via {@link #getContentCaptureManager()}. */ @Nullable private ContentCaptureManager mContentCaptureManager; private final ArrayList<Application.ActivityLifecycleCallbacks> mActivityLifecycleCallbacks = @@ -1092,12 +1091,11 @@ public class Activity extends ContextThemeWrapper case CONTENT_CAPTURE_START: //TODO(b/111276913): decide whether the InteractionSessionId should be // saved / restored in the activity bundle - probably not - int flags = 0; - if ((getWindow().getAttributes().flags - & WindowManager.LayoutParams.FLAG_SECURE) != 0) { - flags |= ContentCaptureContext.FLAG_DISABLED_BY_FLAG_SECURE; + final Window window = getWindow(); + if (window != null) { + cm.updateWindowAttributes(window.getAttributes()); } - cm.onActivityCreated(mToken, getComponentName(), flags); + cm.onActivityCreated(mToken, getComponentName()); break; case CONTENT_CAPTURE_RESUME: cm.onActivityResumed(); @@ -2349,7 +2347,7 @@ public class Activity extends ContextThemeWrapper * * @param cancellationSignal A signal to cancel the operation in progress. * @param callback The callback to send the action list. The actions list cannot - * contain <code>null</code> elements. + * contain <code>null</code> elements. You can call this on any thread. */ public void onGetDirectActions(@NonNull CancellationSignal cancellationSignal, @NonNull Consumer<List<DirectAction>> callback) { @@ -2360,10 +2358,13 @@ public class Activity extends ContextThemeWrapper * This is called to perform an action previously defined by the app. * Apps also have access to {@link #getVoiceInteractor()} to follow up on the action. * - * @param actionId The ID for the action - * @param arguments Any additional arguments provided by the caller + * @param actionId The ID for the action you previously reported via + * {@link #onGetDirectActions(CancellationSignal, Consumer)}. + * @param arguments Any additional arguments provided by the caller that are + * specific to the given action. * @param cancellationSignal A signal to cancel the operation in progress. - * @param resultListener The callback to provide the result back to the caller + * @param resultListener The callback to provide the result back to the caller. + * You can call this on any thread. The result bundle is action specific. * * @see #onGetDirectActions(CancellationSignal, Consumer) */ @@ -3782,6 +3783,9 @@ public class Activity extends ContextThemeWrapper View decor = mDecor; if (decor != null && decor.getParent() != null) { getWindowManager().updateViewLayout(decor, params); + if (mContentCaptureManager != null) { + mContentCaptureManager.updateWindowAttributes(params); + } } } } diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java index fc6fffabc917..b64b2dcc4f61 100644 --- a/core/java/android/app/ActivityView.java +++ b/core/java/android/app/ActivityView.java @@ -317,7 +317,7 @@ public class ActivityView extends ViewGroup { * regions and avoid focus switches by touches on this view. */ public void onLocationChanged() { - updateTapExcludeRegion(); + updateLocationAndTapExcludeRegion(); } @Override @@ -329,39 +329,52 @@ public class ActivityView extends ViewGroup { public boolean gatherTransparentRegion(Region region) { // The tap exclude region may be affected by any view on top of it, so we detect the // possible change by monitoring this function. - updateTapExcludeRegion(); + updateLocationAndTapExcludeRegion(); return super.gatherTransparentRegion(region); } - /** Compute and send current tap exclude region to WM for this view. */ - private void updateTapExcludeRegion() { - if (!isAttachedToWindow()) { - return; - } - if (!canReceivePointerEvents()) { - cleanTapExcludeRegion(); + /** + * Sends current location in window and tap exclude region to WM for this view. + */ + private void updateLocationAndTapExcludeRegion() { + if (mVirtualDisplay == null || !isAttachedToWindow()) { return; } try { + int x = mLocationInWindow[0]; + int y = mLocationInWindow[1]; getLocationInWindow(mLocationInWindow); - final int x = mLocationInWindow[0]; - final int y = mLocationInWindow[1]; - mTapExcludeRegion.set(x, y, x + getWidth(), y + getHeight()); - - // There might be views on top of us. We need to subtract those areas from the tap - // exclude region. - final ViewParent parent = getParent(); - if (parent instanceof ViewGroup) { - ((ViewGroup) parent).subtractObscuredTouchableRegion(mTapExcludeRegion, this); + if (x != mLocationInWindow[0] || y != mLocationInWindow[1]) { + x = mLocationInWindow[0]; + y = mLocationInWindow[1]; + WindowManagerGlobal.getWindowSession().updateDisplayContentLocation( + getWindow(), x, y, mVirtualDisplay.getDisplay().getDisplayId()); } - - WindowManagerGlobal.getWindowSession().updateTapExcludeRegion(getWindow(), hashCode(), - mTapExcludeRegion); + updateTapExcludeRegion(x, y); } catch (RemoteException e) { e.rethrowAsRuntimeException(); } } + /** Computes and sends current tap exclude region to WM for this view. */ + private void updateTapExcludeRegion(int x, int y) throws RemoteException { + if (!canReceivePointerEvents()) { + cleanTapExcludeRegion(); + return; + } + mTapExcludeRegion.set(x, y, x + getWidth(), y + getHeight()); + + // There might be views on top of us. We need to subtract those areas from the tap + // exclude region. + final ViewParent parent = getParent(); + if (parent != null) { + parent.subtractObscuredTouchableRegion(mTapExcludeRegion, this); + } + + WindowManagerGlobal.getWindowSession().updateTapExcludeRegion(getWindow(), hashCode(), + mTapExcludeRegion); + } + private class SurfaceCallback implements SurfaceHolder.Callback { @Override public void surfaceCreated(SurfaceHolder surfaceHolder) { @@ -379,7 +392,7 @@ public class ActivityView extends ViewGroup { mVirtualDisplay.setDisplayState(true); } - updateTapExcludeRegion(); + updateLocationAndTapExcludeRegion(); } @Override @@ -387,7 +400,7 @@ public class ActivityView extends ViewGroup { if (mVirtualDisplay != null) { mVirtualDisplay.resize(width, height, getBaseDisplayDensity()); } - updateTapExcludeRegion(); + updateLocationAndTapExcludeRegion(); } @Override @@ -471,7 +484,8 @@ public class ActivityView extends ViewGroup { try { // TODO: Find a way to consolidate these calls to the server. - wm.reparentDisplayContent(displayId, mRootSurfaceControl); + WindowManagerGlobal.getWindowSession().reparentDisplayContent( + getWindow(), mRootSurfaceControl, displayId); wm.dontOverrideDisplayInfo(displayId); if (mSingleTaskInstance) { mActivityTaskManager.setDisplayToSingleTaskInstance(displayId); diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 82a34ceea8b9..1ad0cfd6e13f 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -2764,6 +2764,9 @@ public class ApplicationPackageManager extends PackageManager { if (itemInfo.packageName != null) { dr = getDrawable(itemInfo.packageName, itemInfo.icon, appInfo); } + if (dr == null && itemInfo != appInfo) { + dr = loadUnbadgedItemIcon(appInfo, appInfo); + } if (dr == null) { dr = itemInfo.loadDefaultIcon(this); } diff --git a/core/java/android/app/DirectAction.java b/core/java/android/app/DirectAction.java index d191f4b67364..ef3627b26a5d 100644 --- a/core/java/android/app/DirectAction.java +++ b/core/java/android/app/DirectAction.java @@ -22,12 +22,19 @@ import android.os.Bundle; import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; +import android.view.accessibility.AccessibilityNodeInfo; import com.android.internal.util.Preconditions; +import java.util.Objects; + /** * Represents a abstract action that can be perform on this app. This are requested from - * outside the app's UI (eg by SystemUI or assistant). + * outside the app's UI (eg by SystemUI or assistant). The semantics of these actions are + * not specified by the OS. This allows open-ended and scalable approach for defining how + * an app interacts with components that expose alternative interaction models to the user + * such as the assistant, SystemUI, etc. You can use {@link #equals(Object)} to compare + * instances of this class. */ public final class DirectAction implements Parcelable { @@ -91,7 +98,7 @@ public final class DirectAction implements Parcelable { } /** - * Returns the ID for this action. + * @return the ID for this action. */ @NonNull public String getId() { @@ -99,7 +106,7 @@ public final class DirectAction implements Parcelable { } /** - * Returns any extras associated with this action. + * @return any extras associated with this action. */ @Nullable public Bundle getExtras() { @@ -107,7 +114,7 @@ public final class DirectAction implements Parcelable { } /** - * Returns the LocusId for the current state for the app + * @return the LocusId for the current state for the app */ @Nullable public LocusId getLocusId() { @@ -120,6 +127,28 @@ public final class DirectAction implements Parcelable { } @Override + public int hashCode() { + return mID.hashCode(); + } + + @Override + public boolean equals(Object other) { + if (other == null) { + return false; + } + + if (other == this) { + return true; + } + + if (getClass() != other.getClass()) { + return false; + } + + return mID.equals(((DirectAction) other).mID); + } + + @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mTaskId); dest.writeStrongBinder(mActivityId); @@ -139,7 +168,8 @@ public final class DirectAction implements Parcelable { /** * Creates a new instance. * - * @param id The mandatory action id. + * @param id The mandatory action id which must be unique in the + * current application state. */ public Builder(@NonNull String id) { Preconditions.checkNotNull(id); @@ -147,7 +177,9 @@ public final class DirectAction implements Parcelable { } /** - * Sets the optional action extras. + * Sets the optional action extras. These extras are action specific + * and their semantics are open-ended potentially representing how + * the action is visualized, interpreted, what its arguments are, etc. * * @param extras The extras. * @return This builder. @@ -158,7 +190,9 @@ public final class DirectAction implements Parcelable { } /** - * Sets the optional locus id. + * Sets the optional locus id. This is an identifier of the application + * state from a user perspective. For example, a specific chat in a + * messaging app. * * @param locusId The locus id. * @return This builder. diff --git a/core/java/android/app/SharedElementCallback.java b/core/java/android/app/SharedElementCallback.java index 9fabfde5d06f..0287564bcc56 100644 --- a/core/java/android/app/SharedElementCallback.java +++ b/core/java/android/app/SharedElementCallback.java @@ -18,6 +18,7 @@ package android.app; import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; +import android.graphics.ColorSpace; import android.graphics.GraphicBuffer; import android.graphics.Matrix; import android.graphics.RectF; @@ -49,6 +50,7 @@ public abstract class SharedElementCallback { private static final String BUNDLE_SNAPSHOT_BITMAP = "sharedElement:snapshot:bitmap"; private static final String BUNDLE_SNAPSHOT_GRAPHIC_BUFFER = "sharedElement:snapshot:graphicBuffer"; + private static final String BUNDLE_SNAPSHOT_COLOR_SPACE = "sharedElement:snapshot:colorSpace"; private static final String BUNDLE_SNAPSHOT_IMAGE_SCALETYPE = "sharedElement:snapshot:imageScaleType"; private static final String BUNDLE_SNAPSHOT_IMAGE_MATRIX = "sharedElement:snapshot:imageMatrix"; @@ -186,6 +188,10 @@ public abstract class SharedElementCallback { } else { GraphicBuffer graphicBuffer = bitmap.createGraphicBufferHandle(); bundle.putParcelable(BUNDLE_SNAPSHOT_GRAPHIC_BUFFER, graphicBuffer); + ColorSpace cs = bitmap.getColorSpace(); + if (cs != null) { + bundle.putInt(BUNDLE_SNAPSHOT_COLOR_SPACE, cs.getId()); + } } bundle.putString(BUNDLE_SNAPSHOT_IMAGE_SCALETYPE, imageView.getScaleType().toString()); @@ -235,8 +241,13 @@ public abstract class SharedElementCallback { return null; } if (bitmap == null) { + ColorSpace colorSpace = null; + int colorSpaceId = bundle.getInt(BUNDLE_SNAPSHOT_COLOR_SPACE, 0); + if (colorSpaceId >= 0 && colorSpaceId < ColorSpace.Named.values().length) { + colorSpace = ColorSpace.get(ColorSpace.Named.values()[colorSpaceId]); + } bitmap = Bitmap.wrapHardwareBuffer(HardwareBuffer.createFromGraphicBuffer(buffer), - null); + colorSpace); } ImageView imageView = new ImageView(context); view = imageView; diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java index 97b9176d4a86..a4a97c4cac1d 100644 --- a/core/java/android/app/TaskStackListener.java +++ b/core/java/android/app/TaskStackListener.java @@ -19,6 +19,7 @@ package android.app; import android.annotation.UnsupportedAppUsage; import android.app.ActivityManager.TaskSnapshot; import android.content.ComponentName; +import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; @@ -155,6 +156,11 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub { @Override @UnsupportedAppUsage public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) throws RemoteException { + if (Binder.getCallingPid() != android.os.Process.myPid() + && snapshot != null && snapshot.getSnapshot() != null) { + // Preemptively clear any reference to the buffer + snapshot.getSnapshot().destroy(); + } } @Override diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java index 0d5a7633f305..3fca3118c9a0 100644 --- a/core/java/android/app/assist/AssistStructure.java +++ b/core/java/android/app/assist/AssistStructure.java @@ -699,6 +699,7 @@ public class AssistStructure implements Parcelable { static final int AUTOFILL_FLAGS_HAS_MIN_TEXT_EMS = 0x100; static final int AUTOFILL_FLAGS_HAS_MAX_TEXT_EMS = 0x200; static final int AUTOFILL_FLAGS_HAS_MAX_TEXT_LENGTH = 0x400; + static final int AUTOFILL_FLAGS_HAS_AUTOFILL_SESSION_ID = 0x800; int mFlags; int mAutofillFlags; @@ -754,6 +755,9 @@ public class AssistStructure implements Parcelable { } else { mAutofillId = new AutofillId(autofillViewId); } + if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_SESSION_ID) != 0) { + mAutofillId.setSessionId(in.readInt()); + } } if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_TYPE) != 0) { mAutofillType = in.readInt(); @@ -899,6 +903,9 @@ public class AssistStructure implements Parcelable { if (mAutofillId.isVirtualInt()) { autofillFlags |= AUTOFILL_FLAGS_HAS_AUTOFILL_VIRTUAL_VIEW_ID; } + if (mAutofillId.hasSession()) { + autofillFlags |= AUTOFILL_FLAGS_HAS_AUTOFILL_SESSION_ID; + } } if (mAutofillValue != null) { autofillFlags |= AUTOFILL_FLAGS_HAS_AUTOFILL_VALUE; @@ -965,7 +972,9 @@ public class AssistStructure implements Parcelable { if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_VIRTUAL_VIEW_ID) != 0) { out.writeInt(mAutofillId.getVirtualChildIntId()); } - // TODO(b/113593220): write session id as well + if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_SESSION_ID) != 0) { + out.writeInt(mAutofillId.getSessionId()); + } } if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_TYPE) != 0) { out.writeInt(mAutofillType); diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java index f8dc20e9af09..7fa436084246 100644 --- a/core/java/android/app/usage/UsageStatsManager.java +++ b/core/java/android/app/usage/UsageStatsManager.java @@ -198,6 +198,8 @@ public final class UsageStatsManager { /** @hide */ public static final int REASON_SUB_USAGE_UNEXEMPTED_SYNC_SCHEDULED = 0x000E; /** @hide */ + public static final int REASON_SUB_USAGE_FOREGROUND_SERVICE_START = 0x000F; + /** @hide */ public static final int REASON_SUB_PREDICTED_RESTORED = 0x0001; @@ -997,6 +999,9 @@ public final class UsageStatsManager { case REASON_SUB_USAGE_UNEXEMPTED_SYNC_SCHEDULED: sb.append("-uss"); break; + case REASON_SUB_USAGE_FOREGROUND_SERVICE_START: + sb.append("-fss"); + break; } break; } diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java index 442b23938ccd..1fe1b10005cf 100644 --- a/core/java/android/bluetooth/BluetoothA2dp.java +++ b/core/java/android/bluetooth/BluetoothA2dp.java @@ -22,23 +22,16 @@ import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.UnsupportedAppUsage; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.os.Binder; import android.os.Build; import android.os.IBinder; import android.os.ParcelUuid; import android.os.RemoteException; -import android.os.UserHandle; import android.util.Log; -import com.android.internal.annotations.GuardedBy; - import java.util.ArrayList; import java.util.List; -import java.util.concurrent.locks.ReentrantReadWriteLock; /** @@ -209,101 +202,32 @@ public final class BluetoothA2dp implements BluetoothProfile { @UnsupportedAppUsage public static final int OPTIONAL_CODECS_PREF_ENABLED = 1; - private Context mContext; - private ServiceListener mServiceListener; - private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock(); - @GuardedBy("mServiceLock") - private IBluetoothA2dp mService; private BluetoothAdapter mAdapter; - - private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = - new IBluetoothStateChangeCallback.Stub() { - public void onBluetoothStateChange(boolean up) { - if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); - if (!up) { - if (VDBG) Log.d(TAG, "Unbinding service..."); - try { - mServiceLock.writeLock().lock(); - if (mService != null) { - mService = null; - mContext.unbindService(mConnection); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } finally { - mServiceLock.writeLock().unlock(); - } - } else { - try { - mServiceLock.readLock().lock(); - if (mService == null) { - if (VDBG) Log.d(TAG, "Binding service..."); - doBind(); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } finally { - mServiceLock.readLock().unlock(); - } - } + private final BluetoothProfileConnector<IBluetoothA2dp> mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.A2DP, "BluetoothA2dp", + IBluetoothA2dp.class.getName()) { + @Override + public IBluetoothA2dp getServiceInterface(IBinder service) { + return IBluetoothA2dp.Stub.asInterface(Binder.allowBlocking(service)); } - }; + }; /** * Create a BluetoothA2dp proxy object for interacting with the local * Bluetooth A2DP service. */ - /*package*/ BluetoothA2dp(Context context, ServiceListener l) { - mContext = context; - mServiceListener = l; + /*package*/ BluetoothA2dp(Context context, ServiceListener listener) { mAdapter = BluetoothAdapter.getDefaultAdapter(); - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - - doBind(); - } - - boolean doBind() { - Intent intent = new Intent(IBluetoothA2dp.class.getName()); - ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); - intent.setComponent(comp); - if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - UserHandle.CURRENT_OR_SELF)) { - Log.e(TAG, "Could not bind to Bluetooth A2DP Service with " + intent); - return false; - } - return true; + mProfileConnector.connect(context, listener); } @UnsupportedAppUsage /*package*/ void close() { - mServiceListener = null; - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); - } catch (Exception e) { - Log.e(TAG, "", e); - } - } + mProfileConnector.disconnect(); + } - try { - mServiceLock.writeLock().lock(); - if (mService != null) { - mService = null; - mContext.unbindService(mConnection); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } finally { - mServiceLock.writeLock().unlock(); - } + private IBluetoothA2dp getService() { + return mProfileConnector.getService(); } @Override @@ -333,17 +257,15 @@ public final class BluetoothA2dp implements BluetoothProfile { public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() && isValidDevice(device)) { - return mService.connect(device); + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { + return service.connect(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - } finally { - mServiceLock.readLock().unlock(); } } @@ -376,17 +298,15 @@ public final class BluetoothA2dp implements BluetoothProfile { public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() && isValidDevice(device)) { - return mService.disconnect(device); + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { + return service.disconnect(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - } finally { - mServiceLock.readLock().unlock(); } } @@ -397,17 +317,15 @@ public final class BluetoothA2dp implements BluetoothProfile { public List<BluetoothDevice> getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { - return mService.getConnectedDevices(); + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled()) { + return service.getConnectedDevices(); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList<BluetoothDevice>(); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList<BluetoothDevice>(); - } finally { - mServiceLock.readLock().unlock(); } } @@ -418,17 +336,15 @@ public final class BluetoothA2dp implements BluetoothProfile { public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { - return mService.getDevicesMatchingConnectionStates(states); + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled()) { + return service.getDevicesMatchingConnectionStates(states); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList<BluetoothDevice>(); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList<BluetoothDevice>(); - } finally { - mServiceLock.readLock().unlock(); } } @@ -439,18 +355,16 @@ public final class BluetoothA2dp implements BluetoothProfile { public @BtProfileState int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { - return mService.getConnectionState(device); + return service.getConnectionState(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return BluetoothProfile.STATE_DISCONNECTED; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return BluetoothProfile.STATE_DISCONNECTED; - } finally { - mServiceLock.readLock().unlock(); } } @@ -480,18 +394,16 @@ public final class BluetoothA2dp implements BluetoothProfile { public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) log("setActiveDevice(" + device + ")"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled() && ((device == null) || isValidDevice(device))) { - return mService.setActiveDevice(device); + return service.setActiveDevice(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - } finally { - mServiceLock.readLock().unlock(); } } @@ -511,17 +423,15 @@ public final class BluetoothA2dp implements BluetoothProfile { public BluetoothDevice getActiveDevice() { if (VDBG) log("getActiveDevice()"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { - return mService.getActiveDevice(); + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled()) { + return service.getActiveDevice(); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return null; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return null; - } finally { - mServiceLock.readLock().unlock(); } } @@ -543,22 +453,20 @@ public final class BluetoothA2dp implements BluetoothProfile { public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && priority != BluetoothProfile.PRIORITY_ON) { return false; } - return mService.setPriority(device, priority); + return service.setPriority(device, priority); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - } finally { - mServiceLock.readLock().unlock(); } } @@ -578,18 +486,16 @@ public final class BluetoothA2dp implements BluetoothProfile { public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { - return mService.getPriority(device); + return service.getPriority(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return BluetoothProfile.PRIORITY_OFF; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return BluetoothProfile.PRIORITY_OFF; - } finally { - mServiceLock.readLock().unlock(); } } @@ -602,17 +508,15 @@ public final class BluetoothA2dp implements BluetoothProfile { public boolean isAvrcpAbsoluteVolumeSupported() { if (DBG) Log.d(TAG, "isAvrcpAbsoluteVolumeSupported"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { - return mService.isAvrcpAbsoluteVolumeSupported(); + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled()) { + return service.isAvrcpAbsoluteVolumeSupported(); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; } catch (RemoteException e) { Log.e(TAG, "Error talking to BT service in isAvrcpAbsoluteVolumeSupported()", e); return false; - } finally { - mServiceLock.readLock().unlock(); } } @@ -625,15 +529,13 @@ public final class BluetoothA2dp implements BluetoothProfile { public void setAvrcpAbsoluteVolume(int volume) { if (DBG) Log.d(TAG, "setAvrcpAbsoluteVolume"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { - mService.setAvrcpAbsoluteVolume(volume); + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled()) { + service.setAvrcpAbsoluteVolume(volume); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); } catch (RemoteException e) { Log.e(TAG, "Error talking to BT service in setAvrcpAbsoluteVolume()", e); - } finally { - mServiceLock.readLock().unlock(); } } @@ -646,18 +548,16 @@ public final class BluetoothA2dp implements BluetoothProfile { */ public boolean isA2dpPlaying(BluetoothDevice device) { try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { - return mService.isA2dpPlaying(device); + return service.isA2dpPlaying(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - } finally { - mServiceLock.readLock().unlock(); } } @@ -694,19 +594,17 @@ public final class BluetoothA2dp implements BluetoothProfile { public @Nullable BluetoothCodecStatus getCodecStatus(BluetoothDevice device) { if (DBG) Log.d(TAG, "getCodecStatus(" + device + ")"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { - return mService.getCodecStatus(device); + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled()) { + return service.getCodecStatus(device); } - if (mService == null) { + if (service == null) { Log.w(TAG, "Proxy not attached to service"); } return null; } catch (RemoteException e) { Log.e(TAG, "Error talking to BT service in getCodecStatus()", e); return null; - } finally { - mServiceLock.readLock().unlock(); } } @@ -723,17 +621,15 @@ public final class BluetoothA2dp implements BluetoothProfile { BluetoothCodecConfig codecConfig) { if (DBG) Log.d(TAG, "setCodecConfigPreference(" + device + ")"); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { - mService.setCodecConfigPreference(device, codecConfig); + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled()) { + service.setCodecConfigPreference(device, codecConfig); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return; } catch (RemoteException e) { Log.e(TAG, "Error talking to BT service in setCodecConfigPreference()", e); return; - } finally { - mServiceLock.readLock().unlock(); } } @@ -772,21 +668,19 @@ public final class BluetoothA2dp implements BluetoothProfile { */ private void enableDisableOptionalCodecs(BluetoothDevice device, boolean enable) { try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled()) { if (enable) { - mService.enableOptionalCodecs(device); + service.enableOptionalCodecs(device); } else { - mService.disableOptionalCodecs(device); + service.disableOptionalCodecs(device); } } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return; } catch (RemoteException e) { Log.e(TAG, "Error talking to BT service in enableDisableOptionalCodecs()", e); return; - } finally { - mServiceLock.readLock().unlock(); } } @@ -801,17 +695,15 @@ public final class BluetoothA2dp implements BluetoothProfile { @UnsupportedAppUsage public int supportsOptionalCodecs(BluetoothDevice device) { try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() && isValidDevice(device)) { - return mService.supportsOptionalCodecs(device); + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { + return service.supportsOptionalCodecs(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return OPTIONAL_CODECS_SUPPORT_UNKNOWN; } catch (RemoteException e) { Log.e(TAG, "Error talking to BT service in getSupportsOptionalCodecs()", e); return OPTIONAL_CODECS_SUPPORT_UNKNOWN; - } finally { - mServiceLock.readLock().unlock(); } } @@ -826,17 +718,15 @@ public final class BluetoothA2dp implements BluetoothProfile { @UnsupportedAppUsage public int getOptionalCodecsEnabled(BluetoothDevice device) { try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() && isValidDevice(device)) { - return mService.getOptionalCodecsEnabled(device); + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { + return service.getOptionalCodecsEnabled(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return OPTIONAL_CODECS_PREF_UNKNOWN; } catch (RemoteException e) { Log.e(TAG, "Error talking to BT service in getSupportsOptionalCodecs()", e); return OPTIONAL_CODECS_PREF_UNKNOWN; - } finally { - mServiceLock.readLock().unlock(); } } @@ -858,18 +748,16 @@ public final class BluetoothA2dp implements BluetoothProfile { Log.e(TAG, "Invalid value passed to setOptionalCodecsEnabled: " + value); return; } - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { - mService.setOptionalCodecsEnabled(device, value); + service.setOptionalCodecsEnabled(device, value); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return; - } finally { - mServiceLock.readLock().unlock(); } } @@ -900,35 +788,6 @@ public final class BluetoothA2dp implements BluetoothProfile { } } - private final ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) Log.d(TAG, "Proxy object connected"); - try { - mServiceLock.writeLock().lock(); - mService = IBluetoothA2dp.Stub.asInterface(Binder.allowBlocking(service)); - } finally { - mServiceLock.writeLock().unlock(); - } - - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.A2DP, BluetoothA2dp.this); - } - } - - public void onServiceDisconnected(ComponentName className) { - if (DBG) Log.d(TAG, "Proxy object disconnected"); - try { - mServiceLock.writeLock().lock(); - mService = null; - } finally { - mServiceLock.writeLock().unlock(); - } - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.A2DP); - } - } - }; - private boolean isEnabled() { if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; return false; diff --git a/core/java/android/bluetooth/BluetoothA2dpSink.java b/core/java/android/bluetooth/BluetoothA2dpSink.java index cb996f3381b7..5a8055a29cfc 100755 --- a/core/java/android/bluetooth/BluetoothA2dpSink.java +++ b/core/java/android/bluetooth/BluetoothA2dpSink.java @@ -17,14 +17,10 @@ package android.bluetooth; import android.annotation.UnsupportedAppUsage; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; -import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -125,93 +121,31 @@ public final class BluetoothA2dpSink implements BluetoothProfile { public static final String EXTRA_AUDIO_CONFIG = "android.bluetooth.a2dp-sink.profile.extra.AUDIO_CONFIG"; - private Context mContext; - private ServiceListener mServiceListener; - private volatile IBluetoothA2dpSink mService; private BluetoothAdapter mAdapter; - - private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = - new IBluetoothStateChangeCallback.Stub() { - public void onBluetoothStateChange(boolean up) { - if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); - if (!up) { - if (VDBG) Log.d(TAG, "Unbinding service..."); - synchronized (mConnection) { - try { - mService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } else { - synchronized (mConnection) { - try { - if (mService == null) { - if (VDBG) Log.d(TAG, "Binding service..."); - doBind(); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } + private final BluetoothProfileConnector<IBluetoothA2dpSink> mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.A2DP_SINK, + "BluetoothA2dpSink", IBluetoothA2dpSink.class.getName()) { + @Override + public IBluetoothA2dpSink getServiceInterface(IBinder service) { + return IBluetoothA2dpSink.Stub.asInterface(Binder.allowBlocking(service)); } - }; + }; /** * Create a BluetoothA2dp proxy object for interacting with the local * Bluetooth A2DP service. */ - /*package*/ BluetoothA2dpSink(Context context, ServiceListener l) { - mContext = context; - mServiceListener = l; + /*package*/ BluetoothA2dpSink(Context context, ServiceListener listener) { mAdapter = BluetoothAdapter.getDefaultAdapter(); - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - - doBind(); - } - - boolean doBind() { - Intent intent = new Intent(IBluetoothA2dpSink.class.getName()); - ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); - intent.setComponent(comp); - if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - UserHandle.CURRENT_OR_SELF)) { - Log.e(TAG, "Could not bind to Bluetooth A2DP Service with " + intent); - return false; - } - return true; + mProfileConnector.connect(context, listener); } /*package*/ void close() { - mServiceListener = null; - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); - } catch (Exception e) { - Log.e(TAG, "", e); - } - } + mProfileConnector.disconnect(); + } - synchronized (mConnection) { - if (mService != null) { - try { - mService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } + private IBluetoothA2dpSink getService() { + return mProfileConnector.getService(); } @Override @@ -242,7 +176,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); - final IBluetoothA2dpSink service = mService; + final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.connect(device); @@ -283,7 +217,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - final IBluetoothA2dpSink service = mService; + final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.disconnect(device); @@ -302,7 +236,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { @Override public List<BluetoothDevice> getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); - final IBluetoothA2dpSink service = mService; + final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled()) { try { return service.getConnectedDevices(); @@ -321,7 +255,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { @Override public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); - final IBluetoothA2dpSink service = mService; + final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled()) { try { return service.getDevicesMatchingConnectionStates(states); @@ -340,7 +274,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { @Override public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); - final IBluetoothA2dpSink service = mService; + final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getConnectionState(device); @@ -366,7 +300,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ public BluetoothAudioConfig getAudioConfig(BluetoothDevice device) { if (VDBG) log("getAudioConfig(" + device + ")"); - final IBluetoothA2dpSink service = mService; + final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getAudioConfig(device); @@ -396,7 +330,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); - final IBluetoothA2dpSink service = mService; + final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && priority != BluetoothProfile.PRIORITY_ON) { @@ -428,7 +362,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - final IBluetoothA2dpSink service = mService; + final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getPriority(device); @@ -449,7 +383,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { * @param device BluetoothDevice device */ public boolean isA2dpPlaying(BluetoothDevice device) { - final IBluetoothA2dpSink service = mService; + final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.isA2dpPlaying(device); @@ -488,25 +422,6 @@ public final class BluetoothA2dpSink implements BluetoothProfile { } } - private final ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothA2dpSink.Stub.asInterface(Binder.allowBlocking(service)); - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.A2DP_SINK, - BluetoothA2dpSink.this); - } - } - - public void onServiceDisconnected(ComponentName className) { - if (DBG) Log.d(TAG, "Proxy object disconnected"); - mService = null; - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.A2DP_SINK); - } - } - }; - private boolean isEnabled() { return mAdapter.getState() == BluetoothAdapter.STATE_ON; } diff --git a/core/java/android/bluetooth/BluetoothAvrcpController.java b/core/java/android/bluetooth/BluetoothAvrcpController.java index c447868d6f0c..4e7e4415c54d 100644 --- a/core/java/android/bluetooth/BluetoothAvrcpController.java +++ b/core/java/android/bluetooth/BluetoothAvrcpController.java @@ -16,14 +16,10 @@ package android.bluetooth; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; -import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -80,93 +76,32 @@ public final class BluetoothAvrcpController implements BluetoothProfile { public static final String EXTRA_PLAYER_SETTING = "android.bluetooth.avrcp-controller.profile.extra.PLAYER_SETTING"; - private Context mContext; - private ServiceListener mServiceListener; - private volatile IBluetoothAvrcpController mService; private BluetoothAdapter mAdapter; - - private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = - new IBluetoothStateChangeCallback.Stub() { - public void onBluetoothStateChange(boolean up) { - if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); - if (!up) { - if (VDBG) Log.d(TAG, "Unbinding service..."); - synchronized (mConnection) { - try { - mService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } else { - synchronized (mConnection) { - try { - if (mService == null) { - if (VDBG) Log.d(TAG, "Binding service..."); - doBind(); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } + private final BluetoothProfileConnector<IBluetoothAvrcpController> mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.AVRCP_CONTROLLER, + "BluetoothAvrcpController", IBluetoothAvrcpController.class.getName()) { + @Override + public IBluetoothAvrcpController getServiceInterface(IBinder service) { + return IBluetoothAvrcpController.Stub.asInterface( + Binder.allowBlocking(service)); } - }; + }; /** * Create a BluetoothAvrcpController proxy object for interacting with the local * Bluetooth AVRCP service. */ - /*package*/ BluetoothAvrcpController(Context context, ServiceListener l) { - mContext = context; - mServiceListener = l; + /*package*/ BluetoothAvrcpController(Context context, ServiceListener listener) { mAdapter = BluetoothAdapter.getDefaultAdapter(); - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - - doBind(); - } - - boolean doBind() { - Intent intent = new Intent(IBluetoothAvrcpController.class.getName()); - ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); - intent.setComponent(comp); - if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - UserHandle.CURRENT_OR_SELF)) { - Log.e(TAG, "Could not bind to Bluetooth AVRCP Controller Service with " + intent); - return false; - } - return true; + mProfileConnector.connect(context, listener); } /*package*/ void close() { - mServiceListener = null; - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); - } catch (Exception e) { - Log.e(TAG, "", e); - } - } + mProfileConnector.disconnect(); + } - synchronized (mConnection) { - if (mService != null) { - try { - mService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } + private IBluetoothAvrcpController getService() { + return mProfileConnector.getService(); } @Override @@ -180,7 +115,8 @@ public final class BluetoothAvrcpController implements BluetoothProfile { @Override public List<BluetoothDevice> getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); - final IBluetoothAvrcpController service = mService; + final IBluetoothAvrcpController service = + getService(); if (service != null && isEnabled()) { try { return service.getConnectedDevices(); @@ -199,7 +135,8 @@ public final class BluetoothAvrcpController implements BluetoothProfile { @Override public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); - final IBluetoothAvrcpController service = mService; + final IBluetoothAvrcpController service = + getService(); if (service != null && isEnabled()) { try { return service.getDevicesMatchingConnectionStates(states); @@ -218,7 +155,8 @@ public final class BluetoothAvrcpController implements BluetoothProfile { @Override public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); - final IBluetoothAvrcpController service = mService; + final IBluetoothAvrcpController service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getConnectionState(device); @@ -239,7 +177,8 @@ public final class BluetoothAvrcpController implements BluetoothProfile { public BluetoothAvrcpPlayerSettings getPlayerSettings(BluetoothDevice device) { if (DBG) Log.d(TAG, "getPlayerSettings"); BluetoothAvrcpPlayerSettings settings = null; - final IBluetoothAvrcpController service = mService; + final IBluetoothAvrcpController service = + getService(); if (service != null && isEnabled()) { try { settings = service.getPlayerSettings(device); @@ -257,7 +196,8 @@ public final class BluetoothAvrcpController implements BluetoothProfile { */ public boolean setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting) { if (DBG) Log.d(TAG, "setPlayerApplicationSetting"); - final IBluetoothAvrcpController service = mService; + final IBluetoothAvrcpController service = + getService(); if (service != null && isEnabled()) { try { return service.setPlayerApplicationSetting(plAppSetting); @@ -277,7 +217,8 @@ public final class BluetoothAvrcpController implements BluetoothProfile { public void sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState) { Log.d(TAG, "sendGroupNavigationCmd dev = " + device + " key " + keyCode + " State = " + keyState); - final IBluetoothAvrcpController service = mService; + final IBluetoothAvrcpController service = + getService(); if (service != null && isEnabled()) { try { service.sendGroupNavigationCmd(device, keyCode, keyState); @@ -290,25 +231,6 @@ public final class BluetoothAvrcpController implements BluetoothProfile { if (service == null) Log.w(TAG, "Proxy not attached to service"); } - private final ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothAvrcpController.Stub.asInterface(Binder.allowBlocking(service)); - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.AVRCP_CONTROLLER, - BluetoothAvrcpController.this); - } - } - - public void onServiceDisconnected(ComponentName className) { - if (DBG) Log.d(TAG, "Proxy object disconnected"); - mService = null; - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.AVRCP_CONTROLLER); - } - } - }; - private boolean isEnabled() { return mAdapter.getState() == BluetoothAdapter.STATE_ON; } diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java index 8d9d340ee68b..9862a63ef238 100644 --- a/core/java/android/bluetooth/BluetoothHeadset.java +++ b/core/java/android/bluetooth/BluetoothHeadset.java @@ -337,19 +337,9 @@ public final class BluetoothHeadset implements BluetoothProfile { public void onBluetoothStateChange(boolean up) { if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); if (!up) { - if (VDBG) Log.d(TAG, "Unbinding service..."); doUnbind(); } else { - synchronized (mConnection) { - try { - if (mService == null) { - if (VDBG) Log.d(TAG, "Binding service..."); - doBind(); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } - } + doBind(); } } }; @@ -374,24 +364,32 @@ public final class BluetoothHeadset implements BluetoothProfile { doBind(); } - boolean doBind() { - try { - return mAdapter.getBluetoothManager().bindBluetoothProfileService( - BluetoothProfile.HEADSET, mConnection); - } catch (RemoteException e) { - Log.e(TAG, "Unable to bind HeadsetService", e); + private boolean doBind() { + synchronized (mConnection) { + if (mService == null) { + if (VDBG) Log.d(TAG, "Binding service..."); + try { + return mAdapter.getBluetoothManager().bindBluetoothProfileService( + BluetoothProfile.HEADSET, mConnection); + } catch (RemoteException e) { + Log.e(TAG, "Unable to bind HeadsetService", e); + } + } } return false; } - void doUnbind() { + private void doUnbind() { synchronized (mConnection) { if (mService != null) { + if (VDBG) Log.d(TAG, "Unbinding service..."); try { mAdapter.getBluetoothManager().unbindBluetoothProfileService( BluetoothProfile.HEADSET, mConnection); } catch (RemoteException e) { Log.e(TAG, "Unable to unbind HeadsetService", e); + } finally { + mService = null; } } } @@ -411,8 +409,8 @@ public final class BluetoothHeadset implements BluetoothProfile { if (mgr != null) { try { mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); - } catch (Exception e) { - Log.e(TAG, "", e); + } catch (RemoteException re) { + Log.e(TAG, "", re); } } mServiceListener = null; @@ -1169,7 +1167,7 @@ public final class BluetoothHeadset implements BluetoothProfile { @Override public void onServiceDisconnected(ComponentName className) { if (DBG) Log.d(TAG, "Proxy object disconnected"); - mService = null; + doUnbind(); mHandler.sendMessage(mHandler.obtainMessage( MESSAGE_HEADSET_SERVICE_DISCONNECTED)); } diff --git a/core/java/android/bluetooth/BluetoothHeadsetClient.java b/core/java/android/bluetooth/BluetoothHeadsetClient.java index 549c1faddd90..05833b5f571d 100644 --- a/core/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/core/java/android/bluetooth/BluetoothHeadsetClient.java @@ -17,15 +17,11 @@ package android.bluetooth; import android.annotation.UnsupportedAppUsage; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; -import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -367,73 +363,22 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { public static final int CALL_ACCEPT_HOLD = 1; public static final int CALL_ACCEPT_TERMINATE = 2; - private Context mContext; - private ServiceListener mServiceListener; - private volatile IBluetoothHeadsetClient mService; private BluetoothAdapter mAdapter; - - private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = - new IBluetoothStateChangeCallback.Stub() { + private final BluetoothProfileConnector<IBluetoothHeadsetClient> mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.HEADSET_CLIENT, + "BluetoothHeadsetClient", IBluetoothHeadsetClient.class.getName()) { @Override - public void onBluetoothStateChange(boolean up) { - if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); - if (!up) { - if (VDBG) Log.d(TAG, "Unbinding service..."); - synchronized (mConnection) { - try { - mService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } else { - synchronized (mConnection) { - try { - if (mService == null) { - if (VDBG) Log.d(TAG, "Binding service..."); - Intent intent = new Intent( - IBluetoothHeadsetClient.class.getName()); - doBind(); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } + public IBluetoothHeadsetClient getServiceInterface(IBinder service) { + return IBluetoothHeadsetClient.Stub.asInterface(Binder.allowBlocking(service)); } - }; + }; /** * Create a BluetoothHeadsetClient proxy object. */ - /*package*/ BluetoothHeadsetClient(Context context, ServiceListener l) { - mContext = context; - mServiceListener = l; + /*package*/ BluetoothHeadsetClient(Context context, ServiceListener listener) { mAdapter = BluetoothAdapter.getDefaultAdapter(); - - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - - doBind(); - } - - boolean doBind() { - Intent intent = new Intent(IBluetoothHeadsetClient.class.getName()); - ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); - intent.setComponent(comp); - if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - UserHandle.CURRENT_OR_SELF)) { - Log.e(TAG, "Could not bind to Bluetooth Headset Client Service with " + intent); - return false; - } - return true; + mProfileConnector.connect(context, listener); } /** @@ -444,27 +389,11 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ /*package*/ void close() { if (VDBG) log("close()"); + mProfileConnector.disconnect(); + } - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); - } catch (Exception e) { - Log.e(TAG, "", e); - } - } - - synchronized (mConnection) { - if (mService != null) { - try { - mService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } - mServiceListener = null; + private IBluetoothHeadsetClient getService() { + return mProfileConnector.getService(); } /** @@ -481,7 +410,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @UnsupportedAppUsage public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.connect(device); @@ -504,7 +434,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.disconnect(device); @@ -525,7 +456,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @Override public List<BluetoothDevice> getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled()) { try { return service.getConnectedDevices(); @@ -548,7 +480,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @Override public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled()) { try { return service.getDevicesMatchingConnectionStates(states); @@ -570,7 +503,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @Override public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getConnectionState(" + device + ")"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getConnectionState(device); @@ -590,7 +524,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && priority != BluetoothProfile.PRIORITY_ON) { @@ -612,7 +547,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getPriority(device); @@ -638,7 +574,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean startVoiceRecognition(BluetoothDevice device) { if (DBG) log("startVoiceRecognition()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.startVoiceRecognition(device); @@ -663,7 +600,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean stopVoiceRecognition(BluetoothDevice device) { if (DBG) log("stopVoiceRecognition()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.stopVoiceRecognition(device); @@ -683,7 +621,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public List<BluetoothHeadsetClientCall> getCurrentCalls(BluetoothDevice device) { if (DBG) log("getCurrentCalls()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getCurrentCalls(device); @@ -703,7 +642,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public Bundle getCurrentAgEvents(BluetoothDevice device) { if (DBG) log("getCurrentCalls()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getCurrentAgEvents(device); @@ -727,7 +667,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @UnsupportedAppUsage public boolean acceptCall(BluetoothDevice device, int flag) { if (DBG) log("acceptCall()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.acceptCall(device, flag); @@ -748,7 +689,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean holdCall(BluetoothDevice device) { if (DBG) log("holdCall()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.holdCall(device); @@ -774,7 +716,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @UnsupportedAppUsage public boolean rejectCall(BluetoothDevice device) { if (DBG) log("rejectCall()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.rejectCall(device); @@ -804,7 +747,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean terminateCall(BluetoothDevice device, BluetoothHeadsetClientCall call) { if (DBG) log("terminateCall()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.terminateCall(device, call); @@ -832,7 +776,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean enterPrivateMode(BluetoothDevice device, int index) { if (DBG) log("enterPrivateMode()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.enterPrivateMode(device, index); @@ -859,7 +804,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean explicitCallTransfer(BluetoothDevice device) { if (DBG) log("explicitCallTransfer()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.explicitCallTransfer(device); @@ -882,7 +828,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) { if (DBG) log("dial()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.dial(device, number); @@ -906,7 +853,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean sendDTMF(BluetoothDevice device, byte code) { if (DBG) log("sendDTMF()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.sendDTMF(device, code); @@ -932,7 +880,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean getLastVoiceTagNumber(BluetoothDevice device) { if (DBG) log("getLastVoiceTagNumber()"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getLastVoiceTagNumber(device); @@ -952,7 +901,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { @UnsupportedAppUsage public int getAudioState(BluetoothDevice device) { if (VDBG) log("getAudioState"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled()) { try { return service.getAudioState(device); @@ -975,7 +925,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public void setAudioRouteAllowed(BluetoothDevice device, boolean allowed) { if (VDBG) log("setAudioRouteAllowed"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled()) { try { service.setAudioRouteAllowed(device, allowed); @@ -997,7 +948,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ public boolean getAudioRouteAllowed(BluetoothDevice device) { if (VDBG) log("getAudioRouteAllowed"); - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled()) { try { return service.getAudioRouteAllowed(device); @@ -1021,7 +973,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * otherwise; upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} intent; */ public boolean connectAudio(BluetoothDevice device) { - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled()) { try { return service.connectAudio(device); @@ -1045,7 +998,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * otherwise; upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} intent; */ public boolean disconnectAudio(BluetoothDevice device) { - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled()) { try { return service.disconnectAudio(device); @@ -1066,7 +1020,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * @return bundle of AG features; null if no service or AG not connected */ public Bundle getCurrentAgFeatures(BluetoothDevice device) { - final IBluetoothHeadsetClient service = mService; + final IBluetoothHeadsetClient service = + getService(); if (service != null && isEnabled()) { try { return service.getCurrentAgFeatures(device); @@ -1080,29 +1035,6 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { return null; } - - private final ServiceConnection mConnection = new ServiceConnection() { - @Override - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothHeadsetClient.Stub.asInterface(Binder.allowBlocking(service)); - - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.HEADSET_CLIENT, - BluetoothHeadsetClient.this); - } - } - - @Override - public void onServiceDisconnected(ComponentName className) { - if (DBG) Log.d(TAG, "Proxy object disconnected"); - mService = null; - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.HEADSET_CLIENT); - } - } - }; - private boolean isEnabled() { return mAdapter.getState() == BluetoothAdapter.STATE_ON; } diff --git a/core/java/android/bluetooth/BluetoothHearingAid.java b/core/java/android/bluetooth/BluetoothHearingAid.java index d6edb90ca27b..60fb6fb122e3 100644 --- a/core/java/android/bluetooth/BluetoothHearingAid.java +++ b/core/java/android/bluetooth/BluetoothHearingAid.java @@ -22,21 +22,14 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; -import android.os.UserHandle; import android.util.Log; -import com.android.internal.annotations.GuardedBy; - import java.util.ArrayList; import java.util.List; -import java.util.concurrent.locks.ReentrantReadWriteLock; /** * This class provides the public APIs to control the Hearing Aid profile. @@ -129,97 +122,31 @@ public final class BluetoothHearingAid implements BluetoothProfile { */ public static final long HI_SYNC_ID_INVALID = IBluetoothHearingAid.HI_SYNC_ID_INVALID; - private Context mContext; - private ServiceListener mServiceListener; - private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock(); - @GuardedBy("mServiceLock") - private IBluetoothHearingAid mService; private BluetoothAdapter mAdapter; - - private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = - new IBluetoothStateChangeCallback.Stub() { - public void onBluetoothStateChange(boolean up) { - if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); - if (!up) { - if (VDBG) Log.d(TAG, "Unbinding service..."); - try { - mServiceLock.writeLock().lock(); - mService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } finally { - mServiceLock.writeLock().unlock(); - } - } else { - try { - mServiceLock.readLock().lock(); - if (mService == null) { - if (VDBG) Log.d(TAG, "Binding service..."); - doBind(); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } finally { - mServiceLock.readLock().unlock(); - } - } + private final BluetoothProfileConnector<IBluetoothHearingAid> mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.HEARING_AID, + "BluetoothHearingAid", IBluetoothHearingAid.class.getName()) { + @Override + public IBluetoothHearingAid getServiceInterface(IBinder service) { + return IBluetoothHearingAid.Stub.asInterface(Binder.allowBlocking(service)); } - }; + }; /** * Create a BluetoothHearingAid proxy object for interacting with the local * Bluetooth Hearing Aid service. */ - /*package*/ BluetoothHearingAid(Context context, ServiceListener l) { - mContext = context; - mServiceListener = l; + /*package*/ BluetoothHearingAid(Context context, ServiceListener listener) { mAdapter = BluetoothAdapter.getDefaultAdapter(); - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - - doBind(); - } - - void doBind() { - Intent intent = new Intent(IBluetoothHearingAid.class.getName()); - ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); - intent.setComponent(comp); - if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - UserHandle.CURRENT_OR_SELF)) { - Log.e(TAG, "Could not bind to Bluetooth Hearing Aid Service with " + intent); - return; - } + mProfileConnector.connect(context, listener); } /*package*/ void close() { - mServiceListener = null; - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); - } catch (Exception e) { - Log.e(TAG, "", e); - } - } + mProfileConnector.disconnect(); + } - try { - mServiceLock.writeLock().lock(); - if (mService != null) { - mService = null; - mContext.unbindService(mConnection); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } finally { - mServiceLock.writeLock().unlock(); - } + private IBluetoothHearingAid getService() { + return mProfileConnector.getService(); } /** @@ -241,18 +168,16 @@ public final class BluetoothHearingAid implements BluetoothProfile { */ public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() && isValidDevice(device)) { - return mService.connect(device); + if (service != null && isEnabled() && isValidDevice(device)) { + return service.connect(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - } finally { - mServiceLock.readLock().unlock(); } } @@ -283,18 +208,16 @@ public final class BluetoothHearingAid implements BluetoothProfile { */ public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() && isValidDevice(device)) { - return mService.disconnect(device); + if (service != null && isEnabled() && isValidDevice(device)) { + return service.disconnect(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - } finally { - mServiceLock.readLock().unlock(); } } @@ -304,18 +227,16 @@ public final class BluetoothHearingAid implements BluetoothProfile { @Override public @NonNull List<BluetoothDevice> getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { - return mService.getConnectedDevices(); + if (service != null && isEnabled()) { + return service.getConnectedDevices(); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList<BluetoothDevice>(); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList<BluetoothDevice>(); - } finally { - mServiceLock.readLock().unlock(); } } @@ -326,18 +247,16 @@ public final class BluetoothHearingAid implements BluetoothProfile { public @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates( @NonNull int[] states) { if (VDBG) log("getDevicesMatchingStates()"); + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { - return mService.getDevicesMatchingConnectionStates(states); + if (service != null && isEnabled()) { + return service.getDevicesMatchingConnectionStates(states); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList<BluetoothDevice>(); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList<BluetoothDevice>(); - } finally { - mServiceLock.readLock().unlock(); } } @@ -348,19 +267,17 @@ public final class BluetoothHearingAid implements BluetoothProfile { public @BluetoothProfile.BtProfileState int getConnectionState( @NonNull BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + if (service != null && isEnabled() && isValidDevice(device)) { - return mService.getConnectionState(device); + return service.getConnectionState(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return BluetoothProfile.STATE_DISCONNECTED; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return BluetoothProfile.STATE_DISCONNECTED; - } finally { - mServiceLock.readLock().unlock(); } } @@ -388,20 +305,18 @@ public final class BluetoothHearingAid implements BluetoothProfile { */ public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) log("setActiveDevice(" + device + ")"); + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + if (service != null && isEnabled() && ((device == null) || isValidDevice(device))) { - mService.setActiveDevice(device); + service.setActiveDevice(device); return true; } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - } finally { - mServiceLock.readLock().unlock(); } } @@ -419,18 +334,16 @@ public final class BluetoothHearingAid implements BluetoothProfile { @RequiresPermission(Manifest.permission.BLUETOOTH) public List<BluetoothDevice> getActiveDevices() { if (VDBG) log("getActiveDevices()"); + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { - return mService.getActiveDevices(); + if (service != null && isEnabled()) { + return service.getActiveDevices(); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return new ArrayList<>(); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return new ArrayList<>(); - } finally { - mServiceLock.readLock().unlock(); } } @@ -451,23 +364,21 @@ public final class BluetoothHearingAid implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + if (service != null && isEnabled() && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && priority != BluetoothProfile.PRIORITY_ON) { return false; } - return mService.setPriority(device, priority); + return service.setPriority(device, priority); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; - } finally { - mServiceLock.readLock().unlock(); } } @@ -485,19 +396,17 @@ public final class BluetoothHearingAid implements BluetoothProfile { @RequiresPermission(Manifest.permission.BLUETOOTH) public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + if (service != null && isEnabled() && isValidDevice(device)) { - return mService.getPriority(device); + return service.getPriority(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return BluetoothProfile.PRIORITY_OFF; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return BluetoothProfile.PRIORITY_OFF; - } finally { - mServiceLock.readLock().unlock(); } } @@ -536,18 +445,16 @@ public final class BluetoothHearingAid implements BluetoothProfile { if (VDBG) { log("getVolume()"); } + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled()) { - return mService.getVolume(); + if (service != null && isEnabled()) { + return service.getVolume(); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return 0; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return 0; - } finally { - mServiceLock.readLock().unlock(); } } @@ -567,21 +474,18 @@ public final class BluetoothHearingAid implements BluetoothProfile { public void adjustVolume(int direction) { if (DBG) log("adjustVolume(" + direction + ")"); + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - - if (mService == null) { + if (service == null) { Log.w(TAG, "Proxy not attached to service"); return; } if (!isEnabled()) return; - mService.adjustVolume(direction); + service.adjustVolume(direction); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - } finally { - mServiceLock.readLock().unlock(); } } @@ -594,20 +498,18 @@ public final class BluetoothHearingAid implements BluetoothProfile { public void setVolume(int volume) { if (DBG) Log.d(TAG, "setVolume(" + volume + ")"); + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService == null) { + if (service == null) { Log.w(TAG, "Proxy not attached to service"); return; } if (!isEnabled()) return; - mService.setVolume(volume); + service.setVolume(volume); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); - } finally { - mServiceLock.readLock().unlock(); } } @@ -623,21 +525,19 @@ public final class BluetoothHearingAid implements BluetoothProfile { if (VDBG) { log("getCustomerId(" + device + ")"); } + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService == null) { + if (service == null) { Log.w(TAG, "Proxy not attached to service"); return HI_SYNC_ID_INVALID; } if (!isEnabled() || !isValidDevice(device)) return HI_SYNC_ID_INVALID; - return mService.getHiSyncId(device); + return service.getHiSyncId(device); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return HI_SYNC_ID_INVALID; - } finally { - mServiceLock.readLock().unlock(); } } @@ -653,19 +553,17 @@ public final class BluetoothHearingAid implements BluetoothProfile { if (VDBG) { log("getDeviceSide(" + device + ")"); } + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + if (service != null && isEnabled() && isValidDevice(device)) { - return mService.getDeviceSide(device); + return service.getDeviceSide(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return SIDE_LEFT; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return SIDE_LEFT; - } finally { - mServiceLock.readLock().unlock(); } } @@ -681,52 +579,20 @@ public final class BluetoothHearingAid implements BluetoothProfile { if (VDBG) { log("getDeviceMode(" + device + ")"); } + final IBluetoothHearingAid service = getService(); try { - mServiceLock.readLock().lock(); - if (mService != null && isEnabled() + if (service != null && isEnabled() && isValidDevice(device)) { - return mService.getDeviceMode(device); + return service.getDeviceMode(device); } - if (mService == null) Log.w(TAG, "Proxy not attached to service"); + if (service == null) Log.w(TAG, "Proxy not attached to service"); return MODE_MONAURAL; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return MODE_MONAURAL; - } finally { - mServiceLock.readLock().unlock(); } } - private final ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) Log.d(TAG, "Proxy object connected"); - try { - mServiceLock.writeLock().lock(); - mService = IBluetoothHearingAid.Stub.asInterface(Binder.allowBlocking(service)); - } finally { - mServiceLock.writeLock().unlock(); - } - - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.HEARING_AID, - BluetoothHearingAid.this); - } - } - - public void onServiceDisconnected(ComponentName className) { - if (DBG) Log.d(TAG, "Proxy object disconnected"); - try { - mServiceLock.writeLock().lock(); - mService = null; - } finally { - mServiceLock.writeLock().unlock(); - } - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.HEARING_AID); - } - } - }; - private boolean isEnabled() { if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; return false; diff --git a/core/java/android/bluetooth/BluetoothHidDevice.java b/core/java/android/bluetooth/BluetoothHidDevice.java index e44f36e90c75..e9b0be2c4cd6 100644 --- a/core/java/android/bluetooth/BluetoothHidDevice.java +++ b/core/java/android/bluetooth/BluetoothHidDevice.java @@ -18,13 +18,10 @@ package android.bluetooth; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; +import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; -import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -328,11 +325,6 @@ public final class BluetoothHidDevice implements BluetoothProfile { } } - private Context mContext; - private ServiceListener mServiceListener; - private volatile IBluetoothHidDevice mService; - private BluetoothAdapter mAdapter; - private static class CallbackWrapper extends IBluetoothHidDeviceCallback.Stub { private final Executor mExecutor; @@ -386,114 +378,33 @@ public final class BluetoothHidDevice implements BluetoothProfile { } } - private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = - new IBluetoothStateChangeCallback.Stub() { - - public void onBluetoothStateChange(boolean up) { - Log.d(TAG, "onBluetoothStateChange: up=" + up); - synchronized (mConnection) { - if (up) { - try { - if (mService == null) { - Log.d(TAG, "Binding HID Device service..."); - doBind(); - } - } catch (IllegalStateException e) { - Log.e(TAG, "onBluetoothStateChange: could not bind to HID Dev " - + "service: ", e); - } catch (SecurityException e) { - Log.e(TAG, "onBluetoothStateChange: could not bind to HID Dev " - + "service: ", e); - } - } else { - Log.d(TAG, "Unbinding service..."); - doUnbind(); - } - } - } - }; - - private final ServiceConnection mConnection = - new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - Log.d(TAG, "onServiceConnected()"); - mService = IBluetoothHidDevice.Stub.asInterface(service); - if (mServiceListener != null) { - mServiceListener.onServiceConnected( - BluetoothProfile.HID_DEVICE, BluetoothHidDevice.this); - } - } - - public void onServiceDisconnected(ComponentName className) { - Log.d(TAG, "onServiceDisconnected()"); - mService = null; - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.HID_DEVICE); - } + private BluetoothAdapter mAdapter; + private final BluetoothProfileConnector<IBluetoothHidDevice> mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.HID_DEVICE, + "BluetoothHidDevice", IBluetoothHidDevice.class.getName()) { + @Override + public IBluetoothHidDevice getServiceInterface(IBinder service) { + return IBluetoothHidDevice.Stub.asInterface(Binder.allowBlocking(service)); } - }; + }; BluetoothHidDevice(Context context, ServiceListener listener) { - mContext = context; - mServiceListener = listener; mAdapter = BluetoothAdapter.getDefaultAdapter(); - - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException e) { - e.printStackTrace(); - } - } - - doBind(); - } - - boolean doBind() { - Intent intent = new Intent(IBluetoothHidDevice.class.getName()); - ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); - intent.setComponent(comp); - if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - UserHandle.CURRENT_OR_SELF)) { - Log.e(TAG, "Could not bind to Bluetooth HID Device Service with " + intent); - return false; - } - Log.d(TAG, "Bound to HID Device Service"); - return true; - } - - void doUnbind() { - if (mService != null) { - mService = null; - try { - mContext.unbindService(mConnection); - } catch (IllegalArgumentException e) { - Log.e(TAG, "Unable to unbind HidDevService", e); - } - } + mProfileConnector.connect(context, listener); } void close() { - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException e) { - e.printStackTrace(); - } - } + mProfileConnector.disconnect(); + } - synchronized (mConnection) { - doUnbind(); - } - mServiceListener = null; + private IBluetoothHidDevice getService() { + return mProfileConnector.getService(); } /** {@inheritDoc} */ @Override public List<BluetoothDevice> getConnectedDevices() { - final IBluetoothHidDevice service = mService; + final IBluetoothHidDevice service = getService(); if (service != null) { try { return service.getConnectedDevices(); @@ -510,7 +421,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { /** {@inheritDoc} */ @Override public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { - final IBluetoothHidDevice service = mService; + final IBluetoothHidDevice service = getService(); if (service != null) { try { return service.getDevicesMatchingConnectionStates(states); @@ -527,7 +438,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { /** {@inheritDoc} */ @Override public int getConnectionState(BluetoothDevice device) { - final IBluetoothHidDevice service = mService; + final IBluetoothHidDevice service = getService(); if (service != null) { try { return service.getConnectionState(device); @@ -584,7 +495,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { throw new IllegalArgumentException("callback parameter cannot be null"); } - final IBluetoothHidDevice service = mService; + final IBluetoothHidDevice service = getService(); if (service != null) { try { CallbackWrapper cbw = new CallbackWrapper(executor, callback); @@ -612,7 +523,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { public boolean unregisterApp() { boolean result = false; - final IBluetoothHidDevice service = mService; + final IBluetoothHidDevice service = getService(); if (service != null) { try { result = service.unregisterApp(); @@ -637,7 +548,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { public boolean sendReport(BluetoothDevice device, int id, byte[] data) { boolean result = false; - final IBluetoothHidDevice service = mService; + final IBluetoothHidDevice service = getService(); if (service != null) { try { result = service.sendReport(device, id, data); @@ -663,7 +574,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { public boolean replyReport(BluetoothDevice device, byte type, byte id, byte[] data) { boolean result = false; - final IBluetoothHidDevice service = mService; + final IBluetoothHidDevice service = getService(); if (service != null) { try { result = service.replyReport(device, type, id, data); @@ -687,7 +598,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { public boolean reportError(BluetoothDevice device, byte error) { boolean result = false; - final IBluetoothHidDevice service = mService; + final IBluetoothHidDevice service = getService(); if (service != null) { try { result = service.reportError(device, error); @@ -708,7 +619,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { * {@hide} */ public String getUserAppName() { - final IBluetoothHidDevice service = mService; + final IBluetoothHidDevice service = getService(); if (service != null) { try { @@ -734,7 +645,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { public boolean connect(BluetoothDevice device) { boolean result = false; - final IBluetoothHidDevice service = mService; + final IBluetoothHidDevice service = getService(); if (service != null) { try { result = service.connect(device); @@ -758,7 +669,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { public boolean disconnect(BluetoothDevice device) { boolean result = false; - final IBluetoothHidDevice service = mService; + final IBluetoothHidDevice service = getService(); if (service != null) { try { result = service.disconnect(device); diff --git a/core/java/android/bluetooth/BluetoothHidHost.java b/core/java/android/bluetooth/BluetoothHidHost.java index 7c925a1cb5ef..4afb382c0660 100644 --- a/core/java/android/bluetooth/BluetoothHidHost.java +++ b/core/java/android/bluetooth/BluetoothHidHost.java @@ -18,14 +18,10 @@ package android.bluetooth; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; -import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -220,97 +216,32 @@ public final class BluetoothHidHost implements BluetoothProfile { public static final String EXTRA_IDLE_TIME = "android.bluetooth.BluetoothHidHost.extra.IDLE_TIME"; - private Context mContext; - private ServiceListener mServiceListener; private BluetoothAdapter mAdapter; - private volatile IBluetoothHidHost mService; - - private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = - new IBluetoothStateChangeCallback.Stub() { - public void onBluetoothStateChange(boolean up) { - if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); - if (!up) { - if (VDBG) Log.d(TAG, "Unbinding service..."); - synchronized (mConnection) { - try { - if (mService != null) { - mService = null; - mContext.unbindService(mConnection); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } else { - synchronized (mConnection) { - try { - if (mService == null) { - if (VDBG) Log.d(TAG, "Binding service..."); - doBind(); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } + private final BluetoothProfileConnector<IBluetoothHidHost> mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.HID_HOST, + "BluetoothHidHost", IBluetoothHidHost.class.getName()) { + @Override + public IBluetoothHidHost getServiceInterface(IBinder service) { + return IBluetoothHidHost.Stub.asInterface(Binder.allowBlocking(service)); } - }; + }; /** * Create a BluetoothHidHost proxy object for interacting with the local * Bluetooth Service which handles the InputDevice profile */ - /*package*/ BluetoothHidHost(Context context, ServiceListener l) { - mContext = context; - mServiceListener = l; + /*package*/ BluetoothHidHost(Context context, ServiceListener listener) { mAdapter = BluetoothAdapter.getDefaultAdapter(); - - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - - doBind(); - } - - boolean doBind() { - Intent intent = new Intent(IBluetoothHidHost.class.getName()); - ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); - intent.setComponent(comp); - if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - UserHandle.CURRENT_OR_SELF)) { - Log.e(TAG, "Could not bind to Bluetooth HID Service with " + intent); - return false; - } - return true; + mProfileConnector.connect(context, listener); } /*package*/ void close() { if (VDBG) log("close()"); - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); - } catch (Exception e) { - Log.e(TAG, "", e); - } - } + mProfileConnector.disconnect(); + } - synchronized (mConnection) { - if (mService != null) { - try { - mService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } - mServiceListener = null; + private IBluetoothHidHost getService() { + return mProfileConnector.getService(); } /** @@ -334,7 +265,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.connect(device); @@ -374,7 +305,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.disconnect(device); @@ -393,7 +324,7 @@ public final class BluetoothHidHost implements BluetoothProfile { @Override public List<BluetoothDevice> getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled()) { try { return service.getConnectedDevices(); @@ -412,7 +343,7 @@ public final class BluetoothHidHost implements BluetoothProfile { @Override public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled()) { try { return service.getDevicesMatchingConnectionStates(states); @@ -431,7 +362,7 @@ public final class BluetoothHidHost implements BluetoothProfile { @Override public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getConnectionState(device); @@ -461,7 +392,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && priority != BluetoothProfile.PRIORITY_ON) { @@ -493,7 +424,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getPriority(device); @@ -506,26 +437,6 @@ public final class BluetoothHidHost implements BluetoothProfile { return BluetoothProfile.PRIORITY_OFF; } - private final ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothHidHost.Stub.asInterface(Binder.allowBlocking(service)); - - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.HID_HOST, - BluetoothHidHost.this); - } - } - - public void onServiceDisconnected(ComponentName className) { - if (DBG) Log.d(TAG, "Proxy object disconnected"); - mService = null; - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.HID_HOST); - } - } - }; - private boolean isEnabled() { return mAdapter.getState() == BluetoothAdapter.STATE_ON; } @@ -545,7 +456,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ public boolean virtualUnplug(BluetoothDevice device) { if (DBG) log("virtualUnplug(" + device + ")"); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.virtualUnplug(device); @@ -571,7 +482,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ public boolean getProtocolMode(BluetoothDevice device) { if (VDBG) log("getProtocolMode(" + device + ")"); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getProtocolMode(device); @@ -595,7 +506,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ public boolean setProtocolMode(BluetoothDevice device, int protocolMode) { if (DBG) log("setProtocolMode(" + device + ")"); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.setProtocolMode(device, protocolMode); @@ -626,7 +537,7 @@ public final class BluetoothHidHost implements BluetoothProfile { log("getReport(" + device + "), reportType=" + reportType + " reportId=" + reportId + "bufferSize=" + bufferSize); } - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getReport(device, reportType, reportId, bufferSize); @@ -652,7 +563,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ public boolean setReport(BluetoothDevice device, byte reportType, String report) { if (VDBG) log("setReport(" + device + "), reportType=" + reportType + " report=" + report); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.setReport(device, reportType, report); @@ -677,7 +588,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ public boolean sendData(BluetoothDevice device, String report) { if (DBG) log("sendData(" + device + "), report=" + report); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.sendData(device, report); @@ -701,7 +612,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ public boolean getIdleTime(BluetoothDevice device) { if (DBG) log("getIdletime(" + device + ")"); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getIdleTime(device); @@ -726,7 +637,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ public boolean setIdleTime(BluetoothDevice device, byte idleTime) { if (DBG) log("setIdletime(" + device + "), idleTime=" + idleTime); - final IBluetoothHidHost service = mService; + final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.setIdleTime(device, idleTime); diff --git a/core/java/android/bluetooth/BluetoothMap.java b/core/java/android/bluetooth/BluetoothMap.java index fc5f830a8940..dd2f150ad4eb 100644 --- a/core/java/android/bluetooth/BluetoothMap.java +++ b/core/java/android/bluetooth/BluetoothMap.java @@ -17,14 +17,10 @@ package android.bluetooth; import android.annotation.UnsupportedAppUsage; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; -import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -45,11 +41,6 @@ public final class BluetoothMap implements BluetoothProfile { public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED"; - private volatile IBluetoothMap mService; - private final Context mContext; - private ServiceListener mServiceListener; - private BluetoothAdapter mAdapter; - /** There was an error trying to obtain the state */ public static final int STATE_ERROR = -1; @@ -58,64 +49,23 @@ public final class BluetoothMap implements BluetoothProfile { /** Connection canceled before completion. */ public static final int RESULT_CANCELED = 2; - private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = - new IBluetoothStateChangeCallback.Stub() { - public void onBluetoothStateChange(boolean up) { - if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); - if (!up) { - if (VDBG) Log.d(TAG, "Unbinding service..."); - synchronized (mConnection) { - try { - mService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } else { - synchronized (mConnection) { - try { - if (mService == null) { - if (VDBG) Log.d(TAG, "Binding service..."); - doBind(); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } + private BluetoothAdapter mAdapter; + private final BluetoothProfileConnector<IBluetoothMap> mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.MAP, + "BluetoothMap", IBluetoothMap.class.getName()) { + @Override + public IBluetoothMap getServiceInterface(IBinder service) { + return IBluetoothMap.Stub.asInterface(Binder.allowBlocking(service)); } - }; + }; /** * Create a BluetoothMap proxy object. */ - /*package*/ BluetoothMap(Context context, ServiceListener l) { + /*package*/ BluetoothMap(Context context, ServiceListener listener) { if (DBG) Log.d(TAG, "Create BluetoothMap proxy object"); - mContext = context; - mServiceListener = l; mAdapter = BluetoothAdapter.getDefaultAdapter(); - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - doBind(); - } - - boolean doBind() { - Intent intent = new Intent(IBluetoothMap.class.getName()); - ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); - intent.setComponent(comp); - if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - UserHandle.CURRENT_OR_SELF)) { - Log.e(TAG, "Could not bind to Bluetooth MAP Service with " + intent); - return false; - } - return true; + mProfileConnector.connect(context, listener); } protected void finalize() throws Throwable { @@ -133,26 +83,11 @@ public final class BluetoothMap implements BluetoothProfile { * are ok. */ public synchronized void close() { - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); - } catch (Exception e) { - Log.e(TAG, "", e); - } - } + mProfileConnector.disconnect(); + } - synchronized (mConnection) { - if (mService != null) { - try { - mService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } - mServiceListener = null; + private IBluetoothMap getService() { + return mProfileConnector.getService(); } /** @@ -163,7 +98,7 @@ public final class BluetoothMap implements BluetoothProfile { */ public int getState() { if (VDBG) log("getState()"); - final IBluetoothMap service = mService; + final IBluetoothMap service = getService(); if (service != null) { try { return service.getState(); @@ -185,7 +120,7 @@ public final class BluetoothMap implements BluetoothProfile { */ public BluetoothDevice getClient() { if (VDBG) log("getClient()"); - final IBluetoothMap service = mService; + final IBluetoothMap service = getService(); if (service != null) { try { return service.getClient(); @@ -206,7 +141,7 @@ public final class BluetoothMap implements BluetoothProfile { */ public boolean isConnected(BluetoothDevice device) { if (VDBG) log("isConnected(" + device + ")"); - final IBluetoothMap service = mService; + final IBluetoothMap service = getService(); if (service != null) { try { return service.isConnected(device); @@ -238,7 +173,7 @@ public final class BluetoothMap implements BluetoothProfile { @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - final IBluetoothMap service = mService; + final IBluetoothMap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.disconnect(device); @@ -279,7 +214,7 @@ public final class BluetoothMap implements BluetoothProfile { */ public List<BluetoothDevice> getConnectedDevices() { if (DBG) log("getConnectedDevices()"); - final IBluetoothMap service = mService; + final IBluetoothMap service = getService(); if (service != null && isEnabled()) { try { return service.getConnectedDevices(); @@ -299,7 +234,7 @@ public final class BluetoothMap implements BluetoothProfile { */ public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { if (DBG) log("getDevicesMatchingStates()"); - final IBluetoothMap service = mService; + final IBluetoothMap service = getService(); if (service != null && isEnabled()) { try { return service.getDevicesMatchingConnectionStates(states); @@ -319,7 +254,7 @@ public final class BluetoothMap implements BluetoothProfile { */ public int getConnectionState(BluetoothDevice device) { if (DBG) log("getConnectionState(" + device + ")"); - final IBluetoothMap service = mService; + final IBluetoothMap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getConnectionState(device); @@ -345,7 +280,7 @@ public final class BluetoothMap implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); - final IBluetoothMap service = mService; + final IBluetoothMap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && priority != BluetoothProfile.PRIORITY_ON) { @@ -374,7 +309,7 @@ public final class BluetoothMap implements BluetoothProfile { */ public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - final IBluetoothMap service = mService; + final IBluetoothMap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getPriority(device); @@ -387,24 +322,6 @@ public final class BluetoothMap implements BluetoothProfile { return PRIORITY_OFF; } - private final ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) log("Proxy object connected"); - mService = IBluetoothMap.Stub.asInterface(Binder.allowBlocking(service)); - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.MAP, BluetoothMap.this); - } - } - - public void onServiceDisconnected(ComponentName className) { - if (DBG) log("Proxy object disconnected"); - mService = null; - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.MAP); - } - } - }; - private static void log(String msg) { Log.d(TAG, msg); } diff --git a/core/java/android/bluetooth/BluetoothMapClient.java b/core/java/android/bluetooth/BluetoothMapClient.java index 1c82e1984b66..ec0180c5adde 100644 --- a/core/java/android/bluetooth/BluetoothMapClient.java +++ b/core/java/android/bluetooth/BluetoothMapClient.java @@ -18,14 +18,11 @@ package android.bluetooth; import android.annotation.UnsupportedAppUsage; import android.app.PendingIntent; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.net.Uri; +import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; -import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -61,11 +58,6 @@ public final class BluetoothMapClient implements BluetoothProfile { public static final String EXTRA_SENDER_CONTACT_NAME = "android.bluetooth.mapmce.profile.extra.SENDER_CONTACT_NAME"; - private volatile IBluetoothMapClient mService; - private final Context mContext; - private ServiceListener mServiceListener; - private BluetoothAdapter mAdapter; - /** There was an error trying to obtain the state */ public static final int STATE_ERROR = -1; @@ -76,64 +68,23 @@ public final class BluetoothMapClient implements BluetoothProfile { private static final int UPLOADING_FEATURE_BITMASK = 0x08; - private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = - new IBluetoothStateChangeCallback.Stub() { - public void onBluetoothStateChange(boolean up) { - if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); - if (!up) { - if (VDBG) Log.d(TAG, "Unbinding service..."); - synchronized (mConnection) { - try { - mService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } else { - synchronized (mConnection) { - try { - if (mService == null) { - if (VDBG) Log.d(TAG, "Binding service..."); - doBind(); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } + private BluetoothAdapter mAdapter; + private final BluetoothProfileConnector<IBluetoothMapClient> mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.MAP_CLIENT, + "BluetoothMapClient", IBluetoothMapClient.class.getName()) { + @Override + public IBluetoothMapClient getServiceInterface(IBinder service) { + return IBluetoothMapClient.Stub.asInterface(Binder.allowBlocking(service)); } - }; + }; /** * Create a BluetoothMapClient proxy object. */ - /*package*/ BluetoothMapClient(Context context, ServiceListener l) { + /*package*/ BluetoothMapClient(Context context, ServiceListener listener) { if (DBG) Log.d(TAG, "Create BluetoothMapClient proxy object"); - mContext = context; - mServiceListener = l; mAdapter = BluetoothAdapter.getDefaultAdapter(); - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - doBind(); - } - - boolean doBind() { - Intent intent = new Intent(IBluetoothMapClient.class.getName()); - ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); - intent.setComponent(comp); - if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - UserHandle.CURRENT_OR_SELF)) { - Log.e(TAG, "Could not bind to Bluetooth MAP MCE Service with " + intent); - return false; - } - return true; + mProfileConnector.connect(context, listener); } protected void finalize() throws Throwable { @@ -151,26 +102,11 @@ public final class BluetoothMapClient implements BluetoothProfile { * are ok. */ public void close() { - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); - } catch (Exception e) { - Log.e(TAG, "", e); - } - } + mProfileConnector.disconnect(); + } - synchronized (mConnection) { - if (mService != null) { - try { - mService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } - mServiceListener = null; + private IBluetoothMapClient getService() { + return mProfileConnector.getService(); } /** @@ -180,7 +116,7 @@ public final class BluetoothMapClient implements BluetoothProfile { */ public boolean isConnected(BluetoothDevice device) { if (VDBG) Log.d(TAG, "isConnected(" + device + ")"); - final IBluetoothMapClient service = mService; + final IBluetoothMapClient service = getService(); if (service != null) { try { return service.isConnected(device); @@ -200,7 +136,7 @@ public final class BluetoothMapClient implements BluetoothProfile { */ public boolean connect(BluetoothDevice device) { if (DBG) Log.d(TAG, "connect(" + device + ")" + "for MAPS MCE"); - final IBluetoothMapClient service = mService; + final IBluetoothMapClient service = getService(); if (service != null) { try { return service.connect(device); @@ -222,7 +158,7 @@ public final class BluetoothMapClient implements BluetoothProfile { */ public boolean disconnect(BluetoothDevice device) { if (DBG) Log.d(TAG, "disconnect(" + device + ")"); - final IBluetoothMapClient service = mService; + final IBluetoothMapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.disconnect(device); @@ -242,7 +178,7 @@ public final class BluetoothMapClient implements BluetoothProfile { @Override public List<BluetoothDevice> getConnectedDevices() { if (DBG) Log.d(TAG, "getConnectedDevices()"); - final IBluetoothMapClient service = mService; + final IBluetoothMapClient service = getService(); if (service != null && isEnabled()) { try { return service.getConnectedDevices(); @@ -263,7 +199,7 @@ public final class BluetoothMapClient implements BluetoothProfile { @Override public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { if (DBG) Log.d(TAG, "getDevicesMatchingStates()"); - final IBluetoothMapClient service = mService; + final IBluetoothMapClient service = getService(); if (service != null && isEnabled()) { try { return service.getDevicesMatchingConnectionStates(states); @@ -284,7 +220,7 @@ public final class BluetoothMapClient implements BluetoothProfile { @Override public int getConnectionState(BluetoothDevice device) { if (DBG) Log.d(TAG, "getConnectionState(" + device + ")"); - final IBluetoothMapClient service = mService; + final IBluetoothMapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getConnectionState(device); @@ -308,7 +244,7 @@ public final class BluetoothMapClient implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) Log.d(TAG, "setPriority(" + device + ", " + priority + ")"); - final IBluetoothMapClient service = mService; + final IBluetoothMapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && priority != BluetoothProfile.PRIORITY_ON) { @@ -337,7 +273,7 @@ public final class BluetoothMapClient implements BluetoothProfile { */ public int getPriority(BluetoothDevice device) { if (VDBG) Log.d(TAG, "getPriority(" + device + ")"); - final IBluetoothMapClient service = mService; + final IBluetoothMapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getPriority(device); @@ -366,7 +302,7 @@ public final class BluetoothMapClient implements BluetoothProfile { public boolean sendMessage(BluetoothDevice device, Uri[] contacts, String message, PendingIntent sentIntent, PendingIntent deliveredIntent) { if (DBG) Log.d(TAG, "sendMessage(" + device + ", " + contacts + ", " + message); - final IBluetoothMapClient service = mService; + final IBluetoothMapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.sendMessage(device, contacts, message, sentIntent, deliveredIntent); @@ -386,7 +322,7 @@ public final class BluetoothMapClient implements BluetoothProfile { */ public boolean getUnreadMessages(BluetoothDevice device) { if (DBG) Log.d(TAG, "getUnreadMessages(" + device + ")"); - final IBluetoothMapClient service = mService; + final IBluetoothMapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getUnreadMessages(device); @@ -406,34 +342,16 @@ public final class BluetoothMapClient implements BluetoothProfile { * MapSupportedFeatures field is set. False is returned otherwise. */ public boolean isUploadingSupported(BluetoothDevice device) { + final IBluetoothMapClient service = getService(); try { - return (mService != null && isEnabled() && isValidDevice(device)) - && ((mService.getSupportedFeatures(device) & UPLOADING_FEATURE_BITMASK) > 0); + return (service != null && isEnabled() && isValidDevice(device)) + && ((service.getSupportedFeatures(device) & UPLOADING_FEATURE_BITMASK) > 0); } catch (RemoteException e) { Log.e(TAG, e.getMessage()); } return false; } - private final ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothMapClient.Stub.asInterface(service); - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.MAP_CLIENT, - BluetoothMapClient.this); - } - } - - public void onServiceDisconnected(ComponentName className) { - if (DBG) Log.d(TAG, "Proxy object disconnected"); - mService = null; - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.MAP_CLIENT); - } - } - }; - private boolean isEnabled() { BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) return true; diff --git a/core/java/android/bluetooth/BluetoothPan.java b/core/java/android/bluetooth/BluetoothPan.java index 8923d734c844..fb78789ba8ad 100644 --- a/core/java/android/bluetooth/BluetoothPan.java +++ b/core/java/android/bluetooth/BluetoothPan.java @@ -19,14 +19,10 @@ package android.bluetooth; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.UnsupportedAppUsage; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; -import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -122,108 +118,42 @@ public final class BluetoothPan implements BluetoothProfile { */ public static final int PAN_OPERATION_SUCCESS = 1004; - private Context mContext; - private ServiceListener mServiceListener; private BluetoothAdapter mAdapter; - private volatile IBluetoothPan mPanService; + private final BluetoothProfileConnector<IBluetoothPan> mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.PAN, + "BluetoothPan", IBluetoothPan.class.getName()) { + @Override + public IBluetoothPan getServiceInterface(IBinder service) { + return IBluetoothPan.Stub.asInterface(Binder.allowBlocking(service)); + } + }; + /** * Create a BluetoothPan proxy object for interacting with the local * Bluetooth Service which handles the Pan profile */ @UnsupportedAppUsage - /*package*/ BluetoothPan(Context context, ServiceListener l) { - mContext = context; - mServiceListener = l; + /*package*/ BluetoothPan(Context context, ServiceListener listener) { mAdapter = BluetoothAdapter.getDefaultAdapter(); - try { - mAdapter.getBluetoothManager().registerStateChangeCallback(mStateChangeCallback); - } catch (RemoteException re) { - Log.w(TAG, "Unable to register BluetoothStateChangeCallback", re); - } - if (VDBG) Log.d(TAG, "BluetoothPan() call bindService"); - doBind(); - } - - @UnsupportedAppUsage - boolean doBind() { - Intent intent = new Intent(IBluetoothPan.class.getName()); - ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); - intent.setComponent(comp); - if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - UserHandle.CURRENT_OR_SELF)) { - Log.e(TAG, "Could not bind to Bluetooth Pan Service with " + intent); - return false; - } - return true; + mProfileConnector.connect(context, listener); } @UnsupportedAppUsage /*package*/ void close() { if (VDBG) log("close()"); + mProfileConnector.disconnect(); + } - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.unregisterStateChangeCallback(mStateChangeCallback); - } catch (RemoteException re) { - Log.w(TAG, "Unable to unregister BluetoothStateChangeCallback", re); - } - } - - synchronized (mConnection) { - if (mPanService != null) { - try { - mPanService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } - mServiceListener = null; + private IBluetoothPan getService() { + return mProfileConnector.getService(); } + protected void finalize() { close(); } - private final IBluetoothStateChangeCallback mStateChangeCallback = - new IBluetoothStateChangeCallback.Stub() { - - @Override - public void onBluetoothStateChange(boolean on) { - // Handle enable request to bind again. - Log.d(TAG, "onBluetoothStateChange on: " + on); - if (on) { - try { - if (mPanService == null) { - if (VDBG) Log.d(TAG, "onBluetoothStateChange calling doBind()"); - doBind(); - } - - } catch (IllegalStateException e) { - Log.e(TAG, "onBluetoothStateChange: could not bind to PAN service: ", - e); - - } catch (SecurityException e) { - Log.e(TAG, "onBluetoothStateChange: could not bind to PAN service: ", - e); - } - } else { - if (VDBG) Log.d(TAG, "Unbinding service..."); - synchronized (mConnection) { - try { - mPanService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } - } - }; - /** * Initiate connection to a profile of the remote bluetooth device. * @@ -244,7 +174,7 @@ public final class BluetoothPan implements BluetoothProfile { @UnsupportedAppUsage public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")"); - final IBluetoothPan service = mPanService; + final IBluetoothPan service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.connect(device); @@ -285,7 +215,7 @@ public final class BluetoothPan implements BluetoothProfile { @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - final IBluetoothPan service = mPanService; + final IBluetoothPan service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.disconnect(device); @@ -304,7 +234,7 @@ public final class BluetoothPan implements BluetoothProfile { @Override public List<BluetoothDevice> getConnectedDevices() { if (VDBG) log("getConnectedDevices()"); - final IBluetoothPan service = mPanService; + final IBluetoothPan service = getService(); if (service != null && isEnabled()) { try { return service.getConnectedDevices(); @@ -323,7 +253,7 @@ public final class BluetoothPan implements BluetoothProfile { @Override public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); - final IBluetoothPan service = mPanService; + final IBluetoothPan service = getService(); if (service != null && isEnabled()) { try { return service.getDevicesMatchingConnectionStates(states); @@ -342,7 +272,7 @@ public final class BluetoothPan implements BluetoothProfile { @Override public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); - final IBluetoothPan service = mPanService; + final IBluetoothPan service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getConnectionState(device); @@ -358,7 +288,7 @@ public final class BluetoothPan implements BluetoothProfile { @UnsupportedAppUsage public void setBluetoothTethering(boolean value) { if (DBG) log("setBluetoothTethering(" + value + ")"); - final IBluetoothPan service = mPanService; + final IBluetoothPan service = getService(); if (service != null && isEnabled()) { try { service.setBluetoothTethering(value); @@ -371,7 +301,7 @@ public final class BluetoothPan implements BluetoothProfile { @UnsupportedAppUsage public boolean isTetheringOn() { if (VDBG) log("isTetheringOn()"); - final IBluetoothPan service = mPanService; + final IBluetoothPan service = getService(); if (service != null && isEnabled()) { try { return service.isTetheringOn(); @@ -382,25 +312,6 @@ public final class BluetoothPan implements BluetoothProfile { return false; } - private final ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) Log.d(TAG, "BluetoothPAN Proxy object connected"); - mPanService = IBluetoothPan.Stub.asInterface(Binder.allowBlocking(service)); - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.PAN, - BluetoothPan.this); - } - } - - public void onServiceDisconnected(ComponentName className) { - if (DBG) Log.d(TAG, "BluetoothPAN Proxy object disconnected"); - mPanService = null; - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.PAN); - } - } - }; - @UnsupportedAppUsage private boolean isEnabled() { return mAdapter.getState() == BluetoothAdapter.STATE_ON; diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java index 359bec686a8a..d94c65742e6c 100644 --- a/core/java/android/bluetooth/BluetoothPbap.java +++ b/core/java/android/bluetooth/BluetoothPbap.java @@ -118,28 +118,9 @@ public class BluetoothPbap implements BluetoothProfile { public void onBluetoothStateChange(boolean up) { log("onBluetoothStateChange: up=" + up); if (!up) { - log("Unbinding service..."); - synchronized (mConnection) { - try { - if (mService != null) { - mService = null; - mContext.unbindService(mConnection); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } - } + doUnbind(); } else { - synchronized (mConnection) { - try { - if (mService == null) { - log("Binding service..."); - doBind(); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } - } + doBind(); } } }; @@ -155,25 +136,51 @@ public class BluetoothPbap implements BluetoothProfile { if (mgr != null) { try { mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException e) { - Log.e(TAG, "", e); + } catch (RemoteException re) { + Log.e(TAG, "", re); } } doBind(); } boolean doBind() { - Intent intent = new Intent(IBluetoothPbap.class.getName()); - ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); - intent.setComponent(comp); - if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - UserHandle.CURRENT_OR_SELF)) { - Log.e(TAG, "Could not bind to Bluetooth Pbap Service with " + intent); - return false; + synchronized (mConnection) { + try { + if (mService == null) { + log("Binding service..."); + Intent intent = new Intent(IBluetoothPbap.class.getName()); + ComponentName comp = intent.resolveSystemService( + mContext.getPackageManager(), 0); + intent.setComponent(comp); + if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, + UserHandle.CURRENT_OR_SELF)) { + Log.e(TAG, "Could not bind to Bluetooth Pbap Service with " + intent); + return false; + } + } + } catch (SecurityException se) { + Log.e(TAG, "", se); + return false; + } } return true; } + private void doUnbind() { + synchronized (mConnection) { + if (mService != null) { + log("Unbinding service..."); + try { + mContext.unbindService(mConnection); + } catch (IllegalArgumentException ie) { + Log.e(TAG, "", ie); + } finally { + mService = null; + } + } + } + } + protected void finalize() throws Throwable { try { close(); @@ -193,21 +200,11 @@ public class BluetoothPbap implements BluetoothProfile { if (mgr != null) { try { mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); - } catch (Exception e) { - Log.e(TAG, "", e); - } - } - - synchronized (mConnection) { - if (mService != null) { - try { - mService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } + } catch (RemoteException re) { + Log.e(TAG, "", re); } } + doUnbind(); mServiceListener = null; } @@ -313,7 +310,7 @@ public class BluetoothPbap implements BluetoothProfile { public void onServiceDisconnected(ComponentName className) { log("Proxy object disconnected"); - mService = null; + doUnbind(); if (mServiceListener != null) { mServiceListener.onServiceDisconnected(); } diff --git a/core/java/android/bluetooth/BluetoothPbapClient.java b/core/java/android/bluetooth/BluetoothPbapClient.java index cbc96c073338..d70e1e7f3f5f 100644 --- a/core/java/android/bluetooth/BluetoothPbapClient.java +++ b/core/java/android/bluetooth/BluetoothPbapClient.java @@ -16,14 +16,10 @@ package android.bluetooth; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; -import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -43,11 +39,6 @@ public final class BluetoothPbapClient implements BluetoothProfile { public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pbapclient.profile.action.CONNECTION_STATE_CHANGED"; - private volatile IBluetoothPbapClient mService; - private final Context mContext; - private ServiceListener mServiceListener; - private BluetoothAdapter mAdapter; - /** There was an error trying to obtain the state */ public static final int STATE_ERROR = -1; @@ -56,72 +47,25 @@ public final class BluetoothPbapClient implements BluetoothProfile { /** Connection canceled before completion. */ public static final int RESULT_CANCELED = 2; - private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = - new IBluetoothStateChangeCallback.Stub() { - public void onBluetoothStateChange(boolean up) { - if (DBG) { - Log.d(TAG, "onBluetoothStateChange: PBAP CLIENT up=" + up); - } - if (!up) { - if (VDBG) { - Log.d(TAG, "Unbinding service..."); - } - synchronized (mConnection) { - try { - mService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } else { - synchronized (mConnection) { - try { - if (mService == null) { - if (VDBG) { - Log.d(TAG, "Binding service..."); - } - doBind(); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } + private BluetoothAdapter mAdapter; + private final BluetoothProfileConnector<IBluetoothPbapClient> mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.PBAP_CLIENT, + "BluetoothPbapClient", IBluetoothPbapClient.class.getName()) { + @Override + public IBluetoothPbapClient getServiceInterface(IBinder service) { + return IBluetoothPbapClient.Stub.asInterface(Binder.allowBlocking(service)); } - }; + }; /** * Create a BluetoothPbapClient proxy object. */ - BluetoothPbapClient(Context context, ServiceListener l) { + BluetoothPbapClient(Context context, ServiceListener listener) { if (DBG) { Log.d(TAG, "Create BluetoothPbapClient proxy object"); } - mContext = context; - mServiceListener = l; mAdapter = BluetoothAdapter.getDefaultAdapter(); - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - doBind(); - } - - private boolean doBind() { - Intent intent = new Intent(IBluetoothPbapClient.class.getName()); - ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); - intent.setComponent(comp); - if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - UserHandle.CURRENT_OR_SELF)) { - Log.e(TAG, "Could not bind to Bluetooth PBAP Client Service with " + intent); - return false; - } - return true; + mProfileConnector.connect(context, listener); } protected void finalize() throws Throwable { @@ -139,26 +83,11 @@ public final class BluetoothPbapClient implements BluetoothProfile { * are ok. */ public synchronized void close() { - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); - } catch (Exception e) { - Log.e(TAG, "", e); - } - } + mProfileConnector.disconnect(); + } - synchronized (mConnection) { - if (mService != null) { - try { - mService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } - mServiceListener = null; + private IBluetoothPbapClient getService() { + return mProfileConnector.getService(); } /** @@ -174,7 +103,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { if (DBG) { log("connect(" + device + ") for PBAP Client."); } - final IBluetoothPbapClient service = mService; + final IBluetoothPbapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.connect(device); @@ -199,7 +128,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { if (DBG) { log("disconnect(" + device + ")" + new Exception()); } - final IBluetoothPbapClient service = mService; + final IBluetoothPbapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { service.disconnect(device); @@ -226,7 +155,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { if (DBG) { log("getConnectedDevices()"); } - final IBluetoothPbapClient service = mService; + final IBluetoothPbapClient service = getService(); if (service != null && isEnabled()) { try { return service.getConnectedDevices(); @@ -251,7 +180,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { if (DBG) { log("getDevicesMatchingStates()"); } - final IBluetoothPbapClient service = mService; + final IBluetoothPbapClient service = getService(); if (service != null && isEnabled()) { try { return service.getDevicesMatchingConnectionStates(states); @@ -276,7 +205,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { if (DBG) { log("getConnectionState(" + device + ")"); } - final IBluetoothPbapClient service = mService; + final IBluetoothPbapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getConnectionState(device); @@ -291,29 +220,6 @@ public final class BluetoothPbapClient implements BluetoothProfile { return BluetoothProfile.STATE_DISCONNECTED; } - private final ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) { - log("Proxy object connected"); - } - mService = IBluetoothPbapClient.Stub.asInterface(Binder.allowBlocking(service)); - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.PBAP_CLIENT, - BluetoothPbapClient.this); - } - } - - public void onServiceDisconnected(ComponentName className) { - if (DBG) { - log("Proxy object disconnected"); - } - mService = null; - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.PBAP_CLIENT); - } - } - }; - private static void log(String msg) { Log.d(TAG, msg); } @@ -346,7 +252,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { if (DBG) { log("setPriority(" + device + ", " + priority + ")"); } - final IBluetoothPbapClient service = mService; + final IBluetoothPbapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && priority != BluetoothProfile.PRIORITY_ON) { @@ -379,7 +285,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { if (VDBG) { log("getPriority(" + device + ")"); } - final IBluetoothPbapClient service = mService; + final IBluetoothPbapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getPriority(device); diff --git a/core/java/android/bluetooth/BluetoothProfileConnector.java b/core/java/android/bluetooth/BluetoothProfileConnector.java new file mode 100644 index 000000000000..d9987249a6e2 --- /dev/null +++ b/core/java/android/bluetooth/BluetoothProfileConnector.java @@ -0,0 +1,166 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.bluetooth; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.UserHandle; +import android.util.Log; + +/** + * Connector for Bluetooth profile proxies to bind manager service and + * profile services + * @param <T> The Bluetooth profile interface for this connection. + * @hide + */ +public abstract class BluetoothProfileConnector<T> { + private int mProfileId; + private BluetoothProfile.ServiceListener mServiceListener; + private BluetoothProfile mProfileProxy; + private Context mContext; + private String mProfileName; + private String mServiceName; + private volatile T mService; + + private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = + new IBluetoothStateChangeCallback.Stub() { + public void onBluetoothStateChange(boolean up) { + if (up) { + doBind(); + } else { + doUnbind(); + } + } + }; + + private final ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + logDebug("Proxy object connected"); + mService = getServiceInterface(service); + + if (mServiceListener != null) { + mServiceListener.onServiceConnected(mProfileId, mProfileProxy); + } + } + + public void onServiceDisconnected(ComponentName className) { + logDebug("Proxy object disconnected"); + doUnbind(); + if (mServiceListener != null) { + mServiceListener.onServiceDisconnected(BluetoothProfile.A2DP); + } + } + }; + + BluetoothProfileConnector(BluetoothProfile profile, int profileId, String profileName, + String serviceName) { + mProfileId = profileId; + mProfileProxy = profile; + mProfileName = profileName; + mServiceName = serviceName; + } + + private boolean doBind() { + synchronized (mConnection) { + if (mService == null) { + logDebug("Binding service..."); + try { + Intent intent = new Intent(mServiceName); + ComponentName comp = intent.resolveSystemService( + mContext.getPackageManager(), 0); + intent.setComponent(comp); + if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, + UserHandle.CURRENT_OR_SELF)) { + logError("Could not bind to Bluetooth Service with " + intent); + return false; + } + } catch (SecurityException se) { + logError("Failed to bind service. " + se); + return false; + } + } + } + return true; + } + + private void doUnbind() { + synchronized (mConnection) { + if (mService != null) { + logDebug("Unbinding service..."); + try { + mContext.unbindService(mConnection); + } catch (IllegalArgumentException ie) { + logError("Unable to unbind service: " + ie); + } finally { + mService = null; + } + } + } + } + + void connect(Context context, BluetoothProfile.ServiceListener listener) { + mContext = context; + mServiceListener = listener; + IBluetoothManager mgr = BluetoothAdapter.getDefaultAdapter().getBluetoothManager(); + if (mgr != null) { + try { + mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); + } catch (RemoteException re) { + logError("Failed to register state change callback. " + re); + } + } + doBind(); + } + + void disconnect() { + mServiceListener = null; + IBluetoothManager mgr = BluetoothAdapter.getDefaultAdapter().getBluetoothManager(); + if (mgr != null) { + try { + mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); + } catch (RemoteException re) { + logError("Failed to unregister state change callback" + re); + } + } + doUnbind(); + } + + T getService() { + return mService; + } + + /** + * This abstract function is used to implement method to get the + * connected Bluetooth service interface. + * @param service the connected binder service. + * @return T the binder interface of {@code service}. + * @hide + */ + public abstract T getServiceInterface(IBinder service); + + private void logDebug(String log) { + Log.d(mProfileName, log); + } + + private void logError(String log) { + Log.e(mProfileName, log); + } +} diff --git a/core/java/android/bluetooth/BluetoothSap.java b/core/java/android/bluetooth/BluetoothSap.java index ebf6bed54475..e0610c890559 100644 --- a/core/java/android/bluetooth/BluetoothSap.java +++ b/core/java/android/bluetooth/BluetoothSap.java @@ -17,14 +17,10 @@ package android.bluetooth; import android.annotation.UnsupportedAppUsage; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; -import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -70,11 +66,6 @@ public final class BluetoothSap implements BluetoothProfile { public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED"; - private volatile IBluetoothSap mService; - private final Context mContext; - private ServiceListener mServiceListener; - private BluetoothAdapter mAdapter; - /** * There was an error trying to obtain the state. * @@ -96,64 +87,23 @@ public final class BluetoothSap implements BluetoothProfile { */ public static final int RESULT_CANCELED = 2; - private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = - new IBluetoothStateChangeCallback.Stub() { - public void onBluetoothStateChange(boolean up) { - if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); - if (!up) { - if (VDBG) Log.d(TAG, "Unbinding service..."); - synchronized (mConnection) { - try { - mService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } else { - synchronized (mConnection) { - try { - if (mService == null) { - if (VDBG) Log.d(TAG, "Binding service..."); - doBind(); - } - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } + private BluetoothAdapter mAdapter; + private final BluetoothProfileConnector<IBluetoothSap> mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.SAP, + "BluetoothSap", IBluetoothSap.class.getName()) { + @Override + public IBluetoothSap getServiceInterface(IBinder service) { + return IBluetoothSap.Stub.asInterface(Binder.allowBlocking(service)); } - }; + }; /** * Create a BluetoothSap proxy object. */ - /*package*/ BluetoothSap(Context context, ServiceListener l) { + /*package*/ BluetoothSap(Context context, ServiceListener listener) { if (DBG) Log.d(TAG, "Create BluetoothSap proxy object"); - mContext = context; - mServiceListener = l; mAdapter = BluetoothAdapter.getDefaultAdapter(); - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - } - doBind(); - } - - boolean doBind() { - Intent intent = new Intent(IBluetoothSap.class.getName()); - ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); - intent.setComponent(comp); - if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, - UserHandle.CURRENT_OR_SELF)) { - Log.e(TAG, "Could not bind to Bluetooth SAP Service with " + intent); - return false; - } - return true; + mProfileConnector.connect(context, listener); } protected void finalize() throws Throwable { @@ -173,26 +123,11 @@ public final class BluetoothSap implements BluetoothProfile { * @hide */ public synchronized void close() { - IBluetoothManager mgr = mAdapter.getBluetoothManager(); - if (mgr != null) { - try { - mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); - } catch (Exception e) { - Log.e(TAG, "", e); - } - } + mProfileConnector.disconnect(); + } - synchronized (mConnection) { - if (mService != null) { - try { - mService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG, "", re); - } - } - } - mServiceListener = null; + private IBluetoothSap getService() { + return mProfileConnector.getService(); } /** @@ -204,7 +139,7 @@ public final class BluetoothSap implements BluetoothProfile { */ public int getState() { if (VDBG) log("getState()"); - final IBluetoothSap service = mService; + final IBluetoothSap service = getService(); if (service != null) { try { return service.getState(); @@ -227,7 +162,7 @@ public final class BluetoothSap implements BluetoothProfile { */ public BluetoothDevice getClient() { if (VDBG) log("getClient()"); - final IBluetoothSap service = mService; + final IBluetoothSap service = getService(); if (service != null) { try { return service.getClient(); @@ -250,7 +185,7 @@ public final class BluetoothSap implements BluetoothProfile { */ public boolean isConnected(BluetoothDevice device) { if (VDBG) log("isConnected(" + device + ")"); - final IBluetoothSap service = mService; + final IBluetoothSap service = getService(); if (service != null) { try { return service.isConnected(device); @@ -285,7 +220,7 @@ public final class BluetoothSap implements BluetoothProfile { @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { if (DBG) log("disconnect(" + device + ")"); - final IBluetoothSap service = mService; + final IBluetoothSap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.disconnect(device); @@ -306,7 +241,7 @@ public final class BluetoothSap implements BluetoothProfile { */ public List<BluetoothDevice> getConnectedDevices() { if (DBG) log("getConnectedDevices()"); - final IBluetoothSap service = mService; + final IBluetoothSap service = getService(); if (service != null && isEnabled()) { try { return service.getConnectedDevices(); @@ -327,7 +262,7 @@ public final class BluetoothSap implements BluetoothProfile { */ public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { if (DBG) log("getDevicesMatchingStates()"); - final IBluetoothSap service = mService; + final IBluetoothSap service = getService(); if (service != null && isEnabled()) { try { return service.getDevicesMatchingConnectionStates(states); @@ -348,7 +283,7 @@ public final class BluetoothSap implements BluetoothProfile { */ public int getConnectionState(BluetoothDevice device) { if (DBG) log("getConnectionState(" + device + ")"); - final IBluetoothSap service = mService; + final IBluetoothSap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getConnectionState(device); @@ -373,7 +308,7 @@ public final class BluetoothSap implements BluetoothProfile { */ public boolean setPriority(BluetoothDevice device, int priority) { if (DBG) log("setPriority(" + device + ", " + priority + ")"); - final IBluetoothSap service = mService; + final IBluetoothSap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { if (priority != BluetoothProfile.PRIORITY_OFF && priority != BluetoothProfile.PRIORITY_ON) { @@ -399,7 +334,7 @@ public final class BluetoothSap implements BluetoothProfile { */ public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); - final IBluetoothSap service = mService; + final IBluetoothSap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { return service.getPriority(device); @@ -412,24 +347,6 @@ public final class BluetoothSap implements BluetoothProfile { return PRIORITY_OFF; } - private final ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) log("Proxy object connected"); - mService = IBluetoothSap.Stub.asInterface(Binder.allowBlocking(service)); - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.SAP, BluetoothSap.this); - } - } - - public void onServiceDisconnected(ComponentName className) { - if (DBG) log("Proxy object disconnected"); - mService = null; - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.SAP); - } - } - }; - private static void log(String msg) { Log.d(TAG, msg); } diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index 5f34f1b2bc0d..804677648d09 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -16,6 +16,8 @@ package android.content; +import static android.provider.DocumentsContract.EXTRA_ORIENTATION; + import android.accounts.Account; import android.annotation.IntDef; import android.annotation.NonNull; @@ -40,6 +42,7 @@ import android.graphics.Bitmap; import android.graphics.ImageDecoder; import android.graphics.ImageDecoder.ImageInfo; import android.graphics.ImageDecoder.Source; +import android.graphics.Matrix; import android.graphics.Point; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; @@ -56,6 +59,7 @@ import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; import android.os.storage.StorageManager; +import android.system.Int32Ref; import android.text.TextUtils; import android.util.EventLog; import android.util.Log; @@ -3563,9 +3567,14 @@ public abstract class ContentResolver implements ContentInterface { // Convert to Point, since that's what the API is defined as final Bundle opts = new Bundle(); opts.putParcelable(EXTRA_SIZE, Point.convert(size)); - - return ImageDecoder.decodeBitmap(ImageDecoder.createSource(() -> { - return content.openTypedAssetFile(uri, "image/*", opts, signal); + final Int32Ref orientation = new Int32Ref(0); + + Bitmap bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(() -> { + final AssetFileDescriptor afd = content.openTypedAssetFile(uri, "image/*", opts, + signal); + final Bundle extras = afd.getExtras(); + orientation.value = (extras != null) ? extras.getInt(EXTRA_ORIENTATION, 0) : 0; + return afd; }), (ImageDecoder decoder, ImageInfo info, Source source) -> { decoder.setAllocator(allocator); @@ -3581,6 +3590,20 @@ public abstract class ContentResolver implements ContentInterface { decoder.setTargetSampleSize(sample); } }); + + // Transform the bitmap if requested. We use a side-channel to + // communicate the orientation, since EXIF thumbnails don't contain + // the rotation flags of the original image. + if (orientation.value != 0) { + final int width = bitmap.getWidth(); + final int height = bitmap.getHeight(); + + final Matrix m = new Matrix(); + m.setRotate(orientation.value, width / 2, height / 2); + bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, m, false); + } + + return bitmap; } /** {@hide} */ diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 3dd510c6a6e6..01123968fcbd 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3009,7 +3009,14 @@ public abstract class Context { /** * Variation of {@link #bindService} that, in the specific case of isolated * services, allows the caller to generate multiple instances of a service - * from a single component declaration. + * from a single component declaration. In other words, you can use this to bind + * to a service that has specified {@link android.R.attr#isolatedProcess} and, in + * addition to the existing behavior of running in an isolated process, you can + * also through the arguments here have the system bring up multiple concurrent + * processes hosting their own instances of that service. The <var>instanceName</var> + * you provide here identifies the different instances, and you can use + * {@link #updateServiceGroup(ServiceConnection, int, int)} to tell the system how it + * should manage each of these instances. * * @param service Identifies the service to connect to. The Intent must * specify an explicit component name. @@ -3027,6 +3034,8 @@ public abstract class Context { * @throws IllegalArgumentException If the instanceName is invalid. * * @see #bindService + * @see #updateServiceGroup + * @see android.R.attr#isolatedProcess */ public boolean bindIsolatedService(@RequiresPermission @NonNull Intent service, @BindServiceFlags int flags, @NonNull String instanceName, @@ -3082,10 +3091,16 @@ public abstract class Context { * are considered to be related. Supplying 0 reverts to the default behavior * of not grouping. * @param importance Additional importance of the processes within a group. Upon calling - * here, this will override any previous group that was set for that - * process. This fine-tunes process killing of all processes within - * a related groups -- higher importance values will be killed before - * lower ones. + * here, this will override any previous importance that was set for that + * process. The most important process is 0, and higher values are + * successively less important. You can view this as describing how + * to order the processes in an array, with the processes at the end of + * the array being the least important. This value has no meaning besides + * indicating how processes should be ordered in that array one after the + * other. This provides a way to fine-tune the system's process killing, + * guiding it to kill processes at the end of the array first. + * + * @see #bindIsolatedService */ public void updateServiceGroup(@NonNull ServiceConnection conn, int group, int importance) { diff --git a/core/java/android/content/om/IOverlayManager.aidl b/core/java/android/content/om/IOverlayManager.aidl index 722c128c0502..43a4fe5bc414 100644 --- a/core/java/android/content/om/IOverlayManager.aidl +++ b/core/java/android/content/om/IOverlayManager.aidl @@ -152,4 +152,9 @@ interface IOverlayManager { * @param userId The user for which to change the overlay. */ boolean setLowestPriority(in String packageName, in int userId); + + /** + * Returns the list of default overlay packages, or an empty array if there are none. + */ + String[] getDefaultOverlayPackages(); } diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index d2f0fb3cc768..a8815ec6cfaa 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -1318,6 +1318,8 @@ public class PackageInstaller { public boolean isMultiPackage; /** {@hide} */ public boolean isStaged; + /** {@hide} */ + public long requiredInstalledVersionCode = PackageManager.VERSION_CODE_HIGHEST; /** * Construct parameters for a new package install session. @@ -1350,6 +1352,7 @@ public class PackageInstaller { installerPackageName = source.readString(); isMultiPackage = source.readBoolean(); isStaged = source.readBoolean(); + requiredInstalledVersionCode = source.readLong(); } /** {@hide} */ @@ -1372,6 +1375,7 @@ public class PackageInstaller { ret.installerPackageName = installerPackageName; ret.isMultiPackage = isMultiPackage; ret.isStaged = isStaged; + ret.requiredInstalledVersionCode = requiredInstalledVersionCode; return ret; } @@ -1509,11 +1513,6 @@ public class PackageInstaller { * state of the permission can be determined only at install time and cannot be * changed on updated or at a later point via the package manager APIs. * - * <p>The whitelisted non-immutably restricted permissions would be added to - * the {@link PackageManager#FLAG_PERMISSION_WHITELIST_INSTALLER installer whitelist} - * while the immutably restricted permissions would be added to the {@link - * PackageManager#FLAG_PERMISSION_WHITELIST_SYSTEM system whitelist} - * * @see PackageManager#addWhitelistedRestrictedPermission(String, String, int) * @see PackageManager#removeWhitelistedRestrictedPermission(String, String, int) */ @@ -1567,6 +1566,19 @@ public class PackageInstaller { } } + /** + * Require the given version of the package be installed. + * The install will only be allowed if the existing version code of + * the package installed on the device matches the given version code. + * Use {@link * PackageManager#VERSION_CODE_HIGHEST} to allow + * installation regardless of the currently installed package version. + * + * @hide + */ + public void setRequiredInstalledVersionCode(long versionCode) { + requiredInstalledVersionCode = versionCode; + } + /** {@hide} */ public void setInstallFlagsForcePermissionPrompt() { installFlags |= PackageManager.INSTALL_FORCE_PERMISSION_PROMPT; @@ -1708,6 +1720,7 @@ public class PackageInstaller { pw.printPair("installerPackageName", installerPackageName); pw.printPair("isMultiPackage", isMultiPackage); pw.printPair("isStaged", isStaged); + pw.printPair("requiredInstalledVersionCode", requiredInstalledVersionCode); pw.println(); } @@ -1736,6 +1749,7 @@ public class PackageInstaller { dest.writeString(installerPackageName); dest.writeBoolean(isMultiPackage); dest.writeBoolean(isStaged); + dest.writeLong(requiredInstalledVersionCode); } public static final Parcelable.Creator<SessionParams> diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index a95e094ce54c..dd5ca6706316 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -1423,6 +1423,14 @@ public abstract class PackageManager { */ public static final int INSTALL_FAILED_MULTIPACKAGE_INCONSISTENCY = -120; + /** + * Installation failed return code: the required installed version code + * does not match the currently installed package version code. + * + * @hide + */ + public static final int INSTALL_FAILED_WRONG_INSTALLED_VERSION = -121; + /** @hide */ @IntDef(flag = true, prefix = { "DELETE_" }, value = { DELETE_KEEP_DATA, @@ -6178,15 +6186,7 @@ public abstract class PackageManager { * @param activity The component name of the activity that is to be preferred. * * @hide - * - * @deprecated This function no longer does anything. It is the platform's - * responsibility to assign preferred activities and this cannot be modified - * directly. To determine the activities resolved by the platform, use - * {@link #resolveActivity} or {@link #queryIntentActivities}. To configure - * an app to be responsible for a particular role and to check current role - * holders, see {@link android.app.role.RoleManager}. */ - @Deprecated @SystemApi public void replacePreferredActivity(@NonNull IntentFilter filter, int match, @NonNull List<ComponentName> set, @NonNull ComponentName activity) { @@ -6926,6 +6926,7 @@ public abstract class PackageManager { case INSTALL_FAILED_BAD_DEX_METADATA: return "INSTALL_FAILED_BAD_DEX_METADATA"; case INSTALL_FAILED_MISSING_SPLIT: return "INSTALL_FAILED_MISSING_SPLIT"; case INSTALL_FAILED_BAD_SIGNATURE: return "INSTALL_FAILED_BAD_SIGNATURE"; + case INSTALL_FAILED_WRONG_INSTALLED_VERSION: return "INSTALL_FAILED_WRONG_INSTALLED_VERSION"; default: return Integer.toString(status); } } diff --git a/core/java/android/database/MatrixCursor.java b/core/java/android/database/MatrixCursor.java index a52e96e249f8..b0d399c64ea9 100644 --- a/core/java/android/database/MatrixCursor.java +++ b/core/java/android/database/MatrixCursor.java @@ -18,6 +18,7 @@ package android.database; import android.annotation.UnsupportedAppUsage; import android.os.Build; + import java.util.ArrayList; /** @@ -240,6 +241,12 @@ public class MatrixCursor extends AbstractCursor { } return this; } + + /** @hide */ + public final RowBuilder add(int columnIndex, Object value) { + data[(row * columnCount) + columnIndex] = value; + return this; + } } // AbstractCursor implementation. diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java index 4aa6fabe8989..099ae291c8f4 100644 --- a/core/java/android/hardware/Sensor.java +++ b/core/java/android/hardware/Sensor.java @@ -364,6 +364,8 @@ public final class Sensor { * count steps if it is not activated. This sensor is ideal for fitness tracking applications. * It is defined as an {@link Sensor#REPORTING_MODE_ON_CHANGE} sensor. * <p> + * This sensor requires permission {@code android.permission.ACTIVITY_RECOGNITION}. + * <p> * See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details. */ public static final int TYPE_STEP_COUNTER = 19; @@ -382,6 +384,8 @@ public final class Sensor { * gyroscope. This sensor uses lower power than the other rotation vectors, because it doesn't * use the gyroscope. However, it is more noisy and will work best outdoors. * <p> + * This sensor requires permission {@code android.permission.ACTIVITY_RECOGNITION}. + * <p> * See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details. */ public static final int TYPE_GEOMAGNETIC_ROTATION_VECTOR = 20; diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java index d7db1f5f8b13..226b8e549a9e 100644 --- a/core/java/android/hardware/camera2/params/OutputConfiguration.java +++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java @@ -36,6 +36,7 @@ import static com.android.internal.util.Preconditions.*; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; /** * A class for describing camera output, which contains a {@link Surface} and its specific @@ -692,7 +693,8 @@ public final class OutputConfiguration implements Parcelable { mIsShared != other.mIsShared || mConfiguredFormat != other.mConfiguredFormat || mConfiguredDataspace != other.mConfiguredDataspace || - mConfiguredGenerationId != other.mConfiguredGenerationId) + mConfiguredGenerationId != other.mConfiguredGenerationId || + !Objects.equals(mPhysicalCameraId, other.mPhysicalCameraId)) return false; int minLen = Math.min(mSurfaces.size(), other.mSurfaces.size()); diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java index 7349f0cbe55d..8596af107ac1 100644 --- a/core/java/android/hardware/face/FaceManager.java +++ b/core/java/android/hardware/face/FaceManager.java @@ -40,6 +40,7 @@ import android.os.IRemoteCallback; import android.os.Looper; import android.os.PowerManager; import android.os.RemoteException; +import android.os.Trace; import android.os.UserHandle; import android.util.Log; import android.util.Slog; @@ -225,6 +226,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan mAuthenticationCallback = callback; mCryptoObject = crypto; long sessionId = crypto != null ? crypto.getOpId() : 0; + Trace.beginSection("FaceManager#authenticate"); mService.authenticate(mToken, sessionId, userId, mServiceReceiver, flags, mContext.getOpPackageName()); } catch (RemoteException e) { @@ -236,6 +238,8 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */)); } + } finally { + Trace.endSection(); } } } @@ -276,6 +280,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan if (mService != null) { try { mEnrollmentCallback = callback; + Trace.beginSection("FaceManager#enroll"); mService.enroll(mToken, token, mServiceReceiver, mContext.getOpPackageName(), disabledFeatures); } catch (RemoteException e) { @@ -287,6 +292,8 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */)); } + } finally { + Trace.endSection(); } } } @@ -965,6 +972,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan @Override public void handleMessage(android.os.Message msg) { + Trace.beginSection("FaceManager#handleMessage: " + Integer.toString(msg.what)); switch (msg.what) { case MSG_ENROLL_RESULT: sendEnrollResult((Face) msg.obj, msg.arg1 /* remaining */); @@ -1000,6 +1008,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan default: Log.w(TAG, "Unknown message: " + msg.what); } + Trace.endSection(); } } diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 94c8b9116dd6..3a4741a97212 100755 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -978,11 +978,6 @@ public class Build { */ public static final int Q = CUR_DEVELOPMENT; - /** - * Stub for a potential new API level after P. - * @hide - */ - public static final int P0 = Q; } /** The type of build, like "user" or "eng". */ diff --git a/core/java/android/os/IncidentManager.java b/core/java/android/os/IncidentManager.java index 08afe31f05dd..1f61a3c47ff1 100644 --- a/core/java/android/os/IncidentManager.java +++ b/core/java/android/os/IncidentManager.java @@ -519,6 +519,13 @@ public class IncidentManager { android.Manifest.permission.PACKAGE_USAGE_STATS }) public @Nullable IncidentReport getIncidentReport(Uri uri) { + final String id = uri.getQueryParameter(URI_PARAM_REPORT_ID); + if (id == null) { + // If there's no report id, it's a bug report, so we can't return the incident + // report. + return null; + } + final String pkg = uri.getQueryParameter(URI_PARAM_CALLING_PACKAGE); if (pkg == null) { throw new RuntimeException("Invalid URI: No " @@ -531,13 +538,6 @@ public class IncidentManager { + URI_PARAM_RECEIVER_CLASS + " parameter. " + uri); } - final String id = uri.getQueryParameter(URI_PARAM_REPORT_ID); - if (cls == null) { - // If there's no report id, it's a bug report, so we can't return the incident - // report. - return null; - } - try { return getCompanionServiceLocked().getIncidentReport(pkg, cls, id); } catch (RemoteException ex) { diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java index 7cc7ccd2ad16..1b41694e7d48 100644 --- a/core/java/android/os/RecoverySystem.java +++ b/core/java/android/os/RecoverySystem.java @@ -866,8 +866,8 @@ public class RecoverySystem { if (storageManager.needsCheckpoint()) { Log.i(TAG, "Rescue Party requested wipe. Aborting update instead."); storageManager.abortChanges("rescueparty", false); + return; } - return; } catch (RemoteException e) { Log.i(TAG, "Failed to handle with checkpointing. Continuing with wipe."); } diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java index 6478de2612c3..85361587119d 100644 --- a/core/java/android/os/ZygoteProcess.java +++ b/core/java/android/os/ZygoteProcess.java @@ -881,6 +881,7 @@ public class ZygoteProcess { maybeSetApiBlacklistExemptions(primaryZygoteState, false); maybeSetHiddenApiAccessLogSampleRate(primaryZygoteState); + maybeSetHiddenApiAccessStatslogSampleRate(primaryZygoteState); } } @@ -896,6 +897,7 @@ public class ZygoteProcess { maybeSetApiBlacklistExemptions(secondaryZygoteState, false); maybeSetHiddenApiAccessLogSampleRate(secondaryZygoteState); + maybeSetHiddenApiAccessStatslogSampleRate(secondaryZygoteState); } } diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java index 1aa5b0656935..2a41c2065c46 100644 --- a/core/java/android/permission/PermissionManager.java +++ b/core/java/android/permission/PermissionManager.java @@ -23,11 +23,8 @@ import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; -import android.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.pm.IPackageManager; -import android.content.pm.PackageManager; -import android.content.pm.PermissionInfo; import android.os.RemoteException; import com.android.internal.annotations.Immutable; @@ -107,11 +104,11 @@ public final class PermissionManager { /** * Get set of permissions that have been split into more granular or dependent permissions. * - * <p>E.g. before {@link android.os.Build.VERSION_CODES#P0} an app that was granted + * <p>E.g. before {@link android.os.Build.VERSION_CODES#Q} an app that was granted * {@link Manifest.permission#ACCESS_COARSE_LOCATION} could access he location while it was in - * foreground and background. On platforms after {@link android.os.Build.VERSION_CODES#P0} + * foreground and background. On platforms after {@link android.os.Build.VERSION_CODES#Q} * the location permission only grants location access while the app is in foreground. This - * would break apps that target before {@link android.os.Build.VERSION_CODES#P0}. Hence whenever + * would break apps that target before {@link android.os.Build.VERSION_CODES#Q}. Hence whenever * such an old app asks for a location permission (i.e. the * {@link SplitPermissionInfo#getSplitPermission()}), then the * {@link Manifest.permission#ACCESS_BACKGROUND_LOCATION} permission (inside diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index 9215de14c21f..9a11104b30c1 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -159,6 +159,14 @@ public final class DeviceConfig { public static final String NAMESPACE_INTELLIGENCE_ATTENTION = "intelligence_attention"; /** + * Definitions for properties related to Content Suggestions. + * + * @hide + */ + public static final String NAMESPACE_INTELLIGENCE_CONTENT_SUGGESTIONS = + "intelligence_content_suggestions"; + + /** * Namespace for all media native related features. * * @hide @@ -255,6 +263,7 @@ public final class DeviceConfig { * Namespace for TextClassifier related features. * * @hide + * @see android.provider.Settings.Global.TEXT_CLASSIFIER_CONSTANTS */ @SystemApi public static final String NAMESPACE_TEXTCLASSIFIER = "textclassifier"; diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java index 4632b7553ca0..4ac485099da8 100644 --- a/core/java/android/provider/DocumentsContract.java +++ b/core/java/android/provider/DocumentsContract.java @@ -36,6 +36,7 @@ import android.graphics.Bitmap; import android.graphics.ImageDecoder; import android.graphics.Point; import android.media.ExifInterface; +import android.media.MediaFile; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -1698,6 +1699,18 @@ public final class DocumentsContract { } catch (IOException e) { } + // Use ImageDecoder to do full image decode of heif format file + // will have right orientation. So, we don't need to add orientation + // information into extras. + final String mimeType = MediaFile.getMimeTypeForFile(file.getName()); + if (mimeType.equals("image/heif") + || mimeType.equals("image/heif-sequence") + || mimeType.equals("image/heic") + || mimeType.equals("image/heic-sequence")) { + return new AssetFileDescriptor(pfd, 0 /* startOffset */, + AssetFileDescriptor.UNKNOWN_LENGTH, null /* extras */); + } + return new AssetFileDescriptor(pfd, 0, AssetFileDescriptor.UNKNOWN_LENGTH, extras); } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 4da0d2da8ee9..75b40fd489f2 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -1791,6 +1791,58 @@ public final class Settings { @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_MANAGE_DOMAIN_URLS = "android.settings.MANAGE_DOMAIN_URLS"; + /** + * Broadcast to trigger notification of asking user to enable MMS. + * Need to specify {@link #EXTRA_ENABLE_MMS_DATA_REQUEST_REASON} and {@link #EXTRA_SUB_ID}. + * + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_ENABLE_MMS_DATA_REQUEST = + "android.settings.ENABLE_MMS_DATA_REQUEST"; + + /** + * Integer value that specifies the reason triggering enable MMS data notification. + * This must be passed as an extra field to the {@link #ACTION_ENABLE_MMS_DATA_REQUEST}. + * Extra with value of EnableMmsDataReason interface. + * @hide + */ + public static final String EXTRA_ENABLE_MMS_DATA_REQUEST_REASON = + "android.settings.extra.ENABLE_MMS_DATA_REQUEST_REASON"; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "ENABLE_MMS_DATA_REQUEST_REASON_" }, value = { + ENABLE_MMS_DATA_REQUEST_REASON_INCOMING_MMS, + ENABLE_MMS_DATA_REQUEST_REASON_OUTGOING_MMS, + }) + public @interface EnableMmsDataReason{} + + /** + * Requesting to enable MMS data because there's an incoming MMS. + * @hide + */ + public static final int ENABLE_MMS_DATA_REQUEST_REASON_INCOMING_MMS = 0; + + /** + * Requesting to enable MMS data because user is sending MMS. + * @hide + */ + public static final int ENABLE_MMS_DATA_REQUEST_REASON_OUTGOING_MMS = 1; + + /** + * Activity Action: Show screen of a cellular subscription and highlight the + * "enable MMS" toggle. + * <p> + * Input: {@link #EXTRA_SUB_ID}: Sub ID of the subscription. + * <p> + * Output: Nothing + * + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_MMS_MESSAGE_SETTING = "android.settings.MMS_MESSAGE_SETTING"; + // End of Intent actions for Settings /** @@ -6053,9 +6105,8 @@ public final class Settings { "unknown_sources_default_reversed"; /** - * Comma-separated list of location providers that are accessible. Do not rely on - * this value being present or correct, or on ContentObserver notifications on the - * corresponding Uri. + * Comma-separated list of location providers that are enabled. Do not rely on this value + * being present or correct, or on ContentObserver notifications on the corresponding Uri. * * @deprecated The preferred methods for checking provider status and listening for changes * are via {@link LocationManager#isProviderEnabled(String)} and @@ -6098,17 +6149,14 @@ public final class Settings { /** * Location mode is off. - * - * @deprecated See {@link #LOCATION_MODE}. */ - @Deprecated public static final int LOCATION_MODE_OFF = 0; /** * This mode no longer has any distinct meaning, but is interpreted as the location mode is * on. * - * @deprecated See {@link #LOCATION_MODE_ON}. + * @deprecated See {@link #LOCATION_MODE}. */ @Deprecated public static final int LOCATION_MODE_SENSORS_ONLY = 1; @@ -6117,7 +6165,7 @@ public final class Settings { * This mode no longer has any distinct meaning, but is interpreted as the location mode is * on. * - * @deprecated See {@link #LOCATION_MODE_ON}. + * @deprecated See {@link #LOCATION_MODE}. */ @Deprecated public static final int LOCATION_MODE_BATTERY_SAVING = 2; @@ -6126,7 +6174,7 @@ public final class Settings { * This mode no longer has any distinct meaning, but is interpreted as the location mode is * on. * - * @deprecated See {@link #LOCATION_MODE_ON}. + * @deprecated See {@link #LOCATION_MODE}. */ @Deprecated public static final int LOCATION_MODE_HIGH_ACCURACY = 3; @@ -6134,9 +6182,9 @@ public final class Settings { /** * Location mode is on. * - * @deprecated See {@link #LOCATION_MODE}. + * @hide */ - @Deprecated + @SystemApi public static final int LOCATION_MODE_ON = LOCATION_MODE_HIGH_ACCURACY; /** @@ -11818,6 +11866,7 @@ public final class Settings { * sync_adapter_duration (long) * exempted_sync_duration (long) * system_interaction_duration (long) + * initial_foreground_service_start_duration (long) * stable_charging_threshold (long) * * idle_duration (long) // This is deprecated and used to circumvent b/26355386. @@ -11913,6 +11962,36 @@ public final class Settings { public static final String JOB_SCHEDULER_CONSTANTS = "job_scheduler_constants"; /** + * Job scheduler QuotaController specific settings. + * This is encoded as a key=value list, separated by commas. Ex: + * + * "max_job_count_working=5,max_job_count_rare=2" + * + * <p> + * Type: string + * + * @hide + * @see com.android.server.job.JobSchedulerService.Constants + */ + public static final String JOB_SCHEDULER_QUOTA_CONTROLLER_CONSTANTS = + "job_scheduler_quota_controller_constants"; + + /** + * Job scheduler TimeController specific settings. + * This is encoded as a key=value list, separated by commas. Ex: + * + * "skip_not_ready_jobs=true5,other_key=2" + * + * <p> + * Type: string + * + * @hide + * @see com.android.server.job.JobSchedulerService.Constants + */ + public static final String JOB_SCHEDULER_TIME_CONTROLLER_CONSTANTS = + "job_scheduler_time_controller_constants"; + + /** * ShortcutManager specific settings. * This is encoded as a key=value list, separated by commas. Ex: * @@ -11960,26 +12039,27 @@ public final class Settings { * entity_list_default use ":" as delimiter for values. Ex: * * <pre> - * smart_linkify_enabled (boolean) - * system_textclassifier_enabled (boolean) - * model_dark_launch_enabled (boolean) - * smart_selection_enabled (boolean) - * smart_text_share_enabled (boolean) - * smart_linkify_enabled (boolean) - * smart_select_animation_enabled (boolean) - * suggest_selection_max_range_length (int) * classify_text_max_range_length (int) - * generate_links_max_text_length (int) - * generate_links_log_sample_rate (int) + * detect_language_from_text_enabled (boolean) * entity_list_default (String[]) - * entity_list_not_editable (String[]) * entity_list_editable (String[]) + * entity_list_not_editable (String[]) + * generate_links_log_sample_rate (int) + * generate_links_max_text_length (int) * in_app_conversation_action_types_default (String[]) - * notification_conversation_action_types_default (String[]) + * lang_id_context_settings (float[]) * lang_id_threshold_override (float) + * local_textclassifier_enabled (boolean) + * model_dark_launch_enabled (boolean) + * notification_conversation_action_types_default (String[]) + * smart_linkify_enabled (boolean) + * smart_select_animation_enabled (boolean) + * smart_selection_enabled (boolean) + * smart_text_share_enabled (boolean) + * suggest_selection_max_range_length (int) + * system_textclassifier_enabled (boolean) * template_intent_factory_enabled (boolean) * translate_in_classification_enabled (boolean) - * detect_language_from_text_enabled (boolean) * </pre> * * <p> diff --git a/core/java/android/service/contentsuggestions/ContentSuggestionsService.java b/core/java/android/service/contentsuggestions/ContentSuggestionsService.java index 55e61410b9e2..516d33673012 100644 --- a/core/java/android/service/contentsuggestions/ContentSuggestionsService.java +++ b/core/java/android/service/contentsuggestions/ContentSuggestionsService.java @@ -30,6 +30,7 @@ import android.app.contentsuggestions.ISelectionsCallback; import android.app.contentsuggestions.SelectionsRequest; import android.content.Intent; import android.graphics.Bitmap; +import android.graphics.ColorSpace; import android.graphics.GraphicBuffer; import android.os.Bundle; import android.os.Handler; @@ -62,11 +63,15 @@ public abstract class ContentSuggestionsService extends Service { private final IContentSuggestionsService mInterface = new IContentSuggestionsService.Stub() { @Override public void provideContextImage(int taskId, GraphicBuffer contextImage, - Bundle imageContextRequestExtras) { + int colorSpaceId, Bundle imageContextRequestExtras) { Bitmap wrappedBuffer = null; if (contextImage != null) { - wrappedBuffer = Bitmap.wrapHardwareBuffer(contextImage, null); + ColorSpace colorSpace = null; + if (colorSpaceId >= 0 && colorSpaceId < ColorSpace.Named.values().length) { + colorSpace = ColorSpace.get(ColorSpace.Named.values()[colorSpaceId]); + } + wrappedBuffer = Bitmap.wrapHardwareBuffer(contextImage, colorSpace); } mHandler.sendMessage( diff --git a/core/java/android/service/contentsuggestions/IContentSuggestionsService.aidl b/core/java/android/service/contentsuggestions/IContentSuggestionsService.aidl index 1926478322c8..6240e00794bd 100644 --- a/core/java/android/service/contentsuggestions/IContentSuggestionsService.aidl +++ b/core/java/android/service/contentsuggestions/IContentSuggestionsService.aidl @@ -32,6 +32,7 @@ oneway interface IContentSuggestionsService { void provideContextImage( int taskId, in GraphicBuffer contextImage, + int colorSpaceId, in Bundle imageContextRequestExtras); void suggestContentSelections( in SelectionsRequest request, diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java index cd8c7aea1bab..c9d37bf86a27 100644 --- a/core/java/android/service/voice/VoiceInteractionSession.java +++ b/core/java/android/service/voice/VoiceInteractionSession.java @@ -1995,7 +1995,8 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall } /** - * @return the index of the activity that this state is for. + * @return the index of the activity that this state is for or -1 + * if there was no assist data captured. */ public @IntRange(from = -1) int getIndex() { return mIndex; @@ -2048,7 +2049,8 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall } /** - * Represents the id of an assist source activity. + * Represents the id of an assist source activity. You can use + * {@link #equals(Object)} to compare instances of this class. */ public static class ActivityId { private final int mTaskId; diff --git a/core/java/android/service/watchdog/ExplicitHealthCheckService.java b/core/java/android/service/watchdog/ExplicitHealthCheckService.java index eeefb4ae10fa..dc9c85808240 100644 --- a/core/java/android/service/watchdog/ExplicitHealthCheckService.java +++ b/core/java/android/service/watchdog/ExplicitHealthCheckService.java @@ -183,7 +183,6 @@ public abstract class ExplicitHealthCheckService extends Service { */ @SystemApi public static final class PackageConfig implements Parcelable { - // TODO: Receive from DeviceConfig flag private static final long DEFAULT_HEALTH_CHECK_TIMEOUT_MILLIS = TimeUnit.HOURS.toMillis(1); private final String mPackageName; diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index e8b0d92c2fd2..ae36e4ecde17 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -36,7 +36,6 @@ public class FeatureFlagUtils { public static final String PERSIST_PREFIX = "persist." + FFLAG_OVERRIDE_PREFIX; public static final String SEAMLESS_TRANSFER = "settings_seamless_transfer"; public static final String HEARING_AID_SETTINGS = "settings_bluetooth_hearing_aid"; - public static final String SAFETY_HUB = "settings_safety_hub"; public static final String SCREENRECORD_LONG_PRESS = "settings_screenrecord_long_press"; public static final String FORCE_GLOBAL_ACTIONS_GRID_ENABLED = "settings_global_actions_force_grid_enabled"; @@ -57,7 +56,6 @@ public class FeatureFlagUtils { DEFAULT_FLAGS.put(DYNAMIC_SYSTEM, "false"); DEFAULT_FLAGS.put(SEAMLESS_TRANSFER, "false"); DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "false"); - DEFAULT_FLAGS.put(SAFETY_HUB, "true"); DEFAULT_FLAGS.put(SCREENRECORD_LONG_PRESS, "false"); DEFAULT_FLAGS.put(FORCE_GLOBAL_ACTIONS_GRID_ENABLED, "false"); DEFAULT_FLAGS.put(GLOBAL_ACTIONS_PANEL_ENABLED, "true"); diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index c730fe2dc114..b347a78a8780 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -621,18 +621,6 @@ interface IWindowManager */ void setShouldShowIme(int displayId, boolean shouldShow); - /** - * Reparent the top layers for a display to the requested surfaceControl. The display that - * is going to be re-parented (the displayId passed in) needs to have been created by the same - * process that is requesting the re-parent. This is to ensure clients can't just re-parent - * display content info to any SurfaceControl, as this would be a security issue. - * - * @param displayId The id of the display. - * @param surfaceControlHandle The SurfaceControl that the top level layers for the - * display should be re-parented to. - */ - void reparentDisplayContent(int displayId, in SurfaceControl sc); - /** * Waits for transactions to get applied before injecting input. * This includes waiting for the input windows to get sent to InputManager. diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index b52fdb8399d1..d269323d50a4 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -257,6 +257,31 @@ interface IWindowSession { void updatePointerIcon(IWindow window); /** + * Reparent the top layers for a display to the requested SurfaceControl. The display that is + * going to be re-parented (the displayId passed in) needs to have been created by the same + * process that is requesting the re-parent. This is to ensure clients can't just re-parent + * display content info to any SurfaceControl, as this would be a security issue. + * + * @param window The window which owns the SurfaceControl. This indicates the z-order of the + * windows of this display against the windows on the parent display. + * @param sc The SurfaceControl that the top level layers for the display should be re-parented + * to. + * @param displayId The id of the display to be re-parented. + */ + void reparentDisplayContent(IWindow window, in SurfaceControl sc, int displayId); + + /** + * Update the location of a child display in its parent window. This enables windows in the + * child display to compute the global transformation matrix. + * + * @param window The parent window of the display. + * @param x The x coordinate in the parent window. + * @param y The y coordinate in the parent window. + * @param displayId The id of the display to be notified. + */ + void updateDisplayContentLocation(IWindow window, int x, int y, int displayId); + + /** * Update a tap exclude region identified by provided id in the window. Touches on this region * will neither be dispatched to this window nor change the focus to this window. Passing an * invalid region will remove the area from the exclude region of this window. diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 8070e76d1b40..bbb90536e8f8 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -848,6 +848,10 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb position.width() / (float) mSurfaceWidth, 0.0f, 0.0f, position.height() / (float) mSurfaceHeight); + if (mViewVisibility) { + mRtTransaction.show(surface); + } + } private void setParentSpaceRectangle(Rect position, long frameNumber) { @@ -914,27 +918,15 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb if (mSurfaceControl == null) { return; } - if (mRtHandlingPositionUpdates) { - mRtHandlingPositionUpdates = false; - // This callback will happen while the UI thread is blocked, so we can - // safely access other member variables at this time. - // So do what the UI thread would have done if RT wasn't handling position - // updates. - if (!mScreenRect.isEmpty() && !mScreenRect.equals(mRTLastReportedPosition)) { - try { - if (DEBUG) { - Log.d(TAG, String.format("%d updateSurfacePosition, " - + "postion = [%d, %d, %d, %d]", - System.identityHashCode(this), - mScreenRect.left, mScreenRect.top, - mScreenRect.right, mScreenRect.bottom)); - } - setParentSpaceRectangle(mScreenRect, frameNumber); - } catch (Exception ex) { - Log.e(TAG, "Exception configuring surface", ex); - } - } + + if (frameNumber > 0) { + final ViewRootImpl viewRoot = getViewRootImpl(); + + mRtTransaction.deferTransactionUntilSurface(mSurfaceControl, viewRoot.mSurface, + frameNumber); } + mRtTransaction.hide(mSurfaceControl); + mRtTransaction.apply(); } }; diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index 3bae4b855374..3d3d5dc7db32 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -177,7 +177,7 @@ public final class ThreadedRenderer extends HardwareRenderer { * Forces smart-dark to be always on. * @hide */ - public static final String DEBUG_FORCE_DARK = "persist.hwui.force_dark"; + public static final String DEBUG_FORCE_DARK = "debug.hwui.force_dark"; public static int EGL_CONTEXT_PRIORITY_HIGH_IMG = 0x3101; public static int EGL_CONTEXT_PRIORITY_MEDIUM_IMG = 0x3102; diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 5929c1b4d6e6..921294a78e5e 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -7054,10 +7054,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private FrameMetricsObserver findFrameMetricsObserver( Window.OnFrameMetricsAvailableListener listener) { - for (int i = 0; i < mFrameMetricsObservers.size(); i++) { - FrameMetricsObserver observer = mFrameMetricsObservers.get(i); - if (observer.mListener == listener) { - return observer; + if (mFrameMetricsObservers != null) { + for (int i = 0; i < mFrameMetricsObservers.size(); i++) { + FrameMetricsObserver observer = mFrameMetricsObservers.get(i); + if (observer.mListener == listener) { + return observer; + } } } diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index c2aec6a40ff4..a25f2eede905 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -508,14 +508,25 @@ public interface WindowManager extends ViewManager { * * @param displayId Display ID. * @param shouldShow Indicates that the display should show IME. - * @see KeyguardManager#isDeviceSecure() - * @see KeyguardManager#isDeviceLocked() * @hide */ @TestApi default void setShouldShowIme(int displayId, boolean shouldShow) { } + /** + * Indicates that the display should show IME. + * + * @param displayId The id of the display. + * @return {@code true} if the display should show IME when an input field becomes + * focused on it. + * @hide + */ + @TestApi + default boolean shouldShowIme(int displayId) { + return false; + } + public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable { /** * X position for this window. With the default gravity it is ignored. diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java index 2e4db5c4eef9..c3494432ebcb 100644 --- a/core/java/android/view/WindowManagerImpl.java +++ b/core/java/android/view/WindowManagerImpl.java @@ -192,4 +192,13 @@ public final class WindowManagerImpl implements WindowManager { } catch (RemoteException e) { } } + + @Override + public boolean shouldShowIme(int displayId) { + try { + return WindowManagerGlobal.getWindowManagerService().shouldShowIme(displayId); + } catch (RemoteException e) { + } + return false; + } } diff --git a/core/java/android/view/autofill/AutofillId.java b/core/java/android/view/autofill/AutofillId.java index 2c7929939053..b387a68dd8a3 100644 --- a/core/java/android/view/autofill/AutofillId.java +++ b/core/java/android/view/autofill/AutofillId.java @@ -16,6 +16,7 @@ package android.view.autofill; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; @@ -34,10 +35,10 @@ public final class AutofillId implements Parcelable { private static final int FLAG_HAS_SESSION = 0x4; private final int mViewId; - private final int mFlags; + private int mFlags; private final int mVirtualIntId; private final long mVirtualLongId; - private final int mSessionId; + private int mSessionId; /** @hide */ @TestApi @@ -72,6 +73,12 @@ public final class AutofillId implements Parcelable { } /** @hide */ + public static AutofillId withoutSession(@NonNull AutofillId id) { + final int flags = id.mFlags & ~FLAG_HAS_SESSION; + return new AutofillId(flags, id.mViewId, id.mVirtualLongId, NO_SESSION); + } + + /** @hide */ public int getViewId() { return mViewId; } @@ -135,7 +142,8 @@ public final class AutofillId implements Parcelable { return !isVirtualInt() && !isVirtualLong(); } - private boolean hasSession() { + /** @hide */ + public boolean hasSession() { return (mFlags & FLAG_HAS_SESSION) != 0; } @@ -144,6 +152,18 @@ public final class AutofillId implements Parcelable { return mSessionId; } + /** @hide */ + public void setSessionId(int sessionId) { + mFlags |= FLAG_HAS_SESSION; + mSessionId = sessionId; + } + + /** @hide */ + public void resetSessionId() { + mFlags &= ~FLAG_HAS_SESSION; + mSessionId = NO_SESSION; + } + ///////////////////////////////// // Object "contract" methods. // ///////////////////////////////// @@ -172,6 +192,17 @@ public final class AutofillId implements Parcelable { return true; } + /** @hide */ + @TestApi + public boolean equalsIgnoreSession(@Nullable AutofillId other) { + if (this == other) return true; + if (other == null) return false; + if (mViewId != other.mViewId) return false; + if (mVirtualIntId != other.mVirtualIntId) return false; + if (mVirtualLongId != other.mVirtualLongId) return false; + return true; + } + @Override public String toString() { final StringBuilder builder = new StringBuilder().append(mViewId); diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index 6503a800acb7..5872d3f7f785 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -1139,6 +1139,7 @@ public final class AutofillManager { if (mEnteredIds == null) { mEnteredIds = new ArraySet<>(1); } + id.resetSessionId(); mEnteredIds.add(id); } @@ -2177,6 +2178,9 @@ public final class AutofillManager { private void setTrackedViews(int sessionId, @Nullable AutofillId[] trackedIds, boolean saveOnAllViewsInvisible, boolean saveOnFinish, @Nullable AutofillId[] fillableIds, @Nullable AutofillId saveTriggerId) { + if (saveTriggerId != null) { + saveTriggerId.resetSessionId(); + } synchronized (mLock) { if (sVerbose) { Log.v(TAG, "setTrackedViews(): sessionId=" + sessionId @@ -2202,6 +2206,7 @@ public final class AutofillManager { mFillableIds = new ArraySet<>(fillableIds.length); } for (AutofillId id : fillableIds) { + id.resetSessionId(); mFillableIds.add(id); } } @@ -2264,6 +2269,11 @@ public final class AutofillManager { * session when they're entered. */ private void setSessionFinished(int newState, @Nullable List<AutofillId> autofillableIds) { + if (autofillableIds != null) { + for (int i = 0; i < autofillableIds.size(); i++) { + autofillableIds.get(i).resetSessionId(); + } + } synchronized (mLock) { if (sVerbose) { Log.v(TAG, "setSessionFinished(): from " + getStateAsStringLocked() + " to " @@ -2879,6 +2889,7 @@ public final class AutofillManager { final int numIds = trackedIds.length; for (int i = 0; i < numIds; i++) { final AutofillId id = trackedIds[i]; + id.resetSessionId(); if (isVisible[i]) { mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id); diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java index c2ad82fd5c5f..e3c2bd1b2a26 100644 --- a/core/java/android/view/contentcapture/ContentCaptureManager.java +++ b/core/java/android/view/contentcapture/ContentCaptureManager.java @@ -37,6 +37,7 @@ import android.os.ServiceManager; import android.util.Log; import android.view.View; import android.view.ViewStructure; +import android.view.WindowManager; import android.view.contentcapture.ContentCaptureSession.FlushReason; import com.android.internal.annotations.GuardedBy; @@ -343,10 +344,9 @@ public final class ContentCaptureManager { /** @hide */ @UiThread public void onActivityCreated(@NonNull IBinder applicationToken, - @NonNull ComponentName activityComponent, int flags) { + @NonNull ComponentName activityComponent) { if (mOptions.lite) return; synchronized (mLock) { - mFlags |= flags; getMainContentCaptureSession().start(applicationToken, activityComponent, mFlags); } } @@ -484,8 +484,43 @@ public final class ContentCaptureManager { Log.d(TAG, "setContentCaptureEnabled(): setting to " + enabled + " for " + mContext); } + MainContentCaptureSession mainSession; synchronized (mLock) { - mFlags |= enabled ? 0 : ContentCaptureContext.FLAG_DISABLED_BY_APP; + if (enabled) { + mFlags &= ~ContentCaptureContext.FLAG_DISABLED_BY_APP; + } else { + mFlags |= ContentCaptureContext.FLAG_DISABLED_BY_APP; + } + mainSession = mMainSession; + } + if (mainSession != null) { + mainSession.setDisabled(!enabled); + } + } + + /** + * Called by apps to update flag secure when window attributes change. + * + * @hide + */ + public void updateWindowAttributes(@NonNull WindowManager.LayoutParams params) { + if (sDebug) { + Log.d(TAG, "updateWindowAttributes(): window flags=" + params.flags); + } + final boolean flagSecureEnabled = + (params.flags & WindowManager.LayoutParams.FLAG_SECURE) != 0; + + MainContentCaptureSession mainSession; + synchronized (mLock) { + if (flagSecureEnabled) { + mFlags |= ContentCaptureContext.FLAG_DISABLED_BY_FLAG_SECURE; + } else { + mFlags &= ~ContentCaptureContext.FLAG_DISABLED_BY_FLAG_SECURE; + } + mainSession = mMainSession; + } + if (mainSession != null) { + mainSession.setDisabled(flagSecureEnabled); } } diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java index 784cf9c32557..7241664602e9 100644 --- a/core/java/android/view/contentcapture/MainContentCaptureSession.java +++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java @@ -28,6 +28,7 @@ import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TREE_APP import static android.view.contentcapture.ContentCaptureHelper.getSanitizedString; import static android.view.contentcapture.ContentCaptureHelper.sDebug; import static android.view.contentcapture.ContentCaptureHelper.sVerbose; +import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_FALSE; import android.annotation.NonNull; import android.annotation.Nullable; @@ -80,6 +81,12 @@ public final class MainContentCaptureSession extends ContentCaptureSession { */ public static final String EXTRA_BINDER = "binder"; + /** + * Name of the {@link IResultReceiver} extra used to pass the content capture enabled state. + * @hide + */ + public static final String EXTRA_ENABLED_STATE = "enabled"; + @NonNull private final AtomicBoolean mDisabled = new AtomicBoolean(false); @@ -155,6 +162,13 @@ public final class MainContentCaptureSession extends ContentCaptureSession { public void send(int resultCode, Bundle resultData) { final IBinder binder; if (resultData != null) { + // Change in content capture enabled. + final boolean hasEnabled = resultData.getBoolean(EXTRA_ENABLED_STATE); + if (hasEnabled) { + final boolean disabled = (resultCode == RESULT_CODE_FALSE); + mDisabled.set(disabled); + return; + } binder = resultData.getBinder(EXTRA_BINDER); if (binder == null) { Log.wtf(TAG, "No " + EXTRA_BINDER + " extra result"); @@ -578,6 +592,15 @@ public final class MainContentCaptureSession extends ContentCaptureSession { return mDisabled.get(); } + /** + * Sets the disabled state of content capture. + * + * @return whether disabled state was changed. + */ + boolean setDisabled(boolean disabled) { + return mDisabled.compareAndSet(!disabled, disabled); + } + // TODO(b/122454205): refactor "notifyXXXX" methods below to a common "Buffer" object that is // shared between ActivityContentCaptureSession and ChildContentCaptureSession objects. Such // change should also get get rid of the "internalNotifyXXXX" methods above diff --git a/core/java/android/view/textclassifier/ConfigParser.java b/core/java/android/view/textclassifier/ConfigParser.java index b475412bf669..63de0590ef72 100644 --- a/core/java/android/view/textclassifier/ConfigParser.java +++ b/core/java/android/view/textclassifier/ConfigParser.java @@ -17,9 +17,19 @@ package android.view.textclassifier; import android.annotation.Nullable; import android.provider.DeviceConfig; +import android.util.ArrayMap; import android.util.KeyValueListParser; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.annotations.VisibleForTesting.Visibility; +import com.android.internal.util.Preconditions; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; /** * Retrieves settings from {@link DeviceConfig} and {@link android.provider.Settings}. @@ -27,80 +37,228 @@ import com.android.internal.annotations.VisibleForTesting; * * @hide */ -@VisibleForTesting +@VisibleForTesting(visibility = Visibility.PACKAGE) public final class ConfigParser { private static final String TAG = "ConfigParser"; - private final KeyValueListParser mParser; + static final boolean ENABLE_DEVICE_CONFIG = true; + + private static final String STRING_LIST_DELIMITER = ":"; - // TODO: Re-enable DeviceConfig when it has reasonable performance or just delete the - // option of using DeviceConfig entirely. - static final boolean ENABLE_DEVICE_CONFIG = false; + private final Supplier<String> mLegacySettingsSupplier; + private final Object mLock = new Object(); + @GuardedBy("mLock") + private final Map<String, Object> mCache = new ArrayMap<>(); + @GuardedBy("mLock") + private @Nullable KeyValueListParser mSettingsParser; // Call getLegacySettings() instead. + + public ConfigParser(Supplier<String> legacySettingsSupplier) { + mLegacySettingsSupplier = Preconditions.checkNotNull(legacySettingsSupplier); + } - public ConfigParser(@Nullable String textClassifierConstants) { - final KeyValueListParser parser = new KeyValueListParser(','); - try { - parser.setString(textClassifierConstants); - } catch (IllegalArgumentException e) { - // Failed to parse the settings string, log this and move on with defaults. - Log.w(TAG, "Bad text_classifier_constants: " + textClassifierConstants); + private KeyValueListParser getLegacySettings() { + synchronized (mLock) { + if (mSettingsParser == null) { + final String legacySettings = mLegacySettingsSupplier.get(); + try { + mSettingsParser = new KeyValueListParser(','); + mSettingsParser.setString(legacySettings); + } catch (IllegalArgumentException e) { + // Failed to parse the settings string, log this and move on with defaults. + Log.w(TAG, "Bad text_classifier_constants: " + legacySettings); + } + } + return mSettingsParser; } - mParser = parser; } /** - * Reads a boolean flag. + * Reads a boolean setting through the cache. */ public boolean getBoolean(String key, boolean defaultValue) { - if (ENABLE_DEVICE_CONFIG) { - return DeviceConfig.getBoolean( - DeviceConfig.NAMESPACE_TEXTCLASSIFIER, - key, - mParser.getBoolean(key, defaultValue)); - } else { - return mParser.getBoolean(key, defaultValue); + synchronized (mLock) { + final Object cached = mCache.get(key); + if (cached instanceof Boolean) { + return (boolean) cached; + } + final boolean value; + if (ENABLE_DEVICE_CONFIG) { + value = DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_TEXTCLASSIFIER, + key, + getLegacySettings().getBoolean(key, defaultValue)); + } else { + value = getLegacySettings().getBoolean(key, defaultValue); + } + mCache.put(key, value); + return value; } } /** - * Reads an integer flag. + * Reads an integer setting through the cache. */ public int getInt(String key, int defaultValue) { - if (ENABLE_DEVICE_CONFIG) { - return DeviceConfig.getInt( - DeviceConfig.NAMESPACE_TEXTCLASSIFIER, - key, - mParser.getInt(key, defaultValue)); - } else { - return mParser.getInt(key, defaultValue); + synchronized (mLock) { + final Object cached = mCache.get(key); + if (cached instanceof Integer) { + return (int) cached; + } + final int value; + if (ENABLE_DEVICE_CONFIG) { + value = DeviceConfig.getInt( + DeviceConfig.NAMESPACE_TEXTCLASSIFIER, + key, + getLegacySettings().getInt(key, defaultValue)); + } else { + value = getLegacySettings().getInt(key, defaultValue); + } + mCache.put(key, value); + return value; } } /** - * Reads a float flag. + * Reads a float setting through the cache. */ public float getFloat(String key, float defaultValue) { - if (ENABLE_DEVICE_CONFIG) { - return DeviceConfig.getFloat( - DeviceConfig.NAMESPACE_TEXTCLASSIFIER, - key, - mParser.getFloat(key, defaultValue)); - } else { - return mParser.getFloat(key, defaultValue); + synchronized (mLock) { + final Object cached = mCache.get(key); + if (cached instanceof Float) { + return (float) cached; + } + final float value; + if (ENABLE_DEVICE_CONFIG) { + value = DeviceConfig.getFloat( + DeviceConfig.NAMESPACE_TEXTCLASSIFIER, + key, + getLegacySettings().getFloat(key, defaultValue)); + } else { + value = getLegacySettings().getFloat(key, defaultValue); + } + mCache.put(key, value); + return value; } } /** - * Reads a string flag. + * Reads a string setting through the cache. */ public String getString(String key, String defaultValue) { - if (ENABLE_DEVICE_CONFIG) { - return DeviceConfig.getString( - DeviceConfig.NAMESPACE_TEXTCLASSIFIER, - key, - mParser.getString(key, defaultValue)); + synchronized (mLock) { + final Object cached = mCache.get(key); + if (cached instanceof String) { + return (String) cached; + } + final String value; + if (ENABLE_DEVICE_CONFIG) { + value = DeviceConfig.getString( + DeviceConfig.NAMESPACE_TEXTCLASSIFIER, + key, + getLegacySettings().getString(key, defaultValue)); + } else { + value = getLegacySettings().getString(key, defaultValue); + } + mCache.put(key, value); + return value; + } + } + + /** + * Reads a string list setting through the cache. + */ + public List<String> getStringList(String key, List<String> defaultValue) { + synchronized (mLock) { + final Object cached = mCache.get(key); + if (cached instanceof List) { + final List asList = (List) cached; + if (asList.isEmpty()) { + return Collections.emptyList(); + } else if (asList.get(0) instanceof String) { + return (List<String>) cached; + } + } + final List<String> value; + if (ENABLE_DEVICE_CONFIG) { + value = getDeviceConfigStringList( + key, + getSettingsStringList(key, defaultValue)); + } else { + value = getSettingsStringList(key, defaultValue); + } + mCache.put(key, value); + return value; + } + } + + /** + * Reads a float array through the cache. The returned array should be expected to be of the + * same length as that of the defaultValue. + */ + public float[] getFloatArray(String key, float[] defaultValue) { + synchronized (mLock) { + final Object cached = mCache.get(key); + if (cached instanceof float[]) { + return (float[]) cached; + } + final float[] value; + if (ENABLE_DEVICE_CONFIG) { + value = getDeviceConfigFloatArray( + key, + getSettingsFloatArray(key, defaultValue)); + } else { + value = getSettingsFloatArray(key, defaultValue); + } + mCache.put(key, value); + return value; + } + } + + private List<String> getSettingsStringList(String key, List<String> defaultValue) { + return parse(mSettingsParser.getString(key, null), defaultValue); + } + + private static List<String> getDeviceConfigStringList(String key, List<String> defaultValue) { + return parse( + DeviceConfig.getString(DeviceConfig.NAMESPACE_TEXTCLASSIFIER, key, null), + defaultValue); + } + + private static float[] getDeviceConfigFloatArray(String key, float[] defaultValue) { + return parse( + DeviceConfig.getString(DeviceConfig.NAMESPACE_TEXTCLASSIFIER, key, null), + defaultValue); + } + + private float[] getSettingsFloatArray(String key, float[] defaultValue) { + return parse(mSettingsParser.getString(key, null), defaultValue); + } + + private static List<String> parse(@Nullable String listStr, List<String> defaultValue) { + if (listStr != null) { + return Collections.unmodifiableList( + Arrays.asList(listStr.split(STRING_LIST_DELIMITER))); + } + return defaultValue; + } + + private static float[] parse(@Nullable String arrayStr, float[] defaultValue) { + if (arrayStr != null) { + final String[] split = arrayStr.split(STRING_LIST_DELIMITER); + if (split.length != defaultValue.length) { + return defaultValue; + } + final float[] result = new float[split.length]; + for (int i = 0; i < split.length; i++) { + try { + result[i] = Float.parseFloat(split[i]); + } catch (NumberFormatException e) { + return defaultValue; + } + } + return result; } else { - return mParser.getString(key, defaultValue); + return defaultValue; } } } diff --git a/core/java/android/view/textclassifier/TextClassificationConstants.java b/core/java/android/view/textclassifier/TextClassificationConstants.java index 876e5cccb2e0..2964f515f005 100644 --- a/core/java/android/view/textclassifier/TextClassificationConstants.java +++ b/core/java/android/view/textclassifier/TextClassificationConstants.java @@ -16,58 +16,39 @@ package android.view.textclassifier; -import android.annotation.Nullable; - import com.android.internal.util.IndentingPrintWriter; import java.util.Arrays; -import java.util.Collections; import java.util.List; -import java.util.StringJoiner; +import java.util.function.Supplier; /** * TextClassifier specific settings. - * This is encoded as a key=value list, separated by commas. Ex: - * - * <pre> - * smart_linkify_enabled (boolean) - * system_textclassifier_enabled (boolean) - * model_dark_launch_enabled (boolean) - * smart_selection_enabled (boolean) - * smart_text_share_enabled (boolean) - * smart_linkify_enabled (boolean) - * smart_select_animation_enabled (boolean) - * suggest_selection_max_range_length (int) - * classify_text_max_range_length (int) - * generate_links_max_text_length (int) - * generate_links_log_sample_rate (int) - * entity_list_default (String[]) - * entity_list_not_editable (String[]) - * entity_list_editable (String[]) - * in_app_conversation_action_types_default (String[]) - * notification_conversation_action_types_default (String[]) - * lang_id_threshold_override (float) - * template_intent_factory_enabled (boolean) - * translate_in_classification_enabled (boolean) - * detect_languages_from_text_enabled (boolean) - * lang_id_context_settings (float[]) - * </pre> - * + * This is encoded as a key=value list, separated by commas. * <p> - * Type: string - * see also android.provider.Settings.Global.TEXT_CLASSIFIER_CONSTANTS - * * Example of setting the values for testing. + * <p> + * <pre> * adb shell settings put global text_classifier_constants \ * model_dark_launch_enabled=true,smart_selection_enabled=true, \ * entity_list_default=phone:address, \ * lang_id_context_settings=20:1.0:0.4 + * </pre> + * <p> + * Settings are also available in device config. These take precedence over those in settings + * global. + * <p> + * <pre> + * adb shell cmd device_config put textclassifier system_textclassifier_enabled true + * </pre> + * + * @see android.provider.Settings.Global.TEXT_CLASSIFIER_CONSTANTS + * @see android.provider.DeviceConfig.NAMESPACE_TEXTCLASSIFIER * @hide */ +// TODO: Rename to TextClassifierSettings. public final class TextClassificationConstants { - private static final String LOG_TAG = TextClassifier.DEFAULT_LOG_TAG; - /** * Whether the smart linkify feature is enabled. */ @@ -188,29 +169,26 @@ public final class TextClassificationConstants { private static final int CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT = 10 * 1000; private static final int GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT = 100 * 1000; private static final int GENERATE_LINKS_LOG_SAMPLE_RATE_DEFAULT = 100; - private static final String STRING_LIST_DELIMITER = ":"; - private static final String ENTITY_LIST_DEFAULT_VALUE = new StringJoiner(STRING_LIST_DELIMITER) - .add(TextClassifier.TYPE_ADDRESS) - .add(TextClassifier.TYPE_EMAIL) - .add(TextClassifier.TYPE_PHONE) - .add(TextClassifier.TYPE_URL) - .add(TextClassifier.TYPE_DATE) - .add(TextClassifier.TYPE_DATE_TIME) - .add(TextClassifier.TYPE_FLIGHT_NUMBER).toString(); - private static final String CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES = - new StringJoiner(STRING_LIST_DELIMITER) - .add(ConversationAction.TYPE_TEXT_REPLY) - .add(ConversationAction.TYPE_CREATE_REMINDER) - .add(ConversationAction.TYPE_CALL_PHONE) - .add(ConversationAction.TYPE_OPEN_URL) - .add(ConversationAction.TYPE_SEND_EMAIL) - .add(ConversationAction.TYPE_SEND_SMS) - .add(ConversationAction.TYPE_TRACK_FLIGHT) - .add(ConversationAction.TYPE_VIEW_CALENDAR) - .add(ConversationAction.TYPE_VIEW_MAP) - .add(ConversationAction.TYPE_ADD_CONTACT) - .add(ConversationAction.TYPE_COPY) - .toString(); + private static final List<String> ENTITY_LIST_DEFAULT_VALUE = Arrays.asList( + TextClassifier.TYPE_ADDRESS, + TextClassifier.TYPE_EMAIL, + TextClassifier.TYPE_PHONE, + TextClassifier.TYPE_URL, + TextClassifier.TYPE_DATE, + TextClassifier.TYPE_DATE_TIME, + TextClassifier.TYPE_FLIGHT_NUMBER); + private static final List<String> CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES = Arrays.asList( + ConversationAction.TYPE_TEXT_REPLY, + ConversationAction.TYPE_CREATE_REMINDER, + ConversationAction.TYPE_CALL_PHONE, + ConversationAction.TYPE_OPEN_URL, + ConversationAction.TYPE_SEND_EMAIL, + ConversationAction.TYPE_SEND_SMS, + ConversationAction.TYPE_TRACK_FLIGHT, + ConversationAction.TYPE_VIEW_CALENDAR, + ConversationAction.TYPE_VIEW_MAP, + ConversationAction.TYPE_ADD_CONTACT, + ConversationAction.TYPE_COPY); /** * < 0 : Not set. Use value from LangId model. * 0 - 1: Override value in LangId model. @@ -221,259 +199,185 @@ public final class TextClassificationConstants { private static final boolean TEMPLATE_INTENT_FACTORY_ENABLED_DEFAULT = true; private static final boolean TRANSLATE_IN_CLASSIFICATION_ENABLED_DEFAULT = true; private static final boolean DETECT_LANGUAGES_FROM_TEXT_ENABLED_DEFAULT = true; - private static final String LANG_ID_CONTEXT_SETTINGS_DEFAULT = - new StringJoiner(STRING_LIST_DELIMITER).add("20").add("1.0").add("0.4").toString(); - - private final boolean mSystemTextClassifierEnabled; - private final boolean mLocalTextClassifierEnabled; - private final boolean mModelDarkLaunchEnabled; - private final boolean mSmartSelectionEnabled; - private final boolean mSmartTextShareEnabled; - private final boolean mSmartLinkifyEnabled; - private final boolean mSmartSelectionAnimationEnabled; - private final int mSuggestSelectionMaxRangeLength; - private final int mClassifyTextMaxRangeLength; - private final int mGenerateLinksMaxTextLength; - private final int mGenerateLinksLogSampleRate; - private final List<String> mEntityListDefault; - private final List<String> mEntityListNotEditable; - private final List<String> mEntityListEditable; - private final List<String> mInAppConversationActionTypesDefault; - private final List<String> mNotificationConversationActionTypesDefault; - private final float mLangIdThresholdOverride; - private final boolean mTemplateIntentFactoryEnabled; - private final boolean mTranslateInClassificationEnabled; - private final boolean mDetectLanguagesFromTextEnabled; - private final float[] mLangIdContextSettings; + private static final float[] LANG_ID_CONTEXT_SETTINGS_DEFAULT = new float[] {20f, 1.0f, 0.4f}; - private TextClassificationConstants(@Nullable String settings) { - ConfigParser configParser = new ConfigParser(settings); - mSystemTextClassifierEnabled = - configParser.getBoolean( - SYSTEM_TEXT_CLASSIFIER_ENABLED, - SYSTEM_TEXT_CLASSIFIER_ENABLED_DEFAULT); - mLocalTextClassifierEnabled = - configParser.getBoolean( - LOCAL_TEXT_CLASSIFIER_ENABLED, - LOCAL_TEXT_CLASSIFIER_ENABLED_DEFAULT); - mModelDarkLaunchEnabled = - configParser.getBoolean( - MODEL_DARK_LAUNCH_ENABLED, - MODEL_DARK_LAUNCH_ENABLED_DEFAULT); - mSmartSelectionEnabled = - configParser.getBoolean( - SMART_SELECTION_ENABLED, - SMART_SELECTION_ENABLED_DEFAULT); - mSmartTextShareEnabled = - configParser.getBoolean( - SMART_TEXT_SHARE_ENABLED, - SMART_TEXT_SHARE_ENABLED_DEFAULT); - mSmartLinkifyEnabled = - configParser.getBoolean( - SMART_LINKIFY_ENABLED, - SMART_LINKIFY_ENABLED_DEFAULT); - mSmartSelectionAnimationEnabled = - configParser.getBoolean( - SMART_SELECT_ANIMATION_ENABLED, - SMART_SELECT_ANIMATION_ENABLED_DEFAULT); - mSuggestSelectionMaxRangeLength = - configParser.getInt( - SUGGEST_SELECTION_MAX_RANGE_LENGTH, - SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT); - mClassifyTextMaxRangeLength = - configParser.getInt( - CLASSIFY_TEXT_MAX_RANGE_LENGTH, - CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT); - mGenerateLinksMaxTextLength = - configParser.getInt( - GENERATE_LINKS_MAX_TEXT_LENGTH, - GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT); - mGenerateLinksLogSampleRate = - configParser.getInt( - GENERATE_LINKS_LOG_SAMPLE_RATE, - GENERATE_LINKS_LOG_SAMPLE_RATE_DEFAULT); - mEntityListDefault = parseStringList( - configParser.getString( - ENTITY_LIST_DEFAULT, - ENTITY_LIST_DEFAULT_VALUE)); - mEntityListNotEditable = parseStringList( - configParser.getString( - ENTITY_LIST_NOT_EDITABLE, - ENTITY_LIST_DEFAULT_VALUE)); - mEntityListEditable = parseStringList( - configParser.getString( - ENTITY_LIST_EDITABLE, - ENTITY_LIST_DEFAULT_VALUE)); - mInAppConversationActionTypesDefault = parseStringList( - configParser.getString( - IN_APP_CONVERSATION_ACTION_TYPES_DEFAULT, - CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES)); - mNotificationConversationActionTypesDefault = parseStringList( - configParser.getString( - NOTIFICATION_CONVERSATION_ACTION_TYPES_DEFAULT, - CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES)); - mLangIdThresholdOverride = - configParser.getFloat( - LANG_ID_THRESHOLD_OVERRIDE, - LANG_ID_THRESHOLD_OVERRIDE_DEFAULT); - mTemplateIntentFactoryEnabled = - configParser.getBoolean( - TEMPLATE_INTENT_FACTORY_ENABLED, - TEMPLATE_INTENT_FACTORY_ENABLED_DEFAULT); - mTranslateInClassificationEnabled = - configParser.getBoolean( - TRANSLATE_IN_CLASSIFICATION_ENABLED, - TRANSLATE_IN_CLASSIFICATION_ENABLED_DEFAULT); - mDetectLanguagesFromTextEnabled = - configParser.getBoolean( - DETECT_LANGUAGES_FROM_TEXT_ENABLED, - DETECT_LANGUAGES_FROM_TEXT_ENABLED_DEFAULT); - mLangIdContextSettings = parseFloatArray( - configParser, - LANG_ID_CONTEXT_SETTINGS, - LANG_ID_CONTEXT_SETTINGS_DEFAULT); - } + private final ConfigParser mConfigParser; - /** Load from a settings string. */ - public static TextClassificationConstants loadFromString(String settings) { - return new TextClassificationConstants(settings); + public TextClassificationConstants(Supplier<String> legacySettingsSupplier) { + mConfigParser = new ConfigParser(legacySettingsSupplier); } public boolean isLocalTextClassifierEnabled() { - return mLocalTextClassifierEnabled; + return mConfigParser.getBoolean( + LOCAL_TEXT_CLASSIFIER_ENABLED, + LOCAL_TEXT_CLASSIFIER_ENABLED_DEFAULT); } public boolean isSystemTextClassifierEnabled() { - return mSystemTextClassifierEnabled; + return mConfigParser.getBoolean( + SYSTEM_TEXT_CLASSIFIER_ENABLED, + SYSTEM_TEXT_CLASSIFIER_ENABLED_DEFAULT); } public boolean isModelDarkLaunchEnabled() { - return mModelDarkLaunchEnabled; + return mConfigParser.getBoolean( + MODEL_DARK_LAUNCH_ENABLED, + MODEL_DARK_LAUNCH_ENABLED_DEFAULT); } public boolean isSmartSelectionEnabled() { - return mSmartSelectionEnabled; + return mConfigParser.getBoolean( + SMART_SELECTION_ENABLED, + SMART_SELECTION_ENABLED_DEFAULT); } public boolean isSmartTextShareEnabled() { - return mSmartTextShareEnabled; + return mConfigParser.getBoolean( + SMART_TEXT_SHARE_ENABLED, + SMART_TEXT_SHARE_ENABLED_DEFAULT); } public boolean isSmartLinkifyEnabled() { - return mSmartLinkifyEnabled; + return mConfigParser.getBoolean( + SMART_LINKIFY_ENABLED, + SMART_LINKIFY_ENABLED_DEFAULT); } public boolean isSmartSelectionAnimationEnabled() { - return mSmartSelectionAnimationEnabled; + return mConfigParser.getBoolean( + SMART_SELECT_ANIMATION_ENABLED, + SMART_SELECT_ANIMATION_ENABLED_DEFAULT); } public int getSuggestSelectionMaxRangeLength() { - return mSuggestSelectionMaxRangeLength; + return mConfigParser.getInt( + SUGGEST_SELECTION_MAX_RANGE_LENGTH, + SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT); } public int getClassifyTextMaxRangeLength() { - return mClassifyTextMaxRangeLength; + return mConfigParser.getInt( + CLASSIFY_TEXT_MAX_RANGE_LENGTH, + CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT); } public int getGenerateLinksMaxTextLength() { - return mGenerateLinksMaxTextLength; + return mConfigParser.getInt( + GENERATE_LINKS_MAX_TEXT_LENGTH, + GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT); } public int getGenerateLinksLogSampleRate() { - return mGenerateLinksLogSampleRate; + return mConfigParser.getInt( + GENERATE_LINKS_LOG_SAMPLE_RATE, + GENERATE_LINKS_LOG_SAMPLE_RATE_DEFAULT); } public List<String> getEntityListDefault() { - return mEntityListDefault; + return mConfigParser.getStringList( + ENTITY_LIST_DEFAULT, + ENTITY_LIST_DEFAULT_VALUE); } public List<String> getEntityListNotEditable() { - return mEntityListNotEditable; + return mConfigParser.getStringList( + ENTITY_LIST_NOT_EDITABLE, + ENTITY_LIST_DEFAULT_VALUE); } public List<String> getEntityListEditable() { - return mEntityListEditable; + return mConfigParser.getStringList( + ENTITY_LIST_EDITABLE, + ENTITY_LIST_DEFAULT_VALUE); } public List<String> getInAppConversationActionTypes() { - return mInAppConversationActionTypesDefault; + return mConfigParser.getStringList( + IN_APP_CONVERSATION_ACTION_TYPES_DEFAULT, + CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES); } public List<String> getNotificationConversationActionTypes() { - return mNotificationConversationActionTypesDefault; + return mConfigParser.getStringList( + NOTIFICATION_CONVERSATION_ACTION_TYPES_DEFAULT, + CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES); } public float getLangIdThresholdOverride() { - return mLangIdThresholdOverride; + return mConfigParser.getFloat( + LANG_ID_THRESHOLD_OVERRIDE, + LANG_ID_THRESHOLD_OVERRIDE_DEFAULT); } public boolean isTemplateIntentFactoryEnabled() { - return mTemplateIntentFactoryEnabled; + return mConfigParser.getBoolean( + TEMPLATE_INTENT_FACTORY_ENABLED, + TEMPLATE_INTENT_FACTORY_ENABLED_DEFAULT); } public boolean isTranslateInClassificationEnabled() { - return mTranslateInClassificationEnabled; + return mConfigParser.getBoolean( + TRANSLATE_IN_CLASSIFICATION_ENABLED, + TRANSLATE_IN_CLASSIFICATION_ENABLED_DEFAULT); } public boolean isDetectLanguagesFromTextEnabled() { - return mDetectLanguagesFromTextEnabled; + return mConfigParser.getBoolean( + DETECT_LANGUAGES_FROM_TEXT_ENABLED, + DETECT_LANGUAGES_FROM_TEXT_ENABLED_DEFAULT); } public float[] getLangIdContextSettings() { - return mLangIdContextSettings; - } - - private static List<String> parseStringList(String listStr) { - return Collections.unmodifiableList(Arrays.asList(listStr.split(STRING_LIST_DELIMITER))); - } - - private static float[] parseFloatArray( - ConfigParser configParser, String key, String defaultStr) { - final String str = configParser.getString(key, defaultStr); - final String[] defaultSplit = defaultStr.split(STRING_LIST_DELIMITER); - String[] split = str.split(STRING_LIST_DELIMITER); - if (split.length != defaultSplit.length) { - Log.v(LOG_TAG, "Error parsing " + key + " flag. Using defaults."); - split = defaultSplit; - } - final float[] result = new float[split.length]; - for (int i = 0; i < split.length; i++) { - try { - result[i] = Float.parseFloat(split[i]); - } catch (NumberFormatException e) { - Log.v(LOG_TAG, "Error parsing part of " + key + " flag. Using defaults."); - result[i] = Float.parseFloat(defaultSplit[i]); - } - } - return result; + return mConfigParser.getFloatArray( + LANG_ID_CONTEXT_SETTINGS, + LANG_ID_CONTEXT_SETTINGS_DEFAULT); } void dump(IndentingPrintWriter pw) { pw.println("TextClassificationConstants:"); pw.increaseIndent(); - pw.printPair("isLocalTextClassifierEnabled", mLocalTextClassifierEnabled); - pw.printPair("isSystemTextClassifierEnabled", mSystemTextClassifierEnabled); - pw.printPair("isModelDarkLaunchEnabled", mModelDarkLaunchEnabled); - pw.printPair("isSmartSelectionEnabled", mSmartSelectionEnabled); - pw.printPair("isSmartTextShareEnabled", mSmartTextShareEnabled); - pw.printPair("isSmartLinkifyEnabled", mSmartLinkifyEnabled); - pw.printPair("isSmartSelectionAnimationEnabled", mSmartSelectionAnimationEnabled); - pw.printPair("getSuggestSelectionMaxRangeLength", mSuggestSelectionMaxRangeLength); - pw.printPair("getClassifyTextMaxRangeLength", mClassifyTextMaxRangeLength); - pw.printPair("getGenerateLinksMaxTextLength", mGenerateLinksMaxTextLength); - pw.printPair("getGenerateLinksLogSampleRate", mGenerateLinksLogSampleRate); - pw.printPair("getEntityListDefault", mEntityListDefault); - pw.printPair("getEntityListNotEditable", mEntityListNotEditable); - pw.printPair("getEntityListEditable", mEntityListEditable); - pw.printPair("getInAppConversationActionTypes", mInAppConversationActionTypesDefault); - pw.printPair("getNotificationConversationActionTypes", - mNotificationConversationActionTypesDefault); - pw.printPair("getLangIdThresholdOverride", mLangIdThresholdOverride); - pw.printPair("isTemplateIntentFactoryEnabled", mTemplateIntentFactoryEnabled); - pw.printPair("isTranslateInClassificationEnabled", mTranslateInClassificationEnabled); - pw.printPair("isDetectLanguageFromTextEnabled", mDetectLanguagesFromTextEnabled); - pw.printPair("getLangIdContextSettings", Arrays.toString(mLangIdContextSettings)); + pw.printPair("classify_text_max_range_length", getClassifyTextMaxRangeLength()) + .println(); + pw.printPair("detect_language_from_text_enabled", isDetectLanguagesFromTextEnabled()) + .println(); + pw.printPair("entity_list_default", getEntityListDefault()) + .println(); + pw.printPair("entity_list_editable", getEntityListEditable()) + .println(); + pw.printPair("entity_list_not_editable", getEntityListNotEditable()) + .println(); + pw.printPair("generate_links_log_sample_rate", getGenerateLinksLogSampleRate()) + .println(); + pw.printPair("generate_links_max_text_length", getGenerateLinksMaxTextLength()) + .println(); + pw.printPair("in_app_conversation_action_types_default", getInAppConversationActionTypes()) + .println(); + pw.printPair("lang_id_context_settings", Arrays.toString(getLangIdContextSettings())) + .println(); + pw.printPair("lang_id_threshold_override", getLangIdThresholdOverride()) + .println(); + pw.printPair("local_textclassifier_enabled", isLocalTextClassifierEnabled()) + .println(); + pw.printPair("model_dark_launch_enabled", isModelDarkLaunchEnabled()) + .println(); + pw.printPair("notification_conversation_action_types_default", + getNotificationConversationActionTypes()).println(); + pw.printPair("smart_linkify_enabled", isSmartLinkifyEnabled()) + .println(); + pw.printPair("smart_select_animation_enabled", isSmartSelectionAnimationEnabled()) + .println(); + pw.printPair("smart_selection_enabled", isSmartSelectionEnabled()) + .println(); + pw.printPair("smart_text_share_enabled", isSmartTextShareEnabled()) + .println(); + pw.printPair("suggest_selection_max_range_length", getSuggestSelectionMaxRangeLength()) + .println(); + pw.printPair("system_textclassifier_enabled", isSystemTextClassifierEnabled()) + .println(); + pw.printPair("template_intent_factory_enabled", isTemplateIntentFactoryEnabled()) + .println(); + pw.printPair("translate_in_classification_enabled", isTranslateInClassificationEnabled()) + .println(); pw.decreaseIndent(); - pw.println(); } -} +}
\ No newline at end of file diff --git a/core/java/android/view/textclassifier/TextClassificationManager.java b/core/java/android/view/textclassifier/TextClassificationManager.java index 417c6e7f3c75..95ca9deb2871 100644 --- a/core/java/android/view/textclassifier/TextClassificationManager.java +++ b/core/java/android/view/textclassifier/TextClassificationManager.java @@ -25,6 +25,7 @@ import android.content.Context; import android.database.ContentObserver; import android.os.ServiceManager; import android.provider.DeviceConfig; +import android.provider.DeviceConfig.Properties; import android.provider.Settings; import android.service.textclassifier.TextClassifierService; import android.view.textclassifier.TextClassifier.TextClassifierType; @@ -44,6 +45,9 @@ public final class TextClassificationManager { private static final String LOG_TAG = "TextClassificationManager"; + private static final TextClassificationConstants sDefaultSettings = + new TextClassificationConstants(() -> null); + private final Object mLock = new Object(); private final TextClassificationSessionFactory mDefaultSessionFactory = classificationContext -> new TextClassificationSession( @@ -128,9 +132,10 @@ public final class TextClassificationManager { private TextClassificationConstants getSettings() { synchronized (mLock) { if (mSettings == null) { - mSettings = TextClassificationConstants.loadFromString(Settings.Global.getString( - getApplicationContext().getContentResolver(), - Settings.Global.TEXT_CLASSIFIER_CONSTANTS)); + mSettings = new TextClassificationConstants( + () -> Settings.Global.getString( + getApplicationContext().getContentResolver(), + Settings.Global.TEXT_CLASSIFIER_CONSTANTS)); } return mSettings; } @@ -199,7 +204,7 @@ public final class TextClassificationManager { getApplicationContext().getContentResolver() .unregisterContentObserver(mSettingsObserver); if (ConfigParser.ENABLE_DEVICE_CONFIG) { - DeviceConfig.removeOnPropertyChangedListener(mSettingsObserver); + DeviceConfig.removeOnPropertiesChangedListener(mSettingsObserver); } } } finally { @@ -250,7 +255,11 @@ public final class TextClassificationManager { /** @hide */ @VisibleForTesting - public void invalidate() { + public void invalidateForTesting() { + invalidate(); + } + + private void invalidate() { synchronized (mLock) { mSettings = null; mLocalTextClassifier = null; @@ -279,14 +288,13 @@ public final class TextClassificationManager { if (tcm != null) { return tcm.getSettings(); } else { - return TextClassificationConstants.loadFromString(Settings.Global.getString( - context.getApplicationContext().getContentResolver(), - Settings.Global.TEXT_CLASSIFIER_CONSTANTS)); + // Use default settings if there is no tcm. + return sDefaultSettings; } } private static final class SettingsObserver extends ContentObserver - implements DeviceConfig.OnPropertyChangedListener { + implements DeviceConfig.OnPropertiesChangedListener { private final WeakReference<TextClassificationManager> mTcm; @@ -298,7 +306,7 @@ public final class TextClassificationManager { false /* notifyForDescendants */, this); if (ConfigParser.ENABLE_DEVICE_CONFIG) { - DeviceConfig.addOnPropertyChangedListener( + DeviceConfig.addOnPropertiesChangedListener( DeviceConfig.NAMESPACE_TEXTCLASSIFIER, ActivityThread.currentApplication().getMainExecutor(), this); @@ -311,7 +319,7 @@ public final class TextClassificationManager { } @Override - public void onPropertyChanged(String namespace, String name, String value) { + public void onPropertiesChanged(Properties properties) { invalidateSettings(); } diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java index 3297523b0da9..3e95f1baf4bb 100644 --- a/core/java/android/view/textclassifier/TextClassifierImpl.java +++ b/core/java/android/view/textclassifier/TextClassifierImpl.java @@ -301,7 +301,7 @@ public final class TextClassifierImpl implements TextClassifier { final ZonedDateTime refTime = ZonedDateTime.now(); final Collection<String> entitiesToIdentify = request.getEntityConfig() != null ? request.getEntityConfig().resolveEntityListModifications( - getEntitiesForHints(request.getEntityConfig().getHints())) + getEntitiesForHints(request.getEntityConfig().getHints())) : mSettings.getEntityListDefault(); final String localesString = concatenateLocales(request.getDefaultLocales()); final String detectLanguageTags = detectLanguageTagsFromText(request.getText()); @@ -779,8 +779,8 @@ public final class TextClassifierImpl implements TextClassifier { final float moreTextScoreRatio = 1f - subjectTextScoreRatio; Log.v(LOG_TAG, String.format(Locale.US, "LangIdContextSettings: " - + "minimumTextSize=%d, penalizeRatio=%.2f, " - + "subjectTextScoreRatio=%.2f, moreTextScoreRatio=%.2f", + + "minimumTextSize=%d, penalizeRatio=%.2f, " + + "subjectTextScoreRatio=%.2f, moreTextScoreRatio=%.2f", minimumTextSize, penalizeRatio, subjectTextScoreRatio, moreTextScoreRatio)); if (end - start < minimumTextSize && penalizeRatio <= 0) { @@ -903,4 +903,3 @@ public final class TextClassifierImpl implements TextClassifier { } } } - diff --git a/core/java/com/android/internal/app/AbstractResolverComparator.java b/core/java/com/android/internal/app/AbstractResolverComparator.java index b7276a0450cc..9ac979b54716 100644 --- a/core/java/com/android/internal/app/AbstractResolverComparator.java +++ b/core/java/com/android/internal/app/AbstractResolverComparator.java @@ -22,9 +22,14 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; import android.os.UserHandle; import android.util.Log; + import com.android.internal.app.ResolverActivity.ResolvedComponentInfo; + import java.util.ArrayList; import java.util.Comparator; import java.util.List; @@ -35,8 +40,10 @@ import java.util.List; abstract class AbstractResolverComparator implements Comparator<ResolvedComponentInfo> { private static final int NUM_OF_TOP_ANNOTATIONS_TO_USE = 3; + private static final boolean DEBUG = false; + private static final String TAG = "AbstractResolverComp"; - private AfterCompute mAfterCompute; + protected AfterCompute mAfterCompute; protected final PackageManager mPm; protected final UsageStatsManager mUsm; protected String[] mAnnotations; @@ -47,12 +54,47 @@ abstract class AbstractResolverComparator implements Comparator<ResolvedComponen // can be null if mHttp == false or current user has no default browser package private final String mDefaultBrowserPackageName; + // message types + static final int RANKER_SERVICE_RESULT = 0; + static final int RANKER_RESULT_TIMEOUT = 1; + + // timeout for establishing connections with a ResolverRankerService, collecting features and + // predicting ranking scores. + private static final int WATCHDOG_TIMEOUT_MILLIS = 500; + + protected final Handler mHandler = new Handler(Looper.getMainLooper()) { + public void handleMessage(Message msg) { + switch (msg.what) { + case RANKER_SERVICE_RESULT: + if (DEBUG) { + Log.d(TAG, "RANKER_SERVICE_RESULT"); + } + if (mHandler.hasMessages(RANKER_RESULT_TIMEOUT)) { + handleResultMessage(msg); + mHandler.removeMessages(RANKER_RESULT_TIMEOUT); + afterCompute(); + } + break; + + case RANKER_RESULT_TIMEOUT: + if (DEBUG) { + Log.d(TAG, "RANKER_RESULT_TIMEOUT; unbinding services"); + } + mHandler.removeMessages(RANKER_SERVICE_RESULT); + afterCompute(); + break; + + default: + super.handleMessage(msg); + } + } + }; + AbstractResolverComparator(Context context, Intent intent) { String scheme = intent.getScheme(); mHttp = "http".equals(scheme) || "https".equals(scheme); mContentType = intent.getType(); getContentAnnotations(intent); - mPm = context.getPackageManager(); mUsm = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE); mDefaultBrowserPackageName = mHttp @@ -142,9 +184,16 @@ abstract class AbstractResolverComparator implements Comparator<ResolvedComponen * #getScore(ComponentName)} or {@link #compare(Object, Object)}, in order to prepare the * comparator for those calls. Note that {@link #getScore(ComponentName)} uses {@link * ComponentName}, so the implementation will have to be prepared to identify a {@link - * ResolvedComponentInfo} by {@link ComponentName}. + * ResolvedComponentInfo} by {@link ComponentName}. {@link #beforeCompute()} will be called + * before doing any computing. */ - abstract void compute(List<ResolvedComponentInfo> targets); + final void compute(List<ResolvedComponentInfo> targets) { + beforeCompute(); + doCompute(targets); + } + + /** Implementation of compute called after {@link #beforeCompute()}. */ + abstract void doCompute(List<ResolvedComponentInfo> targets); /** * Returns the score that was calculated for the corresponding {@link ResolvedComponentInfo} @@ -152,6 +201,9 @@ abstract class AbstractResolverComparator implements Comparator<ResolvedComponen */ abstract float getScore(ComponentName name); + /** Handles result message sent to mHandler. */ + abstract void handleResultMessage(Message message); + /** * Reports to UsageStats what was chosen. */ @@ -172,10 +224,26 @@ abstract class AbstractResolverComparator implements Comparator<ResolvedComponen void updateModel(ComponentName componentName) { } + /** Called before {@link #doCompute(List)}. Sets up 500ms timeout. */ + void beforeCompute() { + if (DEBUG) Log.d(TAG, "Setting watchdog timer for " + WATCHDOG_TIMEOUT_MILLIS + "ms"); + if (mHandler == null) { + Log.d(TAG, "Error: Handler is Null; Needs to be initialized."); + return; + } + mHandler.sendEmptyMessageDelayed(RANKER_RESULT_TIMEOUT, WATCHDOG_TIMEOUT_MILLIS); + } + /** - * Called when the {@link ResolverActivity} is destroyed. + * Called when the {@link ResolverActivity} is destroyed. This calls {@link #afterCompute()}. If + * this call needs to happen at a different time during destroy, the method should be + * overridden. */ - abstract void destroy(); + void destroy() { + mHandler.removeMessages(RANKER_SERVICE_RESULT); + mHandler.removeMessages(RANKER_RESULT_TIMEOUT); + afterCompute(); + } private boolean isDefaultBrowser(ResolveInfo ri) { // It makes sense to prefer the default browser diff --git a/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java b/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java index cb44c67c8c9b..3fddfc8a4602 100644 --- a/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java +++ b/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java @@ -26,8 +26,9 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ResolveInfo; +import android.os.Message; import android.os.UserHandle; -import android.view.textclassifier.Log; +import android.util.Log; import com.android.internal.app.ResolverActivity.ResolvedComponentInfo; @@ -35,29 +36,47 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.Executors; /** - * Uses an {@link AppPredictor} to sort Resolver targets. + * Uses an {@link AppPredictor} to sort Resolver targets. If the AppPredictionService appears to be + * disabled by returning an empty sorted target list, {@link AppPredictionServiceResolverComparator} + * will fallback to using a {@link ResolverRankerServiceResolverComparator}. */ class AppPredictionServiceResolverComparator extends AbstractResolverComparator { private static final String TAG = "APSResolverComparator"; + private static final boolean DEBUG = false; private final AppPredictor mAppPredictor; private final Context mContext; private final Map<ComponentName, Integer> mTargetRanks = new HashMap<>(); private final UserHandle mUser; + private final Intent mIntent; + private final String mReferrerPackage; + // If this is non-null (and this is not destroyed), it means APS is disabled and we should fall + // back to using the ResolverRankerService. + private ResolverRankerServiceResolverComparator mResolverRankerService; AppPredictionServiceResolverComparator( - Context context, Intent intent, AppPredictor appPredictor, UserHandle user) { + Context context, + Intent intent, + String referrerPackage, + AppPredictor appPredictor, + UserHandle user) { super(context, intent); mContext = context; + mIntent = intent; mAppPredictor = appPredictor; mUser = user; + mReferrerPackage = referrerPackage; } @Override int compare(ResolveInfo lhs, ResolveInfo rhs) { + if (mResolverRankerService != null) { + return mResolverRankerService.compare(lhs, rhs); + } Integer lhsRank = mTargetRanks.get(new ComponentName(lhs.activityInfo.packageName, lhs.activityInfo.name)); Integer rhsRank = mTargetRanks.get(new ComponentName(rhs.activityInfo.packageName, @@ -73,25 +92,60 @@ class AppPredictionServiceResolverComparator extends AbstractResolverComparator } @Override - void compute(List<ResolvedComponentInfo> targets) { + void doCompute(List<ResolvedComponentInfo> targets) { + if (targets.isEmpty()) { + mHandler.sendEmptyMessage(RANKER_SERVICE_RESULT); + return; + } List<AppTarget> appTargets = new ArrayList<>(); for (ResolvedComponentInfo target : targets) { appTargets.add(new AppTarget.Builder(new AppTargetId(target.name.flattenToString())) .setTarget(target.name.getPackageName(), mUser) .setClassName(target.name.getClassName()).build()); } - mAppPredictor.sortTargets(appTargets, mContext.getMainExecutor(), + mAppPredictor.sortTargets(appTargets, Executors.newSingleThreadExecutor(), sortedAppTargets -> { - for (int i = 0; i < sortedAppTargets.size(); i++) { - mTargetRanks.put(new ComponentName(sortedAppTargets.get(i).getPackageName(), - sortedAppTargets.get(i).getClassName()), i); + if (sortedAppTargets.isEmpty()) { + if (DEBUG) { + Log.d(TAG, "AppPredictionService disabled. Using resolver."); + } + // APS for chooser is disabled. Fallback to resolver. + mResolverRankerService = + new ResolverRankerServiceResolverComparator( + mContext, mIntent, mReferrerPackage, + () -> mHandler.sendEmptyMessage(RANKER_SERVICE_RESULT)); + mResolverRankerService.compute(targets); + } else { + if (DEBUG) { + Log.d(TAG, "AppPredictionService response received"); + } + Message msg = + Message.obtain(mHandler, RANKER_SERVICE_RESULT, sortedAppTargets); + msg.sendToTarget(); } - afterCompute(); - }); + } + ); + } + + @Override + void handleResultMessage(Message msg) { + // Null value is okay if we have defaulted to the ResolverRankerService. + if (msg.what == RANKER_SERVICE_RESULT && msg.obj != null) { + final List<AppTarget> sortedAppTargets = (List<AppTarget>) msg.obj; + for (int i = 0; i < sortedAppTargets.size(); i++) { + mTargetRanks.put(new ComponentName(sortedAppTargets.get(i).getPackageName(), + sortedAppTargets.get(i).getClassName()), i); + } + } else if (msg.obj == null && mResolverRankerService == null) { + Log.e(TAG, "Unexpected null result"); + } } @Override float getScore(ComponentName name) { + if (mResolverRankerService != null) { + return mResolverRankerService.getScore(name); + } Integer rank = mTargetRanks.get(name); if (rank == null) { Log.w(TAG, "Score requested for unknown component."); @@ -103,6 +157,10 @@ class AppPredictionServiceResolverComparator extends AbstractResolverComparator @Override void updateModel(ComponentName componentName) { + if (mResolverRankerService != null) { + mResolverRankerService.updateModel(componentName); + return; + } mAppPredictor.notifyAppTargetEvent( new AppTargetEvent.Builder( new AppTarget.Builder( @@ -114,6 +172,9 @@ class AppPredictionServiceResolverComparator extends AbstractResolverComparator @Override void destroy() { - // Do nothing. App Predictor destruction is handled by caller. + if (mResolverRankerService != null) { + mResolverRankerService.destroy(); + mResolverRankerService = null; + } } } diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 204012f04cba..2349c31f590f 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -114,6 +114,7 @@ import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.ImageUtils; +import com.android.internal.widget.ResolverDrawerLayout; import com.google.android.collect.Lists; @@ -143,6 +144,8 @@ public class ChooserActivity extends ResolverActivity { public static final String EXTRA_PRIVATE_RETAIN_IN_ON_STOP = "com.android.internal.app.ChooserActivity.EXTRA_PRIVATE_RETAIN_IN_ON_STOP"; + private static final String PREF_NUM_SHEET_EXPANSIONS = "pref_num_sheet_expansions"; + private static final boolean DEBUG = false; /** @@ -150,8 +153,8 @@ public class ChooserActivity extends ResolverActivity { * {@link AppPredictionManager} will be queried for direct share targets. */ // TODO(b/123089490): Replace with system flag - private static final boolean USE_PREDICTION_MANAGER_FOR_DIRECT_TARGETS = false; - private static final boolean USE_PREDICTION_MANAGER_FOR_SHARE_ACTIVITIES = false; + private static final boolean USE_PREDICTION_MANAGER_FOR_DIRECT_TARGETS = true; + private static final boolean USE_PREDICTION_MANAGER_FOR_SHARE_ACTIVITIES = true; // TODO(b/123088566) Share these in a better way. private static final String APP_PREDICTION_SHARE_UI_SURFACE = "share"; public static final String LAUNCH_LOCATON_DIRECT_SHARE = "direct_share"; @@ -450,6 +453,11 @@ public class ChooserActivity extends ResolverActivity { if (mChooserListAdapter == null) { return; } + if (resultList.isEmpty()) { + // APS may be disabled, so try querying targets ourselves. + queryDirectShareTargets(mChooserListAdapter, true); + return; + } final List<DisplayResolveInfo> driList = getDisplayResolveInfos(mChooserListAdapter); final List<ShortcutManager.ShareShortcutInfo> shareShortcutInfos = @@ -502,6 +510,21 @@ public class ChooserActivity extends ResolverActivity { chooserHeader.setElevation(defaultElevation); } }); + + mResolverDrawerLayout.setOnCollapsedChangedListener( + new ResolverDrawerLayout.OnCollapsedChangedListener() { + + // Only consider one expansion per activity creation + private boolean mWrittenOnce = false; + + @Override + public void onCollapsedChanged(boolean isCollapsed) { + if (!isCollapsed && !mWrittenOnce) { + incrementNumSheetExpansions(); + mWrittenOnce = true; + } + } + }); } if (DEBUG) { @@ -881,6 +904,15 @@ public class ChooserActivity extends ResolverActivity { return CONTENT_PREVIEW_TEXT; } + private int getNumSheetExpansions() { + return getPreferences(Context.MODE_PRIVATE).getInt(PREF_NUM_SHEET_EXPANSIONS, 0); + } + + private void incrementNumSheetExpansions() { + getPreferences(Context.MODE_PRIVATE).edit().putInt(PREF_NUM_SHEET_EXPANSIONS, + getNumSheetExpansions() + 1).apply(); + } + @Override protected void onDestroy() { super.onDestroy(); @@ -892,6 +924,8 @@ public class ChooserActivity extends ResolverActivity { mChooserHandler.removeMessages(LIST_VIEW_UPDATE_MESSAGE); mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT); mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_RESULT); + mChooserHandler.removeMessages(SHORTCUT_MANAGER_SHARE_TARGET_RESULT); + mChooserHandler.removeMessages(SHORTCUT_MANAGER_SHARE_TARGET_RESULT_COMPLETED); if (mAppPredictor != null) { mAppPredictor.unregisterPredictionUpdates(mAppPredictorCallback); mAppPredictor.destroy(); @@ -1245,11 +1279,14 @@ public class ChooserActivity extends ResolverActivity { return driList; } - private void queryDirectShareTargets(ChooserListAdapter adapter) { - AppPredictor appPredictor = getAppPredictorForDirectShareIfEnabled(); - if (appPredictor != null) { - appPredictor.requestPredictionUpdate(); - return; + private void queryDirectShareTargets( + ChooserListAdapter adapter, boolean skipAppPredictionService) { + if (!skipAppPredictionService) { + AppPredictor appPredictor = getAppPredictorForDirectShareIfEnabled(); + if (appPredictor != null) { + appPredictor.requestPredictionUpdate(); + return; + } } // Default to just querying ShortcutManager if AppPredictor not present. final IntentFilter filter = getTargetIntentFilter(); @@ -1582,7 +1619,7 @@ public class ChooserActivity extends ResolverActivity { AbstractResolverComparator resolverComparator; if (appPredictor != null) { resolverComparator = new AppPredictionServiceResolverComparator(this, getTargetIntent(), - appPredictor, getUser()); + getReferrerPackageName(), appPredictor, getUser()); } else { resolverComparator = new ResolverRankerServiceResolverComparator(this, getTargetIntent(), @@ -2030,10 +2067,13 @@ public class ChooserActivity extends ResolverActivity { public static final int TARGET_STANDARD_AZ = 3; private static final int MAX_SUGGESTED_APP_TARGETS = 4; - private static final int MAX_TARGETS_PER_SERVICE = 2; + private static final int MAX_CHOOSER_TARGETS_PER_APP = 2; + private static final int MAX_SHORTCUT_TARGETS_PER_APP = 8; private static final int MAX_SERVICE_TARGETS = 8; + private int mNumShortcutResults = 0; + // Reserve spots for incoming direct share targets by adding placeholders private ChooserTargetInfo mPlaceHolderTargetInfo = new PlaceHolderTargetInfo(); private final List<ChooserTargetInfo> mServiceTargets = new ArrayList<>(); @@ -2177,7 +2217,7 @@ public class ChooserActivity extends ResolverActivity { Log.d(TAG, "querying direct share targets from ShortcutManager"); } - queryDirectShareTargets(this); + queryDirectShareTargets(this, false); } if (USE_CHOOSER_TARGET_SERVICE_FOR_DIRECT_TARGETS) { if (DEBUG) { @@ -2356,9 +2396,11 @@ public class ChooserActivity extends ResolverActivity { final float baseScore = getBaseScore(origTarget, isShortcutResult); Collections.sort(targets, mBaseTargetComparator); + final int maxTargets = isShortcutResult ? MAX_SHORTCUT_TARGETS_PER_APP + : MAX_CHOOSER_TARGETS_PER_APP; float lastScore = 0; boolean shouldNotify = false; - for (int i = 0, N = Math.min(targets.size(), MAX_TARGETS_PER_SERVICE); i < N; i++) { + for (int i = 0, count = Math.min(targets.size(), maxTargets); i < count; i++) { final ChooserTarget target = targets.get(i); float targetScore = target.getScore(); targetScore *= baseScore; @@ -2367,9 +2409,15 @@ public class ChooserActivity extends ResolverActivity { // This incents ChooserTargetServices to define what's truly better. targetScore = lastScore * 0.95f; } - shouldNotify |= insertServiceTarget( + boolean isInserted = insertServiceTarget( new SelectableTargetInfo(origTarget, target, targetScore)); + if (isInserted && isShortcutResult) { + mNumShortcutResults++; + } + + shouldNotify |= isInserted; + if (DEBUG) { Log.d(TAG, " => " + target.toString() + " score=" + targetScore + " base=" + target.getScore() @@ -2385,6 +2433,10 @@ public class ChooserActivity extends ResolverActivity { } } + private int getNumShortcutResults() { + return mNumShortcutResults; + } + /** * Use the scoring system along with artificial boosts to create up to 3 distinct buckets: * <ol> @@ -2491,19 +2543,25 @@ public class ChooserActivity extends ResolverActivity { private DirectShareViewHolder mDirectShareViewHolder; private int mChooserTargetWidth = 0; + private boolean mShowAzLabelIfPoss; private static final int VIEW_TYPE_DIRECT_SHARE = 0; private static final int VIEW_TYPE_NORMAL = 1; private static final int VIEW_TYPE_CONTENT_PREVIEW = 2; private static final int VIEW_TYPE_PROFILE = 3; + private static final int VIEW_TYPE_AZ_LABEL = 4; private static final int MAX_TARGETS_PER_ROW_PORTRAIT = 4; private static final int MAX_TARGETS_PER_ROW_LANDSCAPE = 8; + private static final int NUM_EXPANSIONS_TO_HIDE_AZ_LABEL = 20; + public ChooserRowAdapter(ChooserListAdapter wrappedAdapter) { mChooserListAdapter = wrappedAdapter; mLayoutInflater = LayoutInflater.from(ChooserActivity.this); + mShowAzLabelIfPoss = getNumSheetExpansions() < NUM_EXPANSIONS_TO_HIDE_AZ_LABEL; + wrappedAdapter.registerDataSetObserver(new DataSetObserver() { @Override public void onChanged() { @@ -2550,12 +2608,27 @@ public class ChooserActivity extends ResolverActivity { } @Override + public boolean areAllItemsEnabled() { + return false; + } + + @Override + public boolean isEnabled(int position) { + int viewType = getItemViewType(position); + if (viewType == VIEW_TYPE_CONTENT_PREVIEW) { + return false; + } + return true; + } + + @Override public int getCount() { return (int) ( getContentPreviewRowCount() + getProfileRowCount() + getServiceTargetRowCount() + getCallerAndRankedTargetRowCount() + + getAzLabelRowCount() + Math.ceil( (float) mChooserListAdapter.getAlphaTargetCount() / getMaxTargetsPerRow()) @@ -2593,6 +2666,11 @@ public class ChooserActivity extends ResolverActivity { return 0; } + public int getAzLabelRowCount() { + // Only show a label if the a-z list is showing + return (mShowAzLabelIfPoss && mChooserListAdapter.getAlphaTargetCount() > 0) ? 1 : 0; + } + @Override public Object getItem(int position) { // We have nothing useful to return here. @@ -2617,6 +2695,10 @@ public class ChooserActivity extends ResolverActivity { return createProfileView(convertView, parent); } + if (viewType == VIEW_TYPE_AZ_LABEL) { + return createAzLabelView(parent); + } + if (convertView == null) { holder = createViewHolder(viewType, parent); } else { @@ -2630,27 +2712,29 @@ public class ChooserActivity extends ResolverActivity { @Override public int getItemViewType(int position) { - if (position == 0 && getContentPreviewRowCount() == 1) { - return VIEW_TYPE_CONTENT_PREVIEW; - } + int count; - if (getProfileRowCount() == 1 && position == getContentPreviewRowCount()) { - return VIEW_TYPE_PROFILE; - } + int countSum = (count = getContentPreviewRowCount()); + if (count > 0 && position < countSum) return VIEW_TYPE_CONTENT_PREVIEW; - final int start = getFirstRowPosition(position); - final int startType = mChooserListAdapter.getPositionTargetType(start); + countSum += (count = getProfileRowCount()); + if (count > 0 && position < countSum) return VIEW_TYPE_PROFILE; - if (startType == ChooserListAdapter.TARGET_SERVICE) { - return VIEW_TYPE_DIRECT_SHARE; - } + countSum += (count = getServiceTargetRowCount()); + if (count > 0 && position < countSum) return VIEW_TYPE_DIRECT_SHARE; + + countSum += (count = getCallerAndRankedTargetRowCount()); + if (count > 0 && position < countSum) return VIEW_TYPE_NORMAL; + + countSum += (count = getAzLabelRowCount()); + if (count > 0 && position < countSum) return VIEW_TYPE_AZ_LABEL; return VIEW_TYPE_NORMAL; } @Override public int getViewTypeCount() { - return 4; + return 5; } private ViewGroup createContentPreviewView(View convertView, ViewGroup parent) { @@ -2677,6 +2761,10 @@ public class ChooserActivity extends ResolverActivity { return profileRow; } + private View createAzLabelView(ViewGroup parent) { + return mLayoutInflater.inflate(R.layout.chooser_az_label_row, parent, false); + } + private RowViewHolder loadViewsIntoRow(RowViewHolder holder) { final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); final int exactSpec = MeasureSpec.makeMeasureSpec(mChooserTargetWidth, @@ -2775,16 +2863,24 @@ public class ChooserActivity extends ResolverActivity { } /** - * Need to merge CALLER + ranked STANDARD into a single row. All other types - * are placed into their own row as determined by their target type, and dividers - * are added in the list to separate each type. + * Need to merge CALLER + ranked STANDARD into a single row and prevent a separator from + * showing on top of the AZ list if the AZ label is visible. All other types are placed into + * their own row as determined by their target type, and dividers are added in the list to + * separate each type. */ int getRowType(int rowPosition) { + // Merge caller and ranked standard into a single row int positionType = mChooserListAdapter.getPositionTargetType(rowPosition); if (positionType == ChooserListAdapter.TARGET_CALLER) { return ChooserListAdapter.TARGET_STANDARD; } + // If an the A-Z label is shown, prevent a separator from appearing by making the A-Z + // row type the same as the suggestion row type + if (getAzLabelRowCount() > 0 && positionType == ChooserListAdapter.TARGET_STANDARD_AZ) { + return ChooserListAdapter.TARGET_STANDARD; + } + return positionType; } @@ -2797,10 +2893,10 @@ public class ChooserActivity extends ResolverActivity { if (startType != lastStartType || rowPosition == getContentPreviewRowCount() + getProfileRowCount()) { - row.setBackground( + row.setForeground( getResources().getDrawable(R.drawable.chooser_row_layer_list, null)); } else { - row.setBackground(null); + row.setForeground(null); } int columnCount = holder.getColumnCount(); @@ -2864,12 +2960,22 @@ public class ChooserActivity extends ResolverActivity { return serviceCount + (row - serviceRows) * getMaxTargetsPerRow(); } + row -= getAzLabelRowCount(); + return callerAndRankedCount + serviceCount + (row - callerAndRankedRows - serviceRows) * getMaxTargetsPerRow(); } public void handleScroll(View v, int y, int oldy) { - if (mDirectShareViewHolder != null) { + // Only expand direct share area if there is a minimum number of shortcuts, + // which will help reduce the amount of visible shuffling due to older-style + // direct share targets. + int orientation = getResources().getConfiguration().orientation; + boolean canExpandDirectShare = + mChooserListAdapter.getNumShortcutResults() > getMaxTargetsPerRow() + && orientation == Configuration.ORIENTATION_PORTRAIT; + + if (mDirectShareViewHolder != null && canExpandDirectShare) { mDirectShareViewHolder.handleScroll(mAdapterView, y, oldy, getMaxTargetsPerRow()); } } diff --git a/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java b/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java index 726b186d8edb..01e0622063cd 100644 --- a/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java +++ b/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java @@ -18,7 +18,6 @@ package com.android.internal.app; import android.app.usage.UsageStats; -import android.app.usage.UsageStatsManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -28,9 +27,7 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.metrics.LogMaker; -import android.os.Handler; import android.os.IBinder; -import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.os.UserHandle; @@ -67,15 +64,8 @@ class ResolverRankerServiceResolverComparator extends AbstractResolverComparator private static final float RECENCY_MULTIPLIER = 2.f; - // message types - private static final int RESOLVER_RANKER_SERVICE_RESULT = 0; - private static final int RESOLVER_RANKER_RESULT_TIMEOUT = 1; - // timeout for establishing connections with a ResolverRankerService. private static final int CONNECTION_COST_TIMEOUT_MILLIS = 200; - // timeout for establishing connections with a ResolverRankerService, collecting features and - // predicting ranking scores. - private static final int WATCHDOG_TIMEOUT_MILLIS = 500; private final Collator mCollator; private final Map<String, UsageStats> mStats; @@ -93,57 +83,6 @@ class ResolverRankerServiceResolverComparator extends AbstractResolverComparator private Context mContext; private CountDownLatch mConnectSignal; - private final Handler mHandler = new Handler(Looper.getMainLooper()) { - public void handleMessage(Message msg) { - switch (msg.what) { - case RESOLVER_RANKER_SERVICE_RESULT: - if (DEBUG) { - Log.d(TAG, "RESOLVER_RANKER_SERVICE_RESULT"); - } - if (mHandler.hasMessages(RESOLVER_RANKER_RESULT_TIMEOUT)) { - if (msg.obj != null) { - final List<ResolverTarget> receivedTargets = - (List<ResolverTarget>) msg.obj; - if (receivedTargets != null && mTargets != null - && receivedTargets.size() == mTargets.size()) { - final int size = mTargets.size(); - boolean isUpdated = false; - for (int i = 0; i < size; ++i) { - final float predictedProb = - receivedTargets.get(i).getSelectProbability(); - if (predictedProb != mTargets.get(i).getSelectProbability()) { - mTargets.get(i).setSelectProbability(predictedProb); - isUpdated = true; - } - } - if (isUpdated) { - mRankerServiceName = mResolvedRankerName; - } - } else { - Log.e(TAG, "Sizes of sent and received ResolverTargets diff."); - } - } else { - Log.e(TAG, "Receiving null prediction results."); - } - mHandler.removeMessages(RESOLVER_RANKER_RESULT_TIMEOUT); - afterCompute(); - } - break; - - case RESOLVER_RANKER_RESULT_TIMEOUT: - if (DEBUG) { - Log.d(TAG, "RESOLVER_RANKER_RESULT_TIMEOUT; unbinding services"); - } - mHandler.removeMessages(RESOLVER_RANKER_SERVICE_RESULT); - afterCompute(); - break; - - default: - super.handleMessage(msg); - } - } - }; - public ResolverRankerServiceResolverComparator(Context context, Intent intent, String referrerPackage, AfterCompute afterCompute) { super(context, intent); @@ -159,11 +98,39 @@ class ResolverRankerServiceResolverComparator extends AbstractResolverComparator setCallBack(afterCompute); } - // compute features for each target according to usage stats of targets. @Override - public void compute(List<ResolvedComponentInfo> targets) { - reset(); + public void handleResultMessage(Message msg) { + if (msg.what != RANKER_SERVICE_RESULT) { + return; + } + if (msg.obj == null) { + Log.e(TAG, "Receiving null prediction results."); + return; + } + final List<ResolverTarget> receivedTargets = (List<ResolverTarget>) msg.obj; + if (receivedTargets != null && mTargets != null + && receivedTargets.size() == mTargets.size()) { + final int size = mTargets.size(); + boolean isUpdated = false; + for (int i = 0; i < size; ++i) { + final float predictedProb = + receivedTargets.get(i).getSelectProbability(); + if (predictedProb != mTargets.get(i).getSelectProbability()) { + mTargets.get(i).setSelectProbability(predictedProb); + isUpdated = true; + } + } + if (isUpdated) { + mRankerServiceName = mResolvedRankerName; + } + } else { + Log.e(TAG, "Sizes of sent and received ResolverTargets diff."); + } + } + // compute features for each target according to usage stats of targets. + @Override + public void doCompute(List<ResolvedComponentInfo> targets) { final long recentSinceTime = mCurrentTime - RECENCY_TIME_PERIOD; float mostRecencyScore = 1.0f; @@ -322,8 +289,8 @@ class ResolverRankerServiceResolverComparator extends AbstractResolverComparator // unbind the service and clear unhandled messges. @Override public void destroy() { - mHandler.removeMessages(RESOLVER_RANKER_SERVICE_RESULT); - mHandler.removeMessages(RESOLVER_RANKER_RESULT_TIMEOUT); + mHandler.removeMessages(RANKER_SERVICE_RESULT); + mHandler.removeMessages(RANKER_RESULT_TIMEOUT); if (mConnection != null) { mContext.unbindService(mConnection); mConnection.destroy(); @@ -417,15 +384,6 @@ class ResolverRankerServiceResolverComparator extends AbstractResolverComparator return null; } - // set a watchdog, to avoid waiting for ranking service for too long. - private void startWatchDog(int timeOutLimit) { - if (DEBUG) Log.d(TAG, "Setting watchdog timer for " + timeOutLimit + "ms"); - if (mHandler == null) { - Log.d(TAG, "Error: Handler is Null; Needs to be initialized."); - } - mHandler.sendEmptyMessageDelayed(RESOLVER_RANKER_RESULT_TIMEOUT, timeOutLimit); - } - private class ResolverRankerServiceConnection implements ServiceConnection { private final CountDownLatch mConnectSignal; @@ -442,7 +400,7 @@ class ResolverRankerServiceResolverComparator extends AbstractResolverComparator } synchronized (mLock) { final Message msg = Message.obtain(); - msg.what = RESOLVER_RANKER_SERVICE_RESULT; + msg.what = RANKER_SERVICE_RESULT; msg.obj = targets; mHandler.sendMessage(msg); } @@ -477,12 +435,13 @@ class ResolverRankerServiceResolverComparator extends AbstractResolverComparator } } - private void reset() { + @Override + void beforeCompute() { + super.beforeCompute(); mTargetsDict.clear(); mTargets = null; mRankerServiceName = new ComponentName(mContext, this.getClass()); mResolvedRankerName = null; - startWatchDog(WATCHDOG_TIMEOUT_MILLIS); initRanker(mContext); } diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java index 76826d3ce787..9e8bd640026b 100644 --- a/core/java/com/android/internal/content/FileSystemProvider.java +++ b/core/java/com/android/internal/content/FileSystemProvider.java @@ -45,6 +45,7 @@ import android.util.Log; import android.webkit.MimeTypeMap; import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.ArrayUtils; import libcore.io.IoUtils; @@ -450,7 +451,11 @@ public abstract class FileSystemProvider extends DocumentsProvider { @Override public String getDocumentType(String documentId) throws FileNotFoundException { - final File file = getFileForDocId(documentId); + return getDocumentType(documentId, getFileForDocId(documentId)); + } + + private String getDocumentType(final String documentId, final File file) + throws FileNotFoundException { if (file.isDirectory()) { return Document.MIME_TYPE_DIR; } else { @@ -532,51 +537,63 @@ public abstract class FileSystemProvider extends DocumentsProvider { return DocumentsContract.openImageThumbnail(file); } - protected RowBuilder includeFile(MatrixCursor result, String docId, File file) + protected RowBuilder includeFile(final MatrixCursor result, String docId, File file) throws FileNotFoundException { + final String[] columns = result.getColumnNames(); + final RowBuilder row = result.newRow(); + if (docId == null) { docId = getDocIdForFile(file); } else { file = getFileForDocId(docId); } + final String mimeType = getDocumentType(docId, file); + row.add(Document.COLUMN_DOCUMENT_ID, docId); + row.add(Document.COLUMN_MIME_TYPE, mimeType); - int flags = 0; + final int flagIndex = ArrayUtils.indexOf(columns, Document.COLUMN_FLAGS); + if (flagIndex != -1) { + int flags = 0; + if (file.canWrite()) { + if (mimeType.equals(Document.MIME_TYPE_DIR)) { + flags |= Document.FLAG_DIR_SUPPORTS_CREATE; + flags |= Document.FLAG_SUPPORTS_DELETE; + flags |= Document.FLAG_SUPPORTS_RENAME; + flags |= Document.FLAG_SUPPORTS_MOVE; + } else { + flags |= Document.FLAG_SUPPORTS_WRITE; + flags |= Document.FLAG_SUPPORTS_DELETE; + flags |= Document.FLAG_SUPPORTS_RENAME; + flags |= Document.FLAG_SUPPORTS_MOVE; + } + } - if (file.canWrite()) { - if (file.isDirectory()) { - flags |= Document.FLAG_DIR_SUPPORTS_CREATE; - flags |= Document.FLAG_SUPPORTS_DELETE; - flags |= Document.FLAG_SUPPORTS_RENAME; - flags |= Document.FLAG_SUPPORTS_MOVE; - } else { - flags |= Document.FLAG_SUPPORTS_WRITE; - flags |= Document.FLAG_SUPPORTS_DELETE; - flags |= Document.FLAG_SUPPORTS_RENAME; - flags |= Document.FLAG_SUPPORTS_MOVE; + if (mimeType.startsWith("image/")) { + flags |= Document.FLAG_SUPPORTS_THUMBNAIL; } - } - final String mimeType = getDocumentType(docId); - final String displayName = file.getName(); - if (mimeType.startsWith("image/")) { - flags |= Document.FLAG_SUPPORTS_THUMBNAIL; + if (typeSupportsMetadata(mimeType)) { + flags |= Document.FLAG_SUPPORTS_METADATA; + } + row.add(flagIndex, flags); } - if (typeSupportsMetadata(mimeType)) { - flags |= Document.FLAG_SUPPORTS_METADATA; + final int displayNameIndex = ArrayUtils.indexOf(columns, Document.COLUMN_DISPLAY_NAME); + if (displayNameIndex != -1) { + row.add(displayNameIndex, file.getName()); } - final RowBuilder row = result.newRow(); - row.add(Document.COLUMN_DOCUMENT_ID, docId); - row.add(Document.COLUMN_DISPLAY_NAME, displayName); - row.add(Document.COLUMN_SIZE, file.length()); - row.add(Document.COLUMN_MIME_TYPE, mimeType); - row.add(Document.COLUMN_FLAGS, flags); - - // Only publish dates reasonably after epoch - long lastModified = file.lastModified(); - if (lastModified > 31536000000L) { - row.add(Document.COLUMN_LAST_MODIFIED, lastModified); + final int lastModifiedIndex = ArrayUtils.indexOf(columns, Document.COLUMN_LAST_MODIFIED); + if (lastModifiedIndex != -1) { + final long lastModified = file.lastModified(); + // Only publish dates reasonably after epoch + if (lastModified > 31536000000L) { + row.add(lastModifiedIndex, lastModified); + } + } + final int sizeIndex = ArrayUtils.indexOf(columns, Document.COLUMN_SIZE); + if (sizeIndex != -1) { + row.add(sizeIndex, file.length()); } // Return the row builder just in case any subclass want to add more stuff to it. diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java index 5d08a259dace..ad1ff902b7d8 100644 --- a/core/java/com/android/internal/content/PackageHelper.java +++ b/core/java/com/android/internal/content/PackageHelper.java @@ -64,6 +64,8 @@ public class PackageHelper { public static final int RECOMMEND_MEDIA_UNAVAILABLE = -5; public static final int RECOMMEND_FAILED_INVALID_URI = -6; public static final int RECOMMEND_FAILED_VERSION_DOWNGRADE = -7; + /** {@hide} */ + public static final int RECOMMEND_FAILED_WRONG_INSTALLED_VERSION = -8; private static final String TAG = "PackageHelper"; // App installation location settings values diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java index dd6fb72888e2..2987b4e56fec 100644 --- a/core/java/com/android/internal/os/ZygoteConnection.java +++ b/core/java/com/android/internal/os/ZygoteConnection.java @@ -406,8 +406,13 @@ class ZygoteConnection { private int mHiddenApiAccessStatslogSampleRate = 0; public static void setHiddenApiAccessLogSampleRates(int sampleRate, int newSampleRate) { - sInstance.mHiddenApiAccessLogSampleRate = sampleRate; - sInstance.mHiddenApiAccessStatslogSampleRate = newSampleRate; + if (sampleRate != -1) { + sInstance.mHiddenApiAccessLogSampleRate = sampleRate; + } + + if (newSampleRate != -1) { + sInstance.mHiddenApiAccessStatslogSampleRate = newSampleRate; + } } public static HiddenApiUsageLogger getInstance() { diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl index cfc32cf67016..7e501d27d30f 100644 --- a/core/java/com/android/internal/statusbar/IStatusBar.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl @@ -50,9 +50,11 @@ oneway interface IStatusBar * @param mask which flags to change * @param fullscreenBounds the current bounds of the fullscreen stack, in screen coordinates * @param dockedBounds the current bounds of the docked stack, in screen coordinates + * @param navbarColorManagedByIme {@code true} if navigation bar color is managed by IME. */ void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis, int dockedStackVis, - int mask, in Rect fullscreenBounds, in Rect dockedBounds); + int mask, in Rect fullscreenBounds, in Rect dockedBounds, + boolean navbarColorManagedByIme); void topAppWindowChanged(int displayId, boolean menuVisible); void setImeWindowStatus(int displayId, in IBinder token, int vis, int backDisposition, diff --git a/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java b/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java index ff94264d9b90..6b0f8b2f5dc9 100644 --- a/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java +++ b/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java @@ -39,12 +39,13 @@ public final class RegisterStatusBarResult implements Parcelable { public final IBinder mImeToken; public final Rect mFullscreenStackBounds; public final Rect mDockedStackBounds; + public final boolean mNavbarColorManagedByIme; public RegisterStatusBarResult(ArrayMap<String, StatusBarIcon> icons, int disabledFlags1, int systemUiVisibility, boolean menuVisible, int imeWindowVis, int imeBackDisposition, boolean showImeSwitcher, int disabledFlags2, int fullscreenStackSysUiVisibility, int dockedStackSysUiVisibility, IBinder imeToken, Rect fullscreenStackBounds, - Rect dockedStackBounds) { + Rect dockedStackBounds, boolean navbarColorManagedByIme) { mIcons = new ArrayMap<>(icons); mDisabledFlags1 = disabledFlags1; mSystemUiVisibility = systemUiVisibility; @@ -58,6 +59,7 @@ public final class RegisterStatusBarResult implements Parcelable { mImeToken = imeToken; mFullscreenStackBounds = fullscreenStackBounds; mDockedStackBounds = dockedStackBounds; + mNavbarColorManagedByIme = navbarColorManagedByIme; } @Override @@ -80,6 +82,7 @@ public final class RegisterStatusBarResult implements Parcelable { dest.writeStrongBinder(mImeToken); dest.writeTypedObject(mFullscreenStackBounds, flags); dest.writeTypedObject(mDockedStackBounds, flags); + dest.writeBoolean(mNavbarColorManagedByIme); } /** @@ -103,11 +106,12 @@ public final class RegisterStatusBarResult implements Parcelable { final IBinder imeToken = source.readStrongBinder(); final Rect fullscreenStackBounds = source.readTypedObject(Rect.CREATOR); final Rect dockedStackBounds = source.readTypedObject(Rect.CREATOR); + final boolean navbarColorManagedByIme = source.readBoolean(); return new RegisterStatusBarResult(icons, disabledFlags1, systemUiVisibility, menuVisible, imeWindowVis, imeBackDisposition, showImeSwitcher, disabledFlags2, fullscreenStackSysUiVisibility, dockedStackSysUiVisibility, imeToken, fullscreenStackBounds, - dockedStackBounds); + dockedStackBounds, navbarColorManagedByIme); } @Override diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java index 6b0d85ef0b95..3adb36f5e54b 100644 --- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java +++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java @@ -104,6 +104,7 @@ public class ResolverDrawerLayout extends ViewGroup { private OnDismissedListener mOnDismissedListener; private RunOnDismissedListener mRunOnDismissedListener; + private OnCollapsedChangedListener mOnCollapsedChangedListener; private boolean mDismissLocked; @@ -267,6 +268,10 @@ public class ResolverDrawerLayout extends ViewGroup { return mOnDismissedListener != null && !mDismissLocked; } + public void setOnCollapsedChangedListener(OnCollapsedChangedListener listener) { + mOnCollapsedChangedListener = listener; + } + @Override public boolean onInterceptTouchEvent(MotionEvent ev) { final int action = ev.getActionMasked(); @@ -548,6 +553,10 @@ public class ResolverDrawerLayout extends ViewGroup { if (mScrollIndicatorDrawable != null) { setWillNotDraw(!isCollapsed); } + + if (mOnCollapsedChangedListener != null) { + mOnCollapsedChangedListener.onCollapsedChanged(isCollapsed); + } } void dispatchOnDismissed() { @@ -1078,8 +1087,25 @@ public class ResolverDrawerLayout extends ViewGroup { }; } + /** + * Listener for sheet dismissed events. + */ public interface OnDismissedListener { - public void onDismissed(); + /** + * Callback when the sheet is dismissed by the user. + */ + void onDismissed(); + } + + /** + * Listener for sheet collapsed / expanded events. + */ + public interface OnCollapsedChangedListener { + /** + * Callback when the sheet is either fully expanded or collapsed. + * @param isCollapsed true when collapsed, false when expanded. + */ + void onCollapsedChanged(boolean isCollapsed); } private class RunOnDismissedListener implements Runnable { diff --git a/core/jni/android_os_HwBlob.cpp b/core/jni/android_os_HwBlob.cpp index cb55618c685c..e5b72caf2792 100644 --- a/core/jni/android_os_HwBlob.cpp +++ b/core/jni/android_os_HwBlob.cpp @@ -88,7 +88,7 @@ JHwBlob::JHwBlob(JNIEnv *env, jobject thiz, size_t size) mOwnsBuffer(true), mHandle(0) { if (size > 0) { - mBuffer = malloc(size); + mBuffer = calloc(size, 1); } } diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp index c4c16ee0ef9b..43c0bbecd71c 100644 --- a/core/jni/android_view_RenderNode.cpp +++ b/core/jni/android_view_RenderNode.cpp @@ -546,7 +546,7 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject, if (CC_LIKELY(transform.isPureTranslate())) { // snap/round the computed bounds, so they match the rounding behavior // of the clear done in SurfaceView#draw(). - bounds.snapToPixelBoundaries(); + bounds.snapGeometryToPixelBoundaries(false); } else { // Conservatively round out so the punched hole (in the ZOrderOnTop = true case) // doesn't extend beyond the other window diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index ecc9d648ac0d..986771dad802 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -136,8 +136,8 @@ public: // ColorSpace.Named.SRGB.ordinal() = 0; static constexpr jint SRGB = 0; - // ColorSpace.Named.DISPLAY_P3.ordinal() = 6; - static constexpr jint DISPLAY_P3 = 6; + // ColorSpace.Named.DISPLAY_P3.ordinal() = 7; + static constexpr jint DISPLAY_P3 = 7; }; constexpr jint fromDataspaceToNamedColorSpaceValue(const ui::Dataspace dataspace) { diff --git a/core/proto/OWNERS b/core/proto/OWNERS index 480b1eaaf98c..a3d4798647be 100644 --- a/core/proto/OWNERS +++ b/core/proto/OWNERS @@ -1,13 +1,8 @@ # Be sure you are familiar with proto when you modify this directory. # Metrics -bookatz@google.com -cjyu@google.com -jinyithu@google.com joeo@google.com -kwekua@google.com singhtejinder@google.com -yanglu@google.com yaochen@google.com yro@google.com diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto index 1ff7418a3ef8..9cffb2bf1601 100644 --- a/core/proto/android/app/settings_enums.proto +++ b/core/proto/android/app/settings_enums.proto @@ -2354,4 +2354,11 @@ enum PageId { // OPEN: Settings > Pick preferred SIM dialog DIALOG_PREFERRED_SIM_PICKER = 1709; + + // OPEN: Settings > Network & internet > Mobile network > Delete sim + DIALOG_DELETE_SIM_CONFIRMATION = 1713; + + // OPEN: Settings > Network & internet > Mobile network > Delete sim > (answer yes to + // confirmation) + DIALOG_DELETE_SIM_PROGRESS = 1714; } diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto index b47097da0cdd..8f16b418f7db 100644 --- a/core/proto/android/providers/settings/global.proto +++ b/core/proto/android/providers/settings/global.proto @@ -501,7 +501,10 @@ message GlobalSettingsProto { } optional IntentFirewall intent_firewall = 65; - optional SettingProto job_scheduler_constants = 66; + optional SettingProto job_scheduler_constants = 66 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto job_scheduler_quota_controller_constants = 149 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto job_scheduler_time_controller_constants = 150 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto keep_profile_in_background = 67 [ (android.privacy).dest = DEST_AUTOMATIC ]; message LangId { @@ -1049,5 +1052,5 @@ message GlobalSettingsProto { // Please insert fields in alphabetical order and group them into messages // if possible (to avoid reaching the method limit). - // Next tag = 149; + // Next tag = 151; } diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto index dbd219141e3e..e6ae22658f31 100644 --- a/core/proto/android/server/windowmanagerservice.proto +++ b/core/proto/android/server/windowmanagerservice.proto @@ -159,6 +159,9 @@ message DisplayContentProto { optional int32 surface_size = 14 [deprecated=true]; optional string focused_app = 15; optional AppTransitionProto app_transition = 16; + repeated IdentifierProto opening_apps = 17; + repeated IdentifierProto closing_apps = 18; + repeated IdentifierProto changing_apps = 19; } /* represents DisplayFrames */ diff --git a/core/proto/android/stats/devicepolicy/device_policy_enums.proto b/core/proto/android/stats/devicepolicy/device_policy_enums.proto index 0db74243222c..0821d147044d 100644 --- a/core/proto/android/stats/devicepolicy/device_policy_enums.proto +++ b/core/proto/android/stats/devicepolicy/device_policy_enums.proto @@ -149,4 +149,6 @@ enum EventId { PROVISIONING_PREPARE_STARTED = 122; PROVISIONING_PREPARE_COMPLETED = 123; PROVISIONING_FLOW_TYPE = 124; + CROSS_PROFILE_APPS_GET_TARGET_USER_PROFILES = 125; + CROSS_PROFILE_APPS_START_ACTIVITY_AS_USER = 126; } diff --git a/core/proto/android/stats/mediametrics/mediametrics.proto b/core/proto/android/stats/mediametrics/mediametrics.proto new file mode 100644 index 000000000000..34ed90a8c90c --- /dev/null +++ b/core/proto/android/stats/mediametrics/mediametrics.proto @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; + +package android.stats.mediametrics; + +/** + * Track how we arbitrate between microphone/input requests. + * Logged from + * frameworks/av/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp + * frameworks/av/services/mediaanalytics/statsd_audiopolicy.cpp + * Next Tag: 10 + */ +message AudioPolicyData { + optional int32 status = 1; + optional string request_source = 2; + optional string request_package = 3; + optional int32 request_session = 4; + optional string request_device = 5; + optional string active_source = 6; + optional string active_package = 7; + optional int32 active_session = 8; + optional string active_device = 9; +} + +/** + * Track properties of audio recording + * Logged from + * frameworks/av/media/libaudioclient/AudioRecord.cpp + * frameworks/av/services/mediaanalytics/statsd_audiorecord.cpp + * Next Tag: 16 + */ +message AudioRecordData { + optional string encoding = 1; + optional string source = 2; + optional int32 latency = 3; + optional int32 samplerate = 4; + optional int32 channels = 5; + optional int64 created_millis = 6; + optional int64 duration_millis = 7; + optional int32 count = 8; + optional int32 error_code = 9; + optional string error_function = 10; + optional int32 port_id = 11; + optional int32 frame_count = 12; + optional string attributes = 13; + optional int64 channel_mask = 14; + optional int64 start_count = 15; + +} + +/** + * Track audio thread performance data + * Logged from + * frameworks/av/media/libnblog/ReportPerformance.cpp + * frameworks/av/services/mediaanalytics/statsd_audiothread.cpp + * Next Tag: 28 + */ +message AudioThreadData { + optional string type = 1; + optional int32 framecount = 2; + optional int32 samplerate = 3; + optional string work_millis_hist = 4; + optional string latency_millis_hist = 5; + optional string warmup_millis_hist = 6; + optional int64 underruns = 7; + optional int64 overruns = 8; + optional int64 active_millis = 9; + optional int64 duration_millis = 10; + + optional int32 id = 11; + optional int32 port_id = 12; + optional int32 sample_rate = 13; + optional int64 channel_mask = 14; + optional string encoding = 15; + optional int32 frame_count = 16; + optional string output_device = 17; + optional string input_device = 18; + optional double io_jitter_mean_millis = 19; + optional double io_jitter_stddev_millis = 20; + optional double process_time_mean_millis = 21; + optional double process_time_stddev_millis = 22; + optional double timestamp_jitter_mean_millis = 23; + optional double timestamp_jitter_stddev_millis = 24; + optional double latency_mean_millis = 25; + optional double latency_stddev_millis = 26; + +} + +/** + * Track audio track playback data + * Logged from + * frameworks/av/media/libaudioclient/AudioTrack.cpp + * frameworks/av/services/mediaanalytics/statsd_audiotrack.cpp + * Next Tag: 12 + */ +message AudioTrackData { + optional string stream_type = 1; + optional string content_type = 2; + optional string track_usage = 3; + optional int32 sample_rate = 4; + optional int64 channel_mask = 5; + + optional int32 underrun_frames = 6; + optional int32 startup_glitch = 7; + + optional int32 port_id = 8; + optional string encoding = 9; + optional int32 frame_count = 10; + optional string attributes = 11; + + +} + +/** + * Track Media Codec usage + * Logged from: + * frameworks/av/media/libstagefright/MediaCodec.cpp + * frameworks/av/services/mediaanalytics/statsd_codec.cpp + * Next Tag: 21 + */ +message CodecData { + optional string codec = 1; + optional string mime = 2; + optional string mode = 3; + optional int32 encoder = 4; + optional int32 secure = 5; + optional int32 width = 6; + optional int32 height = 7; + optional int32 rotation = 8; + optional int32 crypto = 9; + optional int32 profile = 10; + optional int32 level = 11; + optional int32 max_width = 12; + optional int32 max_height = 13; + optional int32 error_code = 14; + optional string error_state = 15; + optional int64 latency_max = 16; + optional int64 latency_min = 17; + optional int64 latency_avg = 18; + optional int64 latency_count = 19; + optional int64 latency_unknown = 20; +} + +/** + * Track Media Extractor (pulling video/audio streams out of containers) usage + * Logged from: + * frameworks/av/media/libstagefright/RemoteMediaExtractor.cpp + * frameworks/av/services/mediaanalytics/statsd_extractor.cpp + * Next Tag: 4 + */ +message ExtractorData { + optional string format = 1; + optional string mime = 2; + optional int32 tracks = 3; +} + +/** + * Track Media Player usage + * this handles both nuplayer and nuplayer2 + * Logged from: + * frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp + * frameworks/av/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp + * frameworks/av/services/mediaanalytics/statsd_nuplayer.cpp + * Next Tag: 21 + */ +message NuPlayerData { + optional string whichPlayer = 1; + + optional string video_mime = 2; + optional string video_codec = 3; + optional int32 width = 4; + optional int32 height = 5; + optional int64 frames = 6; + optional int64 frames_dropped = 7; + optional double framerate = 8; + optional string audio_mime = 9; + optional string audio_codec = 10; + optional int64 duration_millis = 11; + optional int64 playing_millis = 12; + optional int32 error = 13; + optional int32 error_code = 14; + optional string error_state = 15; + optional string data_source_type = 16; + optional int64 rebuffering_millis = 17; + optional int32 rebuffers = 18; + optional int32 rebuffer_at_exit = 19; + optional int64 frames_dropped_startup = 20; +} + +/** + * Track information about recordings (e.g. camcorder) + * Logged from + * frameworks/av/media/libmediaplayerservice/StagefrightRecorder.cpp + * frameworks/av/services/mediaanalytics/if_statsd.cpp + * Next Tag: 22 + */ +message RecorderData { + optional string audio_mime = 1; + optional string video_mime = 2; + optional int32 video_profile = 3; + optional int32 video_level = 4; + optional int32 width = 5; + optional int32 height = 6; + optional int32 rotation = 7; + optional int32 framerate = 8; + optional int32 capture_fps = 9; + optional double capture_fps_enable = 10; + optional int64 duration_millis = 11; + optional int64 paused_millis = 12; + optional int32 paused_count = 13; + optional int32 audio_bitrate = 14; + optional int32 audio_channels = 15; + optional int32 audio_samplerate = 16; + optional int32 movie_timescale = 17; + optional int32 audio_timescale = 18; + optional int32 video_timescale = 19; + optional int32 video_bitrate = 20; + optional int32 iframe_interval = 21; +} diff --git a/core/res/res/anim/ic_bluetooth_transient_animation_0.xml b/core/res/res/anim/ic_bluetooth_transient_animation_0.xml new file mode 100644 index 000000000000..f14cfcf2beb1 --- /dev/null +++ b/core/res/res/anim/ic_bluetooth_transient_animation_0.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> + +<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> + <objectAnimator android:propertyName="fillAlpha" android:duration="17" + android:startOffset="0" android:valueFrom="1" android:valueTo="0.5" + android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> + <objectAnimator android:propertyName="fillAlpha" android:duration="233" + android:startOffset="17" android:valueFrom="0.5" + android:valueTo="0.5" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> + <objectAnimator android:propertyName="fillAlpha" android:duration="17" + android:startOffset="250" android:valueFrom="0.5" + android:valueTo="1" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> + <objectAnimator android:propertyName="fillAlpha" android:duration="233" + android:startOffset="267" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> + <objectAnimator android:propertyName="fillAlpha" android:duration="17" + android:startOffset="500" android:valueFrom="1" + android:valueTo="0.5" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> + <objectAnimator android:propertyName="fillAlpha" android:duration="233" + android:startOffset="517" android:valueFrom="0.5" + android:valueTo="0.5" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> + <objectAnimator android:propertyName="fillAlpha" android:duration="17" + android:startOffset="750" android:valueFrom="0.5" + android:valueTo="1" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> +</set> diff --git a/core/res/res/anim/ic_bluetooth_transient_animation_1.xml b/core/res/res/anim/ic_bluetooth_transient_animation_1.xml new file mode 100644 index 000000000000..934bd1795765 --- /dev/null +++ b/core/res/res/anim/ic_bluetooth_transient_animation_1.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> + +<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> + <objectAnimator android:propertyName="fillAlpha" android:duration="250" + android:startOffset="0" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> + <objectAnimator android:propertyName="fillAlpha" android:duration="17" + android:startOffset="250" android:valueFrom="1" + android:valueTo="0.5" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> + <objectAnimator android:propertyName="fillAlpha" android:duration="233" + android:startOffset="267" android:valueFrom="0.5" + android:valueTo="0.5" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> + <objectAnimator android:propertyName="fillAlpha" android:duration="17" + android:startOffset="500" android:valueFrom="0.5" + android:valueTo="1" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> + <objectAnimator android:propertyName="fillAlpha" android:duration="233" + android:startOffset="517" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> + <objectAnimator android:propertyName="fillAlpha" android:duration="17" + android:startOffset="750" android:valueFrom="1" + android:valueTo="0.5" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> +</set> diff --git a/core/res/res/anim/ic_bluetooth_transient_animation_2.xml b/core/res/res/anim/ic_bluetooth_transient_animation_2.xml new file mode 100644 index 000000000000..7dce34cc0926 --- /dev/null +++ b/core/res/res/anim/ic_bluetooth_transient_animation_2.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> + <objectAnimator android:propertyName="translateX" android:duration="1000" + android:startOffset="0" android:valueFrom="0" android:valueTo="1" + android:valueType="floatType"/> +</set> diff --git a/core/res/res/anim/ic_hotspot_transient_animation_0.xml b/core/res/res/anim/ic_hotspot_transient_animation_0.xml new file mode 100644 index 000000000000..d5a4c521e7ff --- /dev/null +++ b/core/res/res/anim/ic_hotspot_transient_animation_0.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> + <objectAnimator android:propertyName="fillAlpha" android:duration="17" + android:startOffset="0" android:valueFrom="1" android:valueTo="0.5" + android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> + <objectAnimator android:propertyName="fillAlpha" android:duration="633" + android:startOffset="17" android:valueFrom="0.5" + android:valueTo="0.5" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> + <objectAnimator android:propertyName="fillAlpha" android:duration="17" + android:startOffset="650" android:valueFrom="0.5" + android:valueTo="1" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> +</set> diff --git a/core/res/res/anim/ic_hotspot_transient_animation_1.xml b/core/res/res/anim/ic_hotspot_transient_animation_1.xml new file mode 100644 index 000000000000..36db4b956e90 --- /dev/null +++ b/core/res/res/anim/ic_hotspot_transient_animation_1.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> + <objectAnimator android:propertyName="fillAlpha" android:duration="200" + android:startOffset="0" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> + <objectAnimator android:propertyName="fillAlpha" android:duration="17" + android:startOffset="200" android:valueFrom="1" + android:valueTo="0.5" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> + <objectAnimator android:propertyName="fillAlpha" android:duration="617" + android:startOffset="217" android:valueFrom="0.5" + android:valueTo="0.5" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> + <objectAnimator android:propertyName="fillAlpha" android:duration="17" + android:startOffset="833" android:valueFrom="0.5" + android:valueTo="1" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> +</set> diff --git a/core/res/res/anim/ic_hotspot_transient_animation_2.xml b/core/res/res/anim/ic_hotspot_transient_animation_2.xml new file mode 100644 index 000000000000..3d6743608c87 --- /dev/null +++ b/core/res/res/anim/ic_hotspot_transient_animation_2.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> + +<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> + <objectAnimator android:propertyName="fillAlpha" android:duration="400" + android:startOffset="0" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> + <objectAnimator android:propertyName="fillAlpha" android:duration="17" + android:startOffset="400" android:valueFrom="1" + android:valueTo="0.5" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> + <objectAnimator android:propertyName="fillAlpha" android:duration="617" + android:startOffset="417" android:valueFrom="0.5" + android:valueTo="0.5" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> + <objectAnimator android:propertyName="fillAlpha" android:duration="17" + android:startOffset="1033" android:valueFrom="0.5" + android:valueTo="1" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator"/> +</set> diff --git a/core/res/res/anim/ic_hotspot_transient_animation_3.xml b/core/res/res/anim/ic_hotspot_transient_animation_3.xml new file mode 100644 index 000000000000..1495dc98d852 --- /dev/null +++ b/core/res/res/anim/ic_hotspot_transient_animation_3.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> + +<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> + <objectAnimator android:propertyName="translateX" android:duration="1250" + android:startOffset="0" android:valueFrom="0" android:valueTo="1" + android:valueType="floatType"/> +</set> diff --git a/packages/SystemUI/res/drawable/ic_bluetooth_transient_animation.xml b/core/res/res/anim/ic_signal_wifi_transient_animation_0.xml index dc3c3e24c68a..f71d0ee30def 100644 --- a/packages/SystemUI/res/drawable/ic_bluetooth_transient_animation.xml +++ b/core/res/res/anim/ic_signal_wifi_transient_animation_0.xml @@ -1,5 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2017 The Android Open Source Project +<!-- + Copyright (C) 2019 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -13,13 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. --> -<animated-vector - xmlns:android="http://schemas.android.com/apk/res/android" - android:drawable="@drawable/ic_bluetooth_transient" > - <target - android:name="dot_left" - android:animation="@anim/ic_bluetooth_transient_dot_left_animation" /> - <target - android:name="dot_right" - android:animation="@anim/ic_bluetooth_transient_dot_right_animation" /> -</animated-vector> +<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> + <objectAnimator android:propertyName="scaleY" android:duration="0" + android:startOffset="167" android:valueFrom="1" android:valueTo="0" + android:valueType="floatType"/> +</set>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/ic_sysbar_menu.xml b/core/res/res/anim/ic_signal_wifi_transient_animation_1.xml index d53c95b1cb80..9af42a10cbe9 100644 --- a/packages/SystemUI/res/drawable/ic_sysbar_menu.xml +++ b/core/res/res/anim/ic_signal_wifi_transient_animation_1.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - Copyright (C) 2018 The Android Open Source Project + Copyright (C) 2019 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,13 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. --> -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="21dp" - android:height="21dp" - android:viewportWidth="28" - android:viewportHeight="28"> - - <path - android:fillColor="?attr/singleToneColor" - android:pathData="M14,9.5c1.24,0,2.25-1.01,2.25-2.25S15.24,5,14,5s-2.25,1.01-2.25,2.25S12.76,9.5,14,9.5z M14,11.75 c-1.24,0-2.25,1.01-2.25,2.25s1.01,2.25,2.25,2.25s2.25-1.01,2.25-2.25S15.24,11.75,14,11.75z M14,18.5 c-1.24,0-2.25,1.01-2.25,2.25S12.76,23,14,23s2.25-1.01,2.25-2.25S15.24,18.5,14,18.5z" /> -</vector>
\ No newline at end of file +<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> + <objectAnimator android:propertyName="scaleX" android:duration="0" + android:startOffset="167" android:valueFrom="0" android:valueTo="1" + android:valueType="floatType"/> +</set>
\ No newline at end of file diff --git a/core/res/res/anim/ic_signal_wifi_transient_animation_2.xml b/core/res/res/anim/ic_signal_wifi_transient_animation_2.xml new file mode 100644 index 000000000000..0e7864a4c71b --- /dev/null +++ b/core/res/res/anim/ic_signal_wifi_transient_animation_2.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> + <objectAnimator android:propertyName="scaleY" android:duration="0" + android:startOffset="333" android:valueFrom="1" android:valueTo="0" + android:valueType="floatType"/> +</set>
\ No newline at end of file diff --git a/core/res/res/anim/ic_signal_wifi_transient_animation_3.xml b/core/res/res/anim/ic_signal_wifi_transient_animation_3.xml new file mode 100644 index 000000000000..bbec629c1a1e --- /dev/null +++ b/core/res/res/anim/ic_signal_wifi_transient_animation_3.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> + <objectAnimator android:propertyName="scaleX" android:duration="0" + android:startOffset="333" android:valueFrom="0" android:valueTo="1" + android:valueType="floatType"/> +</set>
\ No newline at end of file diff --git a/core/res/res/anim/ic_signal_wifi_transient_animation_4.xml b/core/res/res/anim/ic_signal_wifi_transient_animation_4.xml new file mode 100644 index 000000000000..2c921b1cec25 --- /dev/null +++ b/core/res/res/anim/ic_signal_wifi_transient_animation_4.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> + <objectAnimator android:propertyName="scaleY" android:duration="0" + android:startOffset="500" android:valueFrom="1" android:valueTo="0" + android:valueType="floatType"/> +</set>
\ No newline at end of file diff --git a/core/res/res/anim/ic_signal_wifi_transient_animation_5.xml b/core/res/res/anim/ic_signal_wifi_transient_animation_5.xml new file mode 100644 index 000000000000..51aedc098785 --- /dev/null +++ b/core/res/res/anim/ic_signal_wifi_transient_animation_5.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> + <objectAnimator android:propertyName="scaleX" android:duration="0" + android:startOffset="500" android:valueFrom="0" android:valueTo="1" + android:valueType="floatType"/> +</set>
\ No newline at end of file diff --git a/core/res/res/anim/ic_signal_wifi_transient_animation_6.xml b/core/res/res/anim/ic_signal_wifi_transient_animation_6.xml new file mode 100644 index 000000000000..d1da131d4d1e --- /dev/null +++ b/core/res/res/anim/ic_signal_wifi_transient_animation_6.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> + <objectAnimator android:propertyName="scaleY" android:duration="0" + android:startOffset="667" android:valueFrom="1" android:valueTo="0" + android:valueType="floatType"/> +</set>
\ No newline at end of file diff --git a/core/res/res/anim/ic_signal_wifi_transient_animation_7.xml b/core/res/res/anim/ic_signal_wifi_transient_animation_7.xml new file mode 100644 index 000000000000..0b6eb428f0ff --- /dev/null +++ b/core/res/res/anim/ic_signal_wifi_transient_animation_7.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> + <objectAnimator android:propertyName="scaleX" android:duration="0" + android:startOffset="667" android:valueFrom="0" android:valueTo="1" + android:valueType="floatType"/> +</set>
\ No newline at end of file diff --git a/core/res/res/anim/ic_signal_wifi_transient_animation_8.xml b/core/res/res/anim/ic_signal_wifi_transient_animation_8.xml new file mode 100644 index 000000000000..3469d43d4e67 --- /dev/null +++ b/core/res/res/anim/ic_signal_wifi_transient_animation_8.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> + <objectAnimator android:propertyName="translateX" android:duration="833" + android:startOffset="0" android:valueFrom="0" android:valueTo="1" + android:valueType="floatType"/> +</set>
\ No newline at end of file diff --git a/core/res/res/drawable/ic_bluetooth_transient_animation.xml b/core/res/res/drawable/ic_bluetooth_transient_animation.xml new file mode 100644 index 000000000000..61b0f1ad39b3 --- /dev/null +++ b/core/res/res/drawable/ic_bluetooth_transient_animation.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@drawable/ic_bluetooth_transient_animation_drawable"> + <target android:name="_R_G_L_0_G_D_1_P_0" + android:animation="@anim/ic_bluetooth_transient_animation_0"/> + <target android:name="_R_G_L_0_G_D_2_P_0" + android:animation="@anim/ic_bluetooth_transient_animation_1"/> + <target android:name="time_group" android:animation="@anim/ic_bluetooth_transient_animation_2"/> +</animated-vector> diff --git a/core/res/res/drawable/ic_bluetooth_transient_animation_drawable.xml b/core/res/res/drawable/ic_bluetooth_transient_animation_drawable.xml new file mode 100644 index 000000000000..a520feabedf1 --- /dev/null +++ b/core/res/res/drawable/ic_bluetooth_transient_animation_drawable.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" + android:width="24dp" android:viewportHeight="24" + android:viewportWidth="24"> + <group android:name="_R_G"> + <group android:name="_R_G_L_0_G_N_1_T_0" android:translateX="12" + android:translateY="12"> + <group android:name="_R_G_L_0_G" android:translateX="-12" + android:translateY="-12"> + <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M17.71 7.71 C17.71,7.71 12,2 12,2 C12,2 11,2 11,2 C11,2 11,9.59 11,9.59 C11,9.59 6.41,5 6.41,5 C6.41,5 5,6.41 5,6.41 C5,6.41 10.59,12 10.59,12 C10.59,12 5,17.59 5,17.59 C5,17.59 6.41,19 6.41,19 C6.41,19 11,14.41 11,14.41 C11,14.41 11,22 11,22 C11,22 12,22 12,22 C12,22 17.71,16.29 17.71,16.29 C17.71,16.29 13.41,12 13.41,12 C13.41,12 17.71,7.71 17.71,7.71c M13 5.83 C13,5.83 14.88,7.71 14.88,7.71 C14.88,7.71 13,9.59 13,9.59 C13,9.59 13,5.83 13,5.83c M14.88 16.29 C14.88,16.29 13,18.17 13,18.17 C13,18.17 13,14.41 13,14.41 C13,14.41 14.88,16.29 14.88,16.29c "/> + <path android:name="_R_G_L_0_G_D_1_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M5 10.5 C5.83,10.5 6.5,11.17 6.5,12 C6.5,12.83 5.83,13.5 5,13.5 C4.17,13.5 3.5,12.83 3.5,12 C3.5,11.17 4.17,10.5 5,10.5c "/> + <path android:name="_R_G_L_0_G_D_2_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M19 10.5 C19.83,10.5 20.5,11.17 20.5,12 C20.5,12.83 19.83,13.5 19,13.5 C18.17,13.5 17.5,12.83 17.5,12 C17.5,11.17 18.17,10.5 19,10.5c "/> + </group> + </group> + </group> + <group android:name="time_group"/> +</vector>
\ No newline at end of file diff --git a/core/res/res/drawable/ic_hotspot_transient_animation.xml b/core/res/res/drawable/ic_hotspot_transient_animation.xml new file mode 100644 index 000000000000..efbdfdbf6a75 --- /dev/null +++ b/core/res/res/drawable/ic_hotspot_transient_animation.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@drawable/ic_hotspot_transient_animation_drawable"> + <target android:name="_R_G_L_0_G_D_0_P_0" + android:animation="@anim/ic_hotspot_transient_animation_0"/> + <target android:name="_R_G_L_0_G_D_1_P_0" + android:animation="@anim/ic_hotspot_transient_animation_1"/> + <target android:name="_R_G_L_0_G_D_2_P_0" + android:animation="@anim/ic_hotspot_transient_animation_2"/> + <target android:name="time_group" + android:animation="@anim/ic_hotspot_transient_animation_3"/> +</animated-vector> diff --git a/core/res/res/drawable/ic_hotspot_transient_animation_drawable.xml b/core/res/res/drawable/ic_hotspot_transient_animation_drawable.xml new file mode 100644 index 000000000000..d81abbb8a0b8 --- /dev/null +++ b/core/res/res/drawable/ic_hotspot_transient_animation_drawable.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportHeight="24" + android:viewportWidth="24"> + <group android:name="_R_G"> + <group android:name="_R_G_L_0_G_N_1_T_0" android:translateX="12" + android:translateY="12"> + <group android:name="_R_G_L_0_G" android:translateX="-10" + android:translateY="-9"> + <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M10 8 C8.9,8 8,8.9 8,10 C8,10.55 8.23,11.05 8.59,11.41 C8.95,11.77 9.45,12 10,12 C10.55,12 11.05,11.77 11.41,11.41 C11.77,11.05 12,10.55 12,10 C12,8.9 11.1,8 10,8c "/> + <path android:name="_R_G_L_0_G_D_1_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M10 4 C6.69,4 4,6.69 4,10 C4,11.66 4.68,13.15 5.76,14.24 C5.76,14.24 7.18,12.82 7.18,12.82 C6.45,12.1 6,11.11 6,10 C6,7.79 7.79,6 10,6 C12.21,6 14,7.79 14,10 C14,11.11 13.55,12.1 12.82,12.82 C12.82,12.82 14.24,14.24 14.24,14.24 C15.32,13.15 16,11.66 16,10 C16,6.69 13.31,4 10,4c "/> + <path android:name="_R_G_L_0_G_D_2_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M10 0 C4.48,0 0,4.48 0,10 C0,12.76 1.12,15.26 2.93,17.07 C2.93,17.07 4.34,15.66 4.34,15.66 C2.9,14.21 2,12.21 2,10 C2,5.58 5.58,2 10,2 C14.42,2 18,5.58 18,10 C18,12.21 17.1,14.2 15.65,15.65 C15.65,15.65 17.06,17.06 17.06,17.06 C18.88,15.26 20,12.76 20,10 C20,4.48 15.52,0 10,0c "/> + </group> + </group> + </group> + <group android:name="time_group"/> +</vector> diff --git a/core/res/res/drawable/ic_signal_wifi_transient_animation.xml b/core/res/res/drawable/ic_signal_wifi_transient_animation.xml new file mode 100644 index 000000000000..0219ae52396f --- /dev/null +++ b/core/res/res/drawable/ic_signal_wifi_transient_animation.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@drawable/ic_signal_wifi_transient_animation_drawable"> + <target android:name="_R_G_L_4_G_N_1_T_0" + android:animation="@anim/ic_signal_wifi_transient_animation_0" /> + <target android:name="_R_G_L_3_G_N_1_T_0" + android:animation="@anim/ic_signal_wifi_transient_animation_1" /> + <target android:name="_R_G_L_3_G_N_1_T_0" + android:animation="@anim/ic_signal_wifi_transient_animation_2" /> + <target android:name="_R_G_L_2_G_N_1_T_0" + android:animation="@anim/ic_signal_wifi_transient_animation_3" /> + <target android:name="_R_G_L_2_G_N_1_T_0" + android:animation="@anim/ic_signal_wifi_transient_animation_4" /> + <target android:name="_R_G_L_1_G_N_1_T_0" + android:animation="@anim/ic_signal_wifi_transient_animation_5" /> + <target android:name="_R_G_L_1_G_N_1_T_0" + android:animation="@anim/ic_signal_wifi_transient_animation_6" /> + <target android:name="_R_G_L_0_G_N_1_T_0" + android:animation="@anim/ic_signal_wifi_transient_animation_7" /> + <target android:name="time_group" + android:animation="@anim/ic_signal_wifi_transient_animation_8" /> +</animated-vector> diff --git a/core/res/res/drawable/ic_signal_wifi_transient_animation_drawable.xml b/core/res/res/drawable/ic_signal_wifi_transient_animation_drawable.xml new file mode 100644 index 000000000000..9cd4925d1f97 --- /dev/null +++ b/core/res/res/drawable/ic_signal_wifi_transient_animation_drawable.xml @@ -0,0 +1,82 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" android:width="24dp" android:viewportHeight="24" android:viewportWidth="24"> + <group android:name="_R_G"> + <group android:name="_R_G_L_4_G_N_1_T_0" android:translateX="12" + android:translateY="12"> + <group android:name="_R_G_L_4_G" android:translateX="-12.25" + android:translateY="-10.25"> + <path android:name="_R_G_L_4_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M12.25 2.25 C15.67,2.25 18.98,3.52 21.55,5.78 C21.55,5.78 12.25,17.1 12.25,17.1 C12.25,17.1 2.95,5.78 2.95,5.78 C5.52,3.52 8.83,2.25 12.25,2.25c M12.25 0.25 C7.5,0.25 3.22,2.33 0.25,5.64 C0.25,5.64 12.25,20.25 12.25,20.25 C12.25,20.25 24.25,5.64 24.25,5.64 C21.28,2.33 17,0.25 12.25,0.25c "/> + </group> + </group> + <group android:name="_R_G_L_3_G_N_1_T_0" android:translateX="12" + android:translateY="12"> + <group android:name="_R_G_L_3_G" android:translateX="-12.25" + android:translateY="-10.25"> + <path android:name="_R_G_L_3_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M12.25 2.25 C15.65,2.25 18.95,3.55 21.55,5.75 C21.55,5.75 12.25,17.05 12.25,17.05 C12.25,17.05 2.95,5.75 2.95,5.75 C5.55,3.55 8.85,2.25 12.25,2.25c M12.25 0.25 C7.45,0.25 3.25,2.35 0.25,5.65 C0.25,5.65 12.25,20.25 12.25,20.25 C12.25,20.25 24.25,5.65 24.25,5.65 C21.25,2.35 17.05,0.25 12.25,0.25c "/> + <path android:name="_R_G_L_3_G_D_1_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M12.25 11.25 C10.15,11.25 8.15,12.15 6.85,13.65 C6.85,13.65 12.25,20.25 12.25,20.25 C12.25,20.25 17.65,13.65 17.65,13.65 C16.35,12.15 14.35,11.25 12.25,11.25c "/> + </group> + </group> + <group android:name="_R_G_L_2_G_N_1_T_0" android:translateX="12" + android:translateY="12"> + <group android:name="_R_G_L_2_G" android:translateX="-12.25" + android:translateY="-10.25"> + <path android:name="_R_G_L_2_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M12.25 2.25 C15.65,2.25 18.95,3.55 21.55,5.75 C21.55,5.75 12.25,17.05 12.25,17.05 C12.25,17.05 2.95,5.75 2.95,5.75 C5.55,3.55 8.85,2.25 12.25,2.25c M12.25 0.25 C7.45,0.25 3.25,2.35 0.25,5.65 C0.25,5.65 12.25,20.25 12.25,20.25 C12.25,20.25 24.25,5.65 24.25,5.65 C21.25,2.35 17.05,0.25 12.25,0.25c "/> + <path android:name="_R_G_L_2_G_D_1_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M12.25 8.25 C9.45,8.25 6.75,9.45 5.05,11.45 C5.05,11.45 12.25,20.25 12.25,20.25 C12.25,20.25 19.45,11.45 19.45,11.45 C17.75,9.45 15.05,8.25 12.25,8.25c "/> + </group> + </group> + <group android:name="_R_G_L_1_G_N_1_T_0" android:translateX="12" + android:translateY="12"> + <group android:name="_R_G_L_1_G" android:translateX="-12.25" + android:translateY="-10.25"> + <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M12.25 2.25 C15.65,2.25 18.95,3.55 21.55,5.75 C21.55,5.75 12.25,17.05 12.25,17.05 C12.25,17.05 2.95,5.75 2.95,5.75 C5.55,3.55 8.85,2.25 12.25,2.25c M12.25 0.25 C7.45,0.25 3.25,2.35 0.25,5.65 C0.25,5.65 12.25,20.25 12.25,20.25 C12.25,20.25 24.25,5.65 24.25,5.65 C21.25,2.35 17.05,0.25 12.25,0.25c "/> + <path android:name="_R_G_L_1_G_D_1_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M12.25 5.25 C8.75,5.25 5.45,6.75 3.25,9.25 C3.25,9.25 12.25,20.25 12.25,20.25 C12.25,20.25 21.25,9.25 21.25,9.25 C19.05,6.75 15.75,5.25 12.25,5.25c "/> + </group> + </group> + <group android:name="_R_G_L_0_G_N_1_T_0" android:translateX="12" + android:translateY="12"> + <group android:name="_R_G_L_0_G" android:translateX="-12.25" + android:translateY="-10.25"> + <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M12.25 2.25 C15.65,2.25 18.95,3.55 21.55,5.75 C21.55,5.75 12.25,17.05 12.25,17.05 C12.25,17.05 2.95,5.75 2.95,5.75 C5.55,3.55 8.85,2.25 12.25,2.25c M12.25 0.25 C7.45,0.25 3.25,2.35 0.25,5.65 C0.25,5.65 12.25,20.25 12.25,20.25 C12.25,20.25 24.25,5.65 24.25,5.65 C21.25,2.35 17.05,0.25 12.25,0.25c "/> + <path android:name="_R_G_L_0_G_D_1_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M12.25 5.25 C8.75,5.25 5.45,6.75 3.25,9.25 C3.25,9.25 12.25,20.25 12.25,20.25 C12.25,20.25 21.25,9.25 21.25,9.25 C19.05,6.75 15.75,5.25 12.25,5.25c "/> + <path android:name="_R_G_L_0_G_D_2_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M12.25 0.25 C7.45,0.25 3.25,2.35 0.25,5.65 C0.25,5.65 12.25,20.25 12.25,20.25 C12.25,20.25 24.25,5.65 24.25,5.65 C21.25,2.35 17.05,0.25 12.25,0.25c "/> + </group> + </group> + </group> + <group android:name="time_group"/> +</vector>
\ No newline at end of file diff --git a/core/res/res/interpolator/transient_interpolator.xml b/core/res/res/interpolator/transient_interpolator.xml new file mode 100644 index 000000000000..653a8cfdcdc0 --- /dev/null +++ b/core/res/res/interpolator/transient_interpolator.xml @@ -0,0 +1,17 @@ +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android" + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/>
\ No newline at end of file diff --git a/core/res/res/layout/chooser_az_label_row.xml b/core/res/res/layout/chooser_az_label_row.xml new file mode 100644 index 000000000000..1b733fc907cc --- /dev/null +++ b/core/res/res/layout/chooser_az_label_row.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> + +<!-- Separator applied as background --> +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + android:text="@string/chooser_all_apps_button_label" + android:contentDescription="@string/chooser_all_apps_button_label" + android:background="@drawable/chooser_row_layer_list" + android:textAppearance="?attr/textAppearanceSmall" + android:textColor="?attr/textColorSecondary" + android:textSize="14sp" + android:singleLine="true" + android:paddingTop="16dp" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="center"/> + diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml index 8727f4c9e0ef..17b5f50669a2 100644 --- a/core/res/res/layout/chooser_grid.xml +++ b/core/res/res/layout/chooser_grid.xml @@ -37,8 +37,8 @@ android:layout_width="24dp" android:layout_height="4dp" android:src="@drawable/ic_drag_handle" - android:clickable="true" android:layout_marginTop="@dimen/chooser_edge_margin_thin" + android:layout_marginBottom="@dimen/chooser_edge_margin_thin" android:tint="@color/lighter_gray" android:layout_centerHorizontal="true" android:layout_alignParentTop="true" /> @@ -46,11 +46,8 @@ <TextView android:id="@+id/title" android:layout_height="wrap_content" android:layout_width="wrap_content" - android:textAppearance="?attr/textAppearanceMedium" - android:textSize="20sp" - android:textColor="?attr/textColorPrimary" + android:textAppearance="@style/TextAppearance.DeviceDefault.WindowTitle" android:gravity="center" - android:paddingTop="@dimen/chooser_edge_margin_thin" android:paddingBottom="@dimen/chooser_view_spacing" android:paddingLeft="24dp" android:paddingRight="24dp" diff --git a/core/res/res/layout/chooser_grid_preview_text.xml b/core/res/res/layout/chooser_grid_preview_text.xml index f3ca0af767b1..96a642c6d446 100644 --- a/core/res/res/layout/chooser_grid_preview_text.xml +++ b/core/res/res/layout/chooser_grid_preview_text.xml @@ -44,6 +44,7 @@ android:layout_toStartOf="@id/copy_button" android:layout_centerVertical="true" android:ellipsize="end" + android:fontFamily="@android:string/config_headlineFontFamily" android:textColor="?android:attr/textColorPrimary" android:maxLines="2"/> @@ -59,7 +60,7 @@ android:minWidth="48dp" android:minHeight="48dp" android:clickable="true" - android:background="?android:attr/selectableItemBackgroundBorderless"> + style="?attr/borderlessButtonStyle"> <ImageView android:layout_width="24dp" @@ -112,8 +113,8 @@ android:layout_gravity="center_vertical" android:ellipsize="end" android:maxLines="2" - android:textSize="20sp" - android:textColor="?android:attr/textColorPrimary"/> + android:textAppearance="@style/TextAppearance.DeviceDefault.WindowTitle" + android:fontFamily="@android:string/config_headlineFontFamily"/> </LinearLayout> </LinearLayout> diff --git a/core/res/res/layout/chooser_profile_row.xml b/core/res/res/layout/chooser_profile_row.xml index 1a24a073a122..fe4f9496be30 100644 --- a/core/res/res/layout/chooser_profile_row.xml +++ b/core/res/res/layout/chooser_profile_row.xml @@ -20,7 +20,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center"> - <TextView + <Button android:id="@+id/profile_button" android:layout_width="wrap_content" android:layout_height="48dp" diff --git a/core/res/res/layout/resolve_grid_item.xml b/core/res/res/layout/resolve_grid_item.xml index 256d94e5c330..7098c958ce36 100644 --- a/core/res/res/layout/resolve_grid_item.xml +++ b/core/res/res/layout/resolve_grid_item.xml @@ -23,7 +23,7 @@ android:minHeight="100dp" android:gravity="center" android:paddingTop="24dp" - android:paddingBottom="8dp" + android:paddingBottom="12dp" android:paddingLeft="12dp" android:paddingRight="12dp" android:focusable="true" diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index ad3203ed96eb..f576feae0663 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1257,6 +1257,8 @@ <bool name="config_use_strict_phone_number_comparation">false</bool> + <bool name="config_use_strict_phone_number_comparation_for_russian">true</bool> + <!-- Display low battery warning when battery level dips to this value. Also, the battery stats are flushed to disk when we hit this level. --> <integer name="config_criticalBatteryWarningLevel">5</integer> @@ -3863,10 +3865,6 @@ {@see android.view.Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS} --> <string name="config_secondaryHomeComponent" translatable="false">com.android.launcher3/com.android.launcher3.SecondaryDisplayLauncher</string> - <!-- Force secondary home launcher specified in config_secondaryHomeComponent always. If this is - not set, secondary home launcher can be replaced by user. --> - <bool name ="config_useSystemProvidedLauncherForSecondary">false</bool> - <!-- If device supports corner radius on windows. This should be turned off on low-end devices to improve animation performance. --> <bool name="config_supportsRoundedCornersOnWindows">true</bool> @@ -4010,7 +4008,7 @@ <string name="config_batterymeterPerimeterPath" translatable="false"> M3.5,2 v0 H1.33 C0.6,2 0,2.6 0,3.33 V13v5.67 C0,19.4 0.6,20 1.33,20 h9.33 C11.4,20 12,19.4 12,18.67 V13V3.33 C12,2.6 11.4,2 10.67,2 H8.5 V0 H3.5 z M2,18v-7V4h8v9v5H2L2,18z </string> - + <string name="config_batterymeterErrorPerimeterPath" translatable="false">@string/config_batterymeterPerimeterPath</string> <string name="config_batterymeterFillMask" translatable="false"> M2,18 v-14 h8 v14 z </string> @@ -4061,4 +4059,38 @@ <!-- Which binder services to include in incident reports containing restricted images. --> <string-array name="config_restrictedImagesServices" translatable="false"/> + + <!-- Messages that should not be shown to the user during face auth enrollment. This should be + used to hide messages that may be too chatty or messages that the user can't do much about. + Entries are defined in android.hardware.biometrics.face@1.0 types.hal --> + <integer-array name="config_face_acquire_enroll_ignorelist" translatable="false" > + </integer-array> + <!-- Same as the above, but are defined by vendorCodes --> + <integer-array name="config_face_acquire_vendor_enroll_ignorelist" translatable="false" > + </integer-array> + + <!-- Messages that should not be shown to the user during face authentication, on keyguard. + This includes both lockscreen and bouncer. This should be used to hide messages that may be + too chatty or messages that the user can't do much about. Entries are defined in + android.hardware.biometrics.face@1.0 types.hal --> + <integer-array name="config_face_acquire_keyguard_ignorelist" translatable="false" > + </integer-array> + <!-- Same as the above, but are defined by vendorCodes --> + <integer-array name="config_face_acquire_vendor_keyguard_ignorelist" translatable="false" > + </integer-array> + + <!-- Messages that should not be shown to the user during face authentication, on + BiometricPrompt. This should be used to hide messages that may be too chatty or messages that + the user can't do much about. Entries are defined in + android.hardware.biometrics.face@1.0 types.hal --> + <integer-array name="config_face_acquire_biometricprompt_ignorelist" translatable="false" > + </integer-array> + <!-- Same as the above, but are defined by vendorCodes --> + <integer-array name="config_face_acquire_vendor_biometricprompt_ignorelist" translatable="false" > + </integer-array> + + <!-- The component name for the default profile supervisor, which can be set as a profile owner + even after user setup is complete. The defined component should be used for supervision purposes + only. The component must be part of a system app. --> + <string name="config_defaultSupervisionProfileOwnerComponent" translatable="false"></string> </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index ef834aa01a36..dd0a6e605f60 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1545,9 +1545,9 @@ <!-- Message shown during face enrollment when the face is too similar to a previous acquisition [CHAR LIMIT=50] --> <string name="face_acquired_too_similar">Too similar, please change your pose.</string> <!-- Message shown during acqusition when the user's face is turned too far left or right [CHAR LIMIT=50] --> - <string name="face_acquired_pan_too_extreme">Please look more directly at the screen.</string> + <string name="face_acquired_pan_too_extreme">Turn your head a little less.</string> <!-- Message shown during acqusition when the user's face is tilted too high or too low [CHAR LIMIT=50] --> - <string name="face_acquired_tilt_too_extreme">Please look more directly at the screen.</string> + <string name="face_acquired_tilt_too_extreme">Turn your head a little less.</string> <!-- Message shown during acquisiton when the user's face is tilted too far left or right [CHAR LIMIT=50] --> <string name="face_acquired_roll_too_extreme">Please straighten your head vertically.</string> <!-- Message shown during acquisition when the user's face is obscured [CHAR LIMIT=50] --> @@ -5373,4 +5373,7 @@ <!-- ChooserActivity - No direct share targets are available. [CHAR LIMIT=NONE] --> <string name="chooser_no_direct_share_targets">Direct share not available</string> + <!-- ChooserActivity - Alphabetically sorted apps list label. [CHAR LIMIT=NONE] --> + <string name="chooser_all_apps_button_label">Apps list</string> + </resources> diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml index b716ab985175..b7c86d266070 100644 --- a/core/res/res/values/styles_device_defaults.xml +++ b/core/res/res/values/styles_device_defaults.xml @@ -68,18 +68,27 @@ easier. <style name="Widget.DeviceDefault.HorizontalScrollView" parent="Widget.Material.HorizontalScrollView"/> <style name="Widget.DeviceDefault.Spinner" parent="Widget.Material.Spinner"/> <style name="Widget.DeviceDefault.CompoundButton.Star" parent="Widget.Material.CompoundButton.Star"/> - <style name="Widget.DeviceDefault.TabWidget" parent="Widget.Material.TabWidget"/> + <style name="Widget.DeviceDefault.TabWidget" parent="Widget.Material.TabWidget"> + <item name="textAppearance">@style/TextAppearance.DeviceDefault.Widget.TabWidget</item> + </style> <style name="Widget.DeviceDefault.WebTextView" parent="Widget.Material.WebTextView"/> <style name="Widget.DeviceDefault.WebView" parent="Widget.Material.WebView"/> - <style name="Widget.DeviceDefault.DropDownItem" parent="Widget.Material.DropDownItem"/> - <style name="Widget.DeviceDefault.DropDownItem.Spinner" parent="Widget.Material.DropDownItem.Spinner"/> - <style name="Widget.DeviceDefault.TextView.SpinnerItem" parent="Widget.Material.TextView.SpinnerItem"/> + <style name="Widget.DeviceDefault.DropDownItem" parent="Widget.Material.DropDownItem"> + <item name="textAppearance">@style/TextAppearance.DeviceDefault.Widget.DropDownItem</item> + </style> + <style name="Widget.DeviceDefault.DropDownItem.Spinner" parent="Widget.DeviceDefault.DropDownItem"/> + <style name="Widget.DeviceDefault.TextView.SpinnerItem" parent="Widget.Material.TextView.SpinnerItem"> + <item name="textAppearance">@style/TextAppearance.DeviceDefault.Widget.TextView.SpinnerItem</item> + </style> <style name="Widget.DeviceDefault.ListPopupWindow" parent="Widget.Material.ListPopupWindow"/> <style name="Widget.DeviceDefault.PopupMenu" parent="Widget.Material.PopupMenu"/> <style name="Widget.DeviceDefault.ActionButton" parent="Widget.Material.ActionButton"/> <style name="Widget.DeviceDefault.ActionButton.Overflow" parent="Widget.Material.ActionButton.Overflow"/> <style name="Widget.DeviceDefault.ActionButton.TextButton" parent="Widget.Material.ActionButton"/> - <style name="Widget.DeviceDefault.ActionMode" parent="Widget.Material.ActionMode"/> + <style name="Widget.DeviceDefault.ActionMode" parent="Widget.Material.ActionMode"> + <item name="titleTextStyle">@style/TextAppearance.DeviceDefault.Widget.ActionMode.Title</item> + <item name="subtitleTextStyle">@style/TextAppearance.DeviceDefault.Widget.ActionMode.Subtitle</item> + </style> <style name="Widget.DeviceDefault.ActionButton.CloseMode" parent="Widget.Material.ActionButton.CloseMode"/> <style name="Widget.DeviceDefault.ActionBar" parent="Widget.Material.ActionBar"/> <style name="Widget.DeviceDefault.Button.Borderless" parent="Widget.Material.Button.Borderless"/> @@ -108,7 +117,9 @@ easier. <style name="Widget.DeviceDefault.AbsListView" parent="Widget.Material.AbsListView"/> <style name="Widget.DeviceDefault.Spinner.DropDown.ActionBar" parent="Widget.Material.Spinner.DropDown.ActionBar"/> <style name="Widget.DeviceDefault.PopupWindow.ActionMode" parent="Widget.Material.PopupWindow.ActionMode"/> - <style name="Widget.DeviceDefault.CompoundButton.Switch" parent="Widget.Material.CompoundButton.Switch"/> + <style name="Widget.DeviceDefault.CompoundButton.Switch" parent="Widget.Material.CompoundButton.Switch"> + <item name="switchTextAppearance">@style/TextAppearance.DeviceDefault.Widget.Switch</item> + </style> <style name="Widget.DeviceDefault.ExpandableListView.White" parent="Widget.Material.ExpandableListView.White"/> <style name="Widget.DeviceDefault.FastScroll" parent="Widget.Material.FastScroll"/> <!-- The FragmentBreadCrumbs widget is deprecated starting in API level 21 ({@link android.os.Build.VERSION_CODES#.L}). --> @@ -146,6 +157,7 @@ easier. <style name="Widget.DeviceDefault.TimePicker" parent="Widget.Material.TimePicker"/> <style name="Widget.DeviceDefault.Toolbar" parent="Widget.Material.Toolbar"> <item name="titleTextAppearance">@style/TextAppearance.DeviceDefault.Widget.Toolbar.Title</item> + <item name="subtitleTextAppearance">@style/TextAppearance.DeviceDefault.Widget.Toolbar.Subtitle</item> </style> <style name="Widget.DeviceDefault.Light" parent="Widget.Material.Light"/> @@ -186,12 +198,12 @@ easier. <style name="Widget.DeviceDefault.Light.HorizontalScrollView" parent="Widget.Material.Light.HorizontalScrollView"/> <style name="Widget.DeviceDefault.Light.Spinner" parent="Widget.Material.Light.Spinner"/> <style name="Widget.DeviceDefault.Light.CompoundButton.Star" parent="Widget.Material.Light.CompoundButton.Star"/> - <style name="Widget.DeviceDefault.Light.TabWidget" parent="Widget.Material.Light.TabWidget"/> + <style name="Widget.DeviceDefault.Light.TabWidget" parent="Widget.DeviceDefault.TabWidget"/> <style name="Widget.DeviceDefault.Light.WebTextView" parent="Widget.Material.Light.WebTextView"/> <style name="Widget.DeviceDefault.Light.WebView" parent="Widget.Material.Light.WebView"/> - <style name="Widget.DeviceDefault.Light.DropDownItem" parent="Widget.Material.Light.DropDownItem"/> - <style name="Widget.DeviceDefault.Light.DropDownItem.Spinner" parent="Widget.Material.Light.DropDownItem.Spinner"/> - <style name="Widget.DeviceDefault.Light.TextView.SpinnerItem" parent="Widget.Material.Light.TextView.SpinnerItem"/> + <style name="Widget.DeviceDefault.Light.DropDownItem" parent="Widget.DeviceDefault.DropDownItem"/> + <style name="Widget.DeviceDefault.Light.DropDownItem.Spinner" parent="Widget.DeviceDefault.DropDownItem.Spinner"/> + <style name="Widget.DeviceDefault.Light.TextView.SpinnerItem" parent="Widget.DeviceDefault.TextView.SpinnerItem"/> <style name="Widget.DeviceDefault.Light.ListPopupWindow" parent="Widget.Material.Light.ListPopupWindow"/> <style name="Widget.DeviceDefault.Light.PopupMenu" parent="Widget.Material.Light.PopupMenu"/> <style name="Widget.DeviceDefault.Light.Tab" parent="Widget.Material.Light.Tab"/> @@ -202,12 +214,10 @@ easier. <style name="Widget.DeviceDefault.Light.ActionMode" parent="Widget.Material.Light.ActionMode"/> <style name="Widget.DeviceDefault.Light.ActionButton.CloseMode" parent="Widget.Material.Light.ActionButton.CloseMode"/> <style name="Widget.DeviceDefault.Light.ActionBar" parent="Widget.Material.Light.ActionBar"/> - <style name="Widget.DeviceDefault.Light.ActionBar.TabView" parent="Widget.Material.Light.ActionBar.TabView"/> - <style name="Widget.DeviceDefault.Light.ActionBar.TabText" parent="Widget.Material.Light.ActionBar.TabText"/> - <style name="Widget.DeviceDefault.Light.ActionBar.TabBar" parent="Widget.Material.Light.ActionBar.TabBar"/> - <style name="Widget.DeviceDefault.Light.ActionBar.Solid" parent="Widget.Material.Light.ActionBar.Solid"> - <item name="titleTextStyle">@style/TextAppearance.DeviceDefault.Widget.ActionBar.Title</item> - </style> + <style name="Widget.DeviceDefault.Light.ActionBar.TabView" parent="Widget.DeviceDefault.ActionBar.TabView" /> + <style name="Widget.DeviceDefault.Light.ActionBar.TabText" parent="Widget.DeviceDefault.ActionBar.TabText" /> + <style name="Widget.DeviceDefault.Light.ActionBar.TabBar" parent="Widget.DeviceDefault.ActionBar.TabBar" /> + <style name="Widget.DeviceDefault.Light.ActionBar.Solid" parent="Widget.DeviceDefault.ActionBar.Solid" /> <!-- @deprecated Action bars are now themed using the inheritable android:theme attribute. --> <style name="Widget.DeviceDefault.Light.ActionBar.Solid.Inverse" parent="Widget.Holo.Light.ActionBar.Solid.Inverse"/> <!-- @deprecated Action bars are now themed using the inheritable android:theme attribute. --> @@ -307,6 +317,9 @@ easier. <style name="TextAppearance.DeviceDefault.Widget.EditText" parent="TextAppearance.Material.Widget.EditText"> <item name="fontFamily">@string/config_bodyFontFamily</item> </style> + <style name="TextAppearance.DeviceDefault.Widget.Switch" parent="TextAppearance.Material.Widget.Switch"> + <item name="fontFamily">@string/config_bodyFontFamilyMedium</item> + </style> <style name="TextAppearance.DeviceDefault.Widget.Button.Borderless.Colored" parent="TextAppearance.DeviceDefault.Widget.Button"> <item name="textColor">@color/btn_colored_borderless_text_material</item> </style> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 085ce56292c6..d178383973fd 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -313,6 +313,7 @@ <java-symbol type="bool" name="config_ui_enableFadingMarquee" /> <java-symbol type="bool" name="config_enableHapticTextHandle" /> <java-symbol type="bool" name="config_use_strict_phone_number_comparation" /> + <java-symbol type="bool" name="config_use_strict_phone_number_comparation_for_russian" /> <java-symbol type="bool" name="config_single_volume" /> <java-symbol type="bool" name="config_voice_capable" /> <java-symbol type="bool" name="config_requireCallCapableAccountForHandle" /> @@ -1406,6 +1407,9 @@ <java-symbol type="drawable" name="ic_wifi_signal_2" /> <java-symbol type="drawable" name="ic_wifi_signal_3" /> <java-symbol type="drawable" name="ic_wifi_signal_4" /> + <java-symbol type="drawable" name="ic_signal_wifi_transient_animation" /> + <java-symbol type="drawable" name="ic_hotspot_transient_animation" /> + <java-symbol type="drawable" name="ic_bluetooth_transient_animation" /> <java-symbol type="drawable" name="stat_notify_rssi_in_range" /> <java-symbol type="drawable" name="stat_sys_gps_on" /> <java-symbol type="drawable" name="stat_sys_tether_wifi" /> @@ -2570,6 +2574,13 @@ <java-symbol type="string" name="face_authenticated_no_confirmation_required" /> <java-symbol type="string" name="face_authenticated_confirmation_required" /> + <java-symbol type="array" name="config_face_acquire_enroll_ignorelist" /> + <java-symbol type="array" name="config_face_acquire_vendor_enroll_ignorelist" /> + <java-symbol type="array" name="config_face_acquire_keyguard_ignorelist" /> + <java-symbol type="array" name="config_face_acquire_vendor_keyguard_ignorelist" /> + <java-symbol type="array" name="config_face_acquire_biometricprompt_ignorelist" /> + <java-symbol type="array" name="config_face_acquire_vendor_biometricprompt_ignorelist" /> + <!-- Face config --> <java-symbol type="integer" name="config_faceMaxTemplatesPerUser" /> @@ -3240,6 +3251,7 @@ <java-symbol type="string" name="config_icon_mask" /> <java-symbol type="string" name="config_batterymeterPerimeterPath" /> + <java-symbol type="string" name="config_batterymeterErrorPerimeterPath" /> <java-symbol type="string" name="config_batterymeterFillMask" /> <java-symbol type="string" name="config_batterymeterBoltPath" /> <java-symbol type="string" name="config_batterymeterPowersavePath" /> @@ -3678,7 +3690,6 @@ <!-- For Secondary Launcher --> <java-symbol type="string" name="config_secondaryHomeComponent" /> - <java-symbol type="bool" name="config_useSystemProvidedLauncherForSecondary" /> <java-symbol type="string" name="battery_saver_notification_channel_name" /> <java-symbol type="string" name="battery_saver_sticky_disabled_notification_title" /> @@ -3772,4 +3783,8 @@ <java-symbol type="color" name="chooser_gradient_highlight" /> <java-symbol type="drawable" name="chooser_direct_share_label_placeholder" /> <java-symbol type="dimen" name="chooser_direct_share_label_placeholder_max_width" /> + <java-symbol type="layout" name="chooser_az_label_row" /> + <java-symbol type="string" name="chooser_all_apps_button_label" /> + + <java-symbol type="string" name="config_defaultSupervisionProfileOwnerComponent" /> </resources> diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index f4d3c8163e9c..61fb8112dbfc 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -294,6 +294,8 @@ public class SettingsBackupTest { Settings.Global.INTENT_FIREWALL_UPDATE_CONTENT_URL, Settings.Global.INTENT_FIREWALL_UPDATE_METADATA_URL, Settings.Global.JOB_SCHEDULER_CONSTANTS, + Settings.Global.JOB_SCHEDULER_QUOTA_CONTROLLER_CONSTANTS, + Settings.Global.JOB_SCHEDULER_TIME_CONTROLLER_CONSTANTS, Settings.Global.KEEP_PROFILE_IN_BACKGROUND, Settings.Global.KERNEL_CPU_THREAD_READER, Settings.Global.LANG_ID_UPDATE_CONTENT_URL, diff --git a/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java b/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java index 2f17b32370f4..a8ca6f048a11 100644 --- a/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java +++ b/core/tests/coretests/src/android/view/autofill/AutofillIdTest.java @@ -16,7 +16,10 @@ package android.view.autofill; +import static android.view.autofill.AutofillId.NO_SESSION; + import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; import static org.testng.Assert.assertThrows; @@ -33,20 +36,10 @@ public class AutofillIdTest { @Test public void testNonVirtual() { final AutofillId id = new AutofillId(42); - assertThat(id.getViewId()).isEqualTo(42); - assertThat(id.isNonVirtual()).isTrue(); - assertThat(id.isVirtualInt()).isFalse(); - assertThat(id.isVirtualLong()).isFalse(); - assertThat(id.getVirtualChildIntId()).isEqualTo(View.NO_ID); - assertThat(id.getVirtualChildLongId()).isEqualTo(View.NO_ID); + assertNonVirtual(id, 42, NO_SESSION); final AutofillId clone = cloneThroughParcel(id); - assertThat(clone.getViewId()).isEqualTo(42); - assertThat(clone.isNonVirtual()).isTrue(); - assertThat(clone.isVirtualInt()).isFalse(); - assertThat(clone.isVirtualLong()).isFalse(); - assertThat(clone.getVirtualChildIntId()).isEqualTo(View.NO_ID); - assertThat(clone.getVirtualChildLongId()).isEqualTo(View.NO_ID); + assertNonVirtual(clone, 42, NO_SESSION); } @Test @@ -124,49 +117,174 @@ public class AutofillIdTest { } @Test - public void testEqualsHashCode() { - final AutofillId realId = new AutofillId(42); - final AutofillId realIdSame = new AutofillId(42); - assertThat(realId).isEqualTo(realIdSame); - assertThat(realIdSame).isEqualTo(realId); - assertThat(realId.hashCode()).isEqualTo(realIdSame.hashCode()); - - final AutofillId realIdDifferent = new AutofillId(108); - assertThat(realId).isNotEqualTo(realIdDifferent); - assertThat(realIdDifferent).isNotEqualTo(realId); - - final AutofillId virtualId = new AutofillId(42, 1); - final AutofillId virtualIdSame = new AutofillId(42, 1); - assertThat(virtualId).isEqualTo(virtualIdSame); - assertThat(virtualIdSame).isEqualTo(virtualId); - assertThat(virtualId.hashCode()).isEqualTo(virtualIdSame.hashCode()); - assertThat(virtualId).isNotEqualTo(realId); - assertThat(realId).isNotEqualTo(virtualId); - - final AutofillId virtualIdDifferentChild = new AutofillId(42, 2); - assertThat(virtualIdDifferentChild).isNotEqualTo(virtualId); - assertThat(virtualId).isNotEqualTo(virtualIdDifferentChild); - assertThat(virtualIdDifferentChild).isNotEqualTo(realId); - assertThat(realId).isNotEqualTo(virtualIdDifferentChild); - - final AutofillId virtualIdDifferentParent = new AutofillId(108, 1); - assertThat(virtualIdDifferentParent).isNotEqualTo(virtualId); - assertThat(virtualId).isNotEqualTo(virtualIdDifferentParent); - assertThat(virtualIdDifferentParent).isNotEqualTo(virtualIdDifferentChild); - assertThat(virtualIdDifferentChild).isNotEqualTo(virtualIdDifferentParent); - - final AutofillId virtualIdDifferentSession = new AutofillId(new AutofillId(42), 1L, 108); - assertThat(virtualIdDifferentSession).isNotEqualTo(virtualId); - assertThat(virtualId).isNotEqualTo(virtualIdDifferentSession); - assertThat(virtualIdDifferentSession).isNotEqualTo(realId); - assertThat(realId).isNotEqualTo(virtualIdDifferentSession); - - final AutofillId sameVirtualIdDifferentSession = - new AutofillId(new AutofillId(42), 1L, 108); - assertThat(sameVirtualIdDifferentSession).isEqualTo(virtualIdDifferentSession); - assertThat(virtualIdDifferentSession).isEqualTo(sameVirtualIdDifferentSession); - assertThat(sameVirtualIdDifferentSession.hashCode()) - .isEqualTo(virtualIdDifferentSession.hashCode()); + public void testFactoryMethod_withoutSession() { + final AutofillId id = new AutofillId(42); + id.setSessionId(108); + assertNonVirtual(id, 42, 108); + final AutofillId idWithoutSession = AutofillId.withoutSession(id); + assertNonVirtual(idWithoutSession, 42, NO_SESSION); + } + + @Test + public void testSetResetSession() { + final AutofillId id = new AutofillId(42); + assertNonVirtual(id, 42, NO_SESSION); + id.setSessionId(108); + assertNonVirtual(id, 42, 108); + + final AutofillId clone1 = cloneThroughParcel(id); + assertNonVirtual(clone1, 42, 108); + + id.resetSessionId(); + assertThat(id.getSessionId()).isEqualTo(NO_SESSION); + final AutofillId clone2 = cloneThroughParcel(id); + assertNonVirtual(clone2, 42, NO_SESSION); + } + + @Test + public void testEqualsHashCode_nonVirtual_same() { + final AutofillId id = new AutofillId(42); + final AutofillId sameId = new AutofillId(42); + + assertThat(id).isEqualTo(sameId); + assertThat(sameId).isEqualTo(id); + assertEqualsIgnoreSession(id, sameId); + assertEqualsIgnoreSession(sameId, id); + assertThat(id.hashCode()).isEqualTo(sameId.hashCode()); + } + + @Test + public void testEqualsHashCode_nonVirtual_other() { + final AutofillId id = new AutofillId(42); + final AutofillId otherId = new AutofillId(108); + + assertThat(id).isNotEqualTo(otherId); + assertThat(otherId).isNotEqualTo(id); + assertNotEqualsIgnoreSession(id, otherId); + assertNotEqualsIgnoreSession(otherId, id); + assertThat(id.hashCode()).isNotEqualTo(otherId.hashCode()); + } + + @Test + public void testEqualsHashCode_virtual_same() { + final AutofillId id = new AutofillId(42); + final AutofillId virtual = new AutofillId(42, 1); + final AutofillId sameVirtual = new AutofillId(42, 1); + + assertThat(virtual).isEqualTo(sameVirtual); + assertThat(sameVirtual).isEqualTo(virtual); + assertEqualsIgnoreSession(virtual, sameVirtual); + assertEqualsIgnoreSession(sameVirtual, virtual); + assertThat(virtual.hashCode()).isEqualTo(sameVirtual.hashCode()); + assertThat(virtual).isNotEqualTo(id); + assertThat(id).isNotEqualTo(virtual); + assertNotEqualsIgnoreSession(id, virtual); + assertNotEqualsIgnoreSession(virtual, id); + } + + @Test + public void testEqualsHashCode_virtual_otherChild() { + final AutofillId id = new AutofillId(42); + final AutofillId virtual = new AutofillId(42, 1); + final AutofillId virtualOtherChild = new AutofillId(42, 2); + + assertThat(virtualOtherChild).isNotEqualTo(virtual); + assertThat(virtual).isNotEqualTo(virtualOtherChild); + assertNotEqualsIgnoreSession(virtualOtherChild, virtual); + assertNotEqualsIgnoreSession(virtual, virtualOtherChild); + assertThat(virtualOtherChild).isNotEqualTo(id); + assertThat(id).isNotEqualTo(virtualOtherChild); + assertNotEqualsIgnoreSession(virtualOtherChild, id); + assertNotEqualsIgnoreSession(id, virtualOtherChild); + } + + @Test + public void testEqualsHashCode_virtual_otherParent() { + final AutofillId virtual = new AutofillId(42, 1); + final AutofillId virtualOtherParent = new AutofillId(108, 1); + final AutofillId virtualOtherChild = new AutofillId(42, 2); + + assertThat(virtualOtherParent).isNotEqualTo(virtual); + assertThat(virtual).isNotEqualTo(virtualOtherParent); + assertNotEqualsIgnoreSession(virtualOtherParent, virtual); + assertNotEqualsIgnoreSession(virtual, virtualOtherParent); + assertThat(virtualOtherParent).isNotEqualTo(virtualOtherChild); + assertThat(virtualOtherChild).isNotEqualTo(virtualOtherParent); + assertNotEqualsIgnoreSession(virtualOtherParent, virtualOtherChild); + assertNotEqualsIgnoreSession(virtualOtherChild, virtualOtherParent); + } + + @Test + public void testEqualsHashCode_virtual_otherSession() { + final AutofillId virtual = new AutofillId(42, 1); + final AutofillId virtualOtherSession = new AutofillId(42, 1); + virtualOtherSession.setSessionId(666); + + assertThat(virtualOtherSession).isNotEqualTo(virtual); + assertThat(virtual).isNotEqualTo(virtualOtherSession); + assertEqualsIgnoreSession(virtualOtherSession, virtual); + assertEqualsIgnoreSession(virtual, virtualOtherSession); + } + + @Test + public void testEqualsHashCode_virtual_longId_same() { + final AutofillId hostId = new AutofillId(42); + final AutofillId virtual = new AutofillId(hostId, 1L, 108); + final AutofillId sameVirtual = new AutofillId(hostId, 1L, 108); + + assertThat(sameVirtual).isEqualTo(virtual); + assertThat(virtual).isEqualTo(sameVirtual); + assertEqualsIgnoreSession(sameVirtual, virtual); + assertEqualsIgnoreSession(virtual, sameVirtual); + assertThat(sameVirtual).isNotEqualTo(hostId); + assertThat(hostId).isNotEqualTo(sameVirtual); + assertNotEqualsIgnoreSession(sameVirtual, hostId); + assertNotEqualsIgnoreSession(hostId, sameVirtual); + } + + @Test + public void testEqualsHashCode_virtual_longId_otherChild() { + final AutofillId hostId = new AutofillId(42); + final AutofillId virtual = new AutofillId(hostId, 1L, 108); + final AutofillId virtualOtherChild = new AutofillId(hostId, 2L, 108); + + assertThat(virtualOtherChild).isNotEqualTo(virtual); + assertThat(virtual).isNotEqualTo(virtualOtherChild); + assertNotEqualsIgnoreSession(virtualOtherChild, virtual); + assertNotEqualsIgnoreSession(virtual, virtualOtherChild); + assertThat(virtualOtherChild).isNotEqualTo(hostId); + assertThat(hostId).isNotEqualTo(virtualOtherChild); + assertNotEqualsIgnoreSession(virtualOtherChild, hostId); + assertNotEqualsIgnoreSession(hostId, virtualOtherChild); + } + + @Test + public void testEqualsHashCode_virtual_longId_otherParent() { + final AutofillId hostId = new AutofillId(42); + final AutofillId virtual = new AutofillId(hostId, 1L, 108); + final AutofillId virtualOtherParent = new AutofillId(new AutofillId(666), 1L, 108); + final AutofillId virtualOtherChild = new AutofillId(hostId, 2L, 108); + + assertThat(virtualOtherParent).isNotEqualTo(virtual); + assertThat(virtual).isNotEqualTo(virtualOtherParent); + assertNotEqualsIgnoreSession(virtualOtherParent, virtual); + assertNotEqualsIgnoreSession(virtual, virtualOtherParent); + assertThat(virtualOtherParent).isNotEqualTo(virtualOtherChild); + assertThat(virtualOtherChild).isNotEqualTo(virtualOtherParent); + assertNotEqualsIgnoreSession(virtualOtherParent, virtualOtherChild); + assertNotEqualsIgnoreSession(virtualOtherChild, virtualOtherParent); + } + + @Test + public void testEqualsHashCode_virtual_longId_otherSession() { + final AutofillId hostId = new AutofillId(42); + final AutofillId virtual = new AutofillId(hostId, 1L, 108); + final AutofillId virtualOtherSession = new AutofillId(hostId, 1L, 666); + + assertThat(virtualOtherSession).isNotEqualTo(virtual); + assertThat(virtual).isNotEqualTo(virtualOtherSession); + assertEqualsIgnoreSession(virtualOtherSession, virtual); + assertEqualsIgnoreSession(virtual, virtualOtherSession); } private AutofillId cloneThroughParcel(AutofillId id) { @@ -186,4 +304,28 @@ public class AutofillIdTest { parcel.recycle(); } } + + private void assertEqualsIgnoreSession(AutofillId id1, AutofillId id2) { + assertWithMessage("id1 is null").that(id1).isNotNull(); + assertWithMessage("id2 is null").that(id2).isNotNull(); + assertWithMessage("%s is not equal to %s", id1, id2).that(id1.equalsIgnoreSession(id2)) + .isTrue(); + } + + private void assertNotEqualsIgnoreSession(AutofillId id1, AutofillId id2) { + assertWithMessage("id1 is null").that(id1).isNotNull(); + assertWithMessage("id2 is null").that(id2).isNotNull(); + assertWithMessage("%s is not equal to %s", id1, id2).that(id1.equalsIgnoreSession(id2)) + .isFalse(); + } + + private void assertNonVirtual(AutofillId id, int expectedId, int expectSessionId) { + assertThat(id.getViewId()).isEqualTo(expectedId); + assertThat(id.isNonVirtual()).isTrue(); + assertThat(id.isVirtualInt()).isFalse(); + assertThat(id.isVirtualLong()).isFalse(); + assertThat(id.getVirtualChildIntId()).isEqualTo(View.NO_ID); + assertThat(id.getVirtualChildLongId()).isEqualTo(View.NO_ID); + assertThat(id.getSessionId()).isEqualTo(expectSessionId); + } } diff --git a/core/tests/coretests/src/android/view/textclassifier/ConfigParserTest.java b/core/tests/coretests/src/android/view/textclassifier/ConfigParserTest.java index f1cfe24762a8..d54ce51c3c37 100644 --- a/core/tests/coretests/src/android/view/textclassifier/ConfigParserTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/ConfigParserTest.java @@ -26,16 +26,17 @@ import androidx.test.runner.AndroidJUnit4; import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import java.io.IOException; +import java.util.function.Supplier; @SmallTest @RunWith(AndroidJUnit4.class) public class ConfigParserTest { - private static final String SETTINGS = "int=42,float=12.3,boolean=true,string=abc"; + private static final Supplier<String> SETTINGS = + () -> "int=42,float=12.3,boolean=true,string=abc"; private static final String CLEAR_DEVICE_CONFIG_KEY_CMD = "device_config delete " + DeviceConfig.NAMESPACE_TEXTCLASSIFIER; private static final String[] DEVICE_CONFIG_KEYS = new String[]{ @@ -59,7 +60,6 @@ public class ConfigParserTest { } @Test - @Ignore // TODO: Re-enable once ConfigParser#ENABLE_DEVICE_CONFIG is finalized public void getBoolean_deviceConfig() { DeviceConfig.setProperty( DeviceConfig.NAMESPACE_TEXTCLASSIFIER, @@ -79,7 +79,6 @@ public class ConfigParserTest { } @Test - @Ignore // TODO: Re-enable once ConfigParser#ENABLE_DEVICE_CONFIG is finalized public void getInt_deviceConfig() { DeviceConfig.setProperty( DeviceConfig.NAMESPACE_TEXTCLASSIFIER, @@ -97,7 +96,6 @@ public class ConfigParserTest { } @Test - @Ignore // TODO: Re-enable once ConfigParser#ENABLE_DEVICE_CONFIG is finalized public void getFloat_deviceConfig() { DeviceConfig.setProperty( DeviceConfig.NAMESPACE_TEXTCLASSIFIER, @@ -115,7 +113,6 @@ public class ConfigParserTest { } @Test - @Ignore // TODO: Re-enable once ConfigParser#ENABLE_DEVICE_CONFIG is finalized public void getString_deviceConfig() { DeviceConfig.setProperty( DeviceConfig.NAMESPACE_TEXTCLASSIFIER, diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java index f6bb1bff3635..789b829456f3 100644 --- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java @@ -50,9 +50,11 @@ public class TextClassificationConstantsTest { + "in_app_conversation_action_types_default=text_reply," + "notification_conversation_action_types_default=send_email:call_phone," + "lang_id_threshold_override=0.3," - + "lang_id_context_settings=10:1:0.5"; - final TextClassificationConstants constants = - TextClassificationConstants.loadFromString(s); + + "lang_id_context_settings=10:1:0.5," + + "detect_language_from_text_enabled=true," + + "template_intent_factory_enabled=true," + + "translate_in_classification_enabled=true"; + final TextClassificationConstants constants = new TextClassificationConstants(() -> s); assertWithMessage("local_textclassifier_enabled") .that(constants.isLocalTextClassifierEnabled()).isTrue(); @@ -95,6 +97,12 @@ public class TextClassificationConstantsTest { .that(constants.getLangIdThresholdOverride()).isWithin(EPSILON).of(0.3f); Assert.assertArrayEquals("lang_id_context_settings", constants.getLangIdContextSettings(), new float[]{10, 1, 0.5f}, EPSILON); + assertWithMessage("detect_language_from_text_enabled") + .that(constants.isLocalTextClassifierEnabled()).isTrue(); + assertWithMessage("template_intent_factory_enabled") + .that(constants.isLocalTextClassifierEnabled()).isTrue(); + assertWithMessage("translate_in_classification_enabled") + .that(constants.isLocalTextClassifierEnabled()).isTrue(); } @Test @@ -116,9 +124,11 @@ public class TextClassificationConstantsTest { + "in_app_conversation_action_types_default=view_map:track_flight," + "notification_conversation_action_types_default=share_location," + "lang_id_threshold_override=2," - + "lang_id_context_settings=30:0.5:0.3"; - final TextClassificationConstants constants = - TextClassificationConstants.loadFromString(s); + + "lang_id_context_settings=30:0.5:0.3," + + "detect_language_from_text_enabled=false," + + "template_intent_factory_enabled=false," + + "translate_in_classification_enabled=false"; + final TextClassificationConstants constants = new TextClassificationConstants(() -> s); assertWithMessage("local_textclassifier_enabled") .that(constants.isLocalTextClassifierEnabled()).isFalse(); @@ -161,12 +171,17 @@ public class TextClassificationConstantsTest { .that(constants.getLangIdThresholdOverride()).isWithin(EPSILON).of(2f); Assert.assertArrayEquals("lang_id_context_settings", constants.getLangIdContextSettings(), new float[]{30, 0.5f, 0.3f}, EPSILON); + assertWithMessage("detect_language_from_text_enabled") + .that(constants.isLocalTextClassifierEnabled()).isFalse(); + assertWithMessage("template_intent_factory_enabled") + .that(constants.isLocalTextClassifierEnabled()).isFalse(); + assertWithMessage("translate_in_classification_enabled") + .that(constants.isLocalTextClassifierEnabled()).isFalse(); } @Test public void testLoadFromString_defaultValues() { - final TextClassificationConstants constants = - TextClassificationConstants.loadFromString(""); + final TextClassificationConstants constants = new TextClassificationConstants(() -> ""); assertWithMessage("local_textclassifier_enabled") .that(constants.isLocalTextClassifierEnabled()).isTrue(); @@ -213,5 +228,11 @@ public class TextClassificationConstantsTest { .that(constants.getLangIdThresholdOverride()).isWithin(EPSILON).of(-1f); Assert.assertArrayEquals("lang_id_context_settings", constants.getLangIdContextSettings(), new float[]{20, 1, 0.4f}, EPSILON); + assertWithMessage("detect_language_from_text_enabled") + .that(constants.isLocalTextClassifierEnabled()).isTrue(); + assertWithMessage("template_intent_factory_enabled") + .that(constants.isLocalTextClassifierEnabled()).isTrue(); + assertWithMessage("translate_in_classification_enabled") + .that(constants.isLocalTextClassifierEnabled()).isTrue(); } } diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java index 4fcd51c9be1d..9148185d904b 100644 --- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java @@ -77,7 +77,7 @@ public class TextClassificationManagerTest { TextClassifier fallback = TextClassifier.NO_OP; TextClassifier classifier = new TextClassifierImpl( - fakeContext, TextClassificationConstants.loadFromString(null), fallback); + fakeContext, new TextClassificationConstants(() -> null), fallback); String text = "Contact me at +12122537077"; String classifiedText = "+12122537077"; diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java index aeb8949c6976..e3eb2a313cf6 100644 --- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java @@ -59,7 +59,7 @@ public class TextClassifierTest { // TODO: Implement TextClassifierService testing. private static final TextClassificationConstants TC_CONSTANTS = - TextClassificationConstants.loadFromString(""); + new TextClassificationConstants(() -> ""); private static final LocaleList LOCALES = LocaleList.forLanguageTags("en-US"); private static final String NO_TYPE = null; diff --git a/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java b/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java index 3cb1e187fef1..b93c3a7a17e2 100644 --- a/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java +++ b/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java @@ -57,7 +57,8 @@ public class RegisterStatusBarResultTest { 0x80 /* dockedStackSysUiVisibility */, new Binder() /* imeToken */, new Rect(0x100, 0x200, 0x400, 0x800) /* fullscreenStackBounds */, - new Rect(0x1000, 0x2000, 0x4000, 0x8000) /* dockedStackBounds */); + new Rect(0x1000, 0x2000, 0x4000, 0x8000) /* dockedStackBounds */, + true /* navbarColorManagedByIme */); final RegisterStatusBarResult copy = clone(original); @@ -80,6 +81,7 @@ public class RegisterStatusBarResultTest { assertThat(copy.mImeToken).isSameAs(original.mImeToken); assertThat(copy.mFullscreenStackBounds).isEqualTo(original.mFullscreenStackBounds); assertThat(copy.mDockedStackBounds).isEqualTo(original.mDockedStackBounds); + assertThat(copy.mNavbarColorManagedByIme).isEqualTo(original.mNavbarColorManagedByIme); } private RegisterStatusBarResult clone(RegisterStatusBarResult original) { diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java b/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java new file mode 100644 index 000000000000..390bb766ab81 --- /dev/null +++ b/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.overlaytest; + +import static java.util.concurrent.TimeUnit.SECONDS; + +import android.app.UiAutomation; +import android.content.res.Resources; +import android.os.ParcelFileDescriptor; + +import androidx.test.InstrumentationRegistry; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.concurrent.Executor; +import java.util.concurrent.FutureTask; + +class LocalOverlayManager { + private static final long TIMEOUT = 30; + + public static void setEnabledAndWait(Executor executor, final String packageName, + boolean enable) throws Exception { + final String pattern = (enable ? "[x]" : "[ ]") + " " + packageName; + if (executeShellCommand("cmd overlay list").contains(pattern)) { + // nothing to do, overlay already in the requested state + return; + } + + final Resources res = InstrumentationRegistry.getContext().getResources(); + final String[] oldApkPaths = res.getAssets().getApkPaths(); + FutureTask<Boolean> task = new FutureTask<>(() -> { + while (true) { + if (!Arrays.equals(oldApkPaths, res.getAssets().getApkPaths())) { + return true; + } + Thread.sleep(10); + } + }); + executor.execute(task); + executeShellCommand("cmd overlay " + (enable ? "enable " : "disable ") + packageName); + task.get(TIMEOUT, SECONDS); + } + + private static String executeShellCommand(final String command) + throws Exception { + final UiAutomation uiAutomation = + InstrumentationRegistry.getInstrumentation().getUiAutomation(); + final ParcelFileDescriptor pfd = uiAutomation.executeShellCommand(command); + try (InputStream in = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) { + final BufferedReader reader = new BufferedReader( + new InputStreamReader(in, StandardCharsets.UTF_8)); + StringBuilder str = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + str.append(line); + } + return str.toString(); + } + } +} diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java index f86743a2fdbc..fdb6bbb4550b 100644 --- a/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java +++ b/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java @@ -21,13 +21,11 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import android.app.UiAutomation; import android.content.res.AssetManager; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.os.LocaleList; -import android.os.ParcelFileDescriptor; import android.util.AttributeSet; import android.util.Xml; @@ -569,60 +567,4 @@ public abstract class OverlayBaseTest { setLocale(new Locale("sv", "SE")); assertResource(resId, 200, 400, 600); } - - /** - * Executes the shell command and reads all the output to ensure the command ran and didn't - * get stuck buffering on output. - */ - protected static String executeShellCommand(UiAutomation automation, String command) - throws Exception { - final ParcelFileDescriptor pfd = automation.executeShellCommand(command); - try (InputStream in = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) { - final BufferedReader reader = new BufferedReader( - new InputStreamReader(in, StandardCharsets.UTF_8)); - StringBuilder str = new StringBuilder(); - String line; - while ((line = reader.readLine()) != null) { - str.append(line); - } - return str.toString(); - } - } - - /** - * Enables overlay packages and waits for a configuration change event before - * returning, to guarantee that Resources are up-to-date. - * @param packages the list of package names to enable. - */ - protected static void enableOverlayPackages(String... packages) throws Exception { - enableOverlayPackages(true, packages); - } - - /** - * Disables overlay packages and waits for a configuration change event before - * returning, to guarantee that Resources are up-to-date. - * @param packages the list of package names to disable. - */ - protected static void disableOverlayPackages(String... packages) throws Exception { - enableOverlayPackages(false, packages); - } - - /** - * Enables/disables overlay packages and waits for a configuration change event before - * returning, to guarantee that Resources are up-to-date. - * @param enable enables the overlays when true, disables when false. - * @param packages the list of package names to enable/disable. - */ - private static void enableOverlayPackages(boolean enable, String[] packages) - throws Exception { - final UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation() - .getUiAutomation(); - for (final String pkg : packages) { - executeShellCommand(uiAutomation, - "cmd overlay " + (enable ? "enable " : "disable ") + pkg); - } - - // Wait for the overlay change to propagate. - Thread.sleep(1000); - } } diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java index cd3ed9dd5e86..d28c47d9c922 100644 --- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java +++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java @@ -22,6 +22,8 @@ import org.junit.BeforeClass; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +import java.util.concurrent.Executor; + @RunWith(JUnit4.class) @MediumTest public class WithMultipleOverlaysTest extends OverlayBaseTest { @@ -31,6 +33,9 @@ public class WithMultipleOverlaysTest extends OverlayBaseTest { @BeforeClass public static void enableOverlay() throws Exception { - enableOverlayPackages(APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG, FRAMEWORK_OVERLAY_PKG); + Executor executor = (cmd) -> new Thread(cmd).start(); + LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_ONE_PKG, true); + LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_TWO_PKG, true); + LocalOverlayManager.setEnabledAndWait(executor, FRAMEWORK_OVERLAY_PKG, true); } } diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java index c0d4281b7e0d..6566ad304c1c 100644 --- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java +++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java @@ -22,6 +22,8 @@ import org.junit.BeforeClass; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +import java.util.concurrent.Executor; + @RunWith(JUnit4.class) @MediumTest public class WithOverlayTest extends OverlayBaseTest { @@ -31,7 +33,9 @@ public class WithOverlayTest extends OverlayBaseTest { @BeforeClass public static void enableOverlay() throws Exception { - disableOverlayPackages(APP_OVERLAY_TWO_PKG); - enableOverlayPackages(APP_OVERLAY_ONE_PKG, FRAMEWORK_OVERLAY_PKG); + Executor executor = (cmd) -> new Thread(cmd).start(); + LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_ONE_PKG, true); + LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_TWO_PKG, false); + LocalOverlayManager.setEnabledAndWait(executor, FRAMEWORK_OVERLAY_PKG, true); } } diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java index 33c7b2591fe2..48cfeab2fbff 100644 --- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java +++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java @@ -22,6 +22,8 @@ import org.junit.BeforeClass; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +import java.util.concurrent.Executor; + @RunWith(JUnit4.class) @MediumTest public class WithoutOverlayTest extends OverlayBaseTest { @@ -31,6 +33,9 @@ public class WithoutOverlayTest extends OverlayBaseTest { @BeforeClass public static void disableOverlays() throws Exception { - disableOverlayPackages(APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG, FRAMEWORK_OVERLAY_PKG); + Executor executor = (cmd) -> new Thread(cmd).start(); + LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_ONE_PKG, false); + LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_TWO_PKG, false); + LocalOverlayManager.setEnabledAndWait(executor, FRAMEWORK_OVERLAY_PKG, false); } } diff --git a/data/etc/car/Android.bp b/data/etc/car/Android.bp new file mode 100644 index 000000000000..37020fc8d79f --- /dev/null +++ b/data/etc/car/Android.bp @@ -0,0 +1,123 @@ +// Copyright (C} 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"}; +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + + +// Privapp permission whitelist files + +prebuilt_etc { + name: "privapp_whitelist_android.car.cluster.loggingrenderer", + sub_dir: "permissions", + src: "android.car.cluster.loggingrenderer.xml", + filename_from_src: true, +} + +prebuilt_etc { + name: "privapp_whitelist_android.car.cluster.sample", + sub_dir: "permissions", + src: "android.car.cluster.sample.xml", + filename_from_src: true, +} + +prebuilt_etc { + name: "privapp_whitelist_android.car.usb.handler", + sub_dir: "permissions", + src: "android.car.usb.handler.xml", + filename_from_src: true, +} + +prebuilt_etc { + name: "privapp_whitelist_com.android.car.carlauncher", + sub_dir: "permissions", + src: "com.android.car.carlauncher.xml", + filename_from_src: true, +} + +prebuilt_etc { + name: "privapp_whitelist_com.android.car.dialer", + sub_dir: "permissions", + src: "com.android.car.dialer.xml", + filename_from_src: true, +} + +prebuilt_etc { + name: "privapp_whitelist_com.android.car.hvac", + sub_dir: "permissions", + src: "com.android.car.hvac.xml", + filename_from_src: true, +} + +prebuilt_etc { + name: "privapp_whitelist_com.android.car.media", + sub_dir: "permissions", + src: "com.android.car.media.xml", + filename_from_src: true, +} + +prebuilt_etc { + name: "privapp_whitelist_com.android.car.notification", + sub_dir: "permissions", + src: "com.android.car.notification.xml", + filename_from_src: true, +} + +prebuilt_etc { + name: "privapp_whitelist_com.android.car.radio", + sub_dir: "permissions", + src: "com.android.car.radio.xml", + filename_from_src: true, +} + +prebuilt_etc { + name: "privapp_whitelist_com.android.car.settings", + sub_dir: "permissions", + src: "com.android.car.settings.xml", + filename_from_src: true, +} + +prebuilt_etc { + name: "privapp_whitelist_com.android.car.themeplayground", + sub_dir: "permissions", + src: "com.android.car.themeplayground.xml", + filename_from_src: true, +} + +prebuilt_etc { + name: "privapp_whitelist_com.android.car.trust", + sub_dir: "permissions", + src: "com.android.car.trust.xml", + filename_from_src: true, +} + +prebuilt_etc { + name: "privapp_whitelist_com.android.car", + sub_dir: "permissions", + src: "com.android.car.xml", + filename_from_src: true, +} + +prebuilt_etc { + name: "privapp_whitelist_com.google.android.car.kitchensink", + sub_dir: "permissions", + src: "com.google.android.car.kitchensink.xml", + filename_from_src: true, +} + +prebuilt_etc { + name: "privapp_whitelist_com.android.car.developeroptions", + sub_dir: "permissions", + src: "com.android.car.developeroptions.xml", + filename_from_src: true, + product_specific: true, +} diff --git a/data/etc/car/android.car.cluster.loggingrenderer.xml b/data/etc/car/android.car.cluster.loggingrenderer.xml new file mode 100644 index 000000000000..784e0e7dde14 --- /dev/null +++ b/data/etc/car/android.car.cluster.loggingrenderer.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<permissions> + <privapp-permissions package="android.car.cluster.loggingrenderer"> + <permission name="android.permission.INTERACT_ACROSS_USERS"/> + </privapp-permissions> +</permissions> diff --git a/data/etc/car/android.car.cluster.sample.xml b/data/etc/car/android.car.cluster.sample.xml new file mode 100644 index 000000000000..75c57b8c1d07 --- /dev/null +++ b/data/etc/car/android.car.cluster.sample.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<permissions> + <privapp-permissions package="android.car.cluster.sample"> + <permission name="android.permission.CONTROL_INCALL_EXPERIENCE"/> + <permission name="android.permission.INTERACT_ACROSS_USERS"/> + <permission name="android.permission.MANAGE_USERS"/> + <permission name="android.permission.WRITE_SECURE_SETTINGS"/> + </privapp-permissions> +</permissions> diff --git a/data/etc/car/android.car.usb.handler.xml b/data/etc/car/android.car.usb.handler.xml new file mode 100644 index 000000000000..c67847c74cf8 --- /dev/null +++ b/data/etc/car/android.car.usb.handler.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<permissions> + <privapp-permissions package="android.car.usb.handler"> + <permission name="android.permission.MANAGE_USB"/> + <permission name="android.permission.MANAGE_USERS"/> + </privapp-permissions> +</permissions> diff --git a/data/etc/car/com.android.car.carlauncher.xml b/data/etc/car/com.android.car.carlauncher.xml new file mode 100644 index 000000000000..8ec1cd41dab4 --- /dev/null +++ b/data/etc/car/com.android.car.carlauncher.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<permissions> + <privapp-permissions package="com.android.car.carlauncher"> + <permission name="android.permission.ACTIVITY_EMBEDDING"/> + <permission name="android.permission.MANAGE_USERS"/> + <permission name="android.permission.MEDIA_CONTENT_CONTROL"/> + <permission name="android.permission.PACKAGE_USAGE_STATS"/> + </privapp-permissions> +</permissions> diff --git a/data/etc/car/com.android.car.developeroptions.xml b/data/etc/car/com.android.car.developeroptions.xml new file mode 100644 index 000000000000..76c8c622ba71 --- /dev/null +++ b/data/etc/car/com.android.car.developeroptions.xml @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<permissions> + <privapp-permissions package="com.android.car.developeroptions"> + <permission name="android.permission.ACCESS_CHECKIN_PROPERTIES"/> + <permission name="android.permission.ACCESS_NOTIFICATIONS"/> + <permission name="android.permission.BACKUP"/> + <permission name="android.permission.BATTERY_STATS"/> + <permission name="android.permission.BLUETOOTH_PRIVILEGED"/> + <permission name="android.permission.CHANGE_APP_IDLE_STATE"/> + <permission name="android.permission.CHANGE_CONFIGURATION"/> + <permission name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS"/> + <permission name="android.permission.DELETE_PACKAGES"/> + <permission name="android.permission.FORCE_STOP_PACKAGES"/> + <permission name="android.permission.LOCAL_MAC_ADDRESS"/> + <permission name="android.permission.MANAGE_DEBUGGING"/> + <permission name="android.permission.MANAGE_FINGERPRINT"/> + <permission name="android.permission.MANAGE_USB"/> + <permission name="android.permission.MANAGE_USER_OEM_UNLOCK_STATE"/> + <permission name="android.permission.MANAGE_USERS"/> + <permission name="android.permission.MASTER_CLEAR"/> + <permission name="android.permission.MEDIA_CONTENT_CONTROL"/> + <permission name="android.permission.MODIFY_PHONE_STATE"/> + <permission name="android.permission.MODIFY_PHONE_STATE"/> + <permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> + <permission name="android.permission.MOVE_PACKAGE"/> + <permission name="android.permission.OVERRIDE_WIFI_CONFIG"/> + <permission name="android.permission.PACKAGE_USAGE_STATS"/> + <permission name="android.permission.READ_SEARCH_INDEXABLES"/> + <permission name="android.permission.REBOOT"/> + <permission name="android.permission.SET_TIME"/> + <permission name="android.permission.STATUS_BAR"/> + <permission name="android.permission.TETHER_PRIVILEGED"/> + <permission name="android.permission.USE_RESERVED_DISK"/> + <permission name="android.permission.USER_ACTIVITY"/> + <permission name="android.permission.WRITE_APN_SETTINGS"/> + <permission name="android.permission.WRITE_MEDIA_STORAGE"/> + <permission name="android.permission.WRITE_SECURE_SETTINGS"/> + </privapp-permissions> +</permissions> diff --git a/data/etc/car/com.android.car.dialer.xml b/data/etc/car/com.android.car.dialer.xml new file mode 100644 index 000000000000..d44f5a1704a2 --- /dev/null +++ b/data/etc/car/com.android.car.dialer.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<permissions> + <privapp-permissions package="com.android.car.dialer"> + <permission name="android.permission.INTERACT_ACROSS_USERS"/> + <permission name="android.permission.MODIFY_PHONE_STATE"/> + </privapp-permissions> +</permissions> diff --git a/data/etc/car/com.android.car.hvac.xml b/data/etc/car/com.android.car.hvac.xml new file mode 100644 index 000000000000..d3631e067f8b --- /dev/null +++ b/data/etc/car/com.android.car.hvac.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<permissions> + <privapp-permissions package="com.android.car.hvac"> + <permission name="android.permission.INTERACT_ACROSS_USERS"/> + </privapp-permissions> +</permissions> diff --git a/data/etc/car/com.android.car.media.xml b/data/etc/car/com.android.car.media.xml new file mode 100644 index 000000000000..d17453d26e59 --- /dev/null +++ b/data/etc/car/com.android.car.media.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<permissions> + <privapp-permissions package="com.android.car.media"> + <permission name="android.permission.MEDIA_CONTENT_CONTROL"/> + </privapp-permissions> +</permissions> diff --git a/data/etc/car/com.android.car.notification.xml b/data/etc/car/com.android.car.notification.xml new file mode 100644 index 000000000000..8479512b049e --- /dev/null +++ b/data/etc/car/com.android.car.notification.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<permissions> + <privapp-permissions package="com.android.car.notification"> + <permission name="android.permission.INTERACT_ACROSS_USERS"/> + <permission name="android.permission.MANAGE_USERS"/> + <permission name="android.permission.MODIFY_PHONE_STATE"/> + </privapp-permissions> +</permissions> diff --git a/data/etc/car/com.android.car.radio.xml b/data/etc/car/com.android.car.radio.xml new file mode 100644 index 000000000000..d7853aba2d90 --- /dev/null +++ b/data/etc/car/com.android.car.radio.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<permissions> + <privapp-permissions package="com.android.car.radio"> + <permission name="android.permission.ACCESS_BROADCAST_RADIO"/> + <permission name="android.permission.MEDIA_CONTENT_CONTROL"/> + </privapp-permissions> +</permissions> diff --git a/data/etc/car/com.android.car.settings.xml b/data/etc/car/com.android.car.settings.xml new file mode 100644 index 000000000000..5f7e1c13decf --- /dev/null +++ b/data/etc/car/com.android.car.settings.xml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<permissions> + <privapp-permissions package="com.android.car.settings"> + <permission name="android.permission.ACCESS_CHECKIN_PROPERTIES"/> + <permission name="android.permission.ACCESS_NOTIFICATIONS"/> + <permission name="android.permission.BACKUP"/> + <permission name="android.permission.BATTERY_STATS"/> + <permission name="android.permission.BLUETOOTH_PRIVILEGED"/> + <permission name="android.permission.CHANGE_APP_IDLE_STATE"/> + <permission name="android.permission.CHANGE_CONFIGURATION"/> + <permission name="android.permission.DELETE_PACKAGES"/> + <permission name="android.permission.DELETE_CACHE_FILES"/> + <permission name="android.permission.DUMP"/> + <permission name="android.permission.FORCE_STOP_PACKAGES"/> + <permission name="android.permission.LOCAL_MAC_ADDRESS"/> + <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/> + <permission name="android.permission.MANAGE_DEBUGGING"/> + <permission name="android.permission.MANAGE_DEVICE_ADMINS"/> + <permission name="android.permission.MANAGE_FINGERPRINT"/> + <permission name="android.permission.MANAGE_USB"/> + <permission name="android.permission.MANAGE_USERS"/> + <permission name="android.permission.MANAGE_USER_OEM_UNLOCK_STATE" /> + <permission name="android.permission.MASTER_CLEAR"/> + <permission name="android.permission.MODIFY_PHONE_STATE"/> + <permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> + <permission name="android.permission.MOVE_PACKAGE"/> + <permission name="android.permission.OVERRIDE_WIFI_CONFIG"/> + <permission name="android.permission.PACKAGE_USAGE_STATS"/> + <permission name="android.permission.READ_SEARCH_INDEXABLES"/> + <permission name="android.permission.REBOOT"/> + <permission name="android.permission.SET_TIME"/> + <permission name="android.permission.SET_TIME_ZONE"/> + <permission name="android.permission.STATUS_BAR"/> + <permission name="android.permission.TETHER_PRIVILEGED"/> + <permission name="android.permission.USE_RESERVED_DISK"/> + <permission name="android.permission.USER_ACTIVITY"/> + <permission name="android.permission.WRITE_APN_SETTINGS"/> + <permission name="android.permission.WRITE_MEDIA_STORAGE"/> + <permission name="android.permission.WRITE_SECURE_SETTINGS"/> + <permission name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" /> + </privapp-permissions> +</permissions> diff --git a/data/etc/car/com.android.car.themeplayground.xml b/data/etc/car/com.android.car.themeplayground.xml new file mode 100644 index 000000000000..cab4718b0461 --- /dev/null +++ b/data/etc/car/com.android.car.themeplayground.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<permissions> + <privapp-permissions package="com.android.car.themeplayground"> + <permission name="android.permission.MODIFY_DAY_NIGHT_MODE"/> + </privapp-permissions> +</permissions> diff --git a/data/etc/car/com.android.car.trust.xml b/data/etc/car/com.android.car.trust.xml new file mode 100644 index 000000000000..dc87af278b27 --- /dev/null +++ b/data/etc/car/com.android.car.trust.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<permissions> + <privapp-permissions package="com.android.car.trust"> + <permission name="android.permission.BLUETOOTH" /> + <permission name="android.permission.BLUETOOTH_ADMIN" /> + <permission name="android.permission.ACCESS_COARSE_LOCATION"/> + <permission name="android.permission.ACCESS_FINE_LOCATION"/> + <permission name="android.permission.INTERACT_ACROSS_USERS"/> + <permission name="android.permission.INTERACT_ACROSS_USERS_FULL" /> + <permission name="android.permission.MANAGE_USERS" /> + <permission name="android.permission.CONTROL_KEYGUARD" /> + <permission name="android.permission.PROVIDE_TRUST_AGENT" /> + <permission name="android.permission.RECEIVE_BOOT_COMPLETED" /> + </privapp-permissions> +</permissions> diff --git a/data/etc/car/com.android.car.xml b/data/etc/car/com.android.car.xml new file mode 100644 index 000000000000..f1797deed075 --- /dev/null +++ b/data/etc/car/com.android.car.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<permissions> + <privapp-permissions package="com.android.car"> + <permission name="android.permission.LOCATION_HARDWARE"/> + <permission name="android.permission.MANAGE_USERS"/> + <permission name="android.permission.MODIFY_AUDIO_ROUTING"/> + <permission name="android.permission.MODIFY_DAY_NIGHT_MODE"/> + <permission name="android.permission.MODIFY_PHONE_STATE"/> + <permission name="android.permission.PROVIDE_TRUST_AGENT"/> + <permission name="android.permission.REAL_GET_TASKS"/> + <permission name="android.permission.REBOOT"/> + <permission name="android.permission.WRITE_SECURE_SETTINGS"/> + </privapp-permissions> +</permissions> diff --git a/data/etc/car/com.google.android.car.kitchensink.xml b/data/etc/car/com.google.android.car.kitchensink.xml new file mode 100644 index 000000000000..6b26e8f3d5f2 --- /dev/null +++ b/data/etc/car/com.google.android.car.kitchensink.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<permissions> + <privapp-permissions package="com.google.android.car.kitchensink"> + <permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/> + <permission name="android.permission.LOCATION_HARDWARE"/> + <permission name="android.permission.MANAGE_USB"/> + <permission name="android.permission.MANAGE_USERS"/> + <permission name="android.permission.MODIFY_AUDIO_ROUTING"/> + <permission name="android.permission.MODIFY_DAY_NIGHT_MODE"/> + <permission name="android.permission.MODIFY_PHONE_STATE"/> + <permission name="android.permission.PROVIDE_TRUST_AGENT"/> + <permission name="android.permission.REAL_GET_TASKS"/> + <permission name="android.permission.REBOOT"/> + <permission name="android.permission.WRITE_SECURE_SETTINGS"/> + </privapp-permissions> +</permissions> diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 485add9fa11f..27e859c3987c 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -83,6 +83,7 @@ applications that come with the platform <permission name="android.permission.SET_TIME_ZONE"/> <permission name="android.permission.SHUTDOWN"/> <permission name="android.permission.WRITE_SECURE_SETTINGS"/> + <permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"/> </privapp-permissions> <privapp-permissions package="com.android.mms.service"> @@ -235,6 +236,7 @@ applications that come with the platform <permission name="android.permission.CALL_PRIVILEGED"/> <permission name="android.permission.INTERACT_ACROSS_USERS"/> <permission name="android.permission.MANAGE_USERS"/> + <permission name="android.permission.MANAGE_ROLE_HOLDERS"/> <permission name="android.permission.MODIFY_AUDIO_ROUTING" /> <permission name="android.permission.MODIFY_PHONE_STATE"/> <permission name="android.permission.STOP_APP_SWITCHES"/> diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index 07f81c18e1ca..44710178da5e 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -2197,8 +2197,12 @@ public final class Bitmap implements Parcelable { } /** - * * @return {@link GraphicBuffer} which is internally used by hardware bitmap + * + * Note: the GraphicBuffer does *not* have an associated {@link ColorSpace}. + * To render this object the same as its rendered with this Bitmap, you + * should also call {@link getColorSpace}. + * * @hide */ @UnsupportedAppUsage diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java index 816d1fda57d1..11d635eaba12 100644 --- a/graphics/java/android/graphics/drawable/GradientDrawable.java +++ b/graphics/java/android/graphics/drawable/GradientDrawable.java @@ -637,6 +637,7 @@ public class GradientDrawable extends Drawable { * @see #setOrientation(Orientation) */ public Orientation getOrientation() { + updateGradientStateOrientation(); return mGradientState.mOrientation; } @@ -653,6 +654,9 @@ public class GradientDrawable extends Drawable { * @see #getOrientation() */ public void setOrientation(Orientation orientation) { + // Update the angle here so that subsequent attempts to obtain the orientation + // from the angle overwrite previously configured values during inflation + mGradientState.mAngle = getAngleFromOrientation(orientation); mGradientState.mOrientation = orientation; mGradientIsDirty = true; invalidateSelf(); @@ -1242,6 +1246,76 @@ public class GradientDrawable extends Drawable { } /** + * Update the orientation of the gradient based on the given angle only if the type is + * {@link #LINEAR_GRADIENT} + */ + private void updateGradientStateOrientation() { + if (mGradientState.mGradient == LINEAR_GRADIENT) { + int angle = mGradientState.mAngle; + if (angle % 45 != 0) { + throw new IllegalArgumentException("Linear gradient requires 'angle' attribute to " + + "be a multiple of 45"); + } + + Orientation orientation; + switch (angle) { + case 0: + orientation = Orientation.LEFT_RIGHT; + break; + case 45: + orientation = Orientation.BL_TR; + break; + case 90: + orientation = Orientation.BOTTOM_TOP; + break; + case 135: + orientation = Orientation.BR_TL; + break; + case 180: + orientation = Orientation.RIGHT_LEFT; + break; + case 225: + orientation = Orientation.TR_BL; + break; + case 270: + orientation = Orientation.TOP_BOTTOM; + break; + case 315: + orientation = Orientation.TL_BR; + break; + default: + // Should not get here as exception is thrown above if angle is not multiple + // of 45 degrees + orientation = Orientation.LEFT_RIGHT; + break; + } + mGradientState.mOrientation = orientation; + } + } + + private int getAngleFromOrientation(Orientation orientation) { + switch (orientation) { + default: + case LEFT_RIGHT: + return 0; + case BL_TR: + return 45; + case BOTTOM_TOP: + return 90; + case BR_TL: + return 135; + case RIGHT_LEFT: + return 180; + case TR_BL: + return 225; + case TOP_BOTTOM: + return 270; + case TL_BR: + return 315; + } + } + + /** * This checks mGradientIsDirty, and if it is true, recomputes both our drawing * rectangle (mRect) and the gradient itself, since it depends on our * rectangle too. @@ -1270,6 +1344,7 @@ public class GradientDrawable extends Drawable { if (st.mGradient == LINEAR_GRADIENT) { final float level = st.mUseLevel ? getLevel() / 10000.0f : 1.0f; + updateGradientStateOrientation(); switch (st.mOrientation) { case TOP_BOTTOM: x0 = r.left; y0 = r.top; @@ -1312,10 +1387,6 @@ public class GradientDrawable extends Drawable { y0 = r.top + (r.bottom - r.top) * st.mCenterY; float radius = st.mGradientRadius; - if (Float.compare(radius, 0.0f) == -1 || Float.isNaN(radius)) { - throw new IllegalArgumentException("Gradient radius must be a valid " - + "number greater than or equal to 0"); - } if (st.mGradientRadiusType == RADIUS_TYPE_FRACTION) { // Fall back to parent width or height if intrinsic // size is not specified. @@ -1511,8 +1582,6 @@ public class GradientDrawable extends Drawable { st.mAttrGradient, R.styleable.GradientDrawableGradient); try { updateGradientDrawableGradient(t.getResources(), a); - } catch (XmlPullParserException e) { - rethrowAsRuntimeException(e); } finally { a.recycle(); } @@ -1700,8 +1769,7 @@ public class GradientDrawable extends Drawable { } } - private void updateGradientDrawableGradient(Resources r, TypedArray a) - throws XmlPullParserException { + private void updateGradientDrawableGradient(Resources r, TypedArray a) { final GradientState st = mGradientState; // Account for any configuration changes. @@ -1764,42 +1832,7 @@ public class GradientDrawable extends Drawable { } int angle = (int) a.getFloat(R.styleable.GradientDrawableGradient_angle, st.mAngle); - angle %= 360; - - if (angle % 45 != 0) { - throw new XmlPullParserException(a.getPositionDescription() - + "<gradient> tag requires 'angle' attribute to " - + "be a multiple of 45"); - } - - st.mAngle = angle; - - switch (angle) { - case 0: - st.mOrientation = Orientation.LEFT_RIGHT; - break; - case 45: - st.mOrientation = Orientation.BL_TR; - break; - case 90: - st.mOrientation = Orientation.BOTTOM_TOP; - break; - case 135: - st.mOrientation = Orientation.BR_TL; - break; - case 180: - st.mOrientation = Orientation.RIGHT_LEFT; - break; - case 225: - st.mOrientation = Orientation.TR_BL; - break; - case 270: - st.mOrientation = Orientation.TOP_BOTTOM; - break; - case 315: - st.mOrientation = Orientation.TL_BR; - break; - } + st.mAngle = angle % 360; final TypedValue tv = a.peekValue(R.styleable.GradientDrawableGradient_gradientRadius); if (tv != null) { diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp index 9248eadbd0ef..8092b1d10659 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp @@ -166,8 +166,6 @@ static void setBufferCount(ANativeWindow* window, uint32_t extraBuffers) { auto min_undequeued_buffers = static_cast<uint32_t>(query_value); int bufferCount = min_undequeued_buffers + 2 + extraBuffers; - ALOGD("Setting buffer count to %d, min_undequeued %u, extraBuffers %u", - bufferCount, min_undequeued_buffers, extraBuffers); native_window_set_buffer_count(window, bufferCount); } diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp index 1d553342415c..159cf497384a 100644 --- a/libs/hwui/renderthread/EglManager.cpp +++ b/libs/hwui/renderthread/EglManager.cpp @@ -235,8 +235,6 @@ bool EglManager::hasEglContext() { } void EglManager::loadConfigs() { - ALOGD("Swap behavior %d", static_cast<int>(mSwapBehavior)); - // Note: The default pixel format is RGBA_8888, when other formats are // available, we should check the target pixel format and configure the // attributes list properly. @@ -246,7 +244,6 @@ void EglManager::loadConfigs() { // Try again without dirty regions enabled ALOGW("Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without..."); mSwapBehavior = SwapBehavior::Discard; - ALOGD("Swap behavior %d", static_cast<int>(mSwapBehavior)); mEglConfig = load8BitsConfig(mEglDisplay, mSwapBehavior); } else { // Failed to get a valid config diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java index 977e790eb42e..8612e1bf324c 100644 --- a/media/java/android/media/AudioAttributes.java +++ b/media/java/android/media/AudioAttributes.java @@ -816,6 +816,8 @@ public final class AudioAttributes implements Parcelable { */ @UnsupportedAppUsage public Builder setInternalLegacyStreamType(int streamType) { + mContentType = CONTENT_TYPE_UNKNOWN; + mUsage = USAGE_UNKNOWN; if (AudioProductStrategy.getAudioProductStrategies().size() > 0) { AudioAttributes attributes = AudioProductStrategy.getAudioAttributesForStrategyWithLegacyStreamType( @@ -828,49 +830,52 @@ public final class AudioAttributes implements Parcelable { mTags = attributes.mTags; mBundle = attributes.mBundle; mSource = attributes.mSource; - return this; } } - switch(streamType) { - case AudioSystem.STREAM_VOICE_CALL: - mContentType = CONTENT_TYPE_SPEECH; - break; - case AudioSystem.STREAM_SYSTEM_ENFORCED: - mFlags |= FLAG_AUDIBILITY_ENFORCED; - // intended fall through, attributes in common with STREAM_SYSTEM - case AudioSystem.STREAM_SYSTEM: - mContentType = CONTENT_TYPE_SONIFICATION; - break; - case AudioSystem.STREAM_RING: - mContentType = CONTENT_TYPE_SONIFICATION; - break; - case AudioSystem.STREAM_MUSIC: - mContentType = CONTENT_TYPE_MUSIC; - break; - case AudioSystem.STREAM_ALARM: - mContentType = CONTENT_TYPE_SONIFICATION; - break; - case AudioSystem.STREAM_NOTIFICATION: - mContentType = CONTENT_TYPE_SONIFICATION; - break; - case AudioSystem.STREAM_BLUETOOTH_SCO: - mContentType = CONTENT_TYPE_SPEECH; - mFlags |= FLAG_SCO; - break; - case AudioSystem.STREAM_DTMF: - mContentType = CONTENT_TYPE_SONIFICATION; - break; - case AudioSystem.STREAM_TTS: - mContentType = CONTENT_TYPE_SONIFICATION; - mFlags |= FLAG_BEACON; - break; - case AudioSystem.STREAM_ACCESSIBILITY: - mContentType = CONTENT_TYPE_SPEECH; - break; - default: - Log.e(TAG, "Invalid stream type " + streamType + " for AudioAttributes"); + if (mContentType == CONTENT_TYPE_UNKNOWN) { + switch (streamType) { + case AudioSystem.STREAM_VOICE_CALL: + mContentType = CONTENT_TYPE_SPEECH; + break; + case AudioSystem.STREAM_SYSTEM_ENFORCED: + mFlags |= FLAG_AUDIBILITY_ENFORCED; + // intended fall through, attributes in common with STREAM_SYSTEM + case AudioSystem.STREAM_SYSTEM: + mContentType = CONTENT_TYPE_SONIFICATION; + break; + case AudioSystem.STREAM_RING: + mContentType = CONTENT_TYPE_SONIFICATION; + break; + case AudioSystem.STREAM_MUSIC: + mContentType = CONTENT_TYPE_MUSIC; + break; + case AudioSystem.STREAM_ALARM: + mContentType = CONTENT_TYPE_SONIFICATION; + break; + case AudioSystem.STREAM_NOTIFICATION: + mContentType = CONTENT_TYPE_SONIFICATION; + break; + case AudioSystem.STREAM_BLUETOOTH_SCO: + mContentType = CONTENT_TYPE_SPEECH; + mFlags |= FLAG_SCO; + break; + case AudioSystem.STREAM_DTMF: + mContentType = CONTENT_TYPE_SONIFICATION; + break; + case AudioSystem.STREAM_TTS: + mContentType = CONTENT_TYPE_SONIFICATION; + mFlags |= FLAG_BEACON; + break; + case AudioSystem.STREAM_ACCESSIBILITY: + mContentType = CONTENT_TYPE_SPEECH; + break; + default: + Log.e(TAG, "Invalid stream type " + streamType + " for AudioAttributes"); + } + } + if (mUsage == USAGE_UNKNOWN) { + mUsage = usageForStreamType(streamType); } - mUsage = usageForStreamType(streamType); return this; } diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java index a56e7f583094..f304f7cc5981 100644 --- a/media/java/android/media/MediaCodecInfo.java +++ b/media/java/android/media/MediaCodecInfo.java @@ -24,6 +24,7 @@ import android.annotation.Nullable; import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; import android.os.Build; +import android.os.SystemProperties; import android.util.Log; import android.util.Pair; import android.util.Range; @@ -1105,7 +1106,11 @@ public final class MediaCodecInfo { mBitrateRange = Range.create(0, Integer.MAX_VALUE); mMaxInputChannelCount = MAX_INPUT_CHANNEL_COUNT; // mBitrateRange = Range.create(1, 320000); - mSampleRateRanges = new Range[] { Range.create(8000, 96000) }; + final int minSampleRate = SystemProperties. + getInt("ro.mediacodec.min_sample_rate", 7350); + final int maxSampleRate = SystemProperties. + getInt("ro.mediacodec.max_sample_rate", 192000); + mSampleRateRanges = new Range[] { Range.create(minSampleRate, maxSampleRate) }; mSampleRates = null; } @@ -1677,6 +1682,13 @@ public final class MediaCodecInfo { return "PerformancePoint(" + info + ")"; } + @Override + public int hashCode() { + // only max frame rate must equal between performance points that equal to one + // another + return mMaxFrameRate; + } + /** * Create a detailed performance point with custom max frame rate and macroblock size. * diff --git a/media/java/android/media/ThumbnailUtils.java b/media/java/android/media/ThumbnailUtils.java index 21b194d73ff9..5de56c718570 100644 --- a/media/java/android/media/ThumbnailUtils.java +++ b/media/java/android/media/ThumbnailUtils.java @@ -244,30 +244,77 @@ public class ThumbnailUtils { final Resizer resizer = new Resizer(size, signal); final String mimeType = MediaFile.getMimeTypeForFile(file.getName()); + Bitmap bitmap = null; + ExifInterface exif = null; + int orientation = 0; + + // get orientation + if (MediaFile.isExifMimeType(mimeType)) { + exif = new ExifInterface(file); + switch (exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0)) { + case ExifInterface.ORIENTATION_ROTATE_90: + orientation = 90; + break; + case ExifInterface.ORIENTATION_ROTATE_180: + orientation = 180; + break; + case ExifInterface.ORIENTATION_ROTATE_270: + orientation = 270; + break; + } + } + + boolean isHeifFile = false; + if (mimeType.equals("image/heif") || mimeType.equals("image/heif-sequence") || mimeType.equals("image/heic") || mimeType.equals("image/heic-sequence")) { + isHeifFile = true; try (MediaMetadataRetriever retriever = new MediaMetadataRetriever()) { retriever.setDataSource(file.getAbsolutePath()); - return retriever.getThumbnailImageAtIndex(-1, + bitmap = retriever.getThumbnailImageAtIndex(-1, new MediaMetadataRetriever.BitmapParams(), size.getWidth(), size.getWidth() * size.getHeight()); } catch (RuntimeException e) { throw new IOException("Failed to create thumbnail", e); } - } else if (MediaFile.isExifMimeType(mimeType)) { - final ExifInterface exif = new ExifInterface(file); + } + + if (bitmap == null && exif != null) { final byte[] raw = exif.getThumbnailBytes(); if (raw != null) { - return ImageDecoder.decodeBitmap(ImageDecoder.createSource(raw), resizer); + try { + bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(raw), resizer); + } catch (ImageDecoder.DecodeException e) { + Log.w(TAG, e); + } } } // Checkpoint before going deeper if (signal != null) signal.throwIfCanceled(); - return ImageDecoder.decodeBitmap(ImageDecoder.createSource(file), resizer); + if (bitmap == null) { + bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(file), resizer); + // Use ImageDecoder to do full image decode of heif format file + // will have right orientation. Don't rotate the bitmap again. + if (isHeifFile) { + return bitmap; + } + } + + // Transform the bitmap if the orientation of the image is not 0. + if (orientation != 0 && bitmap != null) { + final int width = bitmap.getWidth(); + final int height = bitmap.getHeight(); + + final Matrix m = new Matrix(); + m.setRotate(orientation, width / 2, height / 2); + bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, m, false); + } + + return bitmap; } /** diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java index 12b41cabd142..07dbf93ce46e 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java @@ -951,12 +951,10 @@ public class CarStatusBar extends StatusBar implements return mContext.getDrawable(com.android.internal.R.drawable.default_wallpaper); } - /** Returns true if the current user makes it through the setup wizard, false otherwise. */ - private boolean getIsUserSetup() { - return mUserSetup; - } - private void setNotificationViewClipBounds(int height) { + if (height > mNotificationView.getHeight()) { + height = mNotificationView.getHeight(); + } Rect clipBounds = new Rect(); clipBounds.set(0, 0, mNotificationView.getWidth(), height); // Sets the clip region on the notification list view. @@ -980,7 +978,6 @@ public class CarStatusBar extends StatusBar implements } } - private static final int SWIPE_UP_MIN_DISTANCE = 75; private static final int SWIPE_DOWN_MIN_DISTANCE = 25; private static final int SWIPE_MAX_OFF_PATH = 75; private static final int SWIPE_THRESHOLD_VELOCITY = 200; diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java index b2baff5db75b..73c789516754 100644 --- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java +++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java @@ -23,6 +23,7 @@ import static android.service.notification.NotificationListenerService.Ranking.U import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.app.ActivityThread; import android.app.INotificationManager; import android.app.Notification; @@ -71,6 +72,7 @@ import java.util.concurrent.Executors; /** * Notification assistant that provides guidance on notification channel blocking */ +@SuppressLint("OverrideAbstract") public class Assistant extends NotificationAssistantService { private static final String TAG = "ExtAssistant"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @@ -238,7 +240,7 @@ public class Assistant extends NotificationAssistantService { } mSingleThreadExecutor.submit(() -> { NotificationEntry entry = - new NotificationEntry(mPackageManager, sbn, channel, mSmsHelper); + new NotificationEntry(getContext(), mPackageManager, sbn, channel, mSmsHelper); SmartActionsHelper.SmartSuggestions suggestions = mSmartActionsHelper.suggest(entry); if (DEBUG) { Log.d(TAG, String.format( @@ -295,7 +297,7 @@ public class Assistant extends NotificationAssistantService { } Ranking ranking = getRanking(sbn.getKey(), rankingMap); if (ranking != null && ranking.getChannel() != null) { - NotificationEntry entry = new NotificationEntry(mPackageManager, + NotificationEntry entry = new NotificationEntry(getContext(), mPackageManager, sbn, ranking.getChannel(), mSmsHelper); String key = getKey( sbn.getPackageName(), sbn.getUserId(), ranking.getChannel().getId()); diff --git a/packages/ExtServices/src/android/ext/services/notification/AssistantSettings.java b/packages/ExtServices/src/android/ext/services/notification/AssistantSettings.java index 46288bb67b4c..296db46d2624 100644 --- a/packages/ExtServices/src/android/ext/services/notification/AssistantSettings.java +++ b/packages/ExtServices/src/android/ext/services/notification/AssistantSettings.java @@ -102,10 +102,10 @@ final class AssistantSettings extends ContentObserver { } private void registerDeviceConfigs() { - DeviceConfig.addOnPropertyChangedListener( + DeviceConfig.addOnPropertiesChangedListener( DeviceConfig.NAMESPACE_SYSTEMUI, this::postToHandler, - this::onDeviceConfigPropertyChanged); + (properties) -> onDeviceConfigPropertiesChanged(properties.getNamespace())); // Update the fields in this class from the current state of the device config. updateFromDeviceConfigFlags(); @@ -116,10 +116,10 @@ final class AssistantSettings extends ContentObserver { } @VisibleForTesting - void onDeviceConfigPropertyChanged(String namespace, String name, String value) { + void onDeviceConfigPropertiesChanged(String namespace) { if (!DeviceConfig.NAMESPACE_SYSTEMUI.equals(namespace)) { Log.e(LOG_TAG, "Received update from DeviceConfig for unrelated namespace: " - + namespace + " " + name + "=" + value); + + namespace); return; } diff --git a/packages/ExtServices/src/android/ext/services/notification/NotificationEntry.java b/packages/ExtServices/src/android/ext/services/notification/NotificationEntry.java index 84a8a8c577f7..1ffbac941dc3 100644 --- a/packages/ExtServices/src/android/ext/services/notification/NotificationEntry.java +++ b/packages/ExtServices/src/android/ext/services/notification/NotificationEntry.java @@ -28,18 +28,23 @@ import android.app.NotificationChannel; import android.app.Person; import android.app.RemoteInput; import android.content.ComponentName; +import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; +import android.graphics.drawable.Icon; import android.media.AudioAttributes; import android.media.AudioSystem; import android.os.Build; +import android.os.Parcelable; import android.os.RemoteException; import android.service.notification.StatusBarNotification; import android.util.Log; +import android.util.SparseArray; import java.util.ArrayList; import java.util.Objects; +import java.util.Set; /** * Holds data about notifications. @@ -47,6 +52,10 @@ import java.util.Objects; public class NotificationEntry { static final String TAG = "NotificationEntry"; + // Copied from hidden definitions in Notification.TvExtender + private static final String EXTRA_TV_EXTENDER = "android.tv.EXTENSIONS"; + + private final Context mContext; private final StatusBarNotification mSbn; private final IPackageManager mPackageManager; private int mTargetSdkVersion = Build.VERSION_CODES.N_MR1; @@ -60,9 +69,10 @@ public class NotificationEntry { private final Object mLock = new Object(); - public NotificationEntry(IPackageManager packageManager, StatusBarNotification sbn, - NotificationChannel channel, SmsHelper smsHelper) { - mSbn = sbn; + public NotificationEntry(Context applicationContext, IPackageManager packageManager, + StatusBarNotification sbn, NotificationChannel channel, SmsHelper smsHelper) { + mContext = applicationContext; + mSbn = cloneStatusBarNotificationLight(sbn); mChannel = channel; mPackageManager = packageManager; mPreChannelsNotification = isPreChannelsNotification(); @@ -71,6 +81,66 @@ public class NotificationEntry { mSmsHelper = smsHelper; } + /** Adapted from {@code Notification.lightenPayload}. */ + @SuppressWarnings("nullness") + private static void lightenNotificationPayload(Notification notification) { + notification.tickerView = null; + notification.contentView = null; + notification.bigContentView = null; + notification.headsUpContentView = null; + notification.largeIcon = null; + if (notification.extras != null && !notification.extras.isEmpty()) { + final Set<String> keyset = notification.extras.keySet(); + final int keysetSize = keyset.size(); + final String[] keys = keyset.toArray(new String[keysetSize]); + for (int i = 0; i < keysetSize; i++) { + final String key = keys[i]; + if (EXTRA_TV_EXTENDER.equals(key) + || Notification.EXTRA_MESSAGES.equals(key) + || Notification.EXTRA_MESSAGING_PERSON.equals(key) + || Notification.EXTRA_PEOPLE_LIST.equals(key)) { + continue; + } + final Object obj = notification.extras.get(key); + if (obj != null + && (obj instanceof Parcelable + || obj instanceof Parcelable[] + || obj instanceof SparseArray + || obj instanceof ArrayList)) { + notification.extras.remove(key); + } + } + } + } + + /** An interpretation of {@code Notification.cloneInto} with heavy=false. */ + private Notification cloneNotificationLight(Notification notification) { + // We can't just use clone() here because the only way to remove the icons is with the + // builder, which we can only create with a Context. + Notification lightNotification = + Notification.Builder.recoverBuilder(mContext, notification) + .setSmallIcon(0) + .setLargeIcon((Icon) null) + .build(); + lightenNotificationPayload(lightNotification); + return lightNotification; + } + + /** Adapted from {@code StatusBarNotification.cloneLight}. */ + public StatusBarNotification cloneStatusBarNotificationLight(StatusBarNotification sbn) { + return new StatusBarNotification( + sbn.getPackageName(), + sbn.getOpPkg(), + sbn.getId(), + sbn.getTag(), + sbn.getUid(), + /*initialPid=*/ 0, + /*score=*/ 0, + cloneNotificationLight(sbn.getNotification()), + sbn.getUser(), + sbn.getPostTime()); + } + private boolean isPreChannelsNotification() { try { ApplicationInfo info = mPackageManager.getApplicationInfo( diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/AgingHelperTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/AgingHelperTest.java index 3db275acfd06..a87d57cf872b 100644 --- a/packages/ExtServices/tests/src/android/ext/services/notification/AgingHelperTest.java +++ b/packages/ExtServices/tests/src/android/ext/services/notification/AgingHelperTest.java @@ -102,7 +102,8 @@ public class AgingHelperTest { public void testNoSnoozingOnPost() { NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH); StatusBarNotification sbn = generateSbn(channel.getId()); - NotificationEntry entry = new NotificationEntry(mPackageManager, sbn, channel, mSmsHelper); + NotificationEntry entry = new NotificationEntry( + mContext, mPackageManager, sbn, channel, mSmsHelper); mAgingHelper.onNotificationPosted(entry); @@ -113,7 +114,8 @@ public class AgingHelperTest { public void testPostResetsSnooze() { NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH); StatusBarNotification sbn = generateSbn(channel.getId()); - NotificationEntry entry = new NotificationEntry(mPackageManager, sbn, channel, mSmsHelper); + NotificationEntry entry = new NotificationEntry( + mContext, mPackageManager, sbn, channel, mSmsHelper); mAgingHelper.onNotificationPosted(entry); @@ -124,7 +126,8 @@ public class AgingHelperTest { public void testSnoozingOnSeen() { NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH); StatusBarNotification sbn = generateSbn(channel.getId()); - NotificationEntry entry = new NotificationEntry(mPackageManager, sbn, channel, mSmsHelper); + NotificationEntry entry = new NotificationEntry( + mContext, mPackageManager, sbn, channel, mSmsHelper); entry.setSeen(); when(mCategorizer.getCategory(entry)).thenReturn(NotificationCategorizer.CATEGORY_PEOPLE); @@ -137,7 +140,8 @@ public class AgingHelperTest { NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH); channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE); StatusBarNotification sbn = generateSbn(channel.getId()); - NotificationEntry entry = new NotificationEntry(mPackageManager, sbn, channel, mSmsHelper); + NotificationEntry entry = new NotificationEntry( + mContext, mPackageManager, sbn, channel, mSmsHelper); when(mCategorizer.getCategory(entry)).thenReturn(NotificationCategorizer.CATEGORY_PEOPLE); mAgingHelper.onNotificationSeen(entry); diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantSettingsTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantSettingsTest.java index ad52e2b9504b..5c877de23816 100644 --- a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantSettingsTest.java +++ b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantSettingsTest.java @@ -96,10 +96,7 @@ public class AssistantSettingsTest { SystemUiDeviceConfigFlags.NAS_GENERATE_REPLIES, "false", false /* makeDefault */)); - mAssistantSettings.onDeviceConfigPropertyChanged( - DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.NAS_GENERATE_REPLIES, - "false"); + mAssistantSettings.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI); assertFalse(mAssistantSettings.mGenerateReplies); } @@ -111,10 +108,7 @@ public class AssistantSettingsTest { SystemUiDeviceConfigFlags.NAS_GENERATE_REPLIES, "true", false /* makeDefault */)); - mAssistantSettings.onDeviceConfigPropertyChanged( - DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.NAS_GENERATE_REPLIES, - "true"); + mAssistantSettings.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI); assertTrue(mAssistantSettings.mGenerateReplies); } @@ -126,10 +120,7 @@ public class AssistantSettingsTest { SystemUiDeviceConfigFlags.NAS_GENERATE_REPLIES, "false", false /* makeDefault */)); - mAssistantSettings.onDeviceConfigPropertyChanged( - DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.NAS_GENERATE_REPLIES, - "false"); + mAssistantSettings.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI); assertFalse(mAssistantSettings.mGenerateReplies); @@ -138,10 +129,7 @@ public class AssistantSettingsTest { SystemUiDeviceConfigFlags.NAS_GENERATE_REPLIES, null, false /* makeDefault */)); - mAssistantSettings.onDeviceConfigPropertyChanged( - DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.NAS_GENERATE_REPLIES, - null); + mAssistantSettings.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI); // Go back to the default value. assertTrue(mAssistantSettings.mGenerateReplies); @@ -154,10 +142,7 @@ public class AssistantSettingsTest { SystemUiDeviceConfigFlags.NAS_GENERATE_ACTIONS, "false", false /* makeDefault */)); - mAssistantSettings.onDeviceConfigPropertyChanged( - DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.NAS_GENERATE_ACTIONS, - "false"); + mAssistantSettings.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI); assertFalse(mAssistantSettings.mGenerateActions); } @@ -169,10 +154,7 @@ public class AssistantSettingsTest { SystemUiDeviceConfigFlags.NAS_GENERATE_ACTIONS, "true", false /* makeDefault */)); - mAssistantSettings.onDeviceConfigPropertyChanged( - DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.NAS_GENERATE_ACTIONS, - "true"); + mAssistantSettings.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI); assertTrue(mAssistantSettings.mGenerateActions); } @@ -184,10 +166,7 @@ public class AssistantSettingsTest { SystemUiDeviceConfigFlags.NAS_GENERATE_ACTIONS, "false", false /* makeDefault */)); - mAssistantSettings.onDeviceConfigPropertyChanged( - DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.NAS_GENERATE_ACTIONS, - "false"); + mAssistantSettings.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI); assertFalse(mAssistantSettings.mGenerateActions); @@ -196,10 +175,7 @@ public class AssistantSettingsTest { SystemUiDeviceConfigFlags.NAS_GENERATE_ACTIONS, null, false /* makeDefault */)); - mAssistantSettings.onDeviceConfigPropertyChanged( - DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.NAS_GENERATE_ACTIONS, - null); + mAssistantSettings.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI); // Go back to the default value. assertTrue(mAssistantSettings.mGenerateActions); @@ -212,10 +188,7 @@ public class AssistantSettingsTest { SystemUiDeviceConfigFlags.NAS_MAX_MESSAGES_TO_EXTRACT, "10", false /* makeDefault */)); - mAssistantSettings.onDeviceConfigPropertyChanged( - DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.NAS_MAX_MESSAGES_TO_EXTRACT, - "10"); + mAssistantSettings.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI); assertEquals(10, mAssistantSettings.mMaxMessagesToExtract); } @@ -227,20 +200,14 @@ public class AssistantSettingsTest { SystemUiDeviceConfigFlags.NAS_MAX_SUGGESTIONS, "5", false /* makeDefault */)); - mAssistantSettings.onDeviceConfigPropertyChanged( - DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.NAS_MAX_SUGGESTIONS, - "5"); + mAssistantSettings.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI); assertEquals(5, mAssistantSettings.mMaxSuggestions); } @Test public void testMaxSuggestionsEmpty() { - mAssistantSettings.onDeviceConfigPropertyChanged( - DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.NAS_MAX_SUGGESTIONS, - ""); + mAssistantSettings.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI); assertEquals(DEFAULT_MAX_SUGGESTIONS, mAssistantSettings.mMaxSuggestions); } diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java index ee29bc594801..012dcc01fe6f 100644 --- a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java +++ b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java @@ -468,8 +468,10 @@ public class AssistantTest extends ServiceTestCase<Assistant> { @Test public void testAssistantNeverIncreasesImportanceWhenSuggestingSilent() throws Exception { StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C3, "min notif!", null); - Adjustment adjust = mAssistant.createEnqueuedNotificationAdjustment(new NotificationEntry( - mPackageManager, sbn, P1C3, mSmsHelper), new ArrayList<>(), new ArrayList<>()); + Adjustment adjust = mAssistant.createEnqueuedNotificationAdjustment( + new NotificationEntry(mContext, mPackageManager, sbn, P1C3, mSmsHelper), + new ArrayList<>(), + new ArrayList<>()); assertEquals(IMPORTANCE_MIN, adjust.getSignals().getInt(Adjustment.KEY_IMPORTANCE)); } } diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/NotificationEntryTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/NotificationEntryTest.java index f51e911cbe97..c02607927b1b 100644 --- a/packages/ExtServices/tests/src/android/ext/services/notification/NotificationEntryTest.java +++ b/packages/ExtServices/tests/src/android/ext/services/notification/NotificationEntryTest.java @@ -24,6 +24,7 @@ import static android.media.AudioAttributes.USAGE_ALARM; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; +import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.when; @@ -34,6 +35,8 @@ import android.app.Person; import android.content.ComponentName; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; +import android.graphics.Bitmap; +import android.graphics.drawable.Icon; import android.media.AudioAttributes; import android.os.Build; import android.os.Process; @@ -41,9 +44,6 @@ import android.os.UserHandle; import android.service.notification.StatusBarNotification; import android.testing.TestableContext; -import androidx.test.InstrumentationRegistry; -import androidx.test.runner.AndroidJUnit4; - import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -53,6 +53,9 @@ import org.mockito.MockitoAnnotations; import java.util.ArrayList; +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + @RunWith(AndroidJUnit4.class) public class NotificationEntryTest { private String mPkg = "pkg"; @@ -113,7 +116,8 @@ public class NotificationEntryTest { people.add(new Person.Builder().setKey("mailto:testing@android.com").build()); sbn.getNotification().extras.putParcelableArrayList(Notification.EXTRA_PEOPLE_LIST, people); - NotificationEntry entry = new NotificationEntry(mPackageManager, sbn, channel, mSmsHelper); + NotificationEntry entry = new NotificationEntry( + mContext, mPackageManager, sbn, channel, mSmsHelper); assertTrue(entry.involvesPeople()); } @@ -121,7 +125,8 @@ public class NotificationEntryTest { public void testNotPerson() { NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH); StatusBarNotification sbn = generateSbn(channel.getId()); - NotificationEntry entry = new NotificationEntry(mPackageManager, sbn, channel, mSmsHelper); + NotificationEntry entry = new NotificationEntry( + mContext, mPackageManager, sbn, channel, mSmsHelper); assertFalse(entry.involvesPeople()); } @@ -129,7 +134,8 @@ public class NotificationEntryTest { public void testHasPerson_matchesDefaultSmsApp() { NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH); StatusBarNotification sbn = generateSbn(channel.getId(), DEFAULT_SMS_PACKAGE_NAME); - NotificationEntry entry = new NotificationEntry(mPackageManager, sbn, channel, mSmsHelper); + NotificationEntry entry = new NotificationEntry( + mContext, mPackageManager, sbn, channel, mSmsHelper); assertTrue(entry.involvesPeople()); } @@ -137,7 +143,8 @@ public class NotificationEntryTest { public void testHasPerson_doesntMatchDefaultSmsApp() { NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH); StatusBarNotification sbn = generateSbn(channel.getId(), "abc"); - NotificationEntry entry = new NotificationEntry(mPackageManager, sbn, channel, mSmsHelper); + NotificationEntry entry = new NotificationEntry( + mContext, mPackageManager, sbn, channel, mSmsHelper); assertFalse(entry.involvesPeople()); } @@ -148,8 +155,8 @@ public class NotificationEntryTest { Notification n = new Notification.Builder(mContext, channel.getId()) .setStyle(new Notification.InboxStyle()) .build(); - NotificationEntry entry = - new NotificationEntry(mPackageManager, generateSbn(n), channel, mSmsHelper); + NotificationEntry entry = new NotificationEntry( + mContext, mPackageManager, generateSbn(n), channel, mSmsHelper); assertTrue(entry.hasStyle(Notification.InboxStyle.class)); } @@ -160,8 +167,8 @@ public class NotificationEntryTest { Notification n = new Notification.Builder(mContext, channel.getId()) .setStyle(new Notification.MessagingStyle("")) .build(); - NotificationEntry entry = - new NotificationEntry(mPackageManager, generateSbn(n), channel, mSmsHelper); + NotificationEntry entry = new NotificationEntry( + mContext, mPackageManager, generateSbn(n), channel, mSmsHelper); assertTrue(entry.hasStyle(Notification.MessagingStyle.class)); } @@ -172,8 +179,8 @@ public class NotificationEntryTest { Notification n = new Notification.Builder(mContext, channel.getId()) .setStyle(new Notification.BigPictureStyle()) .build(); - NotificationEntry entry = - new NotificationEntry(mPackageManager, generateSbn(n), channel, mSmsHelper); + NotificationEntry entry = new NotificationEntry( + mContext, mPackageManager, generateSbn(n), channel, mSmsHelper); assertFalse(entry.hasStyle(Notification.InboxStyle.class)); assertFalse(entry.hasStyle(Notification.MessagingStyle.class)); } @@ -184,7 +191,7 @@ public class NotificationEntryTest { channel.setSound(null, new AudioAttributes.Builder().setUsage(USAGE_ALARM).build()); NotificationEntry entry = new NotificationEntry( - mPackageManager, generateSbn(channel.getId()), channel, mSmsHelper); + mContext, mPackageManager, generateSbn(channel.getId()), channel, mSmsHelper); assertTrue(entry.isAudioAttributesUsage(USAGE_ALARM)); } @@ -193,7 +200,7 @@ public class NotificationEntryTest { public void testIsNotAudioAttributes() { NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH); NotificationEntry entry = new NotificationEntry( - mPackageManager, generateSbn(channel.getId()), channel, mSmsHelper); + mContext, mPackageManager, generateSbn(channel.getId()), channel, mSmsHelper); assertFalse(entry.isAudioAttributesUsage(USAGE_ALARM)); } @@ -205,8 +212,8 @@ public class NotificationEntryTest { Notification n = new Notification.Builder(mContext, channel.getId()) .setCategory(Notification.CATEGORY_EMAIL) .build(); - NotificationEntry entry = - new NotificationEntry(mPackageManager, generateSbn(n), channel, mSmsHelper); + NotificationEntry entry = new NotificationEntry( + mContext, mPackageManager, generateSbn(n), channel, mSmsHelper); assertTrue(entry.isCategory(Notification.CATEGORY_EMAIL)); assertFalse(entry.isCategory(Notification.CATEGORY_MESSAGE)); @@ -219,8 +226,8 @@ public class NotificationEntryTest { Notification n = new Notification.Builder(mContext, channel.getId()) .setFlag(FLAG_FOREGROUND_SERVICE, true) .build(); - NotificationEntry entry = - new NotificationEntry(mPackageManager, generateSbn(n), channel, mSmsHelper); + NotificationEntry entry = new NotificationEntry( + mContext, mPackageManager, generateSbn(n), channel, mSmsHelper); assertTrue(entry.isOngoing()); } @@ -232,9 +239,28 @@ public class NotificationEntryTest { Notification n = new Notification.Builder(mContext, channel.getId()) .setFlag(FLAG_CAN_COLORIZE, true) .build(); - NotificationEntry entry = - new NotificationEntry(mPackageManager, generateSbn(n), channel, mSmsHelper); + NotificationEntry entry = new NotificationEntry( + mContext, mPackageManager, generateSbn(n), channel, mSmsHelper); assertFalse(entry.isOngoing()); } + + @Test + public void testShrinkNotification() { + Notification n = new Notification.Builder(mContext, "") + .setLargeIcon(Icon.createWithResource( + mContext, android.R.drawable.alert_dark_frame)) + .setSmallIcon(android.R.drawable.sym_def_app_icon) + .build(); + n.largeIcon = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565); + NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH); + + NotificationEntry entry = new NotificationEntry( + mContext, mPackageManager, generateSbn(n), channel, mSmsHelper); + + assertNull(entry.getNotification().getSmallIcon()); + assertNull(entry.getNotification().getLargeIcon()); + assertNull(entry.getNotification().largeIcon); + assertNull(entry.getNotification().extras.getParcelable(Notification.EXTRA_LARGE_ICON)); + } } diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java index dfa1ea0aac90..69abe87cd429 100644 --- a/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java +++ b/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java @@ -46,9 +46,6 @@ import android.view.textclassifier.TextClassificationManager; import android.view.textclassifier.TextClassifier; import android.view.textclassifier.TextClassifierEvent; -import androidx.test.InstrumentationRegistry; -import androidx.test.runner.AndroidJUnit4; - import com.google.common.truth.FailureStrategy; import com.google.common.truth.Subject; import com.google.common.truth.SubjectFactory; @@ -71,9 +68,11 @@ import java.util.Objects; import javax.annotation.Nullable; +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + @RunWith(AndroidJUnit4.class) public class SmartActionsHelperTest { - private static final String NOTIFICATION_KEY = "key"; private static final String RESULT_ID = "id"; private static final float SCORE = 0.7f; private static final CharSequence SMART_REPLY = "Home"; @@ -88,7 +87,6 @@ public class SmartActionsHelperTest { IPackageManager mIPackageManager; @Mock private TextClassifier mTextClassifier; - @Mock private StatusBarNotification mStatusBarNotification; @Mock private SmsHelper mSmsHelper; @@ -108,9 +106,6 @@ public class SmartActionsHelperTest { when(mTextClassifier.suggestConversationActions(any(ConversationActions.Request.class))) .thenReturn(new ConversationActions(Arrays.asList(REPLY_ACTION), RESULT_ID)); - when(mStatusBarNotification.getPackageName()).thenReturn("random.app"); - when(mStatusBarNotification.getUser()).thenReturn(Process.myUserHandle()); - when(mStatusBarNotification.getKey()).thenReturn(NOTIFICATION_KEY); mNotificationBuilder = new Notification.Builder(mContext, "channel"); mSettings = AssistantSettings.createForTesting( null, null, Process.myUserHandle().getIdentifier(), null); @@ -119,10 +114,15 @@ public class SmartActionsHelperTest { mSmartActionsHelper = new SmartActionsHelper(mContext, mSettings); } + private void setStatusBarNotification(Notification n) { + mStatusBarNotification = new StatusBarNotification("random.app", "random.app", 0, + "tag", Process.myUid(), Process.myPid(), n, Process.myUserHandle(), null, 0); + } + @Test public void testSuggest_notMessageNotification() { Notification notification = mNotificationBuilder.setContentText(MESSAGE).build(); - when(mStatusBarNotification.getNotification()).thenReturn(notification); + setStatusBarNotification(notification); mSmartActionsHelper.suggest(createNotificationEntry()); @@ -137,7 +137,7 @@ public class SmartActionsHelperTest { .setContentText(MESSAGE) .setCategory(Notification.CATEGORY_MESSAGE) .build(); - when(mStatusBarNotification.getNotification()).thenReturn(notification); + setStatusBarNotification(notification); ConversationActions.Request request = runSuggestAndCaptureRequest(); @@ -154,7 +154,7 @@ public class SmartActionsHelperTest { mSettings.mGenerateActions = false; mSettings.mGenerateReplies = false; Notification notification = createMessageNotification(); - when(mStatusBarNotification.getNotification()).thenReturn(notification); + setStatusBarNotification(notification); mSmartActionsHelper.suggest(createNotificationEntry()); @@ -167,7 +167,7 @@ public class SmartActionsHelperTest { mSettings.mGenerateReplies = true; mSettings.mGenerateActions = false; Notification notification = createMessageNotification(); - when(mStatusBarNotification.getNotification()).thenReturn(notification); + setStatusBarNotification(notification); ConversationActions.Request request = runSuggestAndCaptureRequest(); @@ -184,7 +184,7 @@ public class SmartActionsHelperTest { mSettings.mGenerateReplies = false; mSettings.mGenerateActions = true; Notification notification = createMessageNotification(); - when(mStatusBarNotification.getNotification()).thenReturn(notification); + setStatusBarNotification(notification); ConversationActions.Request request = runSuggestAndCaptureRequest(); @@ -200,7 +200,7 @@ public class SmartActionsHelperTest { @Test public void testSuggest_nonMessageStyleMessageNotification() { Notification notification = createMessageNotification(); - when(mStatusBarNotification.getNotification()).thenReturn(notification); + setStatusBarNotification(notification); List<ConversationActions.Message> messages = runSuggestAndCaptureRequest().getConversation(); @@ -233,7 +233,7 @@ public class SmartActionsHelperTest { .setStyle(style) .setActions(createReplyAction()) .build(); - when(mStatusBarNotification.getNotification()).thenReturn(notification); + setStatusBarNotification(notification); List<ConversationActions.Message> messages = runSuggestAndCaptureRequest().getConversation(); @@ -288,7 +288,7 @@ public class SmartActionsHelperTest { .setStyle(style) .setActions(createReplyAction()) .build(); - when(mStatusBarNotification.getNotification()).thenReturn(notification); + setStatusBarNotification(notification); mSmartActionsHelper.suggest(createNotificationEntry()); @@ -307,7 +307,7 @@ public class SmartActionsHelperTest { .setStyle(style) .setActions(createReplyAction()) .build(); - when(mStatusBarNotification.getNotification()).thenReturn(notification); + setStatusBarNotification(notification); mSmartActionsHelper.suggest(createNotificationEntry()); @@ -318,11 +318,11 @@ public class SmartActionsHelperTest { @Test public void testOnSuggestedReplySent() { Notification notification = createMessageNotification(); - when(mStatusBarNotification.getNotification()).thenReturn(notification); + setStatusBarNotification(notification); mSmartActionsHelper.suggest(createNotificationEntry()); - mSmartActionsHelper.onSuggestedReplySent( - NOTIFICATION_KEY, SMART_REPLY, NotificationAssistantService.SOURCE_FROM_ASSISTANT); + mSmartActionsHelper.onSuggestedReplySent(mStatusBarNotification.getKey(), SMART_REPLY, + NotificationAssistantService.SOURCE_FROM_ASSISTANT); ArgumentCaptor<TextClassifierEvent> argumentCaptor = ArgumentCaptor.forClass(TextClassifierEvent.class); @@ -338,7 +338,7 @@ public class SmartActionsHelperTest { @Test public void testOnSuggestedReplySent_anotherNotification() { Notification notification = createMessageNotification(); - when(mStatusBarNotification.getNotification()).thenReturn(notification); + setStatusBarNotification(notification); mSmartActionsHelper.suggest(createNotificationEntry()); mSmartActionsHelper.onSuggestedReplySent( @@ -353,11 +353,11 @@ public class SmartActionsHelperTest { when(mTextClassifier.suggestConversationActions(any(ConversationActions.Request.class))) .thenReturn(new ConversationActions(Collections.singletonList(REPLY_ACTION), null)); Notification notification = createMessageNotification(); - when(mStatusBarNotification.getNotification()).thenReturn(notification); + setStatusBarNotification(notification); mSmartActionsHelper.suggest(createNotificationEntry()); - mSmartActionsHelper.onSuggestedReplySent( - NOTIFICATION_KEY, SMART_REPLY, NotificationAssistantService.SOURCE_FROM_ASSISTANT); + mSmartActionsHelper.onSuggestedReplySent(mStatusBarNotification.getKey(), SMART_REPLY, + NotificationAssistantService.SOURCE_FROM_ASSISTANT); verify(mTextClassifier, never()).onTextClassifierEvent(any(TextClassifierEvent.class)); } @@ -365,10 +365,10 @@ public class SmartActionsHelperTest { @Test public void testOnNotificationDirectReply() { Notification notification = createMessageNotification(); - when(mStatusBarNotification.getNotification()).thenReturn(notification); + setStatusBarNotification(notification); mSmartActionsHelper.suggest(createNotificationEntry()); - mSmartActionsHelper.onNotificationDirectReplied(NOTIFICATION_KEY); + mSmartActionsHelper.onNotificationDirectReplied(mStatusBarNotification.getKey()); ArgumentCaptor<TextClassifierEvent> argumentCaptor = ArgumentCaptor.forClass(TextClassifierEvent.class); @@ -381,7 +381,7 @@ public class SmartActionsHelperTest { @Test public void testOnNotificationExpansionChanged() { Notification notification = createMessageNotification(); - when(mStatusBarNotification.getNotification()).thenReturn(notification); + setStatusBarNotification(notification); mSmartActionsHelper.suggest(createNotificationEntry()); mSmartActionsHelper.onNotificationExpansionChanged(createNotificationEntry(), true); @@ -397,7 +397,7 @@ public class SmartActionsHelperTest { @Test public void testOnNotificationsSeen_notExpanded() { Notification notification = createMessageNotification(); - when(mStatusBarNotification.getNotification()).thenReturn(notification); + setStatusBarNotification(notification); mSmartActionsHelper.suggest(createNotificationEntry()); mSmartActionsHelper.onNotificationExpansionChanged(createNotificationEntry(), false); @@ -409,7 +409,7 @@ public class SmartActionsHelperTest { @Test public void testOnNotifications_expanded() { Notification notification = createMessageNotification(); - when(mStatusBarNotification.getNotification()).thenReturn(notification); + setStatusBarNotification(notification); mSmartActionsHelper.suggest(createNotificationEntry()); mSmartActionsHelper.onNotificationExpansionChanged(createNotificationEntry(), true); @@ -438,7 +438,7 @@ public class SmartActionsHelperTest { Collections.singletonList(conversationAction), null)); Notification notification = createMessageNotification(); - when(mStatusBarNotification.getNotification()).thenReturn(notification); + setStatusBarNotification(notification); SmartActionsHelper.SmartSuggestions suggestions = mSmartActionsHelper.suggest(createNotificationEntry()); @@ -477,7 +477,8 @@ public class SmartActionsHelperTest { private NotificationEntry createNotificationEntry() { NotificationChannel channel = new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_DEFAULT); - return new NotificationEntry(mIPackageManager, mStatusBarNotification, channel, mSmsHelper); + return new NotificationEntry( + mContext, mIPackageManager, mStatusBarNotification, channel, mSmsHelper); } private Notification createMessageNotification() { diff --git a/packages/NetworkStack/Android.bp b/packages/NetworkStack/Android.bp index e0bb862c5362..62de2ba45455 100644 --- a/packages/NetworkStack/Android.bp +++ b/packages/NetworkStack/Android.bp @@ -56,15 +56,24 @@ cc_library_shared { srcs: [ "jni/network_stack_utils_jni.cpp" ], - + sdk_version: "current", shared_libs: [ "liblog", - "libcutils", - "libnativehelper", - ], - static_libs: [ - "libpcap", + "libnativehelper_compat_libc++", ], + + // We cannot use plain "libc++" here to link libc++ dynamically because it results in: + // java.lang.UnsatisfiedLinkError: dlopen failed: library "libc++_shared.so" not found + // even if "libc++" is added into jni_libs below. Adding "libc++_shared" into jni_libs doesn't + // build because soong complains of: + // module NetworkStack missing dependencies: libc++_shared + // + // So, link libc++ statically. This means that we also need to ensure that all the C++ libraries + // we depend on do not dynamically link libc++. This is currently the case, because liblog is + // C-only and libnativehelper_compat_libc also uses stl: "c++_static". + // + // TODO: find a better solution for this in R. + stl: "c++_static", cflags: [ "-Wall", "-Werror", @@ -79,7 +88,10 @@ java_defaults { static_libs: [ "NetworkStackBase", ], - jni_libs: ["libnetworkstackutilsjni"], + jni_libs: [ + "libnativehelper_compat_libc++", + "libnetworkstackutilsjni", + ], // Resources already included in NetworkStackBase resource_dirs: [], jarjar_rules: "jarjar-rules-shared.txt", diff --git a/packages/NetworkStack/AndroidManifestBase.xml b/packages/NetworkStack/AndroidManifestBase.xml index d00a55143605..3da566f88659 100644 --- a/packages/NetworkStack/AndroidManifestBase.xml +++ b/packages/NetworkStack/AndroidManifestBase.xml @@ -24,6 +24,7 @@ android:label="NetworkStack" android:defaultToDeviceProtectedStorage="true" android:directBootAware="true" + android:persistent="true" android:usesCleartextTraffic="true"> <service android:name="com.android.server.connectivity.ipmemorystore.RegularMaintenanceJobService" diff --git a/packages/NetworkStack/jni/network_stack_utils_jni.cpp b/packages/NetworkStack/jni/network_stack_utils_jni.cpp index 5544eaa809e7..f2ba5757ed80 100644 --- a/packages/NetworkStack/jni/network_stack_utils_jni.cpp +++ b/packages/NetworkStack/jni/network_stack_utils_jni.cpp @@ -31,7 +31,7 @@ #include <string> #include <nativehelper/JNIHelp.h> -#include <utils/Log.h> +#include <android/log.h> namespace android { constexpr const char NETWORKSTACKUTILS_PKG_NAME[] = "android/net/util/NetworkStackUtils"; @@ -249,7 +249,7 @@ static const JNINativeMethod gNetworkStackUtilsMethods[] = { extern "C" jint JNI_OnLoad(JavaVM* vm, void*) { JNIEnv *env; if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { - ALOGE("ERROR: GetEnv failed"); + __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "ERROR: GetEnv failed"); return JNI_ERR; } @@ -261,4 +261,4 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void*) { return JNI_VERSION_1_6; } -}; // namespace android
\ No newline at end of file +}; // namespace android diff --git a/packages/NetworkStack/src/android/net/apf/ApfFilter.java b/packages/NetworkStack/src/android/net/apf/ApfFilter.java index 663e2f10ffe2..359c85983a94 100644 --- a/packages/NetworkStack/src/android/net/apf/ApfFilter.java +++ b/packages/NetworkStack/src/android/net/apf/ApfFilter.java @@ -39,6 +39,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.net.LinkAddress; import android.net.LinkProperties; +import android.net.NattKeepalivePacketDataParcelable; import android.net.TcpKeepalivePacketDataParcelable; import android.net.apf.ApfGenerator.IllegalInstructionException; import android.net.apf.ApfGenerator.Register; @@ -1691,13 +1692,13 @@ public class ApfFilter { } /** - * Add keepalive ack packet filter. + * Add TCP keepalive ack packet filter. * This will add a filter to drop acks to the keepalive packet passed as an argument. * * @param slot The index used to access the filter. * @param sentKeepalivePacket The attributes of the sent keepalive packet. */ - public synchronized void addKeepalivePacketFilter(final int slot, + public synchronized void addTcpKeepalivePacketFilter(final int slot, final TcpKeepalivePacketDataParcelable sentKeepalivePacket) { log("Adding keepalive ack(" + slot + ")"); if (null != mKeepaliveAcks.get(slot)) { @@ -1711,6 +1712,18 @@ public class ApfFilter { } /** + * Add NATT keepalive packet filter. + * This will add a filter to drop NATT keepalive packet which is passed as an argument. + * + * @param slot The index used to access the filter. + * @param sentKeepalivePacket The attributes of the sent keepalive packet. + */ + public synchronized void addNattKeepalivePacketFilter(final int slot, + final NattKeepalivePacketDataParcelable sentKeepalivePacket) { + Log.e(TAG, "APF add NATT keepalive filter is not implemented"); + } + + /** * Remove keepalive packet filter. * * @param slot The index used to access the filter. diff --git a/packages/NetworkStack/src/android/net/ip/IpClient.java b/packages/NetworkStack/src/android/net/ip/IpClient.java index 96e09fafb6b6..dc74c041c35a 100644 --- a/packages/NetworkStack/src/android/net/ip/IpClient.java +++ b/packages/NetworkStack/src/android/net/ip/IpClient.java @@ -29,6 +29,7 @@ import android.net.INetd; import android.net.IpPrefix; import android.net.LinkAddress; import android.net.LinkProperties; +import android.net.NattKeepalivePacketDataParcelable; import android.net.NetworkStackIpMemoryStore; import android.net.ProvisioningConfigurationParcelable; import android.net.ProxyInfo; @@ -371,6 +372,10 @@ public class IpClient extends StateMachine { private boolean mMulticastFiltering; private long mStartTimeMillis; + /* This must match the definition in KeepaliveTracker.KeepaliveInfo */ + private static final int TYPE_NATT = 1; + private static final int TYPE_TCP = 2; + /** * Reading the snapshot is an asynchronous operation initiated by invoking * Callback.startReadPacketFilter() and completed when the WiFi Service responds with an @@ -553,6 +558,11 @@ public class IpClient extends StateMachine { IpClient.this.addKeepalivePacketFilter(slot, pkt); } @Override + public void addNattKeepalivePacketFilter(int slot, NattKeepalivePacketDataParcelable pkt) { + checkNetworkStackCallingPermission(); + IpClient.this.addNattKeepalivePacketFilter(slot, pkt); + } + @Override public void removeKeepalivePacketFilter(int slot) { checkNetworkStackCallingPermission(); IpClient.this.removeKeepalivePacketFilter(slot); @@ -691,11 +701,20 @@ public class IpClient extends StateMachine { } /** - * Called by WifiStateMachine to add keepalive packet filter before setting up + * Called by WifiStateMachine to add TCP keepalive packet filter before setting up * keepalive offload. */ public void addKeepalivePacketFilter(int slot, @NonNull TcpKeepalivePacketDataParcelable pkt) { - sendMessage(CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF, slot, 0 /* Unused */, pkt); + sendMessage(CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF, slot, TYPE_TCP, pkt); + } + + /** + * Called by WifiStateMachine to add NATT keepalive packet filter before setting up + * keepalive offload. + */ + public void addNattKeepalivePacketFilter(int slot, + @NonNull NattKeepalivePacketDataParcelable pkt) { + sendMessage(CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF, slot, TYPE_NATT, pkt); } /** @@ -1607,9 +1626,16 @@ public class IpClient extends StateMachine { case CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF: { final int slot = msg.arg1; + final int type = msg.arg2; + if (mApfFilter != null) { - mApfFilter.addKeepalivePacketFilter(slot, - (TcpKeepalivePacketDataParcelable) msg.obj); + if (type == TYPE_NATT) { + mApfFilter.addNattKeepalivePacketFilter(slot, + (NattKeepalivePacketDataParcelable) msg.obj); + } else { + mApfFilter.addTcpKeepalivePacketFilter(slot, + (TcpKeepalivePacketDataParcelable) msg.obj); + } } break; } diff --git a/packages/NetworkStack/tests/Android.bp b/packages/NetworkStack/tests/Android.bp index fe3c1e8eb3e5..039f6bf791fb 100644 --- a/packages/NetworkStack/tests/Android.bp +++ b/packages/NetworkStack/tests/Android.bp @@ -56,6 +56,7 @@ android_test { "liblog", "liblzma", "libnativehelper", + "libnativehelper_compat_libc++", "libnetworkstacktestsjni", "libnetworkstackutilsjni", "libpackagelistparser", @@ -99,5 +100,4 @@ cc_library_shared { "libapf", "libpcap", ], - } diff --git a/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java b/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java index a0e508f130a5..93ab3be28fc7 100644 --- a/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java +++ b/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java @@ -1553,7 +1553,7 @@ public class ApfTest { parcel.seq = seqNum; parcel.ack = ackNum; - apfFilter.addKeepalivePacketFilter(slot1, parcel); + apfFilter.addTcpKeepalivePacketFilter(slot1, parcel); program = cb.getApfProgram(); // Verify IPv4 keepalive ack packet is dropped @@ -1592,7 +1592,7 @@ public class ApfTest { ipv6Parcel.seq = seqNum; ipv6Parcel.ack = ackNum; - apfFilter.addKeepalivePacketFilter(slot1, ipv6Parcel); + apfFilter.addTcpKeepalivePacketFilter(slot1, ipv6Parcel); program = cb.getApfProgram(); // Verify IPv6 keepalive ack packet is dropped @@ -1614,8 +1614,8 @@ public class ApfTest { apfFilter.removeKeepalivePacketFilter(slot1); // Verify multiple filters - apfFilter.addKeepalivePacketFilter(slot1, parcel); - apfFilter.addKeepalivePacketFilter(slot2, ipv6Parcel); + apfFilter.addTcpKeepalivePacketFilter(slot1, parcel); + apfFilter.addTcpKeepalivePacketFilter(slot2, ipv6Parcel); program = cb.getApfProgram(); // Verify IPv4 keepalive ack packet is dropped diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java index 0ffd471e64af..05c2f24001e8 100644 --- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java +++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java @@ -174,7 +174,7 @@ public class Tile implements Parcelable { packageManager.getResourcesForApplication(mActivityPackage); title = res.getString(mMetaData.getInt(META_DATA_PREFERENCE_TITLE)); } catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) { - Log.d(TAG, "Couldn't find info", e); + Log.w(TAG, "Couldn't find info", e); } } else { title = mMetaData.getString(META_DATA_PREFERENCE_TITLE); @@ -183,26 +183,16 @@ public class Tile implements Parcelable { // Set the preference title to the activity's label if no // meta-data is found if (title == null) { - title = getActivityInfo(context).loadLabel(packageManager); + final ActivityInfo activityInfo = getActivityInfo(context); + if (activityInfo == null) { + return null; + } + title = activityInfo.loadLabel(packageManager); } return title; } /** - * Returns the raw metadata for summary, this is used for comparing 2 summary text without - * loading the real string. - */ - public String getSummaryReference() { - if (mSummaryOverride != null) { - return mSummaryOverride.toString(); - } - if (mMetaData != null && mMetaData.containsKey(META_DATA_PREFERENCE_SUMMARY)) { - return mMetaData.get(META_DATA_PREFERENCE_SUMMARY).toString(); - } - return null; - } - - /** * Overrides the summary. This can happen when injected tile wants to provide dynamic summary. */ public void overrideSummary(CharSequence summaryOverride) { @@ -302,7 +292,7 @@ public class Tile implements Parcelable { if (iconResId != 0) { final Icon icon = Icon.createWithResource(activityInfo.packageName, iconResId); if (isIconTintable(context)) { - final TypedArray a = context.obtainStyledAttributes(new int[] { + final TypedArray a = context.obtainStyledAttributes(new int[]{ android.R.attr.colorControlNormal}); final int tintColor = a.getColor(0, 0); a.recycle(); @@ -357,6 +347,9 @@ public class Tile implements Parcelable { if (infoList != null && !infoList.isEmpty()) { mActivityInfo = infoList.get(0).activityInfo; mMetaData = mActivityInfo.metaData; + } else { + Log.e(TAG, "Cannot find package info for " + + intent.getComponent().flattenToString()); } } return mActivityInfo; diff --git a/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_background.xml b/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_background.xml index 6b45a479836e..c8a80ac57c00 100644 --- a/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_background.xml +++ b/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_background.xml @@ -14,5 +14,5 @@ limitations under the License. --> <selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:alpha="0.24" android:color="@android:color/black" /> + <item android:alpha="0.24" android:color="?android:attr/colorBackground" /> </selector>
\ No newline at end of file diff --git a/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_fill.xml b/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_fill.xml index 5ef085b32dd6..8dcfdbb8cf1e 100644 --- a/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_fill.xml +++ b/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_fill.xml @@ -14,5 +14,5 @@ limitations under the License. --> <selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:alpha="0.47" android:color="@android:color/black" /> + <item android:alpha="0.47" android:color="?android:attr/colorBackground" /> </selector>
\ No newline at end of file diff --git a/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_background.xml b/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_background.xml index 81f63a48fb9a..34de5489a28b 100644 --- a/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_background.xml +++ b/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_background.xml @@ -14,5 +14,5 @@ limitations under the License. --> <selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:alpha="0.3" android:color="@android:color/white" /> + <item android:alpha="0.3" android:color="?android:attr/colorForeground" /> </selector>
\ No newline at end of file diff --git a/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_fill.xml b/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_fill.xml index 61f4e26cf00b..15944c3a2a07 100644 --- a/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_fill.xml +++ b/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_fill.xml @@ -14,5 +14,5 @@ limitations under the License. --> <selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:color="@android:color/white" /> + <item android:color="?android:attr/colorForeground" /> </selector>
\ No newline at end of file diff --git a/packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt b/packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt index 257943e16149..a5b5312707d0 100644 --- a/packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt +++ b/packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt @@ -44,6 +44,8 @@ open class ThemedBatteryDrawable(private val context: Context, frameColor: Int) // doesn't touch the walls private val perimeterPath = Path() private val scaledPerimeter = Path() + private val errorPerimeterPath = Path() + private val scaledErrorPerimeter = Path() // Fill will cover the whole bounding rect of the fillMask, and be masked by the path private val fillMask = Path() private val scaledFill = Path() @@ -242,7 +244,7 @@ open class ThemedBatteryDrawable(private val context: Context, frameColor: Int) } } else if (powerSaveEnabled) { // If power save is enabled draw the perimeter path with colorError - c.drawPath(scaledPerimeter, errorPaint) + c.drawPath(scaledErrorPerimeter, errorPaint) // And draw the plus sign on top of the fill c.drawPath(scaledPlus, errorPaint) } @@ -364,6 +366,7 @@ open class ThemedBatteryDrawable(private val context: Context, frameColor: Int) } perimeterPath.transform(scaleMatrix, scaledPerimeter) + errorPerimeterPath.transform(scaleMatrix, scaledErrorPerimeter) fillMask.transform(scaleMatrix, scaledFill) scaledFill.computeBounds(fillRect, true) boltPath.transform(scaleMatrix, scaledBolt) @@ -382,8 +385,12 @@ open class ThemedBatteryDrawable(private val context: Context, frameColor: Int) val pathString = context.resources.getString( com.android.internal.R.string.config_batterymeterPerimeterPath) perimeterPath.set(PathParser.createPathFromPathData(pathString)) - val b = RectF() - perimeterPath.computeBounds(b, true) + perimeterPath.computeBounds(RectF(), true) + + val errorPathString = context.resources.getString( + com.android.internal.R.string.config_batterymeterErrorPerimeterPath) + errorPerimeterPath.set(PathParser.createPathFromPathData(errorPathString)) + errorPerimeterPath.computeBounds(RectF(), true) val fillMaskString = context.resources.getString( com.android.internal.R.string.config_batterymeterFillMask) diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java index 5f2bc4e1d490..d8172a0f9430 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java @@ -123,7 +123,8 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro /** * Synchronization lock for managing concurrency between main and worker threads. * - * <p>This lock should be held for all modifications to {@link #mInternalAccessPoints}. + * <p>This lock should be held for all modifications to {@link #mInternalAccessPoints} and + * {@link #mScanner}. */ private final Object mLock = new Object(); @@ -168,6 +169,7 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro private static final String WIFI_SECURITY_OWE = "OWE"; private static final String WIFI_SECURITY_SUITE_B_192 = "SUITE_B_192"; + @GuardedBy("mLock") @VisibleForTesting Scanner mScanner; @@ -276,9 +278,11 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro * <p>Sets {@link #mStaleScanResults} to true. */ private void pauseScanning() { - if (mScanner != null) { - mScanner.pause(); - mScanner = null; + synchronized (mLock) { + if (mScanner != null) { + mScanner.pause(); + mScanner = null; + } } mStaleScanResults = true; } @@ -289,12 +293,14 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro * <p>The score cache should be registered before this method is invoked. */ public void resumeScanning() { - if (mScanner == null) { - mScanner = new Scanner(); - } + synchronized (mLock) { + if (mScanner == null) { + mScanner = new Scanner(); + } - if (isWifiEnabled()) { - mScanner.resume(); + if (isWifiEnabled()) { + mScanner.resume(); + } } } @@ -743,7 +749,6 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro } private void updateNetworkInfo(NetworkInfo networkInfo) { - /* Sticky broadcasts can call this when wifi is disabled */ if (!isWifiEnabled()) { clearAccessPointsAndConditionallyUpdate(); @@ -880,18 +885,25 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro * true. */ private void updateWifiState(int state) { + if (isVerboseLoggingEnabled()) { + Log.d(TAG, "updateWifiState: " + state); + } if (state == WifiManager.WIFI_STATE_ENABLED) { - if (mScanner != null) { - // We only need to resume if mScanner isn't null because - // that means we want to be scanning. - mScanner.resume(); + synchronized (mLock) { + if (mScanner != null) { + // We only need to resume if mScanner isn't null because + // that means we want to be scanning. + mScanner.resume(); + } } } else { clearAccessPointsAndConditionallyUpdate(); mLastInfo = null; mLastNetworkInfo = null; - if (mScanner != null) { - mScanner.pause(); + synchronized (mLock) { + if (mScanner != null) { + mScanner.pause(); + } } mStaleScanResults = true; } @@ -919,12 +931,18 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro private int mRetry = 0; void resume() { + if (isVerboseLoggingEnabled()) { + Log.d(TAG, "Scanner resume"); + } if (!hasMessages(MSG_SCAN)) { sendEmptyMessage(MSG_SCAN); } } void pause() { + if (isVerboseLoggingEnabled()) { + Log.d(TAG, "Scanner pause"); + } mRetry = 0; removeMessages(MSG_SCAN); } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java index d0d1e58effaa..5da6205d45bd 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java @@ -23,6 +23,7 @@ import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.shadow.api.Shadow; import org.robolectric.shadows.ShadowPackageManager; +import org.robolectric.util.ReflectionHelpers; @RunWith(RobolectricTestRunner.class) public class TileTest { @@ -164,4 +165,17 @@ public class TileTest { assertThat(tile.mLastUpdateTime).isNotEqualTo(staleTimeStamp); } + + @Test + public void getTitle_noActivity_returnNull() { + final ResolveInfo info = new ResolveInfo(); + info.activityInfo = mActivityInfo; + final ShadowPackageManager spm = Shadow.extract(mContext.getPackageManager()); + spm.removePackage(mActivityInfo.packageName); + + final Tile tile = new Tile(mActivityInfo, "category"); + ReflectionHelpers.setField(tile, "mActivityInfo", null); + + assertThat(tile.getTitle(RuntimeEnvironment.application)).isNull(); + } } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index cd4f6cf26850..c94ee0e56cba 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -797,6 +797,12 @@ class SettingsProtoDumpUtil { Settings.Global.JOB_SCHEDULER_CONSTANTS, GlobalSettingsProto.JOB_SCHEDULER_CONSTANTS); dumpSetting(s, p, + Settings.Global.JOB_SCHEDULER_QUOTA_CONTROLLER_CONSTANTS, + GlobalSettingsProto.JOB_SCHEDULER_QUOTA_CONTROLLER_CONSTANTS); + dumpSetting(s, p, + Settings.Global.JOB_SCHEDULER_TIME_CONTROLLER_CONSTANTS, + GlobalSettingsProto.JOB_SCHEDULER_TIME_CONTROLLER_CONSTANTS); + dumpSetting(s, p, Settings.Global.KEEP_PROFILE_IN_BACKGROUND, GlobalSettingsProto.KEEP_PROFILE_IN_BACKGROUND); diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java index 66d5d1160bf6..25a3fa2f5f20 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java @@ -17,6 +17,7 @@ package com.android.systemui.plugins; import android.annotation.Nullable; import android.app.PendingIntent; import android.content.Intent; +import android.view.View; import com.android.systemui.plugins.annotations.ProvidesInterface; @@ -32,13 +33,20 @@ public interface ActivityStarter { void startPendingIntentDismissingKeyguard(PendingIntent intent); /** - * Similar to {@link #startPendingIntentDismissingKeyguard(PendingIntent, Runnable)}, but allows + * Similar to {@link #startPendingIntentDismissingKeyguard(PendingIntent)}, but allows * you to specify the callback that is executed on the UI thread after the intent is sent. */ void startPendingIntentDismissingKeyguard(PendingIntent intent, Runnable intentSentUiThreadCallback); /** + * Similar to {@link #startPendingIntentDismissingKeyguard(PendingIntent, Runnable)}, but also + * specifies an associated view that should be used for the activity launch animation. + */ + void startPendingIntentDismissingKeyguard(PendingIntent intent, + Runnable intentSentUiThreadCallback, View associatedView); + + /** * The intent flag can be specified in startActivity(). */ void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade, int flags); diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/GlobalActionsPanelPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/GlobalActionsPanelPlugin.java index 6be770788ce1..68d2ed7d4689 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/GlobalActionsPanelPlugin.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/GlobalActionsPanelPlugin.java @@ -26,8 +26,8 @@ import com.android.systemui.plugins.annotations.ProvidesInterface; * Plugin which provides a "Panel" {@link View} to be rendered inside of the GlobalActions menu. * * Implementations should construct a new {@link PanelViewController} with the given - * {@link Callbacks} instance inside of {@link #onPanelShown(Callbacks)}, and should not hold onto - * a reference, instead allowing Global Actions to manage the lifetime of the object. + * {@link Callbacks} instance inside of {@link #onPanelShown(Callbacks, boolean)}, and should not + * hold onto a reference, instead allowing Global Actions to manage the lifetime of the object. * * Under this assumption, {@link PanelViewController} represents the lifetime of a single invocation * of the Global Actions menu. The {@link View} for the Panel is generated when the @@ -50,9 +50,10 @@ public interface GlobalActionsPanelPlugin extends Plugin { * * @param callbacks {@link Callbacks} instance that can be used by the Panel to interact with * the Global Actions menu. + * @param deviceLocked Indicates whether or not the device is currently locked. * @return A {@link PanelViewController} instance used to receive Global Actions events. */ - PanelViewController onPanelShown(Callbacks callbacks); + PanelViewController onPanelShown(Callbacks callbacks, boolean deviceLocked); /** * Provides methods to interact with the Global Actions menu. @@ -92,5 +93,10 @@ public interface GlobalActionsPanelPlugin extends Plugin { * {@link #getPanelContent()}) is dismissed. */ void onDismissed(); + + /** + * Invoked when the device is either locked or unlocked. + */ + void onDeviceLockStateChanged(boolean locked); } } diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml index d8f543f1f710..112dde6bfdc2 100644 --- a/packages/SystemUI/res-keyguard/values/dimens.xml +++ b/packages/SystemUI/res-keyguard/values/dimens.xml @@ -53,7 +53,7 @@ <!-- Clock with header --> <dimen name="widget_small_font_size">22dp</dimen> <dimen name="widget_vertical_padding">32dp</dimen> - <dimen name="widget_vertical_padding_clock">30dp</dimen> + <dimen name="widget_vertical_padding_clock">12dp</dimen> <!-- Subtitle paddings --> <dimen name="widget_horizontal_padding">8dp</dimen> <dimen name="widget_icon_size">16dp</dimen> diff --git a/packages/SystemUI/res/anim/ic_bluetooth_transient_dot_left_animation.xml b/packages/SystemUI/res/anim/ic_bluetooth_transient_dot_left_animation.xml deleted file mode 100644 index b5dd5c359a9e..000000000000 --- a/packages/SystemUI/res/anim/ic_bluetooth_transient_dot_left_animation.xml +++ /dev/null @@ -1,77 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2017 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<set - xmlns:android="http://schemas.android.com/apk/res/android" > - <set - android:ordering="sequentially" > - <objectAnimator - android:duration="16" - android:propertyName="fillAlpha" - android:valueFrom="0.5" - android:valueTo="1.0" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="233" - android:propertyName="fillAlpha" - android:valueFrom="1.0" - android:valueTo="1.0" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="16" - android:propertyName="fillAlpha" - android:valueFrom="1.0" - android:valueTo="0.5" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="233" - android:propertyName="fillAlpha" - android:valueFrom="0.5" - android:valueTo="0.5" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="16" - android:propertyName="fillAlpha" - android:valueFrom="0.5" - android:valueTo="1.0" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="233" - android:propertyName="fillAlpha" - android:valueFrom="1.0" - android:valueTo="1.0" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="16" - android:propertyName="fillAlpha" - android:valueFrom="1.0" - android:valueTo="0.5" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="233" - android:propertyName="fillAlpha" - android:valueFrom="0.5" - android:valueTo="0.5" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - </set> -</set> diff --git a/packages/SystemUI/res/anim/ic_bluetooth_transient_dot_right_animation.xml b/packages/SystemUI/res/anim/ic_bluetooth_transient_dot_right_animation.xml deleted file mode 100644 index 14704bf3da5d..000000000000 --- a/packages/SystemUI/res/anim/ic_bluetooth_transient_dot_right_animation.xml +++ /dev/null @@ -1,77 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2017 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<set - xmlns:android="http://schemas.android.com/apk/res/android" > - <set - android:ordering="sequentially" > - <objectAnimator - android:duration="16" - android:propertyName="fillAlpha" - android:valueFrom="1.0" - android:valueTo="0.5" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="233" - android:propertyName="fillAlpha" - android:valueFrom="0.5" - android:valueTo="0.5" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="16" - android:propertyName="fillAlpha" - android:valueFrom="0.5" - android:valueTo="1.0" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="233" - android:propertyName="fillAlpha" - android:valueFrom="1.0" - android:valueTo="1.0" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="16" - android:propertyName="fillAlpha" - android:valueFrom="1.0" - android:valueTo="0.5" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="233" - android:propertyName="fillAlpha" - android:valueFrom="0.5" - android:valueTo="0.5" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="16" - android:propertyName="fillAlpha" - android:valueFrom="0.5" - android:valueTo="1.0" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="233" - android:propertyName="fillAlpha" - android:valueFrom="1.0" - android:valueTo="1.0" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - </set> -</set> diff --git a/packages/SystemUI/res/anim/ic_caret_down_left_animation.xml b/packages/SystemUI/res/anim/ic_caret_down_left_animation.xml deleted file mode 100644 index d465f198a679..000000000000 --- a/packages/SystemUI/res/anim/ic_caret_down_left_animation.xml +++ /dev/null @@ -1,32 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2017 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<set - xmlns:android="http://schemas.android.com/apk/res/android" > - <objectAnimator - android:duration="250" - android:propertyXName="translateX" - android:propertyYName="translateY" - android:pathData="M 12.699,8.701 c 0.0,1.09767 0.0,5.48833 0.0,6.586" - android:interpolator="@android:interpolator/fast_out_slow_in" /> - <objectAnimator - android:duration="200" - android:propertyName="rotation" - android:valueFrom="-45.0" - android:valueTo="45.0" - android:valueType="floatType" - android:interpolator="@interpolator/ic_caret_down_animation_interpolator_0" /> -</set> diff --git a/packages/SystemUI/res/anim/ic_caret_down_right_animation.xml b/packages/SystemUI/res/anim/ic_caret_down_right_animation.xml deleted file mode 100644 index 7a38ac323242..000000000000 --- a/packages/SystemUI/res/anim/ic_caret_down_right_animation.xml +++ /dev/null @@ -1,32 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2017 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<set - xmlns:android="http://schemas.android.com/apk/res/android" > - <objectAnimator - android:duration="250" - android:propertyXName="translateX" - android:propertyYName="translateY" - android:pathData="M 11.287,8.701 c 0.0,1.09767 0.0,5.48833 0.0,6.586" - android:interpolator="@android:interpolator/fast_out_slow_in" /> - <objectAnimator - android:duration="200" - android:propertyName="rotation" - android:valueFrom="45.0" - android:valueTo="-45.0" - android:valueType="floatType" - android:interpolator="@interpolator/ic_caret_down_animation_interpolator_0" /> -</set> diff --git a/packages/SystemUI/res/anim/ic_caret_up_left_animation.xml b/packages/SystemUI/res/anim/ic_caret_up_left_animation.xml deleted file mode 100644 index 125a6164bfd1..000000000000 --- a/packages/SystemUI/res/anim/ic_caret_up_left_animation.xml +++ /dev/null @@ -1,32 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2017 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<set - xmlns:android="http://schemas.android.com/apk/res/android" > - <objectAnimator - android:duration="250" - android:propertyXName="translateX" - android:propertyYName="translateY" - android:pathData="M 12.699,15.287 c -0.04833,0.452 -0.04833,-7.03583 0.0,-6.586" - android:interpolator="@android:interpolator/fast_out_slow_in" /> - <objectAnimator - android:duration="200" - android:propertyName="rotation" - android:valueFrom="45.0" - android:valueTo="-45.0" - android:valueType="floatType" - android:interpolator="@interpolator/ic_caret_up_animation_interpolator_0" /> -</set> diff --git a/packages/SystemUI/res/anim/ic_caret_up_right_animation.xml b/packages/SystemUI/res/anim/ic_caret_up_right_animation.xml deleted file mode 100644 index 3fd4df58f51c..000000000000 --- a/packages/SystemUI/res/anim/ic_caret_up_right_animation.xml +++ /dev/null @@ -1,32 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2017 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<set - xmlns:android="http://schemas.android.com/apk/res/android" > - <objectAnimator - android:duration="250" - android:propertyXName="translateX" - android:propertyYName="translateY" - android:pathData="M 11.287,15.287 c 0.04883,0.452 0.04883,-7.03583 0.0,-6.586" - android:interpolator="@android:interpolator/fast_out_slow_in" /> - <objectAnimator - android:duration="200" - android:propertyName="rotation" - android:valueFrom="-45.0" - android:valueTo="45.0" - android:valueType="floatType" - android:interpolator="@interpolator/ic_caret_up_animation_interpolator_0" /> -</set> diff --git a/packages/SystemUI/res/anim/ic_hotspot_transient_circle_1_animation.xml b/packages/SystemUI/res/anim/ic_hotspot_transient_circle_1_animation.xml deleted file mode 100644 index 5e7184700614..000000000000 --- a/packages/SystemUI/res/anim/ic_hotspot_transient_circle_1_animation.xml +++ /dev/null @@ -1,43 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2017 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<set - xmlns:android="http://schemas.android.com/apk/res/android" > - <set - android:ordering="sequentially" > - <objectAnimator - android:duration="16" - android:propertyName="fillAlpha" - android:valueFrom="1.0" - android:valueTo="0.5" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="633" - android:propertyName="fillAlpha" - android:valueFrom="0.5" - android:valueTo="0.5" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="16" - android:propertyName="fillAlpha" - android:valueFrom="0.5" - android:valueTo="1.0" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - </set> -</set> diff --git a/packages/SystemUI/res/anim/ic_hotspot_transient_circle_2_animation.xml b/packages/SystemUI/res/anim/ic_hotspot_transient_circle_2_animation.xml deleted file mode 100644 index 9081e6f48f11..000000000000 --- a/packages/SystemUI/res/anim/ic_hotspot_transient_circle_2_animation.xml +++ /dev/null @@ -1,50 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2017 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<set - xmlns:android="http://schemas.android.com/apk/res/android" > - <set - android:ordering="sequentially" > - <objectAnimator - android:duration="200" - android:propertyName="fillAlpha" - android:valueFrom="1.0" - android:valueTo="1.0" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="16" - android:propertyName="fillAlpha" - android:valueFrom="1.0" - android:valueTo="0.5" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="633" - android:propertyName="fillAlpha" - android:valueFrom="0.5" - android:valueTo="0.5" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="16" - android:propertyName="fillAlpha" - android:valueFrom="0.5" - android:valueTo="1.0" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - </set> -</set> diff --git a/packages/SystemUI/res/anim/ic_hotspot_transient_circle_3_animation.xml b/packages/SystemUI/res/anim/ic_hotspot_transient_circle_3_animation.xml deleted file mode 100644 index eaf5f05b8863..000000000000 --- a/packages/SystemUI/res/anim/ic_hotspot_transient_circle_3_animation.xml +++ /dev/null @@ -1,50 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2017 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<set - xmlns:android="http://schemas.android.com/apk/res/android" > - <set - android:ordering="sequentially" > - <objectAnimator - android:duration="400" - android:propertyName="fillAlpha" - android:valueFrom="1.0" - android:valueTo="1.0" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="16" - android:propertyName="fillAlpha" - android:valueFrom="1.0" - android:valueTo="0.5" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="633" - android:propertyName="fillAlpha" - android:valueFrom="0.5" - android:valueTo="0.5" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="16" - android:propertyName="fillAlpha" - android:valueFrom="0.5" - android:valueTo="1.0" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - </set> -</set> diff --git a/packages/SystemUI/res/anim/ic_signal_wifi_transient_wifi_1_animation.xml b/packages/SystemUI/res/anim/ic_signal_wifi_transient_wifi_1_animation.xml deleted file mode 100644 index 919a4ddd4f08..000000000000 --- a/packages/SystemUI/res/anim/ic_signal_wifi_transient_wifi_1_animation.xml +++ /dev/null @@ -1,66 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<set - xmlns:android="http://schemas.android.com/apk/res/android" > - <set - android:ordering="sequentially" > - <objectAnimator - android:duration="333" - android:propertyName="pathData" - android:valueFrom="M 0.0169982910156,18.4394989014 c 0.0,0.0 23.2140045166,-28.766998291 23.2140045166,-28.766998291 c -0.900009155273,-0.675003051758 -9.82899475098,-8.13400268555 -23.2320098877,-8.13400268555 c -13.4029998779,0.0 -22.3299865723,7.45899963379 -23.2299957275,8.13400268555 c 0.0,0.0 23.2140045166,28.766998291 23.2140045166,28.766998291 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z" - android:valueTo="M 0.0169982910156,18.4394989014 c 0.0,0.0 23.2140045166,-28.766998291 23.2140045166,-28.766998291 c -0.900009155273,-0.675003051758 -9.82899475098,-8.13400268555 -23.2320098877,-8.13400268555 c -13.4029998779,0.0 -22.3299865723,7.45899963379 -23.2299957275,8.13400268555 c 0.0,0.0 23.2140045166,28.766998291 23.2140045166,28.766998291 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z" - android:valueType="pathType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="16" - android:propertyName="pathData" - android:valueFrom="M 0.0169982910156,18.4394989014 c 0.0,0.0 23.2140045166,-28.766998291 23.2140045166,-28.766998291 c -0.900009155273,-0.675003051758 -9.82899475098,-8.13400268555 -23.2320098877,-8.13400268555 c -13.4029998779,0.0 -22.3299865723,7.45899963379 -23.2299957275,8.13400268555 c 0.0,0.0 23.2140045166,28.766998291 23.2140045166,28.766998291 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z" - android:valueTo="M 0.0169982910156,18.4394989014 c 0.0,0.0 9.55569458008,-11.8414916992 9.55569458008,-11.8414916992 c 0.0,0.0 -3.32373046875,-3.83329772949 -9.59307861328,-3.7864074707 c -6.26933288574,0.046875 -9.61039733887,3.71441650391 -9.61039733887,3.71441650391 c 0.0,0.0 9.61378479004,11.913482666 9.61378479004,11.913482666 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z" - android:valueType="pathType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="316" - android:propertyName="pathData" - android:valueFrom="M 0.0169982910156,18.4394989014 c 0.0,0.0 9.55569458008,-11.8414916992 9.55569458008,-11.8414916992 c 0.0,0.0 -3.32373046875,-3.83329772949 -9.59307861328,-3.7864074707 c -6.26933288574,0.046875 -9.61039733887,3.71441650391 -9.61039733887,3.71441650391 c 0.0,0.0 9.61378479004,11.913482666 9.61378479004,11.913482666 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z" - android:valueTo="M 0.0169982910156,18.4394989014 c 0.0,0.0 9.55569458008,-11.8414916992 9.55569458008,-11.8414916992 c 0.0,0.0 -3.32373046875,-3.83329772949 -9.59307861328,-3.7864074707 c -6.26933288574,0.046875 -9.61039733887,3.71441650391 -9.61039733887,3.71441650391 c 0.0,0.0 9.61378479004,11.913482666 9.61378479004,11.913482666 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z" - android:valueType="pathType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="16" - android:propertyName="pathData" - android:valueFrom="M 0.0169982910156,18.4394989014 c 0.0,0.0 9.55569458008,-11.8414916992 9.55569458008,-11.8414916992 c 0.0,0.0 -3.32373046875,-3.83329772949 -9.59307861328,-3.7864074707 c -6.26933288574,0.046875 -9.61039733887,3.71441650391 -9.61039733887,3.71441650391 c 0.0,0.0 9.61378479004,11.913482666 9.61378479004,11.913482666 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z" - android:valueTo="M 0.0169982910156,18.4394989014 c 0.0,0.0 16.9385528564,-20.9904174805 16.9385528564,-20.9904174805 c -0.486480712891,-0.364868164062 -6.84008789062,-6.15798950195 -16.9654541016,-6.13645935059 c -10.1253662109,0.0215454101562 -16.4858551025,5.73852539062 -16.9723510742,6.10339355469 c 0.0,0.0 16.9652557373,21.0234832764 16.9652557373,21.0234832764 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z" - android:valueType="pathType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="316" - android:propertyName="pathData" - android:valueFrom="M 0.0169982910156,18.4394989014 c 0.0,0.0 16.9385528564,-20.9904174805 16.9385528564,-20.9904174805 c -0.486480712891,-0.364868164062 -6.84008789062,-6.15798950195 -16.9654541016,-6.13645935059 c -10.1253662109,0.0215454101562 -16.4858551025,5.73852539062 -16.9723510742,6.10339355469 c 0.0,0.0 16.9652557373,21.0234832764 16.9652557373,21.0234832764 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z" - android:valueTo="M 0.0169982910156,18.4394989014 c 0.0,0.0 16.9385528564,-20.9904174805 16.9385528564,-20.9904174805 c -0.486480712891,-0.364868164062 -6.84008789062,-6.15798950195 -16.9654541016,-6.13645935059 c -10.1253662109,0.0215454101562 -16.4858551025,5.73852539062 -16.9723510742,6.10339355469 c 0.0,0.0 16.9652557373,21.0234832764 16.9652557373,21.0234832764 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z" - android:valueType="pathType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="16" - android:propertyName="pathData" - android:valueFrom="M 0.0169982910156,18.4394989014 c 0.0,0.0 16.9385528564,-20.9904174805 16.9385528564,-20.9904174805 c -0.486480712891,-0.364868164062 -6.84008789062,-6.15798950195 -16.9654541016,-6.13645935059 c -10.1253662109,0.0215454101562 -16.4858551025,5.73852539062 -16.9723510742,6.10339355469 c 0.0,0.0 16.9652557373,21.0234832764 16.9652557373,21.0234832764 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z" - android:valueTo="M 0.0169982910156,18.4394989014 c 0.0,0.0 23.2140045166,-28.766998291 23.2140045166,-28.766998291 c -0.900009155273,-0.675003051758 -9.82899475098,-8.13400268555 -23.2320098877,-8.13400268555 c -13.4029998779,0.0 -22.3299865723,7.45899963379 -23.2299957275,8.13400268555 c 0.0,0.0 23.2140045166,28.766998291 23.2140045166,28.766998291 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z" - android:valueType="pathType" - android:interpolator="@android:interpolator/linear" /> - </set> - <set - android:ordering="sequentially" > - <objectAnimator - android:duration="333" - android:propertyName="fillAlpha" - android:valueFrom="0.0" - android:valueTo="0.0" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - <objectAnimator - android:duration="16" - android:propertyName="fillAlpha" - android:valueFrom="0.0" - android:valueTo="1.0" - android:valueType="floatType" - android:interpolator="@android:interpolator/linear" /> - </set> -</set> diff --git a/packages/SystemUI/res/drawable/button_border_selected.xml b/packages/SystemUI/res/drawable/button_border_selected.xml index d9299ec0622b..01e7099844a9 100644 --- a/packages/SystemUI/res/drawable/button_border_selected.xml +++ b/packages/SystemUI/res/drawable/button_border_selected.xml @@ -19,7 +19,7 @@ <solid android:color="@color/notification_guts_selection_bg" /> <stroke - android:width="2dp" - android:color="?android:attr/colorAccent"/> + android:width="1dp" + android:color="@color/GM2_grey_300"/> <corners android:radius="@dimen/rect_button_radius" /> </shape>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/button_border_unselected.xml b/packages/SystemUI/res/drawable/button_border_unselected.xml index 4ea37640780f..b9c4ced3631b 100644 --- a/packages/SystemUI/res/drawable/button_border_unselected.xml +++ b/packages/SystemUI/res/drawable/button_border_unselected.xml @@ -18,7 +18,7 @@ android:shape="rectangle" android:color="@color/notification_guts_selection_bg"> <stroke - android:width="2dp" + android:width="1dp" android:color="@color/GM2_grey_300"/> <corners android:radius="@dimen/rect_button_radius" /> diff --git a/packages/SystemUI/res/drawable/ic_bluetooth_transient.xml b/packages/SystemUI/res/drawable/ic_bluetooth_transient.xml deleted file mode 100644 index 33d1fb3fbe2b..000000000000 --- a/packages/SystemUI/res/drawable/ic_bluetooth_transient.xml +++ /dev/null @@ -1,76 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2017 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<vector - xmlns:android="http://schemas.android.com/apk/res/android" - android:name="ic_bluetooth_transient" - android:width="48dp" - android:viewportWidth="48" - android:height="48dp" - android:viewportHeight="48" > - <group - android:name="ic_bluetooth_transient_0" - android:translateX="23.99883" - android:translateY="23.99839" > - <group - android:name="ic_bluetooth_transient_pivot" - android:translateX="-23.99883" - android:translateY="-23.99839" > - <group - android:name="ic_bluetooth_white_outlines" - android:translateX="22.73986" - android:translateY="23.99839" > - <group - android:name="ic_bluetooth_white_outlines_pivot" - android:translateX="-12.84937" - android:translateY="-20.4772" > - <path - android:name="b_shape_merged" - android:pathData="M 17.1289978027,20.4790039062 c 0.0,0.0 7.5,-7.48100280762 7.5,-7.48100280762 c 0.81999206543,-0.819000244141 0.81999206543,-2.13899230957 0.0,-2.95999145508 c 0.0,0.0 -8.93899536133,-8.93899536133 -8.93899536133,-8.93899536133 c 0.0,0.0 -0.0610046386719,-0.0610046386719 -0.0610046386718,-0.0610046386719 c -0.844009399414,-0.788009643555 -2.16799926758,-0.74299621582 -2.95600891113,0.102005004883 c -0.359985351562,0.384994506836 -0.561996459961,0.891998291016 -0.56298828125,1.41899108887 c 0.0,0.0 0.0,12.8800048828 0.0,12.8800048828 c 0.0,0.0 -8.10000610352,-8.10000610352 -8.10000610352,-8.10000610352 c -0.81999206543,-0.81999206543 -2.12100219727,-0.81999206543 -2.9409942627,0.0 c -0.819000244141,0.819000244141 -0.819000244141,2.12001037598 0.0,2.94000244141 c 0.0,0.0 10.1799926758,10.1999969482 10.1799926758,10.1999969482 c 0.0,0.0 -10.1799926758,10.1790008545 -10.1799926758,10.1790008545 c -0.819000244141,0.820999145508 -0.819000244141,2.12100219727 0.0,2.94100952148 c 0.81999206543,0.81999206543 2.12100219727,0.81999206543 2.9409942627,0.0 c 0.0,0.0 8.10000610352,-8.1009979248 8.10000610352,-8.1009979248 c 0.0,0.0 0.0,12.9009857178 0.0,12.9009857178 c 0.0,1.14801025391 0.929992675781,2.08000183105 2.08000183105,2.08000183105 c 0.526992797852,0.0 1.03399658203,-0.199996948242 1.41999816895,-0.559997558594 c 0.0,0.0 0.0989990234375,-0.100006103516 0.0989990234375,-0.100006103516 c 0.0,0.0 8.91999816895,-8.91999816895 8.91999816895,-8.91999816895 c 0.81999206543,-0.820999145508 0.81999206543,-2.13999938965 0.0,-2.95999145508 c 0.0,0.0 -7.5,-7.46000671387 -7.5,-7.46000671387 Z M 16.0899963379,15.8190002441 c 0.0,0.0 0.0,-8.59999084473 0.0,-8.59999084473 c 0.0,0.0 4.30000305176,4.30000305176 4.30000305176,4.30000305176 c 0.0,0.0 -4.30000305176,4.29998779297 -4.30000305176,4.29998779297 Z M 16.0899963379,33.7190093994 c 0.0,0.0 0.0,-8.6009979248 0.0,-8.6009979248 c 0.0,0.0 4.30000305176,4.30099487305 4.30000305176,4.30099487305 c 0.0,0.0 -4.30000305176,4.30000305176 -4.30000305176,4.30000305176 Z" - android:fillColor="#FFFFFFFF" /> - </group> - </group> - <group - android:name="dot_left_outlines" - android:translateX="20.6501" - android:translateY="24.00011" > - <group - android:name="dot_left_outlines_pivot" - android:translateX="-14.2" - android:translateY="-3.55" > - <path - android:name="dot_left" - android:pathData="M 5.66999816895,5.66999816895 c -1.18000793457,1.17999267578 -3.08000183105,1.17999267578 -4.24000549316,0.0 c -1.17999267578,-1.16000366211 -1.17999267578,-3.03999328613 -0.0199890136719,-4.2200012207 c 0.0,0.0 0.0199890136719,-0.0200042724609 0.0199890136719,-0.0200042724609 c 1.16000366211,-1.17999267578 3.04000854492,-1.17999267578 4.2200012207,-0.0199890136719 c 0.0,0.0 0.0200042724609,0.0199890136719 0.0200042724609,0.0199890136719 c 1.17999267578,1.17900085449 1.17999267578,3.06001281738 0.0,4.24000549316 Z" - android:fillColor="#FFFFFFFF" - android:fillAlpha="0.5" /> - </group> - </group> - <group - android:name="dot_right_outlines" - android:translateX="27.3501" - android:translateY="23.99741" > - <group - android:name="dot_right_outlines_pivot" - android:translateX="7.1" - android:translateY="-3.5525" > - <path - android:name="dot_right" - android:pathData="M 5.66999816895,1.43499755859 c 1.17999267578,1.18000793457 1.17999267578,3.08000183105 0.0,4.24000549316 c -1.18000793457,1.17999267578 -3.08000183105,1.17999267578 -4.24000549316,0.0 c -1.17999267578,-1.16000366211 -1.17999267578,-3.04000854492 -0.0200042724609,-4.21899414062 c 0.0,0.0 0.0200042724609,-0.0210113525391 0.0200042724609,-0.0210113525391 c 1.15299987793,-1.17098999023 3.03799438477,-1.18499755859 4.20899963379,-0.0309906005859 c 0.0,0.0 0.031005859375,0.0309906005859 0.031005859375,0.0309906005859 Z" - android:fillColor="#FFFFFFFF" /> - </group> - </group> - </group> - </group> -</vector> diff --git a/packages/SystemUI/res/drawable/ic_hotspot_transient.xml b/packages/SystemUI/res/drawable/ic_hotspot_transient.xml deleted file mode 100644 index 22f72675ffc0..000000000000 --- a/packages/SystemUI/res/drawable/ic_hotspot_transient.xml +++ /dev/null @@ -1,76 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2017 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<vector - xmlns:android="http://schemas.android.com/apk/res/android" - android:name="ic_hotspot_transient" - android:width="48dp" - android:viewportWidth="48" - android:height="48dp" - android:viewportHeight="48" > - <group - android:name="ic_hotspot_transient_0" - android:translateX="24.00209" - android:translateY="24.2354" > - <group - android:name="ic_hotspot_transient_pivot" - android:translateX="-24.00209" - android:translateY="-24.2354" > - <group - android:name="circle01" - android:translateX="24.0002" - android:translateY="25.9996" > - <group - android:name="circle01_pivot" - android:translateX="-24.0002" - android:translateY="-25.9996" > - <path - android:name="circle01_0" - android:pathData="M 24.0001983643,21.9996032715 c -2.19999694824,0.0 -4.0,1.80000305176 -4.0,4.0 c 0.0,2.20100402832 1.80000305176,4.0 4.0,4.0 c 2.19999694824,0.0 4.0,-1.79899597168 4.0,-4.0 c 0.0,-2.19999694824 -1.80000305176,-4.0 -4.0,-4.0 Z" - android:fillColor="#FFFFFFFF" /> - </group> - </group> - <group - android:name="circle02" - android:translateX="24.00071" - android:translateY="24.74686" > - <group - android:name="circle02_pivot" - android:translateX="-24.00071" - android:translateY="-24.74686" > - <path - android:name="circle02_0" - android:pathData="M 36.0001983643,25.9996032715 c -0.00299072265625,-6.62699890137 -5.37899780273,-11.9969940186 -12.0059967041,-11.9940032959 c -0.5,0.0 -0.999008178711,0.031005859375 -1.4940032959,0.0940093994141 c -5.24000549316,0.640991210938 -9.55999755859,4.81999206543 -10.3600006104,10.0399932861 c -0.639999389648,4.2799987793 0.979995727539,8.22099304199 3.83999633789,10.7799987793 c 0.960006713867,0.86100769043 2.48001098633,0.660003662109 3.1190032959,-0.460006713867 c 0.481002807617,-0.839996337891 0.281005859375,-1.87998962402 -0.438995361328,-2.51899719238 c -2.21299743652,-1.97099304199 -3.15200805664,-5.00500488281 -2.44000244141,-7.8809967041 c 0.695007324219,-2.86999511719 2.93099975586,-5.11500549316 5.79899597168,-5.81900024414 c 4.29100036621,-1.08599853516 8.65000915527,1.51100158691 9.73600769043,5.80200195312 c 0.162002563477,0.639999389648 0.244003295898,1.29699707031 0.244995117188,1.95700073242 c 0.0,2.36099243164 -1.02000427246,4.45999145508 -2.66000366211,5.91999816895 c -0.720001220703,0.660003662109 -0.939987182617,1.69999694824 -0.459991455078,2.53999328613 c 0.619995117188,1.08000183105 2.08000183105,1.38000488281 3.0,0.560012817383 c 2.61599731445,-2.26699829102 4.1190032959,-5.55801391602 4.11999511719,-9.02000427246 Z" - android:fillColor="#FFFFFFFF" /> - </group> - </group> - <group - android:name="circle03" - android:translateX="24.00209" - android:translateY="24.23539" > - <group - android:name="circle03_pivot" - android:translateX="-24.00209" - android:translateY="-24.23539" > - <path - android:name="circle03_0" - android:pathData="M 21.6602020264,6.13960266113 c -9.24000549316,1.03999328613 -16.6999969482,8.66000366211 -17.5599975586,17.9199981689 c -0.690002441406,7.00500488281 2.36599731445,13.8540039062 8.03999328613,18.0200042725 c 0.958999633789,0.700988769531 2.32000732422,0.401000976562 2.91900634766,-0.620010375977 c 0.5,-0.860000610352 0.280990600586,-1.97898864746 -0.518997192383,-2.57998657227 c -4.56001281738,-3.38000488281 -7.30000305176,-9.09901428223 -6.32000732422,-15.3990020752 c 1.08000183105,-7.0 6.91900634766,-12.5800018311 13.9600067139,-13.3610076904 c 9.63999938965,-1.09999084473 17.8199920654,6.44000244141 17.8199920654,15.8800048828 c 0.0,5.30000305176 -2.57998657227,9.95999145508 -6.53999328613,12.8800048828 c -0.800003051758,0.600997924805 -1.02000427246,1.69999694824 -0.520004272461,2.57998657227 c 0.600006103516,1.04000854492 1.96000671387,1.32099914551 2.91999816895,0.620010375977 c 5.12100219727,-3.75500488281 8.14500427246,-9.72799682617 8.13999938965,-16.0800018311 c 0.0,-11.8200073242 -10.2599945068,-21.2400054932 -22.3399963379,-19.8600006104 Z" - android:fillColor="#FFFFFFFF" /> - </group> - </group> - </group> - </group> -</vector> diff --git a/packages/SystemUI/res/drawable/ic_hotspot_transient_animation.xml b/packages/SystemUI/res/drawable/ic_hotspot_transient_animation.xml deleted file mode 100644 index 929a941164a6..000000000000 --- a/packages/SystemUI/res/drawable/ic_hotspot_transient_animation.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2017 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<animated-vector - xmlns:android="http://schemas.android.com/apk/res/android" - android:drawable="@drawable/ic_hotspot_transient" > - <target - android:name="circle01_0" - android:animation="@anim/ic_hotspot_transient_circle_1_animation" /> - <target - android:name="circle02_0" - android:animation="@anim/ic_hotspot_transient_circle_2_animation" /> - <target - android:name="circle03_0" - android:animation="@anim/ic_hotspot_transient_circle_3_animation" /> -</animated-vector> diff --git a/packages/SystemUI/res/drawable/ic_signal_wifi_transient.xml b/packages/SystemUI/res/drawable/ic_signal_wifi_transient.xml deleted file mode 100644 index 880e1bb075c7..000000000000 --- a/packages/SystemUI/res/drawable/ic_signal_wifi_transient.xml +++ /dev/null @@ -1,39 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<vector - xmlns:android="http://schemas.android.com/apk/res/android" - android:name="ic_signal_wifi_transient" - android:width="48dp" - android:viewportWidth="48" - android:height="48dp" - android:viewportHeight="48" > - <group - android:name="ic_signal_wifi_4_bar_48px_2" - android:translateX="24.25" - android:translateY="25.73401" > - <group - android:name="ic_signal_wifi_4_bar_48px_2_pivot" - android:translateX="-23.21545" - android:translateY="-18.86649" > - <group - android:name="wifi_2" - android:translateX="23.481" - android:translateY="18.71151" > - <path - android:name="wifi" - android:pathData="M 0.0169982910156,18.4394989014 c 0.0,0.0 23.2140045166,-28.766998291 23.2140045166,-28.766998291 c -0.900009155273,-0.675003051758 -9.82899475098,-8.13400268555 -23.2320098877,-8.13400268555 c -13.4029998779,0.0 -22.3299865723,7.45899963379 -23.2299957275,8.13400268555 c 0.0,0.0 23.2140045166,28.766998291 23.2140045166,28.766998291 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z" - android:fillColor="#FFFFFFFF" - android:fillAlpha="0.5" /> - </group> - <group - android:name="wifi_0" - android:translateX="23.481" - android:translateY="18.71151" > - <path - android:name="wifi_1" - android:pathData="M 0.0169982910156,18.4394989014 c 0.0,0.0 23.2140045166,-28.766998291 23.2140045166,-28.766998291 c -0.900009155273,-0.675003051758 -9.82899475098,-8.13400268555 -23.2320098877,-8.13400268555 c -13.4029998779,0.0 -22.3299865723,7.45899963379 -23.2299957275,8.13400268555 c 0.0,0.0 23.2140045166,28.766998291 23.2140045166,28.766998291 c 0.0,0.0 0.0159912109375,0.0220031738281 0.0159912109375,0.0220031738281 c 0.0,0.0 0.00100708007812,-0.00100708007812 0.00100708007812,-0.0010070800781 c 0.0,0.0 0.00100708007812,0.00100708007812 0.00100708007812,0.0010070800781 c 0.0,0.0 0.0159912109375,-0.0220031738281 0.0159912109375,-0.0220031738281 Z" - android:fillColor="#FFFFFFFF" - android:fillAlpha="0" /> - </group> - </group> - </group> -</vector> diff --git a/packages/SystemUI/res/drawable/ic_signal_wifi_transient_animation.xml b/packages/SystemUI/res/drawable/ic_signal_wifi_transient_animation.xml deleted file mode 100644 index 9f5aaebaf650..000000000000 --- a/packages/SystemUI/res/drawable/ic_signal_wifi_transient_animation.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<animated-vector - xmlns:android="http://schemas.android.com/apk/res/android" - android:drawable="@drawable/ic_signal_wifi_transient" > - <target - android:name="wifi_1" - android:animation="@anim/ic_signal_wifi_transient_wifi_1_animation" /> -</animated-vector> diff --git a/packages/SystemUI/res/drawable/ic_volume_collapse.xml b/packages/SystemUI/res/drawable/ic_volume_collapse.xml deleted file mode 100644 index 3853d121ce2c..000000000000 --- a/packages/SystemUI/res/drawable/ic_volume_collapse.xml +++ /dev/null @@ -1,55 +0,0 @@ -<!-- - Copyright (C) 2017 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<vector - xmlns:android="http://schemas.android.com/apk/res/android" - android:name="ic_caret_down" - android:width="24dp" - android:viewportWidth="24" - android:height="24dp" - android:viewportHeight="24" - android:tint="?android:attr/colorControlNormal" > - <group - android:name="caret___4" > - <group - android:name="right" - android:translateX="11.287" - android:translateY="8.701" - android:rotation="45" > - <group - android:name="right_pivot" - android:translateX="4.242" > - <path - android:name="rectangle_path_1" - android:fillColor="#FFFFFFFF" - android:pathData="M -3.242,-1.0 l 6.484,0.0 c 0.5522847498,0.0 1.0,0.4477152502 1.0,1.0 l 0.0,0.0 c 0.0,0.5522847498 -0.4477152502,1.0 -1.0,1.0 l -6.484,0.0 c -0.5522847498,0.0 -1.0,-0.4477152502 -1.0,-1.0 l 0.0,0.0 c 0.0,-0.5522847498 0.4477152502,-1.0 1.0,-1.0 Z" /> - </group> - </group> - <group - android:name="left" - android:translateX="12.699" - android:translateY="8.701" - android:rotation="-45" > - <group - android:name="left_pivot" - android:translateX="-4.242" > - <path - android:name="rectangle_path_2" - android:fillColor="#FFFFFFFF" - android:pathData="M -3.242,-1.0 l 6.484,0.0 c 0.5522847498,0.0 1.0,0.4477152502 1.0,1.0 l 0.0,0.0 c 0.0,0.5522847498 -0.4477152502,1.0 -1.0,1.0 l -6.484,0.0 c -0.5522847498,0.0 -1.0,-0.4477152502 -1.0,-1.0 l 0.0,0.0 c 0.0,-0.5522847498 0.4477152502,-1.0 1.0,-1.0 Z" /> - </group> - </group> - </group> -</vector> diff --git a/packages/SystemUI/res/drawable/ic_volume_collapse_animation.xml b/packages/SystemUI/res/drawable/ic_volume_collapse_animation.xml index 18c6307b9ad9..101a0c89857c 100644 --- a/packages/SystemUI/res/drawable/ic_volume_collapse_animation.xml +++ b/packages/SystemUI/res/drawable/ic_volume_collapse_animation.xml @@ -13,13 +13,223 @@ See the License for the specific language governing permissions and limitations under the License. --> -<animated-vector - xmlns:android="http://schemas.android.com/apk/res/android" - android:drawable="@drawable/ic_volume_collapse" > - <target - android:name="right" - android:animation="@anim/ic_caret_down_right_animation" /> - <target - android:name="left" - android:animation="@anim/ic_caret_down_left_animation" /> +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:aapt="http://schemas.android.com/aapt"> + <aapt:attr name="android:drawable"> + <vector android:height="24dp" android:width="24dp" android:viewportHeight="24" + android:viewportWidth="24"> + <group android:name="_R_G"> + <group android:name="_R_G_L_1_G_N_1_T_0" android:translateX="12" + android:translateY="12"> + <group android:name="_R_G_L_1_G_T_1" android:translateX="-0.004" + android:translateY="-3.608" android:rotation="0"> + <group android:name="_R_G_L_1_G" android:translateX="-0.246" + android:translateY="-1.642"> + <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M6.84 9.66 C6.84,9.66 8.25,8.25 8.25,8.25 C8.25,8.25 0.25,0.25 0.25,0.25 C0.25,0.25 -1.12,1.71 -1.12,1.71 C-1.12,1.71 6.84,9.66 6.84,9.66c "/> + </group> + </group> + </group> + <group android:name="_R_G_L_0_G_N_1_T_0" android:translateX="12" + android:translateY="12"> + <group android:name="_R_G_L_0_G_T_1" android:translateX="0" + android:translateY="-3.585" android:rotation="0"> + <group android:name="_R_G_L_0_G" android:translateX="-8.25" + android:translateY="-1.665"> + <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 8.25,0.25 8.25,0.25 C8.25,0.25 9.64,1.69 9.64,1.69 C9.64,1.69 1.66,9.66 1.66,9.66c "/> + </group> + </group> + </group> + </group> + <group android:name="time_group"/> + </vector> + </aapt:attr> + <target android:name="_R_G_L_1_G_D_0_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="pathData" android:duration="50" + android:startOffset="0" + android:valueFrom="M6.84 9.66 C6.84,9.66 8.25,8.25 8.25,8.25 C8.25,8.25 0.25,0.25 0.25,0.25 C0.25,0.25 -1.12,1.71 -1.12,1.71 C-1.12,1.71 6.84,9.66 6.84,9.66c " + android:valueTo="M6.84 9.66 C6.84,9.66 8.25,8.25 8.25,8.25 C8.25,8.25 0.96,0.9 0.96,0.9 C0.96,0.9 -1.12,1.71 -1.12,1.71 C-1.12,1.71 6.84,9.66 6.84,9.66c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="33" + android:startOffset="50" + android:valueFrom="M6.84 9.66 C6.84,9.66 8.25,8.25 8.25,8.25 C8.25,8.25 0.96,0.9 0.96,0.9 C0.96,0.9 -1.12,1.71 -1.12,1.71 C-1.12,1.71 6.84,9.66 6.84,9.66c " + android:valueTo="M6.84 9.66 C6.84,9.66 8.25,8.25 8.25,8.25 C8.25,8.25 0.96,0.9 0.96,0.9 C0.96,0.9 -0.55,2.35 -0.55,2.35 C-0.55,2.35 6.84,9.66 6.84,9.66c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="17" + android:startOffset="83" + android:valueFrom="M6.84 9.66 C6.84,9.66 8.25,8.25 8.25,8.25 C8.25,8.25 0.96,0.9 0.96,0.9 C0.96,0.9 -0.55,2.35 -0.55,2.35 C-0.55,2.35 6.84,9.66 6.84,9.66c " + android:valueTo="M6.84 9.66 C6.84,9.66 8.25,8.25 8.25,8.25 C8.25,8.25 0.25,0.25 0.25,0.25 C0.25,0.25 -0.87,2.12 -0.87,2.12 C-0.87,2.12 6.84,9.66 6.84,9.66c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="33" + android:startOffset="100" + android:valueFrom="M6.84 9.66 C6.84,9.66 8.25,8.25 8.25,8.25 C8.25,8.25 0.25,0.25 0.25,0.25 C0.25,0.25 -0.87,2.12 -0.87,2.12 C-0.87,2.12 6.84,9.66 6.84,9.66c " + android:valueTo="M6.84 9.66 C6.84,9.66 8.25,8.25 8.25,8.25 C8.25,8.25 0.25,0.25 0.25,0.25 C0.25,0.25 -1.05,1.92 -1.05,1.92 C-1.05,1.92 6.84,9.66 6.84,9.66c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="17" + android:startOffset="133" + android:valueFrom="M6.84 9.66 C6.84,9.66 8.25,8.25 8.25,8.25 C8.25,8.25 0.25,0.25 0.25,0.25 C0.25,0.25 -1.05,1.92 -1.05,1.92 C-1.05,1.92 6.84,9.66 6.84,9.66c " + android:valueTo="M6.84 9.66 C6.84,9.66 8.25,8.25 8.25,8.25 C8.25,8.25 0.25,0.25 0.25,0.25 C0.25,0.25 -1.12,1.71 -1.12,1.71 C-1.12,1.71 6.84,9.66 6.84,9.66c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_1_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="translateXY" android:duration="250" + android:startOffset="0" android:propertyXName="translateX" + android:propertyYName="translateY" + android:pathData="M -0.004,-3.608C -0.004,-2.39758332538605 -0.004,2.44358332538605 -0.004,3.654"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_1_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="rotation" android:duration="200" + android:startOffset="0" android:valueFrom="0" android:valueTo="-90" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_D_0_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="pathData" android:duration="50" + android:startOffset="0" + android:valueFrom="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 8.25,0.25 8.25,0.25 C8.25,0.25 9.64,1.69 9.64,1.69 C9.64,1.69 1.66,9.66 1.66,9.66c " + android:valueTo="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 8.2,0.59 8.2,0.59 C8.2,0.59 9.84,1.88 9.84,1.88 C9.84,1.88 1.66,9.66 1.66,9.66c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="17" + android:startOffset="50" + android:valueFrom="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 8.2,0.59 8.2,0.59 C8.2,0.59 9.84,1.88 9.84,1.88 C9.84,1.88 1.66,9.66 1.66,9.66c " + android:valueTo="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 8.25,0.25 8.25,0.25 C8.25,0.25 9.64,1.69 9.64,1.69 C9.64,1.69 1.66,9.66 1.66,9.66c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="17" + android:startOffset="67" + android:valueFrom="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 8.25,0.25 8.25,0.25 C8.25,0.25 9.64,1.69 9.64,1.69 C9.64,1.69 1.66,9.66 1.66,9.66c " + android:valueTo="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 7.45,0.93 7.45,0.93 C7.45,0.93 9.23,2.03 9.23,2.03 C9.23,2.03 1.66,9.66 1.66,9.66c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="17" + android:startOffset="83" + android:valueFrom="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 7.45,0.93 7.45,0.93 C7.45,0.93 9.23,2.03 9.23,2.03 C9.23,2.03 1.66,9.66 1.66,9.66c " + android:valueTo="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 8.25,0.25 8.25,0.25 C8.25,0.25 9.37,1.99 9.37,1.99 C9.37,1.99 1.66,9.66 1.66,9.66c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="33" + android:startOffset="100" + android:valueFrom="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 8.25,0.25 8.25,0.25 C8.25,0.25 9.37,1.99 9.37,1.99 C9.37,1.99 1.66,9.66 1.66,9.66c " + android:valueTo="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 8.25,0.25 8.25,0.25 C8.25,0.25 9.64,1.69 9.64,1.69 C9.64,1.69 1.66,9.66 1.66,9.66c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="17" + android:startOffset="133" + android:valueFrom="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 8.25,0.25 8.25,0.25 C8.25,0.25 9.64,1.69 9.64,1.69 C9.64,1.69 1.66,9.66 1.66,9.66c " + android:valueTo="M1.66 9.66 C1.66,9.66 0.25,8.25 0.25,8.25 C0.25,8.25 8.25,0.25 8.25,0.25 C8.25,0.25 9.64,1.69 9.64,1.69 C9.64,1.69 1.66,9.66 1.66,9.66c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="translateXY" android:duration="250" + android:startOffset="0" android:propertyXName="translateX" + android:propertyYName="translateY" + android:pathData="M 0,-3.585C 0,-2.3787500476837202 0,2.4457500476837204 0,3.652"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="rotation" android:duration="200" + android:startOffset="0" android:valueFrom="0" android:valueTo="90" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="time_group"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="translateX" android:duration="267" + android:startOffset="0" android:valueFrom="0" android:valueTo="1" + android:valueType="floatType"/> + </set> + </aapt:attr> + </target> </animated-vector> diff --git a/packages/SystemUI/res/drawable/ic_volume_expand.xml b/packages/SystemUI/res/drawable/ic_volume_expand.xml deleted file mode 100644 index 79ff80806438..000000000000 --- a/packages/SystemUI/res/drawable/ic_volume_expand.xml +++ /dev/null @@ -1,55 +0,0 @@ -<!-- - Copyright (C) 2017 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<vector - xmlns:android="http://schemas.android.com/apk/res/android" - android:name="ic_caret_up" - android:width="24dp" - android:viewportWidth="24" - android:height="24dp" - android:viewportHeight="24" - android:tint="?android:attr/colorControlNormal" > - <group - android:name="caret___4" > - <group - android:name="right" - android:translateX="11.287" - android:translateY="15.287" - android:rotation="-45" > - <group - android:name="right_pivot" - android:translateX="4.242" > - <path - android:name="rectangle_path_1" - android:fillColor="#FFFFFFFF" - android:pathData="M -3.242,-1.0 l 6.484,0.0 c 0.5522847498,0.0 1.0,0.4477152502 1.0,1.0 l 0.0,0.0 c 0.0,0.5522847498 -0.4477152502,1.0 -1.0,1.0 l -6.484,0.0 c -0.5522847498,0.0 -1.0,-0.4477152502 -1.0,-1.0 l 0.0,0.0 c 0.0,-0.5522847498 0.4477152502,-1.0 1.0,-1.0 Z" /> - </group> - </group> - <group - android:name="left" - android:translateX="12.699" - android:translateY="15.287" - android:rotation="45" > - <group - android:name="left_pivot" - android:translateX="-4.242" > - <path - android:name="rectangle_path_2" - android:fillColor="#FFFFFFFF" - android:pathData="M -3.242,-1.0 l 6.484,0.0 c 0.5522847498,0.0 1.0,0.4477152502 1.0,1.0 l 0.0,0.0 c 0.0,0.5522847498 -0.4477152502,1.0 -1.0,1.0 l -6.484,0.0 c -0.5522847498,0.0 -1.0,-0.4477152502 -1.0,-1.0 l 0.0,0.0 c 0.0,-0.5522847498 0.4477152502,-1.0 1.0,-1.0 Z" /> - </group> - </group> - </group> -</vector> diff --git a/packages/SystemUI/res/drawable/ic_volume_expand_animation.xml b/packages/SystemUI/res/drawable/ic_volume_expand_animation.xml index abd6678e335f..152d26c8c1d7 100644 --- a/packages/SystemUI/res/drawable/ic_volume_expand_animation.xml +++ b/packages/SystemUI/res/drawable/ic_volume_expand_animation.xml @@ -13,13 +13,233 @@ See the License for the specific language governing permissions and limitations under the License. --> -<animated-vector - xmlns:android="http://schemas.android.com/apk/res/android" - android:drawable="@drawable/ic_volume_expand" > - <target - android:name="right" - android:animation="@anim/ic_caret_up_right_animation" /> - <target - android:name="left" - android:animation="@anim/ic_caret_up_left_animation" /> +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:aapt="http://schemas.android.com/aapt"> + <aapt:attr name="android:drawable"> + <vector android:height="24dp" android:width="24dp" android:viewportHeight="24" + android:viewportWidth="24"> + <group android:name="_R_G"> + <group android:name="_R_G_L_1_G_N_1_T_0" android:translateX="12" + android:translateY="12"> + <group android:name="_R_G_L_1_G_T_1" android:translateX="0" + android:translateY="3.439" android:rotation="0"> + <group android:name="_R_G_L_1_G" android:translateX="-8.25" + android:translateY="-8.1"> + <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 8.25,9.66 8.25,9.66 C8.25,9.66 8.25,6.83 8.25,6.83 C8.25,6.83 1.66,0.25 1.66,0.25c "/> + </group> + </group> + </group> + <group android:name="_R_G_L_0_G_N_1_T_0" android:translateX="12" + android:translateY="12"> + <group android:name="_R_G_L_0_G_T_1" android:translateX="0" + android:translateY="3.439" android:rotation="0"> + <group android:name="_R_G_L_0_G" android:translateX="-0.25" + android:translateY="-8.1"> + <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.25,9.66 0.25,9.66 C0.25,9.66 0.25,6.83 0.25,6.83 C0.25,6.83 6.84,0.25 6.84,0.25c "/> + </group> + </group> + </group> + </group> + <group android:name="time_group"/> + </vector> + </aapt:attr> + <target android:name="_R_G_L_1_G_D_0_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="pathData" android:duration="33" + android:startOffset="0" + android:valueFrom="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 8.25,9.66 8.25,9.66 C8.25,9.66 8.25,6.83 8.25,6.83 C8.25,6.83 1.66,0.25 1.66,0.25c " + android:valueTo="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 7.97,9.5 7.97,9.5 C7.97,9.5 8.59,7.15 8.59,7.15 C8.59,7.15 1.66,0.25 1.66,0.25c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="17" + android:startOffset="33" + android:valueFrom="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 7.97,9.5 7.97,9.5 C7.97,9.5 8.59,7.15 8.59,7.15 C8.59,7.15 1.66,0.25 1.66,0.25c " + android:valueTo="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 7.71,9.4 7.71,9.4 C7.71,9.4 8.59,7.15 8.59,7.15 C8.59,7.15 1.66,0.25 1.66,0.25c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="17" + android:startOffset="50" + android:valueFrom="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 7.71,9.4 7.71,9.4 C7.71,9.4 8.59,7.15 8.59,7.15 C8.59,7.15 1.66,0.25 1.66,0.25c " + android:valueTo="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 7.43,9.08 7.43,9.08 C7.43,9.08 8.87,7.47 8.87,7.47 C8.87,7.47 1.66,0.25 1.66,0.25c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="17" + android:startOffset="67" + android:valueFrom="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 7.43,9.08 7.43,9.08 C7.43,9.08 8.87,7.47 8.87,7.47 C8.87,7.47 1.66,0.25 1.66,0.25c " + android:valueTo="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 7.43,9.08 7.43,9.08 C7.43,9.08 9,7.68 9,7.68 C9,7.68 1.66,0.25 1.66,0.25c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="17" + android:startOffset="83" + android:valueFrom="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 7.43,9.08 7.43,9.08 C7.43,9.08 9,7.68 9,7.68 C9,7.68 1.66,0.25 1.66,0.25c " + android:valueTo="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 7.43,9.08 7.43,9.08 C7.43,9.08 9.08,7.81 9.08,7.81 C9.08,7.81 1.66,0.25 1.66,0.25c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="50" + android:startOffset="100" + android:valueFrom="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 7.43,9.08 7.43,9.08 C7.43,9.08 9.08,7.81 9.08,7.81 C9.08,7.81 1.66,0.25 1.66,0.25c " + android:valueTo="M1.66 0.25 C1.66,0.25 0.25,1.66 0.25,1.66 C0.25,1.66 7.95,9.61 7.95,9.61 C7.95,9.61 9.58,8.01 9.58,8.01 C9.58,8.01 1.66,0.25 1.66,0.25c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_1_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="translateXY" android:duration="250" + android:startOffset="0" android:propertyXName="translateX" + android:propertyYName="translateY" + android:pathData="M 0,3.439C 0,2.24316667461395 0,-2.54016667461395 0,-3.736"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_1_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="rotation" android:duration="200" + android:startOffset="0" android:valueFrom="0" android:valueTo="-90" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_D_0_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="pathData" android:duration="17" + android:startOffset="0" + android:valueFrom="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.24,9.63 0.24,9.63 C0.24,9.63 -0.15,7.2 -0.15,7.2 C-0.15,7.2 6.84,0.25 6.84,0.25c " + android:valueTo="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.22,9.59 0.22,9.59 C0.22,9.59 -0.4,7.45 -0.4,7.45 C-0.4,7.45 6.84,0.25 6.84,0.25c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="17" + android:startOffset="17" + android:valueFrom="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.22,9.59 0.22,9.59 C0.22,9.59 -0.4,7.45 -0.4,7.45 C-0.4,7.45 6.84,0.25 6.84,0.25c " + android:valueTo="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.36,9.46 0.36,9.46 C0.36,9.46 -0.44,7.6 -0.44,7.6 C-0.44,7.6 6.84,0.25 6.84,0.25c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="17" + android:startOffset="33" + android:valueFrom="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.36,9.46 0.36,9.46 C0.36,9.46 -0.44,7.6 -0.44,7.6 C-0.44,7.6 6.84,0.25 6.84,0.25c " + android:valueTo="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.71,9.4 0.71,9.4 C0.71,9.4 -0.61,7.63 -0.61,7.63 C-0.61,7.63 6.84,0.25 6.84,0.25c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="33" + android:startOffset="50" + android:valueFrom="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.71,9.4 0.71,9.4 C0.71,9.4 -0.61,7.63 -0.61,7.63 C-0.61,7.63 6.84,0.25 6.84,0.25c " + android:valueTo="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.93,9.29 0.93,9.29 C0.93,9.29 -0.5,7.69 -0.5,7.69 C-0.5,7.69 6.84,0.25 6.84,0.25c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="17" + android:startOffset="83" + android:valueFrom="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.93,9.29 0.93,9.29 C0.93,9.29 -0.5,7.69 -0.5,7.69 C-0.5,7.69 6.84,0.25 6.84,0.25c " + android:valueTo="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.84,9.33 0.84,9.33 C0.84,9.33 -0.53,7.8 -0.53,7.8 C-0.53,7.8 6.84,0.25 6.84,0.25c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator android:propertyName="pathData" android:duration="117" + android:startOffset="100" + android:valueFrom="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.84,9.33 0.84,9.33 C0.84,9.33 -0.53,7.8 -0.53,7.8 C-0.53,7.8 6.84,0.25 6.84,0.25c " + android:valueTo="M6.84 0.25 C6.84,0.25 8.25,1.66 8.25,1.66 C8.25,1.66 0.56,9.63 0.56,9.63 C0.56,9.63 -1.08,8.07 -1.08,8.07 C-1.08,8.07 6.84,0.25 6.84,0.25c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="translateXY" android:duration="250" + android:startOffset="0" android:propertyXName="translateX" + android:propertyYName="translateY" + android:pathData="M 0,3.439C 0,2.24316667461395 0,-2.54016667461395 0,-3.736"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="rotation" android:duration="200" + android:startOffset="0" android:valueFrom="0" android:valueTo="90" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="time_group"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator android:propertyName="translateX" android:duration="267" + android:startOffset="0" android:valueFrom="0" android:valueTo="1" + android:valueType="floatType"/> + </set> + </aapt:attr> + </target> </animated-vector> diff --git a/packages/SystemUI/res/layout-land/global_actions_column.xml b/packages/SystemUI/res/layout-land/global_actions_column.xml new file mode 100644 index 000000000000..99a4e13d2064 --- /dev/null +++ b/packages/SystemUI/res/layout-land/global_actions_column.xml @@ -0,0 +1,68 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> + +<com.android.systemui.globalactions.GlobalActionsColumnLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@id/global_actions_view" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="horizontal" + android:clipToPadding="false" + android:theme="@style/qs_theme" + android:gravity="center_horizontal | top" + android:clipChildren="false" +> + <LinearLayout + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:padding="0dp" + android:orientation="horizontal" + android:clipChildren="false" + android:clipToPadding="false" + > + <!-- Grid of action items --> + <LinearLayout + android:id="@android:id/list" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:layout_marginTop="@dimen/global_actions_grid_side_margin" + android:translationZ="@dimen/global_actions_translate" + android:paddingLeft="@dimen/global_actions_grid_horizontal_padding" + android:paddingRight="@dimen/global_actions_grid_horizontal_padding" + android:paddingTop="@dimen/global_actions_grid_vertical_padding" + android:paddingBottom="@dimen/global_actions_grid_vertical_padding" + android:background="?android:attr/colorBackgroundFloating" + /> + <!-- For separated items--> + <LinearLayout + android:id="@+id/separated_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginLeft="@dimen/global_actions_grid_side_margin" + android:layout_marginTop="@dimen/global_actions_grid_side_margin" + android:paddingLeft="@dimen/global_actions_grid_horizontal_padding" + android:paddingRight="@dimen/global_actions_grid_horizontal_padding" + android:paddingTop="@dimen/global_actions_grid_vertical_padding" + android:paddingBottom="@dimen/global_actions_grid_vertical_padding" + android:orientation="horizontal" + android:background="?android:attr/colorBackgroundFloating" + android:translationZ="@dimen/global_actions_translate" + /> + </LinearLayout> + +</com.android.systemui.globalactions.GlobalActionsColumnLayout> diff --git a/packages/SystemUI/res/layout-land/global_actions_column_seascape.xml b/packages/SystemUI/res/layout-land/global_actions_column_seascape.xml new file mode 100644 index 000000000000..0f8613194959 --- /dev/null +++ b/packages/SystemUI/res/layout-land/global_actions_column_seascape.xml @@ -0,0 +1,68 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> + +<com.android.systemui.globalactions.GlobalActionsColumnLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@id/global_actions_view" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="horizontal" + android:clipToPadding="false" + android:theme="@style/qs_theme" + android:gravity="center_horizontal | bottom" + android:clipChildren="false" +> + <LinearLayout + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:padding="0dp" + android:orientation="horizontal" + > + <!-- Grid of action items --> + <com.android.systemui.globalactions.ListGridLayout + android:id="@android:id/list" + android:layout_gravity="bottom|left" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:layout_marginBottom="@dimen/global_actions_grid_side_margin" + android:translationZ="@dimen/global_actions_translate" + android:paddingLeft="@dimen/global_actions_grid_horizontal_padding" + android:paddingRight="@dimen/global_actions_grid_horizontal_padding" + android:paddingTop="@dimen/global_actions_grid_vertical_padding" + android:paddingBottom="@dimen/global_actions_grid_vertical_padding" + android:background="?android:attr/colorBackgroundFloating" + /> + <!-- For separated items--> + <LinearLayout + android:id="@+id/separated_button" + android:layout_gravity="top|left" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginRight="@dimen/global_actions_grid_side_margin" + android:layout_marginBottom="@dimen/global_actions_grid_side_margin" + android:paddingLeft="@dimen/global_actions_grid_horizontal_padding" + android:paddingRight="@dimen/global_actions_grid_horizontal_padding" + android:paddingTop="@dimen/global_actions_grid_vertical_padding" + android:paddingBottom="@dimen/global_actions_grid_vertical_padding" + android:orientation="horizontal" + android:background="?android:attr/colorBackgroundFloating" + android:translationZ="@dimen/global_actions_translate" + /> + </LinearLayout> + +</com.android.systemui.globalactions.GlobalActionsColumnLayout> diff --git a/packages/SystemUI/res/layout/bubble_expanded_view.xml b/packages/SystemUI/res/layout/bubble_expanded_view.xml index 65ede3def821..b73412526f5e 100644 --- a/packages/SystemUI/res/layout/bubble_expanded_view.xml +++ b/packages/SystemUI/res/layout/bubble_expanded_view.xml @@ -33,13 +33,15 @@ android:layout_height="wrap_content" android:animateLayoutChanges="true"> - <ImageView + <com.android.systemui.statusbar.AlphaOptimizedButton + style="@android:style/Widget.Material.Button.Borderless" android:id="@+id/settings_button" - android:layout_width="@dimen/bubble_header_icon_size" - android:layout_height="@dimen/bubble_header_icon_size" - android:src="@drawable/ic_settings" - android:scaleType="center" - android:layout_gravity="end"/> + android:layout_gravity="end" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:focusable="true" + android:text="@string/manage_bubbles_text" + android:textColor="?attr/wallpaperTextColor"/> <include layout="@layout/bubble_permission_view" android:id="@+id/permission_layout" diff --git a/packages/SystemUI/res/layout/global_actions_column.xml b/packages/SystemUI/res/layout/global_actions_column.xml new file mode 100644 index 000000000000..b58146b6ce41 --- /dev/null +++ b/packages/SystemUI/res/layout/global_actions_column.xml @@ -0,0 +1,70 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> + +<com.android.systemui.globalactions.GlobalActionsColumnLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@id/global_actions_view" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + android:clipToPadding="false" + android:theme="@style/qs_theme" + android:gravity="center_vertical | right" + android:clipChildren="false" +> + <LinearLayout + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:gravity="top | right" + android:orientation="vertical" + android:padding="0dp" + android:layout_marginTop="@dimen/global_actions_grid_container_bottom_margin" + android:layout_marginBottom="@dimen/global_actions_grid_container_bottom_margin" + > + <!-- Global actions is right-aligned to be physically near power button --> + <LinearLayout + android:id="@android:id/list" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical" + android:gravity="right" + android:translationZ="@dimen/global_actions_translate" + android:layout_marginRight="@dimen/global_actions_grid_side_margin" + android:paddingLeft="@dimen/global_actions_grid_horizontal_padding" + android:paddingRight="@dimen/global_actions_grid_horizontal_padding" + android:paddingTop="@dimen/global_actions_grid_vertical_padding" + android:paddingBottom="@dimen/global_actions_grid_vertical_padding" + /> + + <!-- For separated items--> + <LinearLayout + android:id="@+id/separated_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/global_actions_grid_side_margin" + android:layout_marginRight="@dimen/global_actions_grid_side_margin" + android:paddingLeft="@dimen/global_actions_grid_horizontal_padding" + android:paddingRight="@dimen/global_actions_grid_horizontal_padding" + android:paddingTop="@dimen/global_actions_grid_vertical_padding" + android:paddingBottom="@dimen/global_actions_grid_vertical_padding" + android:orientation="vertical" + android:gravity="center" + android:translationZ="@dimen/global_actions_translate" + /> + </LinearLayout> + +</com.android.systemui.globalactions.GlobalActionsColumnLayout> diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml index 58af4a2c24b6..087e0bd8089f 100644 --- a/packages/SystemUI/res/layout/notification_info.xml +++ b/packages/SystemUI/res/layout/notification_info.xml @@ -103,7 +103,6 @@ asked for it --> android:id="@+id/channel_info" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginBottom="@dimen/notification_guts_button_spacing" android:paddingEnd="@*android:dimen/notification_content_margin_end" android:gravity="center" android:orientation="vertical"> @@ -127,6 +126,7 @@ asked for it --> android:id="@+id/blocking_helper" android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_marginTop="@dimen/notification_guts_button_spacing" android:layout_marginBottom="@dimen/notification_guts_button_spacing" android:paddingEnd="@*android:dimen/notification_content_margin_end" android:clipChildren="false" @@ -216,67 +216,107 @@ asked for it --> android:id="@+id/interruptiveness_settings" android:layout_width="match_parent" android:layout_height="wrap_content" + android:gravity="center" android:orientation="vertical"> - <LinearLayout - android:id="@+id/buttons" + <RelativeLayout + android:id="@+id/alert" android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="horizontal" - android:gravity="center"> - - <Button - android:id="@+id/alert" - android:minWidth="@dimen/notification_importance_button_width" + android:padding="@dimen/notification_importance_button_padding" + android:clickable="true" + android:focusable="true"> + <ImageView + android:id="@+id/alert_icon" + android:src="@drawable/ic_notification_interruptive" + android:background="@android:color/transparent" + android:layout_gravity="center" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:minHeight="@dimen/notification_importance_toggle_size" - android:paddingStart="@dimen/notification_importance_button_horiz_padding" - android:paddingEnd="@dimen/notification_importance_button_horiz_padding" - android:drawablePadding="@dimen/notification_importance_drawable_padding" - android:foreground="@drawable/button_ripple_radius" - android:drawableLeft="@drawable/ic_notification_interruptive" - android:text="@string/notification_alert_title" /> - - <Button - android:id="@+id/silence" - android:minWidth="@dimen/notification_importance_button_width" - android:layout_width="wrap_content" + android:clickable="false" + android:focusable="false"/> + <TextView + android:id="@+id/alert_label" + android:layout_width="match_parent" android:layout_height="wrap_content" - android:minHeight="@dimen/notification_importance_toggle_size" - android:paddingStart="@dimen/notification_importance_button_horiz_padding" - android:paddingEnd="@dimen/notification_importance_button_horiz_padding" - android:drawablePadding="@dimen/notification_importance_drawable_padding" - android:foreground="@drawable/button_ripple_radius" - android:layout_marginStart="@dimen/notification_importance_button_separation" - android:drawableLeft="@drawable/ic_notification_gentle" - android:text="@string/notification_silence_title" /> - </LinearLayout> + android:ellipsize="end" + android:maxLines="1" + android:clickable="false" + android:focusable="false" + android:layout_toEndOf="@id/alert_icon" + android:layout_marginStart="@dimen/notification_importance_drawable_padding" + android:textAppearance="@style/TextAppearance.NotificationImportanceButton.Unselected" + android:text="@string/notification_alert_title"/> + <TextView + android:id="@+id/alert_summary" + android:paddingTop="@dimen/notification_importance_button_padding" + android:text="@string/notification_channel_summary_default" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:clickable="false" + android:focusable="false" + android:ellipsize="end" + android:maxLines="2" + android:layout_below="@id/alert_icon" + android:textAppearance="@style/TextAppearance.NotificationImportanceDetail"/> + </RelativeLayout> - <TextView - android:id="@+id/description" + <RelativeLayout + android:id="@+id/silence" android:layout_width="match_parent" android:layout_height="wrap_content" - android:text="@string/notification_alert_title" - android:gravity="center" - android:layout_marginTop="@dimen/notification_importance_text_marginTop" - android:paddingStart="@dimen/notification_importance_description_padding" - android:paddingEnd="@dimen/notification_importance_description_padding" - android:textAppearance="@style/TextAppearance.NotificationImportanceDetail" /> + android:padding="@dimen/notification_importance_button_padding" + android:layout_marginTop="@dimen/notification_importance_button_separation" + android:clickable="true" + android:focusable="true"> + <ImageView + android:id="@+id/silence_icon" + android:src="@drawable/ic_notification_gentle" + android:background="@android:color/transparent" + android:layout_gravity="center" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:clickable="false" + android:focusable="false"/> + <TextView + android:id="@+id/silence_label" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:ellipsize="end" + android:maxLines="1" + android:clickable="false" + android:focusable="false" + android:layout_toEndOf="@id/silence_icon" + android:layout_marginStart="@dimen/notification_importance_drawable_padding" + android:textAppearance="@style/TextAppearance.NotificationImportanceButton.Unselected" + android:text="@string/notification_silence_title"/> + <TextView + android:id="@+id/silence_summary" + android:paddingTop="@dimen/notification_importance_button_padding" + android:text="@string/notification_channel_summary_default" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:clickable="false" + android:focusable="false" + android:ellipsize="end" + android:maxLines="2" + android:layout_below="@id/silence_icon" + android:textAppearance="@style/TextAppearance.NotificationImportanceDetail"/> + </RelativeLayout> + </LinearLayout> <RelativeLayout android:id="@+id/bottom_buttons" android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingTop="@dimen/notification_guts_button_spacing" > + android:layout_marginTop="@dimen/notification_guts_button_spacing" > <TextView android:id="@+id/turn_off_notifications" android:text="@string/inline_turn_off_notifications" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentStart="true" - android:layout_centerVertical="true" android:minWidth="@dimen/notification_importance_toggle_size" android:minHeight="@dimen/notification_importance_toggle_size" android:maxWidth="200dp" @@ -286,11 +326,11 @@ asked for it --> android:text="@string/inline_ok_button" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_centerVertical="true" + android:layout_alignParentEnd="true" + android:gravity="center_vertical|end" android:maxWidth="125dp" android:minWidth="@dimen/notification_importance_toggle_size" android:minHeight="@dimen/notification_importance_toggle_size" - android:layout_alignParentEnd="true" style="@style/TextAppearance.NotificationInfo.Button"/> </RelativeLayout> diff --git a/packages/SystemUI/res/layout/qs_carrier.xml b/packages/SystemUI/res/layout/qs_carrier.xml index 8f748069aa40..8dd06f040d09 100644 --- a/packages/SystemUI/res/layout/qs_carrier.xml +++ b/packages/SystemUI/res/layout/qs_carrier.xml @@ -42,6 +42,7 @@ android:layout_weight="1" android:textAppearance="@style/TextAppearance.QS.Status" android:textDirection="locale" + android:marqueeRepeatLimit="marquee_forever" android:singleLine="true" android:maxEms="7"/> diff --git a/packages/SystemUI/res/layout/quick_qs_status_icons.xml b/packages/SystemUI/res/layout/quick_qs_status_icons.xml index 7f69cf4d239c..5b7e7e7d59a3 100644 --- a/packages/SystemUI/res/layout/quick_qs_status_icons.xml +++ b/packages/SystemUI/res/layout/quick_qs_status_icons.xml @@ -19,8 +19,8 @@ android:id="@+id/quick_qs_status_icons" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginTop="@dimen/qs_header_top_margin" - android:layout_marginBottom="14dp" + android:paddingTop="@dimen/qs_header_top_padding" + android:paddingBottom="@dimen/qs_header_bottom_padding" android:paddingStart="@dimen/status_bar_padding_start" android:paddingEnd="@dimen/status_bar_padding_end" android:layout_below="@id/quick_status_bar_system_icons" diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml index f9bf47bb7d08..89d1a19737ee 100644 --- a/packages/SystemUI/res/values-night/colors.xml +++ b/packages/SystemUI/res/values-night/colors.xml @@ -37,16 +37,17 @@ <color name="notification_ripple_untinted_color">#30ffffff</color> <!-- The "inside" of a notification, reached via longpress --> - <color name="notification_guts_bg_color">@*android:color/notification_material_background_color</color> + <color name="notification_guts_bg_color">@color/GM2_grey_900</color> <!-- The color of the text inside a notification --> <color name="notification_primary_text_color">@*android:color/notification_primary_text_color_dark</color> - <color name="notification_guts_selection_bg">#202124</color> - <color name="notification_guts_selection_border">#669DF6</color> - <color name="notification_guts_link_icon_tint">@color/GM2_grey_200</color> + <color name="notification_guts_selection_bg">@color/GM2_grey_800</color> + <color name="notification_guts_selection_border">@color/GM2_grey_700</color> + <color name="notification_guts_link_icon_tint">@color/GM2_grey_500</color> <color name="notification_guts_sub_text_color">@color/GM2_grey_200</color> - <color name="notification_guts_header_text_color">@color/GM2_grey_100</color> + <color name="notification_guts_header_text_color">@color/GM2_grey_200</color> + <color name="notification_guts_button_color">@color/GM2_blue_200</color> <!-- The color of the background in the top part of QSCustomizer --> <color name="qs_customize_background">@color/GM2_grey_900</color> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index 4e1a7d07e687..b673e5207f80 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -95,6 +95,7 @@ <color name="notification_guts_header_text_color">@color/GM2_grey_900</color> <color name="notification_silence_color">#FF32c1de</color> <color name="notification_alert_color">#FFF87B2B</color> + <color name="notification_guts_button_color">@color/GM2_blue_700</color> <color name="assist_orb_color">#ffffff</color> @@ -177,6 +178,9 @@ <color name="GM2_red_300">#F28B82</color> <color name="GM2_red_500">#B71C1C</color> + <color name="GM2_blue_200">#AECBFA</color> + <color name="GM2_blue_700">#1967D2</color> + <color name="GM2_yellow_500">#FFFBBC04</color> <color name="GM2_green_500">#FF34A853</color> </resources> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 4abe9f0bfb5c..2871d06d8f8e 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -494,4 +494,7 @@ <!-- Launcher package name for overlaying icons. --> <string name="launcher_overlayable_package" translatable="false">com.android.launcher3</string> + <!-- ThemePicker package name for overlaying icons. --> + <string name="themepicker_overlayable_package" translatable="false">com.android.wallpaper</string> + </resources> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 955cfb022125..ce958ab3f739 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -212,7 +212,7 @@ <dimen name="notification_guts_option_horizontal_padding">15dp</dimen> <!-- The vertical space between items in the alert selections in the inline settings --> - <dimen name="notification_guts_option_vertical_padding">24dp</dimen> + <dimen name="notification_guts_option_vertical_padding">20dp</dimen> <!-- The vertical space between the alert selections in the inline settings --> <dimen name="notification_guts_option_vertical_margin">6dp</dimen> @@ -226,10 +226,12 @@ <dimen name="notification_importance_button_horiz_padding">28dp</dimen> <dimen name="notification_importance_drawable_padding">8dp</dimen> <dimen name="notification_importance_description_padding">20dp</dimen> - <dimen name="notification_importance_description_text">12sp</dimen> + <dimen name="notification_importance_header_text">12sp</dimen> + <dimen name="notification_importance_description_text">14sp</dimen> <dimen name="notification_importance_channel_text">16sp</dimen> <dimen name="notification_importance_channel_group_text">14sp</dimen> <dimen name="notification_importance_button_text">16sp</dimen> + <dimen name="notification_importance_button_padding">14dp</dimen> <dimen name="rect_button_radius">8dp</dimen> <!-- The minimum height for the snackbar shown after the snooze option has been chosen. --> @@ -492,7 +494,8 @@ <dimen name="qs_footer_padding_end">16dp</dimen> <dimen name="qs_footer_icon_size">16dp</dimen> <dimen name="qs_paged_tile_layout_padding_bottom">0dp</dimen> - <dimen name="qs_header_top_margin">15dp</dimen> + <dimen name="qs_header_top_padding">15dp</dimen> + <dimen name="qs_header_bottom_padding">14dp</dimen> <dimen name="qs_notif_collapsed_space">64dp</dimen> diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml index 501b1b56a848..f75f255324a6 100644 --- a/packages/SystemUI/res/values/ids.xml +++ b/packages/SystemUI/res/values/ids.xml @@ -108,6 +108,8 @@ <item type="id" name="display_cutout" /> + <item type="id" name="row_tag_for_content_view" /> + <!-- Optional cancel button on Keyguard --> <item type="id" name="cancel_button"/> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 60d71260058f..dc35653e5f7d 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1659,19 +1659,19 @@ <string name="notification_alert_title">Prioritized</string> <!-- [CHAR LIMIT=150] Notification Importance title: low importance level summary --> - <string name="notification_channel_summary_low">Always silent. Displays in pull-down shade.</string> + <string name="notification_channel_summary_low">Helps you focus with notifications only in the pull-down shade. Always silent.</string> <!-- [CHAR LIMIT=150] Notification Importance title: low importance level summary --> - <string name="notification_channel_summary_low_status">Always silent. Displays in pull-down shade & status bar.</string> + <string name="notification_channel_summary_low_status">Displays below priority notifications. Always silent.</string> <!-- [CHAR LIMIT=150] Notification Importance title: low importance level summary --> - <string name="notification_channel_summary_low_lock">Always silent. Displays in pull-down shade & on lock screen.</string> + <string name="notification_channel_summary_low_lock">Displays below priority notifications. Always silent.</string> <!-- [CHAR LIMIT=150] Notification Importance title: low importance level summary --> - <string name="notification_channel_summary_low_status_lock">Always silent. Displays in pull-down shade, status bar & on lock screen.</string> + <string name="notification_channel_summary_low_status_lock">Displays below priority notifications. Always silent.</string> <!-- [CHAR LIMIT=150] Notification Importance title: normal importance level summary --> - <string name="notification_channel_summary_default">Makes sound and displays in pull-down shade, status bar & on lock screen.</string> + <string name="notification_channel_summary_default">Gets your attention with sound & a status bar icon. Shows on lock screen.</string> <!-- Notification: Control panel: Label that displays when the app's notifications cannot be blocked. --> <string name="notification_unblockable_desc">These notifications can\'t be modified.</string> @@ -2414,6 +2414,8 @@ <!-- Text for asking the user whether bubbles (floating app content) should be enabled for an app. [CHAR LIMIT=NONE] --> <string name="bubbles_prompt">Allow bubbles from <xliff:g id="app_name" example="YouTube">%1$s</xliff:g>?</string> + <!-- The text for the manage bubbles link. [CHAR LIMIT=NONE] --> + <string name="manage_bubbles_text">Manage</string> <!-- Text used for button allowing user to opt out of bubbles [CHAR LIMIT=20] --> <string name="no_bubbles">Deny</string> <!-- Text used for button allowing user to approve / enable bubbles [CHAR LIMIT=20] --> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index d765f0c157bd..aa89dce4114a 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -452,7 +452,7 @@ <style name="TextAppearance.NotificationInfo.Button"> <item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item> <item name="android:textSize">16sp</item> - <item name="android:textColor">?android:attr/colorAccent</item> + <item name="android:textColor">@color/notification_guts_button_color</item> <item name="android:background">@drawable/btn_borderless_rect</item> <item name="android:gravity">center_vertical</item> <item name="android:focusable">true</item> @@ -473,7 +473,7 @@ </style> <style name="TextAppearance.NotificationImportanceHeader"> - <item name="android:textSize">@dimen/notification_importance_description_text</item> + <item name="android:textSize">@dimen/notification_importance_header_text</item> <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item> <item name="android:textColor">@color/notification_guts_header_text_color</item> </style> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java index 1a684a0ffb8f..f384507d302c 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java @@ -151,19 +151,34 @@ public class PluginInstanceManager<T extends Plugin> { return plugins.size() != 0; } - private void disable(PluginInfo info, - @PluginEnabler.DisableReason int reason) { + private boolean isPluginWhitelisted(ComponentName pluginName) { + for (String componentNameOrPackage : mWhitelistedPlugins) { + ComponentName componentName = ComponentName.unflattenFromString(componentNameOrPackage); + if (componentName == null) { + if (componentNameOrPackage.equals(pluginName.getPackageName())) { + return true; + } + } else { + if (componentName.equals(pluginName)) { + return true; + } + } + } + return false; + } + + private void disable(PluginInfo info, @PluginEnabler.DisableReason int reason) { // Live by the sword, die by the sword. // Misbehaving plugins get disabled and won't come back until uninstall/reinstall. + ComponentName pluginComponent = new ComponentName(info.mPackage, info.mClass); // If a plugin is detected in the stack of a crash then this will be called for that // plugin, if the plugin causing a crash cannot be identified, they are all disabled // assuming one of them must be bad. - if (mWhitelistedPlugins.contains(info.mPackage)) { + if (isPluginWhitelisted(pluginComponent)) { // Don't disable whitelisted plugins as they are a part of the OS. return; } - ComponentName pluginComponent = new ComponentName(info.mPackage, info.mClass); Log.w(TAG, "Disabling plugin " + pluginComponent.flattenToShortString()); mManager.getPluginEnabler().setDisabled(pluginComponent, reason); } @@ -288,6 +303,13 @@ public class PluginInstanceManager<T extends Plugin> { if (result.size() > 1 && !mAllowMultiple) { // TODO: Show warning. Log.w(TAG, "Multiple plugins found for " + mAction); + if (DEBUG) { + for (ResolveInfo info : result) { + ComponentName name = new ComponentName(info.serviceInfo.packageName, + info.serviceInfo.name); + Log.w(TAG, " " + name); + } + } return; } for (ResolveInfo info : result) { @@ -295,15 +317,17 @@ public class PluginInstanceManager<T extends Plugin> { info.serviceInfo.name); PluginInfo<T> t = handleLoadPlugin(name); if (t == null) continue; - mMainHandler.obtainMessage(mMainHandler.PLUGIN_CONNECTED, t).sendToTarget(); + + // add plugin before sending PLUGIN_CONNECTED message mPlugins.add(t); + mMainHandler.obtainMessage(mMainHandler.PLUGIN_CONNECTED, t).sendToTarget(); } } protected PluginInfo<T> handleLoadPlugin(ComponentName component) { // This was already checked, but do it again here to make extra extra sure, we don't // use these on production builds. - if (!isDebuggable && !mWhitelistedPlugins.contains(component.getPackageName())) { + if (!isDebuggable && !isPluginWhitelisted(component)) { // Never ever ever allow these on production builds, they are only for prototyping. Log.w(TAG, "Plugin cannot be loaded on production build: " + component); return null; diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl index 2c5fa60d3516..b826b30f3245 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl @@ -38,11 +38,6 @@ interface ISystemUiProxy { void startScreenPinning(int taskId) = 1; /** - * Enables/disables launcher/overview interaction features {@link InteractionType}. - */ - void setInteractionState(int flags) = 4; - - /** * Notifies SystemUI that split screen has been invoked. */ void onSplitScreenInvoked() = 5; diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java deleted file mode 100644 index 8d6f830c6d8e..000000000000 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.systemui.shared.system; - -import android.annotation.IntDef; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -public class NavigationBarCompat extends QuickStepContract { - - @Retention(RetentionPolicy.SOURCE) - @IntDef({HIT_TARGET_NONE, HIT_TARGET_BACK, HIT_TARGET_HOME, HIT_TARGET_OVERVIEW}) - public @interface HitTarget{} - - public static final int HIT_TARGET_NONE = 0; - public static final int HIT_TARGET_BACK = 1; - public static final int HIT_TARGET_HOME = 2; - public static final int HIT_TARGET_OVERVIEW = 3; - public static final int HIT_TARGET_ROTATION = 4; - public static final int HIT_TARGET_DEAD_ZONE = 5; - - @Retention(RetentionPolicy.SOURCE) - @IntDef({FLAG_DISABLE_SWIPE_UP, - FLAG_DISABLE_QUICK_SCRUB, - FLAG_SHOW_OVERVIEW_BUTTON - }) - public @interface InteractionType {} - - /** - * Interaction type: whether the gesture to swipe up from the navigation bar will trigger - * launcher to show overview - */ - public static final int FLAG_DISABLE_SWIPE_UP = 0x1; - /** - * Interaction type: enable quick scrub interaction on the home button - */ - public static final int FLAG_DISABLE_QUICK_SCRUB = 0x2; - - /** - * Interaction type: show/hide the overview button while this service is connected to launcher - */ - public static final int FLAG_SHOW_OVERVIEW_BUTTON = 0x4; - - -} diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java index 6e3eb7c397bc..d250acca32c5 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java @@ -70,6 +70,15 @@ public class TaskStackChangeListeners extends TaskStackListener { public void removeListener(TaskStackChangeListener listener) { mTaskStackListeners.remove(listener); + if (mTaskStackListeners.isEmpty() && mRegistered) { + // Unregister mTaskStackListener once we have no more listeners + try { + ActivityTaskManager.getService().unregisterTaskStackListener(this); + mRegistered = false; + } catch (Exception e) { + Log.w(TAG, "Failed to call unregisterTaskStackListener", e); + } + } } @Override diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java index 0a6885c0ae69..037a8d3146d2 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java @@ -59,14 +59,22 @@ public class KeyguardMessageArea extends TextView implements SecurityMessageDisp private ColorStateList mDefaultColorState; private CharSequence mMessage; private ColorStateList mNextMessageColorState = ColorStateList.valueOf(DEFAULT_COLOR); + private boolean mBouncerVisible; private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() { public void onFinishedGoingToSleep(int why) { setSelected(false); - }; + } + public void onStartedWakingUp() { setSelected(true); - }; + } + + @Override + public void onKeyguardBouncerChanged(boolean bouncer) { + mBouncerVisible = bouncer; + update(); + } }; public KeyguardMessageArea(Context context) { @@ -188,7 +196,7 @@ public class KeyguardMessageArea extends TextView implements SecurityMessageDisp private void update() { CharSequence status = mMessage; - setVisibility(TextUtils.isEmpty(status) ? INVISIBLE : VISIBLE); + setVisibility(TextUtils.isEmpty(status) || !mBouncerVisible ? INVISIBLE : VISIBLE); setText(status); ColorStateList colorState = mDefaultColorState; if (mNextMessageColorState.getDefaultColor() != DEFAULT_COLOR) { diff --git a/packages/SystemUI/src/com/android/keyguard/clock/AnalogClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/AnalogClockController.java index 123e1380db0a..eec1d701cb5c 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/AnalogClockController.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/AnalogClockController.java @@ -189,6 +189,7 @@ public class AnalogClockController implements ClockPlugin { @Override public void setDarkAmount(float darkAmount) { mClockPosition.setDarkAmount(darkAmount); + mBigClockView.setDarkAmount(darkAmount); } @Override diff --git a/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java index 8db61b827f3c..5fec61b419fc 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java @@ -181,6 +181,7 @@ public class BubbleClockController implements ClockPlugin { @Override public void setDarkAmount(float darkAmount) { mClockPosition.setDarkAmount(darkAmount); + mView.setDarkAmount(darkAmount); } @Override diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java index 962b8f832fb7..19083459a87c 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java @@ -20,6 +20,7 @@ import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset; import android.content.Context; import android.content.res.Resources; import android.util.AttributeSet; +import android.util.MathUtils; import android.view.View; import android.widget.FrameLayout; @@ -43,6 +44,8 @@ public class ClockLayout extends FrameLayout { private int mBurnInPreventionOffsetX; private int mBurnInPreventionOffsetY; + private float mDarkAmount; + public ClockLayout(Context context) { this(context, null); } @@ -78,11 +81,21 @@ public class ClockLayout extends FrameLayout { positionChildren(); } + /** + * See {@link com.android.systemui.plugins.ClockPlugin#setDarkAmount(float)}. + */ + void setDarkAmount(float darkAmount) { + mDarkAmount = darkAmount; + positionChildren(); + } + private void positionChildren() { - final float offsetX = getBurnInOffset(mBurnInPreventionOffsetX * 2, true) - - mBurnInPreventionOffsetX; - final float offsetY = getBurnInOffset(mBurnInPreventionOffsetY * 2, false) - - mBurnInPreventionOffsetY; + final float offsetX = MathUtils.lerp(0f, + getBurnInOffset(mBurnInPreventionOffsetX * 2, true) - mBurnInPreventionOffsetX, + mDarkAmount); + final float offsetY = MathUtils.lerp(0f, + getBurnInOffset(mBurnInPreventionOffsetY * 2, false) - mBurnInPreventionOffsetY, + mDarkAmount); // Put the analog clock in the middle of the screen. if (mAnalogClock != null) { diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java index 07c2f10e58b3..9f4c403ed787 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java @@ -32,7 +32,6 @@ import android.view.LayoutInflater; import androidx.annotation.VisibleForTesting; import androidx.lifecycle.Observer; -import com.android.systemui.SysUiServiceProvider; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dock.DockManager; import com.android.systemui.dock.DockManager.DockEventListener; @@ -89,6 +88,7 @@ public final class ClockManager { private final Observer<Integer> mCurrentUserObserver = (newUserId) -> reload(); private final PluginManager mPluginManager; + @Nullable private final DockManager mDockManager; /** * Observe changes to dock state to know when to switch the clock face. @@ -102,7 +102,6 @@ public final class ClockManager { reload(); } }; - @Nullable private DockManager mDockManager; /** * When docked, the DOCKED_CLOCK_FACE setting will be checked for the custom clock face @@ -125,21 +124,24 @@ public final class ClockManager { @Inject public ClockManager(Context context, InjectionInflationController injectionInflater, - PluginManager pluginManager, SysuiColorExtractor colorExtractor) { + PluginManager pluginManager, SysuiColorExtractor colorExtractor, + @Nullable DockManager dockManager) { this(context, injectionInflater, pluginManager, colorExtractor, context.getContentResolver(), new CurrentUserObservable(context), - new SettingsWrapper(context.getContentResolver())); + new SettingsWrapper(context.getContentResolver()), dockManager); } + @VisibleForTesting ClockManager(Context context, InjectionInflationController injectionInflater, PluginManager pluginManager, SysuiColorExtractor colorExtractor, ContentResolver contentResolver, CurrentUserObservable currentUserObservable, - SettingsWrapper settingsWrapper) { + SettingsWrapper settingsWrapper, DockManager dockManager) { mContext = context; mPluginManager = pluginManager; mContentResolver = contentResolver; mSettingsWrapper = settingsWrapper; mCurrentUserObservable = currentUserObservable; + mDockManager = dockManager; mPreviewClocks = new AvailableClocks(); Resources res = context.getResources(); @@ -223,9 +225,6 @@ public final class ClockManager { Settings.Secure.getUriFor(Settings.Secure.DOCKED_CLOCK_FACE), false, mContentObserver, UserHandle.USER_ALL); mCurrentUserObservable.getCurrentUser().observeForever(mCurrentUserObserver); - if (mDockManager == null) { - mDockManager = SysUiServiceProvider.getComponent(mContext, DockManager.class); - } if (mDockManager != null) { mDockManager.addListener(mDockEventListener); } diff --git a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java index 04f887bb6b2f..41a7bc43eaa0 100644 --- a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java +++ b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java @@ -16,6 +16,7 @@ package com.android.systemui; import android.app.PendingIntent; import android.content.Intent; +import android.view.View; import com.android.systemui.plugins.ActivityStarter; @@ -53,6 +54,16 @@ public class ActivityStarterDelegate implements ActivityStarter { } @Override + public void startPendingIntentDismissingKeyguard(PendingIntent intent, + Runnable intentSentCallback, View associatedView) { + if (mActualStarter == null) { + return; + } + mActualStarter.startPendingIntentDismissingKeyguard(intent, intentSentCallback, + associatedView); + } + + @Override public void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade, int flags) { if (mActualStarter == null) { diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index 1feb63d884fd..4b6306ad8fc0 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -35,6 +35,7 @@ import com.android.systemui.appops.AppOpsController; import com.android.systemui.assist.AssistManager; import com.android.systemui.bubbles.BubbleController; import com.android.systemui.colorextraction.SysuiColorExtractor; +import com.android.systemui.dock.DockManager; import com.android.systemui.fragments.FragmentService; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.keyguard.WakefulnessLifecycle; @@ -296,6 +297,7 @@ public class Dependency extends SystemUI { @Inject Lazy<PackageManagerWrapper> mPackageManagerWrapper; @Inject Lazy<SensorPrivacyController> mSensorPrivacyController; @Inject Lazy<DumpController> mDumpController; + @Inject Lazy<DockManager> mDockManager; @Inject public Dependency() { @@ -470,6 +472,7 @@ public class Dependency extends SystemUI { mProviders.put(PackageManagerWrapper.class, mPackageManagerWrapper::get); mProviders.put(SensorPrivacyController.class, mSensorPrivacyController::get); mProviders.put(DumpController.class, mDumpController::get); + mProviders.put(DockManager.class, mDockManager::get); // TODO(b/118592525): to support multi-display , we start to add something which is // per-display, while others may be global. I think it's time to add diff --git a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java index 802903db7ba8..ad2e0024065f 100644 --- a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java +++ b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java @@ -80,16 +80,6 @@ public class HardwareUiLayout extends MultiListLayout implements Tunable { } @Override - public void removeAllItems() { - if (mList != null) { - mList.removeAllViews(); - } - if (mSeparatedView != null) { - mSeparatedView.removeAllViews(); - } - } - - @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); updateSettings(); diff --git a/packages/SystemUI/src/com/android/systemui/MultiListLayout.java b/packages/SystemUI/src/com/android/systemui/MultiListLayout.java index f8287a4dc2dc..d153fb039802 100644 --- a/packages/SystemUI/src/com/android/systemui/MultiListLayout.java +++ b/packages/SystemUI/src/com/android/systemui/MultiListLayout.java @@ -45,11 +45,6 @@ public abstract class MultiListLayout extends LinearLayout { protected abstract ViewGroup getListView(); /** - * Removes all child items from the separated and list views, if they exist. - */ - protected abstract void removeAllItems(); - - /** * Sets the divided view, which may have a differently-colored background. */ public abstract void setDivisionView(View v); @@ -110,6 +105,25 @@ public abstract class MultiListLayout extends LinearLayout { onUpdateList(); } + protected void removeAllSeparatedViews() { + ViewGroup separated = getSeparatedView(); + if (separated != null) { + separated.removeAllViews(); + } + } + + protected void removeAllListViews() { + ViewGroup list = getListView(); + if (list != null) { + list.removeAllViews(); + } + } + + protected void removeAllItems() { + removeAllListViews(); + removeAllSeparatedViews(); + } + protected void onUpdateList() { removeAllItems(); setSeparatedViewVisibility(mAdapter.hasSeparatedItems()); diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java index 27975707bd8d..10ae3434daaa 100644 --- a/packages/SystemUI/src/com/android/systemui/Prefs.java +++ b/packages/SystemUI/src/com/android/systemui/Prefs.java @@ -57,7 +57,6 @@ public final class Prefs { Key.SEEN_RINGER_GUIDANCE_COUNT, Key.QS_HAS_TURNED_OFF_MOBILE_DATA, Key.TOUCHED_RINGER_TOGGLE, - Key.QUICK_STEP_INTERACTION_FLAGS, Key.HAS_SEEN_ODI_CAPTIONS_TOOLTIP }) public @interface Key { @@ -103,7 +102,6 @@ public final class Prefs { String QS_TILE_SPECS_REVEALED = "QsTileSpecsRevealed"; String QS_HAS_TURNED_OFF_MOBILE_DATA = "QsHasTurnedOffMobileData"; String TOUCHED_RINGER_TOGGLE = "TouchedRingerToggle"; - String QUICK_STEP_INTERACTION_FLAGS = "QuickStepInteractionFlags"; String HAS_SEEN_ODI_CAPTIONS_TOOLTIP = "HasSeenODICaptionsTooltip"; } diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java index ffb5e810fb29..f9926f3550ec 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java @@ -34,6 +34,7 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.assist.AssistManager; import com.android.systemui.classifier.FalsingManager; +import com.android.systemui.dock.DockManager; import com.android.systemui.fragments.FragmentService; import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -179,6 +180,13 @@ public class SystemUIFactory { @Singleton @Provides + @Nullable + public DockManager provideDockManager(Context context) { + return null; + } + + @Singleton + @Provides public NotificationEntryManager provideNotificationEntryManager(Context context) { return new NotificationEntryManager(context); } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java index 03324777e4ea..7094d28c29f5 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java @@ -16,9 +16,12 @@ package com.android.systemui.bubbles; +import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE; + import android.os.UserHandle; import android.view.LayoutInflater; +import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.R; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -40,15 +43,24 @@ class Bubble { public NotificationEntry entry; BubbleView iconView; BubbleExpandedView expandedView; + private long mLastUpdated; + private long mLastAccessed; private static String groupId(NotificationEntry entry) { UserHandle user = entry.notification.getUser(); - return user.getIdentifier() + '|' + entry.notification.getPackageName(); + return user.getIdentifier() + "|" + entry.notification.getPackageName(); + } + + /** Used in tests when no UI is required. */ + @VisibleForTesting(visibility = PRIVATE) + Bubble(NotificationEntry e) { + this (e, null); } Bubble(NotificationEntry e, BubbleExpandedView.OnBubbleBlockedListener listener) { entry = e; mKey = e.key; + mLastUpdated = e.notification.getPostTime(); mGroupId = groupId(e); mListener = listener; } @@ -101,12 +113,37 @@ class Bubble { void setEntry(NotificationEntry entry) { this.entry = entry; + mLastUpdated = entry.notification.getPostTime(); if (mInflated) { iconView.update(entry); expandedView.update(entry); } } + public long getLastActivity() { + return Math.max(mLastUpdated, mLastAccessed); + } + + /** + * Should be invoked whenever a Bubble is accessed (selected while expanded). + */ + void markAsAccessedAt(long lastAccessedMillis) { + mLastAccessed = lastAccessedMillis; + entry.setShowInShadeWhenBubble(false); + } + + /** + * @return whether bubble is from a notification associated with a foreground service. + */ + public boolean isOngoing() { + return entry.isForegroundService(); + } + + @Override + public String toString() { + return "Bubble{" + mKey + '}'; + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index ef383add644e..d0713636c540 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -41,6 +41,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.provider.Settings; import android.service.notification.StatusBarNotification; +import android.util.Log; import android.view.Display; import android.view.IPinnedStackController; import android.view.IPinnedStackListener; @@ -83,6 +84,7 @@ import javax.inject.Singleton; public class BubbleController implements ConfigurationController.ConfigurationListener { private static final String TAG = "BubbleController"; + private static final boolean DEBUG = true; @Retention(SOURCE) @IntDef({DISMISS_USER_GESTURE, DISMISS_AGED, DISMISS_TASK_FINISHED, DISMISS_BLOCKED, @@ -203,6 +205,9 @@ public class BubbleController implements ConfigurationController.ConfigurationLi configurationController.addCallback(this /* configurationListener */); + mBubbleData = data; + mBubbleData.setListener(mBubbleDataListener); + mNotificationEntryManager = Dependency.get(NotificationEntryManager.class); mNotificationEntryManager.addNotificationEntryListener(mEntryListener); @@ -219,9 +224,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi } catch (RemoteException e) { e.printStackTrace(); } - - mBubbleData = data; - mBubbleData.setListener(mBubbleDataListener); mSurfaceSynchronizer = synchronizer; mBarService = IStatusBarService.Stub.asInterface( @@ -482,7 +484,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi } @Override - public void onSelectionChanged(Bubble selectedBubble) { + public void onSelectionChanged(@Nullable Bubble selectedBubble) { if (mStackView != null) { mStackView.setSelectedBubble(selectedBubble); } @@ -506,6 +508,18 @@ public class BubbleController implements ConfigurationController.ConfigurationLi public void apply() { mNotificationEntryManager.updateNotifications(); updateVisibility(); + + if (DEBUG) { + Log.d(TAG, "[BubbleData]"); + Log.d(TAG, formatBubblesString(mBubbleData.getBubbles(), + mBubbleData.getSelectedBubble())); + + if (mStackView != null) { + Log.d(TAG, "[BubbleStackView]"); + Log.d(TAG, formatBubblesString(mStackView.getBubblesOnScreen(), + mStackView.getExpandedBubble())); + } + } } }; @@ -623,6 +637,23 @@ public class BubbleController implements ConfigurationController.ConfigurationLi entry.setShowInShadeWhenBubble(!suppressNotification); } + static String formatBubblesString(List<Bubble> bubbles, Bubble selected) { + StringBuilder sb = new StringBuilder(); + for (Bubble bubble : bubbles) { + if (bubble == null) { + sb.append(" <null> !!!!!\n"); + } else { + boolean isSelected = (bubble == selected); + sb.append(String.format("%s Bubble{act=%12d, ongoing=%d, key=%s}\n", + ((isSelected) ? "->" : " "), + bubble.getLastActivity(), + (bubble.isOngoing() ? 1 : 0), + bubble.getKey())); + } + } + return sb.toString(); + } + /** * Return true if the applications with the package name is running in foreground. * diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java index 259665dedf5b..38ba91e0d81d 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java @@ -17,21 +17,26 @@ package com.android.systemui.bubbles; import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE; -import android.app.ActivityManager; +import static java.util.stream.Collectors.toList; + import android.app.Notification; import android.app.PendingIntent; import android.content.Context; import android.util.Log; +import androidx.annotation.Nullable; + import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.bubbles.BubbleController.DismissReason; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Objects; import javax.inject.Inject; @@ -44,6 +49,15 @@ import javax.inject.Singleton; public class BubbleData { private static final String TAG = "BubbleData"; + private static final boolean DEBUG = false; + + private static final int MAX_BUBBLES = 5; + + private static final Comparator<Bubble> BUBBLES_BY_LAST_ACTIVITY_DESCENDING = + Comparator.comparing(Bubble::getLastActivity).reversed(); + + private static final Comparator<Map.Entry<String, Long>> GROUPS_BY_LAST_ACTIVITY_DESCENDING = + Comparator.<Map.Entry<String, Long>, Long>comparing(Map.Entry::getValue).reversed(); /** * This interface reports changes to the state and appearance of bubbles which should be applied @@ -83,7 +97,7 @@ public class BubbleData { void onOrderChanged(List<Bubble> bubbles); /** Indicates the selected bubble changed. */ - void onSelectionChanged(Bubble selectedBubble); + void onSelectionChanged(@Nullable Bubble selectedBubble); /** * The UI should transition to the given state, incorporating any pending changes during @@ -98,16 +112,28 @@ public class BubbleData { void apply(); } + interface TimeSource { + long currentTimeMillis(); + } + private final Context mContext; - private final List<Bubble> mBubbles = new ArrayList<>(); + private List<Bubble> mBubbles; private Bubble mSelectedBubble; private boolean mExpanded; + + // TODO: ensure this is invalidated at the appropriate time + private int mSelectedBubbleExpandedPosition = -1; + + private TimeSource mTimeSource = System::currentTimeMillis; + + @Nullable private Listener mListener; @VisibleForTesting @Inject public BubbleData(Context context) { mContext = context; + mBubbles = new ArrayList<>(); } public boolean hasBubbles() { @@ -122,29 +148,41 @@ public class BubbleData { return getBubbleWithKey(key) != null; } + @Nullable + public Bubble getSelectedBubble() { + return mSelectedBubble; + } + public void setExpanded(boolean expanded) { if (setExpandedInternal(expanded)) { - mListener.apply(); + dispatchApply(); } } public void setSelectedBubble(Bubble bubble) { + if (DEBUG) { + Log.d(TAG, "setSelectedBubble: " + bubble); + } if (setSelectedBubbleInternal(bubble)) { - mListener.apply(); + dispatchApply(); } } public void notificationEntryUpdated(NotificationEntry entry) { + if (DEBUG) { + Log.d(TAG, "notificationEntryUpdated: " + entry); + } Bubble bubble = getBubbleWithKey(entry.key); if (bubble == null) { // Create a new bubble bubble = new Bubble(entry, this::onBubbleBlocked); - mBubbles.add(0, bubble); // TODO: reorder/group - mListener.onBubbleAdded(bubble); + doAdd(bubble); + dispatchOnBubbleAdded(bubble); } else { // Updates an existing bubble bubble.setEntry(entry); - mListener.onBubbleUpdated(bubble); + doUpdate(bubble); + dispatchOnBubbleUpdated(bubble); } if (shouldAutoExpand(entry)) { setSelectedBubbleInternal(bubble); @@ -154,46 +192,145 @@ public class BubbleData { } else if (mSelectedBubble == null) { setSelectedBubbleInternal(bubble); } - // TODO: reorder/group - mListener.apply(); + dispatchApply(); + } + + private void doAdd(Bubble bubble) { + if (DEBUG) { + Log.d(TAG, "doAdd: " + bubble); + } + int minInsertPoint = 0; + boolean newGroup = !hasBubbleWithGroupId(bubble.getGroupId()); + if (isExpanded()) { + // first bubble of a group goes to the end, otherwise it goes within the existing group + minInsertPoint = + newGroup ? mBubbles.size() : findFirstIndexForGroup(bubble.getGroupId()); + } + insertBubble(minInsertPoint, bubble); + if (!isExpanded()) { + packGroup(findFirstIndexForGroup(bubble.getGroupId())); + } + if (mBubbles.size() > MAX_BUBBLES) { + mBubbles.stream() + // sort oldest first (ascending lastActivity) + .sorted(Comparator.comparingLong(Bubble::getLastActivity)) + // skip the selected bubble + .filter((b) -> !b.equals(mSelectedBubble)) + .findFirst() + .ifPresent((b) -> { + doRemove(b.getKey(), BubbleController.DISMISS_AGED); + dispatchApply(); + }); + } + } + + private void doUpdate(Bubble bubble) { + if (DEBUG) { + Log.d(TAG, "doUpdate: " + bubble); + } + if (!isExpanded()) { + // while collapsed, update causes re-sort + mBubbles.remove(bubble); + insertBubble(0, bubble); + packGroup(findFirstIndexForGroup(bubble.getGroupId())); + } } public void notificationEntryRemoved(NotificationEntry entry, @DismissReason int reason) { - int indexToRemove = indexForKey(entry.key); - if (indexToRemove >= 0) { - Bubble removed = mBubbles.remove(indexToRemove); - removed.setDismissed(); - mListener.onBubbleRemoved(removed, reason); - maybeSendDeleteIntent(reason, removed.entry); + if (DEBUG) { + Log.d(TAG, "notificationEntryRemoved: entry=" + entry + " reason=" + reason); + } + doRemove(entry.key, reason); + dispatchApply(); + } - if (mBubbles.isEmpty()) { + private void doRemove(String key, @DismissReason int reason) { + int indexToRemove = indexForKey(key); + if (indexToRemove >= 0) { + Bubble bubbleToRemove = mBubbles.get(indexToRemove); + if (mBubbles.size() == 1) { + // Going to become empty, handle specially. setExpandedInternal(false); setSelectedBubbleInternal(null); - } else if (removed == mSelectedBubble) { + } + mBubbles.remove(indexToRemove); + dispatchOnBubbleRemoved(bubbleToRemove, reason); + + // Note: If mBubbles.isEmpty(), then mSelectedBubble is now null. + if (Objects.equals(mSelectedBubble, bubbleToRemove)) { + // Move selection to the new bubble at the same position. int newIndex = Math.min(indexToRemove, mBubbles.size() - 1); Bubble newSelected = mBubbles.get(newIndex); setSelectedBubbleInternal(newSelected); } - // TODO: reorder/group - mListener.apply(); + bubbleToRemove.setDismissed(); + maybeSendDeleteIntent(reason, bubbleToRemove.entry); } } public void dismissAll(@DismissReason int reason) { - boolean changed = setExpandedInternal(false); + if (DEBUG) { + Log.d(TAG, "dismissAll: reason=" + reason); + } + if (mBubbles.isEmpty()) { + return; + } + setExpandedInternal(false); + setSelectedBubbleInternal(null); while (!mBubbles.isEmpty()) { Bubble bubble = mBubbles.remove(0); bubble.setDismissed(); maybeSendDeleteIntent(reason, bubble.entry); + dispatchOnBubbleRemoved(bubble, reason); + } + dispatchApply(); + } + + private void dispatchApply() { + if (mListener != null) { + mListener.apply(); + } + } + + private void dispatchOnBubbleAdded(Bubble bubble) { + if (mListener != null) { + mListener.onBubbleAdded(bubble); + } + } + + private void dispatchOnBubbleRemoved(Bubble bubble, @DismissReason int reason) { + if (mListener != null) { mListener.onBubbleRemoved(bubble, reason); - changed = true; } - if (setSelectedBubbleInternal(null)) { - changed = true; + } + + private void dispatchOnExpandedChanged(boolean expanded) { + if (mListener != null) { + mListener.onExpandedChanged(expanded); } - if (changed) { - // TODO: reorder/group - mListener.apply(); + } + + private void dispatchOnSelectionChanged(@Nullable Bubble bubble) { + if (mListener != null) { + mListener.onSelectionChanged(bubble); + } + } + + private void dispatchOnBubbleUpdated(Bubble bubble) { + if (mListener != null) { + mListener.onBubbleUpdated(bubble); + } + } + + private void dispatchOnOrderChanged(List<Bubble> bubbles) { + if (mListener != null) { + mListener.onOrderChanged(bubbles); + } + } + + private void dispatchShowFlyoutText(Bubble bubble, String text) { + if (mListener != null) { + mListener.showFlyoutText(bubble, text); } } @@ -204,7 +341,10 @@ public class BubbleData { * @param bubble the new selected bubble * @return true if the state changed as a result */ - private boolean setSelectedBubbleInternal(Bubble bubble) { + private boolean setSelectedBubbleInternal(@Nullable Bubble bubble) { + if (DEBUG) { + Log.d(TAG, "setSelectedBubbleInternal: " + bubble); + } if (Objects.equals(bubble, mSelectedBubble)) { return false; } @@ -213,16 +353,17 @@ public class BubbleData { + " (" + bubble + ") bubbles=" + mBubbles); return false; } - if (mExpanded) { - // TODO: bubble.markAsActive() ? - bubble.entry.setShowInShadeWhenBubble(false); + if (mExpanded && bubble != null) { + mSelectedBubble.markAsAccessedAt(mTimeSource.currentTimeMillis()); } mSelectedBubble = bubble; - mListener.onSelectionChanged(mSelectedBubble); + dispatchOnSelectionChanged(mSelectedBubble); + if (!mExpanded || mSelectedBubble == null) { + mSelectedBubbleExpandedPosition = -1; + } return true; } - /** * Requests a change to the expanded state. Calls {@link Listener#onExpandedChanged} if * the value changes. @@ -231,9 +372,15 @@ public class BubbleData { * @return true if the state changed as a result */ private boolean setExpandedInternal(boolean shouldExpand) { + if (DEBUG) { + Log.d(TAG, "setExpandedInternal: shouldExpand=" + shouldExpand); + } if (mExpanded == shouldExpand) { return false; } + if (mSelectedBubble != null) { + mSelectedBubble.markAsAccessedAt(mTimeSource.currentTimeMillis()); + } if (shouldExpand) { if (mBubbles.isEmpty()) { Log.e(TAG, "Attempt to expand stack when empty!"); @@ -243,15 +390,126 @@ public class BubbleData { Log.e(TAG, "Attempt to expand stack without selected bubble!"); return false; } - // TODO: bubble.markAsActive() ? - mSelectedBubble.entry.setShowInShadeWhenBubble(false); + } else { + repackAll(); } - // TODO: reorder/regroup mExpanded = shouldExpand; - mListener.onExpandedChanged(mExpanded); + dispatchOnExpandedChanged(mExpanded); return true; } + private static long sortKey(Bubble bubble) { + long key = bubble.getLastActivity(); + if (bubble.isOngoing()) { + // Set 2nd highest bit (signed long int), to partition between ongoing and regular + key |= 0x4000000000000000L; + } + return key; + } + + /** + * Locates and inserts the bubble into a sorted position. The is inserted + * based on sort key, groupId is not considered. A call to {@link #packGroup(int)} may be + * required to keep grouping intact. + * + * @param minPosition the first insert point to consider + * @param newBubble the bubble to insert + * @return the position where the bubble was inserted + */ + private int insertBubble(int minPosition, Bubble newBubble) { + long newBubbleSortKey = sortKey(newBubble); + String previousGroupId = null; + + for (int pos = minPosition; pos < mBubbles.size(); pos++) { + Bubble bubbleAtPos = mBubbles.get(pos); + String groupIdAtPos = bubbleAtPos.getGroupId(); + boolean atStartOfGroup = !groupIdAtPos.equals(previousGroupId); + + if (atStartOfGroup && newBubbleSortKey > sortKey(bubbleAtPos)) { + // Insert before the start of first group which has older bubbles. + mBubbles.add(pos, newBubble); + return pos; + } + previousGroupId = groupIdAtPos; + } + mBubbles.add(newBubble); + return mBubbles.size() - 1; + } + + private boolean hasBubbleWithGroupId(String groupId) { + return mBubbles.stream().anyMatch(b -> b.getGroupId().equals(groupId)); + } + + private int findFirstIndexForGroup(String appId) { + for (int i = 0; i < mBubbles.size(); i++) { + Bubble bubbleAtPos = mBubbles.get(i); + if (bubbleAtPos.getGroupId().equals(appId)) { + return i; + } + } + return 0; + } + + /** + * Starting at the given position, moves all bubbles with the same group id to follow. Bubbles + * at positions lower than {@code position} are unchanged. Relative order within the group + * unchanged. Relative order of any other bubbles are also unchanged. + * + * @param position the position of the first bubble for the group + */ + private void packGroup(int position) { + if (DEBUG) { + Log.d(TAG, "packGroup: position=" + position); + } + Bubble groupStart = mBubbles.get(position); + final String groupAppId = groupStart.getGroupId(); + List<Bubble> moving = new ArrayList<>(); + + // Walk backward, collect bubbles within the group + for (int i = mBubbles.size() - 1; i > position; i--) { + if (mBubbles.get(i).getGroupId().equals(groupAppId)) { + moving.add(0, mBubbles.get(i)); + } + } + mBubbles.removeAll(moving); + mBubbles.addAll(position + 1, moving); + } + + private void repackAll() { + if (DEBUG) { + Log.d(TAG, "repackAll()"); + } + if (mBubbles.isEmpty()) { + return; + } + Map<String, Long> groupLastActivity = new HashMap<>(); + for (Bubble bubble : mBubbles) { + long maxSortKeyForGroup = groupLastActivity.getOrDefault(bubble.getGroupId(), 0L); + long sortKeyForBubble = sortKey(bubble); + if (sortKeyForBubble > maxSortKeyForGroup) { + groupLastActivity.put(bubble.getGroupId(), sortKeyForBubble); + } + } + + // Sort groups by their most recently active bubble + List<String> groupsByMostRecentActivity = + groupLastActivity.entrySet().stream() + .sorted(GROUPS_BY_LAST_ACTIVITY_DESCENDING) + .map(Map.Entry::getKey) + .collect(toList()); + + List<Bubble> repacked = new ArrayList<>(mBubbles.size()); + + // For each group, add bubbles, freshest to oldest + for (String appId : groupsByMostRecentActivity) { + mBubbles.stream() + .filter((b) -> b.getGroupId().equals(appId)) + .sorted(BUBBLES_BY_LAST_ACTIVITY_DESCENDING) + .forEachOrdered(repacked::add); + } + mBubbles = repacked; + } + private void maybeSendDeleteIntent(@DismissReason int reason, NotificationEntry entry) { if (reason == BubbleController.DISMISS_USER_GESTURE) { Notification.BubbleMetadata bubbleMetadata = entry.getBubbleMetadata(); @@ -275,13 +533,14 @@ public class BubbleData { Bubble bubble = i.next(); if (bubble.getPackageName().equals(blockedPackage)) { i.remove(); - mListener.onBubbleRemoved(bubble, BubbleController.DISMISS_BLOCKED); + // TODO: handle removal of selected bubble, and collapse safely if emptied (see + // dismissAll) + dispatchOnBubbleRemoved(bubble, BubbleController.DISMISS_BLOCKED); changed = true; } } if (changed) { - // TODO: reorder/group - mListener.apply(); + dispatchApply(); } } @@ -295,24 +554,11 @@ public class BubbleData { return -1; } - private Bubble removeBubbleWithKey(String key) { - for (int i = 0; i < mBubbles.size(); i++) { - Bubble bubble = mBubbles.get(i); - if (bubble.getKey().equals(key)) { - mBubbles.remove(i); - return bubble; - } - } - return null; - } - /** * The set of bubbles. - * - * @deprecated */ - @Deprecated - public Collection<Bubble> getBubbles() { + @VisibleForTesting(visibility = PRIVATE) + public List<Bubble> getBubbles() { return Collections.unmodifiableList(mBubbles); } @@ -327,6 +573,11 @@ public class BubbleData { return null; } + @VisibleForTesting(visibility = PRIVATE) + void setTimeSource(TimeSource timeSource) { + mTimeSource = timeSource; + } + public void setListener(Listener listener) { mListener = listener; } @@ -334,17 +585,6 @@ public class BubbleData { boolean shouldAutoExpand(NotificationEntry entry) { Notification.BubbleMetadata metadata = entry.getBubbleMetadata(); return metadata != null && metadata.getAutoExpandBubble() - && isForegroundApp(entry.notification.getPackageName()); - } - - /** - * Return true if the applications with the package name is running in foreground. - * - * @param pkgName application package name. - */ - boolean isForegroundApp(String pkgName) { - ActivityManager am = mContext.getSystemService(ActivityManager.class); - List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1 /* maxNum */); - return !tasks.isEmpty() && pkgName.equals(tasks.get(0).topActivity.getPackageName()); + && BubbleController.isForegroundApp(mContext, entry.notification.getPackageName()); } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java index 346660df7fb5..67f327490a6e 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java @@ -40,11 +40,8 @@ import android.content.res.TypedArray; import android.graphics.Color; import android.graphics.Insets; import android.graphics.Point; -import android.graphics.drawable.AdaptiveIconDrawable; -import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; -import android.graphics.drawable.InsetDrawable; import android.graphics.drawable.ShapeDrawable; import android.os.RemoteException; import android.os.ServiceManager; @@ -66,6 +63,7 @@ import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.recents.TriangleShape; +import com.android.systemui.statusbar.AlphaOptimizedButton; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.stack.ExpandableViewState; @@ -80,7 +78,7 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList private View mPointerView; private int mPointerMargin; - private ImageView mSettingsIcon; + private AlphaOptimizedButton mSettingsIcon; // Permission view private View mPermissionView; @@ -100,8 +98,6 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList private int mSettingsIconHeight; private int mBubbleHeight; private int mPermissionHeight; - private int mIconInset; - private Drawable mSettingsIconDrawable; private int mPointerWidth; private int mPointerHeight; @@ -218,10 +214,7 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList mSettingsIconHeight = getContext().getResources().getDimensionPixelSize( R.dimen.bubble_expanded_header_height); mSettingsIcon = findViewById(R.id.settings_button); - mIconInset = getResources().getDimensionPixelSize(R.dimen.bubble_icon_inset); mSettingsIcon.setOnClickListener(this); - // Save initial drawable to create adaptive icons that will take its place. - mSettingsIconDrawable = mSettingsIcon.getDrawable(); mPermissionHeight = getContext().getResources().getDimensionPixelSize( R.dimen.bubble_permission_height); @@ -377,16 +370,6 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList int foregroundColor = ta.getColor(1, Color.BLACK /* default */); ta.recycle(); - // Must clear tint first - otherwise tint updates inconsistently. - mSettingsIconDrawable.setTintList(null); - mSettingsIconDrawable.setTint(foregroundColor); - - InsetDrawable foreground = new InsetDrawable(mSettingsIconDrawable, mIconInset); - ColorDrawable background = new ColorDrawable(backgroundColor); - AdaptiveIconDrawable adaptiveIcon = new AdaptiveIconDrawable(background, - foreground); - mSettingsIcon.setImageDrawable(adaptiveIcon); - // Update permission prompt color. mPermissionView.setBackground(createPermissionBackground(backgroundColor)); mPermissionPrompt.setTextColor(foregroundColor); @@ -649,11 +632,12 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList } private Intent getSettingsIntent(String packageName, final int appUid) { - final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS); + final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_BUBBLE_SETTINGS); intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName); intent.putExtra(Settings.EXTRA_APP_UID, appUid); intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); return intent; } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index 7029931f72d6..d1bc9a91636c 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -66,6 +66,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import java.math.BigDecimal; import java.math.RoundingMode; +import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -227,7 +228,7 @@ public class BubbleStackView extends FrameLayout { mBubbleData = data; mInflater = LayoutInflater.from(context); - mTouchHandler = new BubbleTouchHandler(context, this); + mTouchHandler = new BubbleTouchHandler(this, data, context); setOnTouchListener(mTouchHandler); mInflater = LayoutInflater.from(context); @@ -503,6 +504,9 @@ public class BubbleStackView extends FrameLayout { // via BubbleData.Listener void addBubble(Bubble bubble) { + if (DEBUG) { + Log.d(TAG, "addBubble: " + bubble); + } bubble.inflate(mInflater, this); mBubbleContainer.addView(bubble.iconView, 0, new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT)); @@ -513,10 +517,17 @@ public class BubbleStackView extends FrameLayout { // via BubbleData.Listener void removeBubble(Bubble bubble) { + if (DEBUG) { + Log.d(TAG, "removeBubble: " + bubble); + } // Remove it from the views int removedIndex = mBubbleContainer.indexOfChild(bubble.iconView); - mBubbleContainer.removeViewAt(removedIndex); - logBubbleEvent(bubble, StatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED); + if (removedIndex >= 0) { + mBubbleContainer.removeViewAt(removedIndex); + logBubbleEvent(bubble, StatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED); + } else { + Log.d(TAG, "was asked to remove Bubble, but didn't find the view! " + bubble); + } } // via BubbleData.Listener @@ -531,7 +542,10 @@ public class BubbleStackView extends FrameLayout { * position of any bubble. */ // via BubbleData.Listener - public void setSelectedBubble(Bubble bubbleToSelect) { + public void setSelectedBubble(@Nullable Bubble bubbleToSelect) { + if (DEBUG) { + Log.d(TAG, "setSelectedBubble: " + bubbleToSelect); + } if (mExpandedBubble != null && mExpandedBubble.equals(bubbleToSelect)) { return; } @@ -562,6 +576,9 @@ public class BubbleStackView extends FrameLayout { */ // via BubbleData.Listener public void setExpanded(boolean shouldExpand) { + if (DEBUG) { + Log.d(TAG, "setExpanded: " + shouldExpand); + } boolean wasExpanded = mIsExpanded; if (shouldExpand == wasExpanded) { return; @@ -586,6 +603,9 @@ public class BubbleStackView extends FrameLayout { */ @Deprecated void stackDismissed(int reason) { + if (DEBUG) { + Log.d(TAG, "stackDismissed: reason=" + reason); + } mBubbleData.dismissAll(reason); logBubbleEvent(null /* no bubble associated with bubble stack dismiss */, StatsLog.BUBBLE_UICHANGED__ACTION__STACK_DISMISSED); @@ -633,6 +653,9 @@ public class BubbleStackView extends FrameLayout { @Deprecated @MainThread void collapseStack() { + if (DEBUG) { + Log.d(TAG, "collapseStack()"); + } mBubbleData.setExpanded(false); } @@ -642,6 +665,9 @@ public class BubbleStackView extends FrameLayout { @Deprecated @MainThread void collapseStack(Runnable endRunnable) { + if (DEBUG) { + Log.d(TAG, "collapseStack(endRunnable)"); + } collapseStack(); // TODO - use the runnable at end of animation endRunnable.run(); @@ -657,6 +683,9 @@ public class BubbleStackView extends FrameLayout { @Deprecated @MainThread void expandStack() { + if (DEBUG) { + Log.d(TAG, "expandStack()"); + } mBubbleData.setExpanded(true); } @@ -664,6 +693,9 @@ public class BubbleStackView extends FrameLayout { * Tell the stack to animate to collapsed or expanded state. */ private void animateExpansion(boolean shouldExpand) { + if (DEBUG) { + Log.d(TAG, "animateExpansion: shouldExpand=" + shouldExpand); + } if (mIsExpanded != shouldExpand) { hideFlyoutImmediate(); @@ -745,6 +777,9 @@ public class BubbleStackView extends FrameLayout { /** Called when a drag operation on an individual bubble has started. */ public void onBubbleDragStart(View bubble) { + if (DEBUG) { + Log.d(TAG, "onBubbleDragStart: bubble=" + bubble); + } mExpandedAnimationController.prepareForBubbleDrag(bubble); } @@ -760,6 +795,9 @@ public class BubbleStackView extends FrameLayout { /** Called when a drag operation on an individual bubble has finished. */ public void onBubbleDragFinish( View bubble, float x, float y, float velX, float velY, boolean dismissed) { + if (DEBUG) { + Log.d(TAG, "onBubbleDragFinish: bubble=" + bubble + ", dismissed=" + dismissed); + } if (!mIsExpanded || mIsExpansionAnimating) { return; } @@ -772,6 +810,9 @@ public class BubbleStackView extends FrameLayout { } void onDragStart() { + if (DEBUG) { + Log.d(TAG, "onDragStart()"); + } if (mIsExpanded || mIsExpansionAnimating) { return; } @@ -792,6 +833,9 @@ public class BubbleStackView extends FrameLayout { } void onDragFinish(float x, float y, float velX, float velY) { + if (DEBUG) { + Log.d(TAG, "onDragFinish"); + } // TODO: Add fling to bottom to dismiss. mIsDragging = false; @@ -958,6 +1002,9 @@ public class BubbleStackView extends FrameLayout { } private void updateExpandedBubble() { + if (DEBUG) { + Log.d(TAG, "updateExpandedBubble()"); + } mExpandedViewContainer.removeAllViews(); if (mExpandedBubble != null && mIsExpanded) { mExpandedViewContainer.addView(mExpandedBubble.expandedView); @@ -1036,7 +1083,9 @@ public class BubbleStackView extends FrameLayout { } private void applyCurrentState() { - Log.d(TAG, "applyCurrentState: mIsExpanded=" + mIsExpanded); + if (DEBUG) { + Log.d(TAG, "applyCurrentState: mIsExpanded=" + mIsExpanded); + } mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE); if (mIsExpanded) { // First update the view so that it calculates a new height (ensuring the y position @@ -1075,10 +1124,14 @@ public class BubbleStackView extends FrameLayout { } private void updatePointerPosition() { - if (mExpandedBubble != null) { - float pointerPosition = mExpandedBubble.iconView.getTranslationX() - + (mExpandedBubble.iconView.getWidth() / 2f); - mExpandedBubble.expandedView.setPointerPosition((int) pointerPosition); + if (DEBUG) { + Log.d(TAG, "updatePointerPosition()"); + } + Bubble expandedBubble = getExpandedBubble(); + if (expandedBubble != null) { + BubbleView iconView = expandedBubble.iconView; + float pointerPosition = iconView.getTranslationX() + (iconView.getWidth() / 2f); + expandedBubble.expandedView.setPointerPosition((int) pointerPosition); } } @@ -1174,4 +1227,18 @@ public class BubbleStackView extends FrameLayout { } return mExpandedBubble.expandedView.performBackPressIfNeeded(); } + + /** For debugging only */ + List<Bubble> getBubblesOnScreen() { + List<Bubble> bubbles = new ArrayList<>(); + for (int i = 0; i < mBubbleContainer.getChildCount(); i++) { + View child = mBubbleContainer.getChildAt(i); + if (child instanceof BubbleView) { + String key = ((BubbleView) child).getKey(); + Bubble bubble = mBubbleData.getBubbleWithKey(key); + bubbles.add(bubble); + } + } + return bubbles; + } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java index a51d46c0a848..82e6279772f4 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java @@ -37,9 +37,12 @@ class BubbleTouchHandler implements View.OnTouchListener { /** Velocity required to dismiss a bubble without dragging it into the dismiss target. */ private static final float DISMISS_MIN_VELOCITY = 4000f; + private static final String TAG = "BubbleTouchHandler"; + 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 PipDismissViewController mDismissViewController; @@ -60,10 +63,12 @@ class BubbleTouchHandler implements View.OnTouchListener { /** View that was initially touched, when we received the first ACTION_DOWN event. */ private View mTouchedView; - BubbleTouchHandler(Context context, BubbleStackView stackView) { + BubbleTouchHandler(BubbleStackView stackView, + BubbleData bubbleData, Context context) { final int touchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); mTouchSlopSquared = touchSlop * touchSlop; mDismissViewController = new PipDismissViewController(context); + mBubbleData = bubbleData; mStack = stackView; } @@ -80,7 +85,7 @@ class BubbleTouchHandler implements View.OnTouchListener { // 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) { - mStack.collapseStack(); + mBubbleData.setExpanded(false); resetForNextGesture(); return false; } @@ -151,8 +156,8 @@ class BubbleTouchHandler implements View.OnTouchListener { mStack.onDragFinishAsDismiss(); } else if (isFlyout) { // TODO(b/129768381): Expand if tapped, dismiss if swiped away. - if (!mStack.isExpanded() && !mMovedEnough) { - mStack.expandStack(); + if (!mBubbleData.isExpanded() && !mMovedEnough) { + mBubbleData.setExpanded(true); } } else if (mMovedEnough) { mVelocityTracker.computeCurrentVelocity(/* maxVelocity */ 1000); @@ -170,15 +175,13 @@ class BubbleTouchHandler implements View.OnTouchListener { } } } else if (mTouchedView == mStack.getExpandedBubbleView()) { - mStack.collapseStack(); + mBubbleData.setExpanded(false); } else if (isStack) { - if (mStack.isExpanded()) { - mStack.collapseStack(); - } else { - mStack.expandStack(); - } + // Toggle expansion + mBubbleData.setExpanded(!mBubbleData.isExpanded()); } else { - mStack.setExpandedBubble(((BubbleView) mTouchedView).getKey()); + final String key = ((BubbleView) mTouchedView).getKey(); + mBubbleData.setSelectedBubble(mBubbleData.getBubbleWithKey(key)); } resetForNextGesture(); diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java index a63fdbd63e02..d4e7bbca1107 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java @@ -32,6 +32,7 @@ import android.view.InputDevice; import android.view.MotionEvent; import android.view.accessibility.AccessibilityManager; +import com.android.internal.logging.MetricsLogger; import com.android.systemui.Dependency; import com.android.systemui.UiOffloadThread; import com.android.systemui.analytics.DataCollector; @@ -62,6 +63,8 @@ public class FalsingManager implements SensorEventListener, StateListener { Sensor.TYPE_LIGHT, Sensor.TYPE_ROTATION_VECTOR, }; + private static final String FALSING_REMAIN_LOCKED = "falsing_failure_after_attempts"; + private static final String FALSING_SUCCESS = "falsing_success_after_attempts"; private final Handler mHandler = new Handler(Looper.getMainLooper()); private final Context mContext; @@ -83,6 +86,8 @@ public class FalsingManager implements SensorEventListener, StateListener { private boolean mScreenOn; private boolean mShowingAod; private Runnable mPendingWtf; + private int mIsFalseTouchCalls; + private MetricsLogger mMetricsLogger; protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) { @Override @@ -99,6 +104,7 @@ public class FalsingManager implements SensorEventListener, StateListener { mHumanInteractionClassifier = HumanInteractionClassifier.getInstance(mContext); mUiOffloadThread = Dependency.get(UiOffloadThread.class); mScreenOn = context.getSystemService(PowerManager.class).isInteractive(); + mMetricsLogger = new MetricsLogger(); mContext.getContentResolver().registerContentObserver( Settings.Secure.getUriFor(ENFORCE_BOUNCER), false, @@ -143,6 +149,14 @@ public class FalsingManager implements SensorEventListener, StateListener { private void sessionExitpoint(boolean force) { if (mSessionActive && (force || !shouldSessionBeActive())) { mSessionActive = false; + if (mIsFalseTouchCalls != 0) { + if (FalsingLog.ENABLED) { + FalsingLog.i( + "isFalseTouchCalls", "Calls before failure: " + mIsFalseTouchCalls); + } + mMetricsLogger.histogram(FALSING_REMAIN_LOCKED, mIsFalseTouchCalls); + mIsFalseTouchCalls = 0; + } // This can be expensive, and doesn't need to happen on the main thread. mUiOffloadThread.submit(() -> { @@ -166,6 +180,7 @@ public class FalsingManager implements SensorEventListener, StateListener { } mBouncerOn = false; mSessionActive = true; + mIsFalseTouchCalls = 0; if (mHumanInteractionClassifier.isEnabled()) { registerSensors(CLASSIFIER_SENSORS); @@ -250,7 +265,16 @@ public class FalsingManager implements SensorEventListener, StateListener { // anti-falsed. return false; } - return mHumanInteractionClassifier.isFalseTouch(); + mIsFalseTouchCalls++; + boolean isFalse = mHumanInteractionClassifier.isFalseTouch(); + if (!isFalse) { + if (FalsingLog.ENABLED) { + FalsingLog.i("isFalseTouchCalls", "Calls before success: " + mIsFalseTouchCalls); + } + mMetricsLogger.histogram(FALSING_SUCCESS, mIsFalseTouchCalls); + mIsFalseTouchCalls = 0; + } + return isFalse; } private void clearPendingWtf() { diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java index 060765495f48..5196ec639453 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java @@ -27,7 +27,6 @@ import android.os.Handler; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.Dependency; import com.android.systemui.R; -import com.android.systemui.SysUiServiceProvider; import com.android.systemui.SystemUIApplication; import com.android.systemui.classifier.FalsingManager; import com.android.systemui.dock.DockManager; @@ -46,7 +45,7 @@ public class DozeFactory { Context context = dozeService; SensorManager sensorManager = Dependency.get(AsyncSensorManager.class); AlarmManager alarmManager = context.getSystemService(AlarmManager.class); - DockManager dockManager = SysUiServiceProvider.getComponent(context, DockManager.class); + DockManager dockManager = Dependency.get(DockManager.class); DozeHost host = getHost(dozeService); AmbientDisplayConfiguration config = new AmbientDisplayConfiguration(context); diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsColumnLayout.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsColumnLayout.java new file mode 100644 index 000000000000..59070287cc5c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsColumnLayout.java @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.globalactions; + +import static com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE; +import static com.android.systemui.util.leak.RotationUtils.ROTATION_NONE; +import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.Gravity; +import android.view.View; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.R; + +/** + * Grid-based implementation of the button layout created by the global actions dialog. + */ +public class GlobalActionsColumnLayout extends GlobalActionsLayout { + private boolean mLastSnap; + + public GlobalActionsColumnLayout(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + super.onLayout(changed, l, t, r, b); + + post(() -> updateSnap()); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + @VisibleForTesting + protected boolean shouldReverseListItems() { + int rotation = getCurrentRotation(); + if (rotation == ROTATION_NONE) { + return false; + } + if (getCurrentLayoutDirection() == View.LAYOUT_DIRECTION_RTL) { + return rotation == ROTATION_LANDSCAPE; + } + return rotation == ROTATION_SEASCAPE; + } + + @Override + public void onUpdateList() { + super.onUpdateList(); + updateChildOrdering(); + } + + private void updateChildOrdering() { + if (shouldReverseListItems()) { + getListView().bringToFront(); + } else { + getSeparatedView().bringToFront(); + } + } + + /** + * Snap this layout to align with the power button. + */ + @VisibleForTesting + protected void snapToPowerButton() { + int offset = getPowerButtonOffsetDistance(); + switch (getCurrentRotation()) { + case (ROTATION_LANDSCAPE): + setPadding(offset, 0, 0, 0); + setGravity(Gravity.LEFT | Gravity.TOP); + break; + case (ROTATION_SEASCAPE): + setPadding(0, 0, offset, 0); + setGravity(Gravity.RIGHT | Gravity.BOTTOM); + break; + default: + setPadding(0, offset, 0, 0); + setGravity(Gravity.TOP | Gravity.RIGHT); + break; + } + } + + /** + * Detach this layout from snapping to the power button and instead center along that edge. + */ + @VisibleForTesting + protected void centerAlongEdge() { + switch (getCurrentRotation()) { + case (ROTATION_LANDSCAPE): + setPadding(0, 0, 0, 0); + setGravity(Gravity.CENTER_HORIZONTAL | Gravity.TOP); + break; + case (ROTATION_SEASCAPE): + setPadding(0, 0, 0, 0); + setGravity(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM); + break; + default: + setPadding(0, 0, 0, 0); + setGravity(Gravity.CENTER_VERTICAL | Gravity.RIGHT); + break; + } + } + + /** + * Determines the distance from the top of the screen to the power button. + */ + @VisibleForTesting + protected int getPowerButtonOffsetDistance() { + return Math.round(getContext().getResources().getDimension( + R.dimen.global_actions_top_padding)); + } + + /** + * Check whether there is enough extra space below the dialog such that we can offset the top + * of the dialog from the top of the phone to line it up with the power button, then either + * snap the dialog to the power button or center it along the edge with snapToPowerButton. + */ + @VisibleForTesting + protected boolean shouldSnapToPowerButton() { + int offsetSize = getPowerButtonOffsetDistance(); + int dialogSize; + int screenSize; + View wrapper = getWrapper(); + int rotation = getCurrentRotation(); + if (rotation == ROTATION_NONE) { + dialogSize = wrapper.getMeasuredHeight(); + screenSize = getMeasuredHeight(); + } else { + dialogSize = wrapper.getMeasuredWidth(); + screenSize = getMeasuredWidth(); + } + return dialogSize + offsetSize < screenSize; + } + + @VisibleForTesting + protected void updateSnap() { + boolean snap = shouldSnapToPowerButton(); + if (snap != mLastSnap) { + if (snap) { + snapToPowerButton(); + } else { + centerAlongEdge(); + } + } + mLastSnap = snap; + } + + @VisibleForTesting + protected float getGridItemSize() { + return getContext().getResources().getDimension(R.dimen.global_actions_grid_item_height); + } + + @VisibleForTesting + protected float getAnimationDistance() { + return getGridItemSize() / 2; + } + + @Override + public float getAnimationOffsetX() { + if (getCurrentRotation() == ROTATION_NONE) { + return getAnimationDistance(); + } + return 0; + } + + @Override + public float getAnimationOffsetY() { + switch (getCurrentRotation()) { + case ROTATION_LANDSCAPE: + return -getAnimationDistance(); + case ROTATION_SEASCAPE: + return getAnimationDistance(); + default: // Portrait + return 0; + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index 64511a0b4cc3..d6c897186628 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -410,8 +410,10 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, mActivityStarter .startPendingIntentDismissingKeyguard(intent); } - }) + }, + mKeyguardManager.isDeviceLocked()) : null; + ActionsDialog dialog = new ActionsDialog(mContext, mAdapter, panelViewController); dialog.setCanceledOnTouchOutside(false); // Handled by the custom class. dialog.setKeyguardShowing(mKeyguardShowing); @@ -1583,13 +1585,20 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, } private int getGlobalActionsLayoutId(Context context) { - if (isForceGridEnabled(context) || shouldUsePanel()) { - if (RotationUtils.getRotation(context) == RotationUtils.ROTATION_SEASCAPE) { + boolean useGridLayout = isForceGridEnabled(context) || shouldUsePanel(); + if (RotationUtils.getRotation(context) == RotationUtils.ROTATION_SEASCAPE) { + if (useGridLayout) { return com.android.systemui.R.layout.global_actions_grid_seascape; + } else { + return com.android.systemui.R.layout.global_actions_column_seascape; + } + } else { + if (useGridLayout) { + return com.android.systemui.R.layout.global_actions_grid; + } else { + return com.android.systemui.R.layout.global_actions_column; } - return com.android.systemui.R.layout.global_actions_grid; } - return com.android.systemui.R.layout.global_actions_wrapped; } @Override @@ -1712,7 +1721,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, } public void onRotate(int from, int to) { - if (mShowing && (shouldUsePanel() || isForceGridEnabled(mContext))) { + if (mShowing) { refreshDialog(); } } diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java index 554ed738c335..e1462d15c887 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsGridLayout.java @@ -22,58 +22,23 @@ import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE; import android.content.Context; import android.util.AttributeSet; -import android.util.Log; import android.view.View; import android.view.ViewGroup; import com.android.internal.annotations.VisibleForTesting; -import com.android.systemui.HardwareBgDrawable; -import com.android.systemui.MultiListLayout; -import com.android.systemui.util.leak.RotationUtils; /** * Grid-based implementation of the button layout created by the global actions dialog. */ -public class GlobalActionsGridLayout extends MultiListLayout { - - boolean mBackgroundsSet; - +public class GlobalActionsGridLayout extends GlobalActionsLayout { public GlobalActionsGridLayout(Context context, AttributeSet attrs) { super(context, attrs); } - private void setBackgrounds() { - int gridBackgroundColor = getResources().getColor( - com.android.systemui.R.color.global_actions_grid_background, null); - int separatedBackgroundColor = getResources().getColor( - com.android.systemui.R.color.global_actions_separated_background, null); - HardwareBgDrawable listBackground = new HardwareBgDrawable(true, true, getContext()); - HardwareBgDrawable separatedBackground = new HardwareBgDrawable(true, true, getContext()); - listBackground.setTint(gridBackgroundColor); - separatedBackground.setTint(separatedBackgroundColor); - getListView().setBackground(listBackground); - getSeparatedView().setBackground(separatedBackground); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - - // backgrounds set only once, the first time onMeasure is called after inflation - if (getListView() != null && !mBackgroundsSet) { - setBackgrounds(); - mBackgroundsSet = true; - } - } - - @VisibleForTesting - protected int getCurrentRotation() { - return RotationUtils.getRotation(mContext); - } - @VisibleForTesting - protected void setupListView(ListGridLayout listView, int itemCount) { - listView.setExpectedCount(itemCount); + protected void setupListView() { + ListGridLayout listView = getListView(); + listView.setExpectedCount(mAdapter.countListItems()); listView.setReverseSublists(shouldReverseSublists()); listView.setReverseItems(shouldReverseListItems()); listView.setSwapRowsAndColumns(shouldSwapRowsAndColumns()); @@ -81,29 +46,8 @@ public class GlobalActionsGridLayout extends MultiListLayout { @Override public void onUpdateList() { + setupListView(); super.onUpdateList(); - - ViewGroup separatedView = getSeparatedView(); - ListGridLayout listView = getListView(); - setupListView(listView, mAdapter.countListItems()); - - for (int i = 0; i < mAdapter.getCount(); i++) { - // generate the view item - View v; - boolean separated = mAdapter.shouldBeSeparated(i); - if (separated) { - v = mAdapter.getView(i, null, separatedView); - } else { - v = mAdapter.getView(i, null, listView); - } - Log.d("GlobalActionsGridLayout", "View: " + v); - - if (separated) { - separatedView.addView(v); - } else { - listView.addItem(v); - } - } updateSeparatedItemSize(); } @@ -111,7 +55,8 @@ public class GlobalActionsGridLayout extends MultiListLayout { * If the separated view contains only one item, expand the bounds of that item to take up the * entire view, so that the whole thing is touch-able. */ - private void updateSeparatedItemSize() { + @VisibleForTesting + protected void updateSeparatedItemSize() { ViewGroup separated = getSeparatedView(); if (separated.getChildCount() == 0) { return; @@ -129,13 +74,24 @@ public class GlobalActionsGridLayout extends MultiListLayout { } @Override - protected ViewGroup getSeparatedView() { - return findViewById(com.android.systemui.R.id.separated_button); + protected ListGridLayout getListView() { + return (ListGridLayout) super.getListView(); } @Override - protected ListGridLayout getListView() { - return findViewById(android.R.id.list); + protected void removeAllListViews() { + ListGridLayout list = getListView(); + if (list != null) { + list.removeAllItems(); + } + } + + @Override + protected void addToListView(View v, boolean reverse) { + ListGridLayout list = getListView(); + if (list != null) { + list.addItem(v); + } } @Override @@ -174,12 +130,7 @@ public class GlobalActionsGridLayout extends MultiListLayout { return true; } - /** - * Determines whether the ListGridLayout should reverse the ordering of items within sublists. - * Used for RTL languages to ensure that items appear in the same positions, without having to - * override layoutDirection, which breaks Talkback ordering. - */ - @VisibleForTesting + @Override protected boolean shouldReverseListItems() { int rotation = getCurrentRotation(); boolean reverse = false; // should we add items to parents in the reverse order? @@ -187,20 +138,13 @@ public class GlobalActionsGridLayout extends MultiListLayout { || rotation == ROTATION_SEASCAPE) { reverse = !reverse; // if we're in portrait or seascape, reverse items } - if (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) { + if (getCurrentLayoutDirection() == View.LAYOUT_DIRECTION_RTL) { reverse = !reverse; // if we're in an RTL language, reverse items (again) } return reverse; } - /** - * Not ued in this implementation of the Global Actions Menu, but necessary for some others. - */ - @Override - public void setDivisionView(View v) { - // do nothing - } - + @VisibleForTesting protected float getAnimationDistance() { int rows = getListView().getRowCount(); float gridItemSize = getContext().getResources().getDimension( diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsLayout.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsLayout.java new file mode 100644 index 000000000000..f755a93ed275 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsLayout.java @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.globalactions; + +import android.content.Context; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.HardwareBgDrawable; +import com.android.systemui.MultiListLayout; +import com.android.systemui.R; +import com.android.systemui.util.leak.RotationUtils; + +import java.util.Locale; + +/** + * Grid-based implementation of the button layout created by the global actions dialog. + */ +public abstract class GlobalActionsLayout extends MultiListLayout { + + boolean mBackgroundsSet; + + public GlobalActionsLayout(Context context, AttributeSet attrs) { + super(context, attrs); + } + + private void setBackgrounds() { + int gridBackgroundColor = getResources().getColor( + R.color.global_actions_grid_background, null); + int separatedBackgroundColor = getResources().getColor( + R.color.global_actions_separated_background, null); + HardwareBgDrawable listBackground = new HardwareBgDrawable(true, true, getContext()); + HardwareBgDrawable separatedBackground = new HardwareBgDrawable(true, true, getContext()); + listBackground.setTint(gridBackgroundColor); + separatedBackground.setTint(separatedBackgroundColor); + getListView().setBackground(listBackground); + getSeparatedView().setBackground(separatedBackground); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + + // backgrounds set only once, the first time onMeasure is called after inflation + if (getListView() != null && !mBackgroundsSet) { + setBackgrounds(); + mBackgroundsSet = true; + } + } + + protected void addToListView(View v, boolean reverse) { + if (reverse) { + getListView().addView(v, 0); + } else { + getListView().addView(v); + } + } + + protected void addToSeparatedView(View v, boolean reverse) { + if (reverse) { + getSeparatedView().addView(v, 0); + } else { + getSeparatedView().addView(v); + } + } + + @VisibleForTesting + protected int getCurrentLayoutDirection() { + return TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()); + } + + @VisibleForTesting + protected int getCurrentRotation() { + return RotationUtils.getRotation(mContext); + } + + /** + * Determines whether the ListGridLayout should reverse the ordering of items within sublists. + * Used for RTL languages to ensure that items appear in the same positions, without having to + * override layoutDirection, which breaks Talkback ordering. + */ + protected abstract boolean shouldReverseListItems(); + + @Override + public void onUpdateList() { + super.onUpdateList(); + + ViewGroup separatedView = getSeparatedView(); + ViewGroup listView = getListView(); + + for (int i = 0; i < mAdapter.getCount(); i++) { + // generate the view item + View v; + boolean separated = mAdapter.shouldBeSeparated(i); + if (separated) { + v = mAdapter.getView(i, null, separatedView); + } else { + v = mAdapter.getView(i, null, listView); + } + if (separated) { + addToSeparatedView(v, false); + } else { + addToListView(v, shouldReverseListItems()); + } + } + } + + @Override + protected ViewGroup getSeparatedView() { + return findViewById(R.id.separated_button); + } + + @Override + protected ViewGroup getListView() { + return findViewById(android.R.id.list); + } + + protected View getWrapper() { + return getChildAt(0); + } + + /** + * Not used in this implementation of the Global Actions Menu, but necessary for some others. + */ + @Override + public void setDivisionView(View v) { + // do nothing + } +} diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java index 477e7d7ebf72..24a4b9e3052b 100644 --- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java +++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java @@ -30,13 +30,15 @@ import android.os.Message; import android.util.Log; /** - * A helper class that computes histogram and percentile 85 from a bitmap. - * Percentile 85 will be computed each time the user picks a new image wallpaper. + * A helper class that computes threshold from a bitmap. + * Threshold will be computed each time the user picks a new image wallpaper. */ class ImageProcessHelper { private static final String TAG = ImageProcessHelper.class.getSimpleName(); - private static final float DEFAULT_PER85 = 0.8f; - private static final int MSG_UPDATE_PER85 = 1; + private static final float DEFAULT_THRESHOLD = 0.8f; + private static final float DEFAULT_OTSU_THRESHOLD = 0f; + private static final float MAX_THRESHOLD = 0.89f; + private static final int MSG_UPDATE_THRESHOLD = 1; /** * This color matrix will be applied to each pixel to get luminance from rgb by below formula: @@ -53,8 +55,8 @@ class ImageProcessHelper { @Override public boolean handleMessage(Message msg) { switch (msg.what) { - case MSG_UPDATE_PER85: - mPer85 = (float) msg.obj; + case MSG_UPDATE_THRESHOLD: + mThreshold = (float) msg.obj; return true; default: return false; @@ -62,20 +64,20 @@ class ImageProcessHelper { } }); - private float mPer85 = DEFAULT_PER85; + private float mThreshold = DEFAULT_THRESHOLD; - void startComputingPercentile85(Bitmap bitmap) { - new Per85ComputeTask(mHandler).execute(bitmap); + void start(Bitmap bitmap) { + new ThresholdComputeTask(mHandler).execute(bitmap); } - float getPercentile85() { - return mPer85; + float getThreshold() { + return Math.min(mThreshold, MAX_THRESHOLD); } - private static class Per85ComputeTask extends AsyncTask<Bitmap, Void, Float> { + private static class ThresholdComputeTask extends AsyncTask<Bitmap, Void, Float> { private Handler mUpdateHandler; - Per85ComputeTask(Handler handler) { + ThresholdComputeTask(Handler handler) { super(handler); mUpdateHandler = handler; } @@ -84,35 +86,55 @@ class ImageProcessHelper { protected Float doInBackground(Bitmap... bitmaps) { Bitmap bitmap = bitmaps[0]; if (bitmap != null) { - int[] histogram = processHistogram(bitmap); - return computePercentile85(bitmap, histogram); + return new Threshold().compute(bitmap); } - Log.e(TAG, "Per85ComputeTask: Can't get bitmap"); - return DEFAULT_PER85; + Log.e(TAG, "ThresholdComputeTask: Can't get bitmap"); + return DEFAULT_THRESHOLD; } @Override protected void onPostExecute(Float result) { - Message msg = mUpdateHandler.obtainMessage(MSG_UPDATE_PER85, result); + Message msg = mUpdateHandler.obtainMessage(MSG_UPDATE_THRESHOLD, result); mUpdateHandler.sendMessage(msg); } + } + + private static class Threshold { + public float compute(Bitmap bitmap) { + Bitmap grayscale = toGrayscale(bitmap); + int[] histogram = getHistogram(grayscale); + boolean isSolidColor = isSolidColor(grayscale, histogram); + + // We will see gray wallpaper during the transition if solid color wallpaper is set, + // please refer to b/130360362#comment16. + // As a result, we use Percentile85 rather than Otsus if a solid color wallpaper is set. + ThresholdAlgorithm algorithm = isSolidColor ? new Percentile85() : new Otsus(); + return algorithm.compute(grayscale, histogram); + } - private int[] processHistogram(Bitmap bitmap) { + private Bitmap toGrayscale(Bitmap bitmap) { int width = bitmap.getWidth(); int height = bitmap.getHeight(); - Bitmap target = Bitmap.createBitmap(width, height, bitmap.getConfig()); - Canvas canvas = new Canvas(target); + Bitmap grayscale = Bitmap.createBitmap(width, height, bitmap.getConfig()); + Canvas canvas = new Canvas(grayscale); ColorMatrix cm = new ColorMatrix(LUMINOSITY_MATRIX); Paint paint = new Paint(); paint.setColorFilter(new ColorMatrixColorFilter(cm)); canvas.drawBitmap(bitmap, new Matrix(), paint); + return grayscale; + } + + private int[] getHistogram(Bitmap grayscale) { + int width = grayscale.getWidth(); + int height = grayscale.getHeight(); + // TODO: Fine tune the performance here, tracking on b/123615079. int[] histogram = new int[256]; for (int row = 0; row < height; row++) { for (int col = 0; col < width; col++) { - int pixel = target.getPixel(col, row); + int pixel = grayscale.getPixel(col, row); int y = Color.red(pixel) + Color.green(pixel) + Color.blue(pixel); histogram[y]++; } @@ -121,8 +143,29 @@ class ImageProcessHelper { return histogram; } - private float computePercentile85(Bitmap bitmap, int[] histogram) { - float per85 = DEFAULT_PER85; + private boolean isSolidColor(Bitmap bitmap, int[] histogram) { + boolean solidColor = false; + int pixels = bitmap.getWidth() * bitmap.getHeight(); + + // In solid color case, only one element of histogram has value, + // which is pixel counts and the value of other elements should be 0. + for (int value : histogram) { + if (value != 0 && value != pixels) { + break; + } + if (value == pixels) { + solidColor = true; + break; + } + } + return solidColor; + } + } + + private static class Percentile85 implements ThresholdAlgorithm { + @Override + public float compute(Bitmap bitmap, int[] histogram) { + float per85 = DEFAULT_THRESHOLD; int pixelCount = bitmap.getWidth() * bitmap.getHeight(); float[] acc = new float[256]; for (int i = 0; i < acc.length; i++) { @@ -141,4 +184,51 @@ class ImageProcessHelper { return per85; } } + + private static class Otsus implements ThresholdAlgorithm { + @Override + public float compute(Bitmap bitmap, int[] histogram) { + float threshold = DEFAULT_OTSU_THRESHOLD; + float maxVariance = 0; + float pixelCount = bitmap.getWidth() * bitmap.getHeight(); + float[] w = new float[2]; + float[] m = new float[2]; + float[] u = new float[2]; + + for (int i = 0; i < histogram.length; i++) { + m[1] += i * histogram[i]; + } + + w[1] = pixelCount; + for (int tonalValue = 0; tonalValue < histogram.length; tonalValue++) { + float dU; + float variance; + float numPixels = histogram[tonalValue]; + float tmp = numPixels * tonalValue; + w[0] += numPixels; + w[1] -= numPixels; + + if (w[0] == 0 || w[1] == 0) { + continue; + } + + m[0] += tmp; + m[1] -= tmp; + u[0] = m[0] / w[0]; + u[1] = m[1] / w[1]; + dU = u[0] - u[1]; + variance = w[0] * w[1] * dU * dU; + + if (variance > maxVariance) { + threshold = (tonalValue + 1f) / histogram.length; + maxVariance = variance; + } + } + return threshold; + } + } + + private interface ThresholdAlgorithm { + float compute(Bitmap bitmap, int[] histogram); + } } diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java index 464cbe33c5f0..5bbfe84b6319 100644 --- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java +++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java @@ -78,8 +78,8 @@ public class ImageWallpaperRenderer implements GLSurfaceView.Renderer, mBitmap = mWallpaperManager.getBitmap(); mBitmapWidth = mBitmap.getWidth(); mBitmapHeight = mBitmap.getHeight(); - // Compute per85 as transition threshold, this is an async work. - mImageProcessHelper.startComputingPercentile85(mBitmap); + // Compute threshold of the image, this is an async work. + mImageProcessHelper.start(mBitmap); mWallpaperManager.forgetLoadedWallpaper(); } } @@ -108,13 +108,13 @@ public class ImageWallpaperRenderer implements GLSurfaceView.Renderer, @Override public void onDrawFrame(GL10 gl) { - float per85 = mImageProcessHelper.getPercentile85(); + float threshold = mImageProcessHelper.getThreshold(); float reveal = mImageRevealHelper.getReveal(); glClear(GL_COLOR_BUFFER_BIT); glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_AOD2OPACITY), 1); - glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_PER85), per85); + glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_PER85), threshold); glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_REVEAL), reveal); scaleViewport(reveal); diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java index 89ecc6abd623..d50f294e4999 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java +++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java @@ -43,7 +43,7 @@ import com.android.systemui.util.NotificationChannels; */ public class PipNotification { private static final String TAG = "PipNotification"; - private static final String NOTIFICATION_TAG = PipNotification.class.getName(); + private static final String NOTIFICATION_TAG = PipNotification.class.getSimpleName(); private static final boolean DEBUG = PipManager.DEBUG; private static final String ACTION_MENU = "PipNotification.menu"; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java b/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java index e352b58ea552..4571ef3ef4ef 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java @@ -77,6 +77,7 @@ public class QSCarrierGroup extends LinearLayout implements @Override public void onClick(View v) { + if (!v.isVisibleToUser()) return; mActivityStarter.postStartActivityDismissingKeyguard(new Intent( Settings.ACTION_WIRELESS_SETTINGS), 0); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java index 6adce8347da1..42c616c054f8 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java @@ -38,6 +38,7 @@ import android.provider.Settings; import android.service.notification.ZenModeConfig; import android.text.format.DateUtils; import android.util.AttributeSet; +import android.util.Log; import android.util.Pair; import android.util.StatsLog; import android.view.ContextThemeWrapper; @@ -523,12 +524,12 @@ public class QuickStatusBarHeader extends RelativeLayout implements if (v == mClockView) { mActivityStarter.postStartActivityDismissingKeyguard(new Intent( AlarmClock.ACTION_SHOW_ALARMS), 0); - } else if (v == mNextAlarmContainer) { - if (mNextAlarm.getShowIntent() != null - && mNextAlarm.getShowIntent().getIntent() != null) { + } else if (v == mNextAlarmContainer && mNextAlarmContainer.isVisibleToUser()) { + if (mNextAlarm.getShowIntent() != null) { mActivityStarter.postStartActivityDismissingKeyguard( - mNextAlarm.getShowIntent().getIntent(), 0); + mNextAlarm.getShowIntent()); } else { + Log.d(TAG, "No PendingIntent for next alarm. Using default intent"); mActivityStarter.postStartActivityDismissingKeyguard(new Intent( AlarmClock.ACTION_SHOW_ALARMS), 0); } @@ -544,7 +545,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements new Intent(Intent.ACTION_REVIEW_ONGOING_PERMISSION_USAGE), 0); mHost.collapsePanels(); }); - } else if (v == mRingerContainer) { + } else if (v == mRingerContainer && mRingerContainer.isVisibleToUser()) { mActivityStarter.postStartActivityDismissingKeyguard(new Intent( Settings.ACTION_SOUND_SETTINGS), 0); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java index ca040762047c..9282a2e3b312 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java @@ -144,7 +144,8 @@ public class BluetoothTile extends QSTileImpl<BooleanState> { mContext.getString(R.string.accessibility_bluetooth_name, state.label) + ", " + state.secondaryLabel; } else if (state.isTransient) { - state.icon = ResourceIcon.get(R.drawable.ic_bluetooth_transient_animation); + state.icon = ResourceIcon.get( + com.android.internal.R.drawable.ic_bluetooth_transient_animation); state.contentDescription = state.secondaryLabel; } else { state.icon = diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java index 5e6f18e511d9..001e09406e3a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java @@ -130,7 +130,8 @@ public class HotspotTile extends QSTileImpl<BooleanState> { state.isTransient = isTransient; state.slash.isSlashed = !state.value && !state.isTransient; if (state.isTransient) { - state.icon = ResourceIcon.get(R.drawable.ic_hotspot_transient_animation); + state.icon = ResourceIcon.get( + com.android.internal.R.drawable.ic_hotspot_transient_animation); } state.expandedAccessibilityClassName = Switch.class.getName(); state.contentDescription = state.label; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java index 15df1f161159..0e7362c3d081 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java @@ -186,7 +186,8 @@ public class WifiTile extends QSTileImpl<SignalState> { final StringBuffer minimalContentDescription = new StringBuffer(); final Resources r = mContext.getResources(); if (isTransient) { - state.icon = ResourceIcon.get(R.drawable.ic_signal_wifi_transient_animation); + state.icon = ResourceIcon.get( + com.android.internal.R.drawable.ic_signal_wifi_transient_animation); state.label = r.getString(R.string.quick_settings_wifi_label); } else if (!state.value) { state.slash.isSlashed = true; diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 5ed6a429cc2f..78c7cd406ba1 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -17,14 +17,12 @@ package com.android.systemui.recents; import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; +import static android.view.Display.DEFAULT_DISPLAY; import static android.view.MotionEvent.ACTION_CANCEL; import static android.view.MotionEvent.ACTION_DOWN; import static android.view.MotionEvent.ACTION_UP; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON; -import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_SWIPE_UP; -import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_SHOW_OVERVIEW_BUTTON; -import static com.android.systemui.shared.system.NavigationBarCompat.InteractionType; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_INPUT_MONITOR; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SUPPORTS_WINDOW_CORNERS; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY; @@ -32,7 +30,6 @@ import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_WIN import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED; -import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING; import android.annotation.FloatRange; import android.app.ActivityTaskManager; @@ -61,7 +58,6 @@ import android.view.accessibility.AccessibilityManager; import com.android.internal.policy.ScreenDecorationsUtils; import com.android.systemui.Dependency; import com.android.systemui.Dumpable; -import com.android.systemui.Prefs; import com.android.systemui.SysUiServiceProvider; import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener; import com.android.systemui.shared.recents.IOverviewProxy; @@ -103,10 +99,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis // Max backoff caps at 5 mins private static final long MAX_BACKOFF_MILLIS = 10 * 60 * 1000; - // Default interaction flags if swipe up is disabled before connecting to launcher - private static final int DEFAULT_DISABLE_SWIPE_UP_STATE = FLAG_DISABLE_SWIPE_UP - | FLAG_SHOW_OVERVIEW_BUTTON; - private final Context mContext; private final Handler mHandler; private final Runnable mConnectionRunnable = this::internalConnectToCurrentUser; @@ -119,7 +111,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis private IOverviewProxy mOverviewProxy; private int mConnectionBackoffAttempts; - private @InteractionType int mInteractionFlags; private @SystemUiStateFlags int mSysUiStateFlags; private boolean mBound; private boolean mIsEnabled; @@ -233,27 +224,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis } @Override - public void setInteractionState(@InteractionType int flags) { - if (!verifyCaller("setInteractionState")) { - return; - } - long token = Binder.clearCallingIdentity(); - try { - if (mInteractionFlags != flags) { - mInteractionFlags = flags; - mHandler.post(() -> { - for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) { - mConnectionCallbacks.get(i).onInteractionFlagsChanged(flags); - } - }); - } - } finally { - Prefs.putInt(mContext, Prefs.Key.QUICK_STEP_INTERACTION_FLAGS, mInteractionFlags); - Binder.restoreCallingIdentity(token); - } - } - - @Override public Rect getNonMinimizedSplitScreenSecondaryBounds() { if (!verifyCaller("getNonMinimizedSplitScreenSecondaryBounds")) { return null; @@ -380,12 +350,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis public void onReceive(Context context, Intent intent) { updateEnabledState(); - // When launcher service is disabled, reset interaction flags because it is inactive - if (!isEnabled()) { - mInteractionFlags = getDefaultInteractionFlags(); - Prefs.remove(mContext, Prefs.Key.QUICK_STEP_INTERACTION_FLAGS); - } - // Reconnect immediately, instead of waiting for resume to arrive. startConnectionToCurrentUser(); } @@ -479,8 +443,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis com.android.internal.R.string.config_recentsComponentName)); mQuickStepIntent = new Intent(ACTION_QUICKSTEP) .setPackage(mRecentsComponentName.getPackageName()); - mInteractionFlags = Prefs.getInt(mContext, Prefs.Key.QUICK_STEP_INTERACTION_FLAGS, - getDefaultInteractionFlags()); mWindowCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(mContext.getResources()); mSupportsRoundedCornersOnWindows = ScreenDecorationsUtils .supportsRoundedCornersOnWindows(mContext.getResources()); @@ -515,7 +477,12 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis } } - public void setSystemUiStateFlag(int flag, boolean enabled) { + public void setSystemUiStateFlag(int flag, boolean enabled, int displayId) { + if (displayId != DEFAULT_DISPLAY) { + // Ignore non-default displays for now + return; + } + int newState = mSysUiStateFlags; if (enabled) { newState |= flag; @@ -540,8 +507,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis && statusBar.getPanel().isFullyExpanded(); final boolean bouncerShowing = statusBar != null && statusBar.isBouncerShowing(); mSysUiStateFlags = 0; - mSysUiStateFlags |= ActivityManagerWrapper.getInstance().isScreenPinningActive() - ? SYSUI_STATE_SCREEN_PINNING : 0; mSysUiStateFlags |= (navBarFragment != null && !navBarFragment.isNavBarWindowVisible()) ? SYSUI_STATE_NAV_BAR_HIDDEN : 0; mSysUiStateFlags |= panelExpanded @@ -653,7 +618,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis public void addCallback(OverviewProxyListener listener) { mConnectionCallbacks.add(listener); listener.onConnectionChanged(mOverviewProxy != null); - listener.onInteractionFlagsChanged(mInteractionFlags); listener.onBackButtonAlphaChanged(mBackButtonAlpha, false); } @@ -663,7 +627,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis } public boolean shouldShowSwipeUpUI() { - return isEnabled() && ((mInteractionFlags & FLAG_DISABLE_SWIPE_UP) == 0); + return isEnabled() && !QuickStepContract.isLegacyMode(mNavBarMode); } public boolean isEnabled() { @@ -674,10 +638,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis return mOverviewProxy; } - public int getInteractionFlags() { - return mInteractionFlags; - } - private void disconnectFromLauncherService() { if (mBound) { // Always unbind the service (ie. if called through onNullBinding or onBindingDied) @@ -693,13 +653,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis } } - private int getDefaultInteractionFlags() { - // If there is no settings available use device default or get it from settings - return QuickStepContract.isLegacyMode(mNavBarMode) - ? DEFAULT_DISABLE_SWIPE_UP_STATE - : 0; - } - private void notifyBackButtonAlphaChanged(float alpha, boolean animate) { for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) { mConnectionCallbacks.get(i).onBackButtonAlphaChanged(alpha, animate); @@ -765,7 +718,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis pw.print(" isCurrentUserSetup="); pw.println(mDeviceProvisionedController .isCurrentUserSetup()); pw.print(" connectionBackoffAttempts="); pw.println(mConnectionBackoffAttempts); - pw.print(" interactionFlags="); pw.println(mInteractionFlags); pw.print(" quickStepIntent="); pw.println(mQuickStepIntent); pw.print(" quickStepIntentResolved="); pw.println(isEnabled()); @@ -775,7 +727,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis public interface OverviewProxyListener { default void onConnectionChanged(boolean isConnected) {} default void onQuickStepStarted() {} - default void onInteractionFlagsChanged(@InteractionType int flags) {} default void onOverviewShown(boolean fromHome) {} default void onQuickScrubStarted() {} default void onBackButtonAlphaChanged(float alpha, boolean animate) {} diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java index ade903d7e272..c3c0d63f66c4 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java +++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java @@ -16,7 +16,6 @@ package com.android.systemui.recents; -import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING; import static com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE; import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE; @@ -139,7 +138,6 @@ public class ScreenPinningRequest implements View.OnClickListener, if (v.getId() == R.id.screen_pinning_ok_button || mRequestWindow == v) { try { ActivityTaskManager.getService().startSystemLockTaskMode(taskId); - mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_SCREEN_PINNING, true); } catch (RemoteException e) {} } clearPrompt(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index a688f36b47a6..f97be1ea8358 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -78,7 +78,7 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< private static final int MSG_COLLAPSE_PANELS = 4 << MSG_SHIFT; private static final int MSG_EXPAND_SETTINGS = 5 << MSG_SHIFT; private static final int MSG_SET_SYSTEMUI_VISIBILITY = 6 << MSG_SHIFT; - private static final int MSG_TOP_APP_WINDOW_CHANGED = 7 << MSG_SHIFT; + private static final int MSG_DISPLAY_READY = 7 << MSG_SHIFT; private static final int MSG_SHOW_IME_BUTTON = 8 << MSG_SHIFT; private static final int MSG_TOGGLE_RECENT_APPS = 9 << MSG_SHIFT; private static final int MSG_PRELOAD_RECENT_APPS = 10 << MSG_SHIFT; @@ -115,7 +115,6 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< private static final int MSG_SHOW_CHARGING_ANIMATION = 44 << MSG_SHIFT; private static final int MSG_SHOW_PINNING_TOAST_ENTER_EXIT = 45 << MSG_SHIFT; private static final int MSG_SHOW_PINNING_TOAST_ESCAPE = 46 << MSG_SHIFT; - private static final int MSG_DISPLAY_READY = 47 << MSG_SHIFT; public static final int FLAG_EXCLUDE_NONE = 0; public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0; @@ -175,21 +174,14 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< * @param fullscreenStackBounds The current bounds of the fullscreen stack, in screen * coordinates. * @param dockedStackBounds The current bounds of the docked stack, in screen coordinates. + * @param navbarColorManagedByIme {@code true} if navigation bar color is managed by IME. */ default void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis, - int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) { + int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds, + boolean navbarColorManagedByIme) { } /** - * Called to notify top app window changes. - * @see IStatusBar#topAppWindowChanged(int, boolean) - * - * @param displayId The id of the display to notify. - * @param visible {@code true} to show menu button. - */ - default void topAppWindowChanged(int displayId, boolean visible) { } - - /** * Called to notify IME window status changes. * * @param displayId The id of the display to notify. @@ -459,7 +451,8 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< @Override public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis, - int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) { + int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds, + boolean navbarColorManagedByIme) { synchronized (mLock) { // Don't coalesce these, since it might have one time flags set such as // STATUS_BAR_UNHIDE which might get lost. @@ -469,6 +462,7 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< args.argi3 = fullscreenStackVis; args.argi4 = dockedStackVis; args.argi5 = mask; + args.argi6 = navbarColorManagedByIme ? 1 : 0; args.arg1 = fullscreenStackBounds; args.arg2 = dockedStackBounds; mHandler.obtainMessage(MSG_SET_SYSTEMUI_VISIBILITY, args).sendToTarget(); @@ -476,13 +470,7 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< } @Override - public void topAppWindowChanged(int displayId, boolean menuVisible) { - synchronized (mLock) { - mHandler.removeMessages(MSG_TOP_APP_WINDOW_CHANGED); - mHandler.obtainMessage(MSG_TOP_APP_WINDOW_CHANGED, - displayId, menuVisible ? 1 : 0, null).sendToTarget(); - } - } + public void topAppWindowChanged(int displayId, boolean menuVisible) { } @Override public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition, @@ -879,15 +867,11 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< args = (SomeArgs) msg.obj; for (int i = 0; i < mCallbacks.size(); i++) { mCallbacks.get(i).setSystemUiVisibility(args.argi1, args.argi2, args.argi3, - args.argi4, args.argi5, (Rect) args.arg1, (Rect) args.arg2); + args.argi4, args.argi5, (Rect) args.arg1, (Rect) args.arg2, + args.argi6 == 1); } args.recycle(); break; - case MSG_TOP_APP_WINDOW_CHANGED: - for (int i = 0; i < mCallbacks.size(); i++) { - mCallbacks.get(i).topAppWindowChanged(msg.arg1, msg.arg2 != 0); - } - break; case MSG_SHOW_IME_BUTTON: args = (SomeArgs) msg.obj; handleShowImeButton(args.argi1 /* displayId */, (IBinder) args.arg1 /* token */, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt index b7ae4edc1522..4b2d13121f6b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt @@ -16,7 +16,6 @@ package com.android.systemui.statusbar -import android.annotation.ColorInt import android.content.Context import android.graphics.Bitmap import android.graphics.Canvas @@ -28,6 +27,7 @@ import android.renderscript.RenderScript import android.renderscript.ScriptIntrinsicBlur import android.util.MathUtils import com.android.internal.graphics.ColorUtils +import com.android.systemui.statusbar.notification.MediaNotificationProcessor import javax.inject.Inject import javax.inject.Singleton @@ -42,7 +42,7 @@ class MediaArtworkProcessor @Inject constructor() { private val mTmpSize = Point() private var mArtworkCache: Bitmap? = null - fun processArtwork(context: Context, artwork: Bitmap, @ColorInt color: Int): Bitmap { + fun processArtwork(context: Context, artwork: Bitmap): Bitmap { if (mArtworkCache != null) { return mArtworkCache!! } @@ -71,13 +71,15 @@ class MediaArtworkProcessor @Inject constructor() { blur.forEach(output) output.copyTo(outBitmap) + val swatch = MediaNotificationProcessor.findBackgroundSwatch(artwork) + input.destroy() output.destroy() inBitmap.recycle() blur.destroy() val canvas = Canvas(outBitmap) - canvas.drawColor(ColorUtils.setAlphaComponent(color, COLOR_ALPHA)) + canvas.drawColor(ColorUtils.setAlphaComponent(swatch.rgb, COLOR_ALPHA)) return outBitmap } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java index 85848cafbfcb..bd25209d5c0f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java @@ -53,7 +53,7 @@ import javax.inject.Singleton; @Singleton public class NavigationBarController implements Callbacks { - private static final String TAG = NavigationBarController.class.getName(); + private static final String TAG = NavigationBarController.class.getSimpleName(); private final Context mContext; private final Handler mHandler; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java index fa223670208b..b9e0c60fabeb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java @@ -21,11 +21,11 @@ import static com.android.systemui.statusbar.phone.StatusBar.DEBUG_MEDIA_FAKE_AR import static com.android.systemui.statusbar.phone.StatusBar.ENABLE_LOCKSCREEN_WALLPAPER; import static com.android.systemui.statusbar.phone.StatusBar.SHOW_LOCKSCREEN_MEDIA_ARTWORK; +import android.annotation.MainThread; import android.annotation.Nullable; import android.app.Notification; import android.content.Context; import android.graphics.Bitmap; -import android.graphics.Color; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; @@ -35,10 +35,13 @@ import android.media.session.MediaController; import android.media.session.MediaSession; import android.media.session.MediaSessionManager; import android.media.session.PlaybackState; +import android.os.AsyncTask; import android.os.Handler; import android.os.Trace; import android.os.UserHandle; import android.provider.DeviceConfig; +import android.provider.DeviceConfig.Properties; +import android.util.ArraySet; import android.util.Log; import android.view.View; import android.widget.ImageView; @@ -63,8 +66,10 @@ import com.android.systemui.statusbar.policy.KeyguardMonitor; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; +import java.util.Set; import javax.inject.Inject; import javax.inject.Singleton; @@ -107,6 +112,7 @@ public class NotificationMediaManager implements Dumpable { private final MediaSessionManager mMediaSessionManager; private final ArrayList<MediaListener> mMediaListeners; private final MediaArtworkProcessor mMediaArtworkProcessor; + private final Set<AsyncTask<?, ?, ?>> mProcessArtworkTasks = new ArraySet<>(); protected NotificationPresenter mPresenter; private MediaController mMediaController; @@ -118,15 +124,18 @@ public class NotificationMediaManager implements Dumpable { private ImageView mBackdropBack; private boolean mShowCompactMediaSeekbar; - private final DeviceConfig.OnPropertyChangedListener mPropertyChangedListener = - new DeviceConfig.OnPropertyChangedListener() { + private final DeviceConfig.OnPropertiesChangedListener mPropertiesChangedListener = + new DeviceConfig.OnPropertiesChangedListener() { @Override - public void onPropertyChanged(String namespace, String name, String value) { - if (SystemUiDeviceConfigFlags.COMPACT_MEDIA_SEEKBAR_ENABLED.equals(name)) { - if (DEBUG_MEDIA) { - Log.v(TAG, "DEBUG_MEDIA: compact media seekbar flag updated: " + value); + public void onPropertiesChanged(Properties properties) { + for (String name : properties.getKeyset()) { + if (SystemUiDeviceConfigFlags.COMPACT_MEDIA_SEEKBAR_ENABLED.equals(name)) { + String value = properties.getString(name, null); + if (DEBUG_MEDIA) { + Log.v(TAG, "DEBUG_MEDIA: compact media seekbar flag updated: " + value); + } + mShowCompactMediaSeekbar = "true".equals(value); } - mShowCompactMediaSeekbar = "true".equals(value); } } }; @@ -189,9 +198,9 @@ public class NotificationMediaManager implements Dumpable { DeviceConfig.getProperty(DeviceConfig.NAMESPACE_SYSTEMUI, SystemUiDeviceConfigFlags.COMPACT_MEDIA_SEEKBAR_ENABLED)); - DeviceConfig.addOnPropertyChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, + DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, mContext.getMainExecutor(), - mPropertyChangedListener); + mPropertiesChangedListener); } public void setUpWithPresenter(NotificationPresenter presenter) { @@ -445,28 +454,37 @@ public class NotificationMediaManager implements Dumpable { + " state=" + mStatusBarStateController.getState()); } - Drawable artworkDrawable = null; + Bitmap artworkBitmap = null; if (mediaMetadata != null) { - Bitmap artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ART); + artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ART); if (artworkBitmap == null) { artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART); - // might still be null } - if (artworkBitmap != null) { - int notificationColor; - synchronized (mEntryManager.getNotificationData()) { - NotificationEntry entry = mEntryManager.getNotificationData() - .get(mMediaNotificationKey); - if (entry == null || entry.getRow() == null) { - notificationColor = Color.TRANSPARENT; - } else { - notificationColor = entry.getRow().calculateBgColor(); - } - } - Bitmap bmp = mMediaArtworkProcessor.processArtwork(mContext, artworkBitmap, - notificationColor); - artworkDrawable = new BitmapDrawable(mBackdropBack.getResources(), bmp); + } + + // Process artwork on a background thread and send the resulting bitmap to + // finishUpdateMediaMetaData. + if (metaDataChanged) { + for (AsyncTask<?, ?, ?> task : mProcessArtworkTasks) { + task.cancel(true); } + mProcessArtworkTasks.clear(); + } + if (artworkBitmap != null) { + mProcessArtworkTasks.add(new ProcessArtworkTask(this, metaDataChanged, + allowEnterAnimation).execute(artworkBitmap)); + } else { + finishUpdateMediaMetaData(metaDataChanged, allowEnterAnimation, null); + } + + Trace.endSection(); + } + + private void finishUpdateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation, + @Nullable Bitmap bmp) { + Drawable artworkDrawable = null; + if (bmp != null) { + artworkDrawable = new BitmapDrawable(mBackdropBack.getResources(), bmp); } boolean allowWhenShade = false; if (ENABLE_LOCKSCREEN_WALLPAPER && artworkDrawable == null) { @@ -594,7 +612,6 @@ public class NotificationMediaManager implements Dumpable { } } } - Trace.endSection(); } public void setup(BackDropView backdrop, ImageView backdropFront, ImageView backdropBack, @@ -625,6 +642,61 @@ public class NotificationMediaManager implements Dumpable { } }; + private Bitmap processArtwork(Bitmap artwork) { + return mMediaArtworkProcessor.processArtwork(mContext, artwork); + } + + @MainThread + private void removeTask(AsyncTask<?, ?, ?> task) { + mProcessArtworkTasks.remove(task); + } + + /** + * {@link AsyncTask} to prepare album art for use as backdrop on lock screen. + */ + private static final class ProcessArtworkTask extends AsyncTask<Bitmap, Void, Bitmap> { + + private final WeakReference<NotificationMediaManager> mManagerRef; + private final boolean mMetaDataChanged; + private final boolean mAllowEnterAnimation; + + ProcessArtworkTask(NotificationMediaManager manager, boolean changed, + boolean allowAnimation) { + mManagerRef = new WeakReference<>(manager); + mMetaDataChanged = changed; + mAllowEnterAnimation = allowAnimation; + } + + @Override + protected Bitmap doInBackground(Bitmap... bitmaps) { + NotificationMediaManager manager = mManagerRef.get(); + if (manager == null || bitmaps.length == 0 || isCancelled()) { + return null; + } + return manager.processArtwork(bitmaps[0]); + } + + @Override + protected void onPostExecute(@Nullable Bitmap result) { + NotificationMediaManager manager = mManagerRef.get(); + if (manager != null && !isCancelled()) { + manager.removeTask(this); + manager.finishUpdateMediaMetaData(mMetaDataChanged, mAllowEnterAnimation, result); + } + } + + @Override + protected void onCancelled(Bitmap result) { + if (result != null) { + result.recycle(); + } + NotificationMediaManager manager = mManagerRef.get(); + if (manager != null) { + manager.removeTask(this); + } + } + } + public interface MediaListener { void onMetadataChanged(MediaMetadata metadata); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java index 2793b2a0f009..fe8c6b7c1e2c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java @@ -51,6 +51,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.NotificationVisibility; import com.android.systemui.Dumpable; +import com.android.systemui.R; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -273,10 +274,20 @@ public class NotificationRemoteInputManager implements Dumpable { notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() { @Override + public void onPreEntryUpdated(NotificationEntry entry) { + // Mark smart replies as sent whenever a notification is updated - otherwise the + // smart replies are never marked as sent. + mSmartReplyController.stopSending(entry); + } + + @Override public void onEntryRemoved( @Nullable NotificationEntry entry, NotificationVisibility visibility, boolean removedByUser) { + // We're removing the notification, the smart controller can forget about it. + mSmartReplyController.stopSending(entry); + if (removedByUser && entry != null) { onPerformRemoveNotification(entry, entry.key); } @@ -348,24 +359,18 @@ public class NotificationRemoteInputManager implements Dumpable { ViewParent p = view.getParent(); RemoteInputView riv = null; + ExpandableNotificationRow row = null; while (p != null) { if (p instanceof View) { View pv = (View) p; if (pv.isRootNamespace()) { riv = findRemoteInputView(pv); + row = (ExpandableNotificationRow) pv.getTag(R.id.row_tag_for_content_view); break; } } p = p.getParent(); } - ExpandableNotificationRow row = null; - while (p != null) { - if (p instanceof ExpandableNotificationRow) { - row = (ExpandableNotificationRow) p; - break; - } - p = p.getParent(); - } if (row == null) { return false; @@ -391,10 +396,13 @@ public class NotificationRemoteInputManager implements Dumpable { if (riv == null) { return false; } - if (!row.getPrivateLayout().getExpandedChild().isShown()) { - mCallback.onMakeExpandedVisibleForRemoteInput(row, view); - return true; - } + } + if (riv == row.getPrivateLayout().getExpandedRemoteInput() + && !row.getPrivateLayout().getExpandedChild().isShown()) { + // The expanded layout is selected, but it's not shown yet, let's wait on it to + // show before we do the animation. + mCallback.onMakeExpandedVisibleForRemoteInput(row, view); + return true; } int width = view.getWidth(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java index 333239e77d1b..0d9f4e7b909d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java @@ -30,6 +30,7 @@ import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationTarget; import android.view.SyncRtSurfaceTransactionApplier; import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams; +import android.view.View; import com.android.internal.policy.ScreenDecorationsUtils; import com.android.systemui.Interpolators; @@ -79,11 +80,12 @@ public class ActivityLaunchAnimator { } public RemoteAnimationAdapter getLaunchAnimation( - ExpandableNotificationRow sourceNotification, boolean occluded) { - if (!mCallback.areLaunchAnimationsEnabled() || occluded) { + View sourceView, boolean occluded) { + if (!(sourceView instanceof ExpandableNotificationRow) || !mCallback.areLaunchAnimationsEnabled() || occluded) { return null; } - AnimationRunner animationRunner = new AnimationRunner(sourceNotification); + AnimationRunner animationRunner = new AnimationRunner( + (ExpandableNotificationRow) sourceView); return new RemoteAnimationAdapter(animationRunner, ANIMATION_DURATION, ANIMATION_DURATION - 150 /* statusBarTransitionDelay */); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java index ab94008d656a..5d1ab4fd9ad2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java @@ -69,8 +69,7 @@ public class MediaNotificationProcessor { private static final int RESIZE_BITMAP_AREA = 150 * 150; private final ImageGradientColorizer mColorizer; private final Context mContext; - private float[] mFilteredBackgroundHsl = null; - private Palette.Filter mBlackWhiteFilter = (rgb, hsl) -> !isWhiteOrBlack(hsl); + private final Palette.Filter mBlackWhiteFilter = (rgb, hsl) -> !isWhiteOrBlack(hsl); /** * The context of the notification. This is the app context of the package posting the @@ -121,23 +120,21 @@ public class MediaNotificationProcessor { drawable.setBounds(0, 0, width, height); drawable.draw(canvas); - // for the background we only take the left side of the image to ensure - // a smooth transition - Palette.Builder paletteBuilder = Palette.from(bitmap) - .setRegion(0, 0, bitmap.getWidth() / 2, bitmap.getHeight()) - .clearFilters() // we want all colors, red / white / black ones too! - .resizeBitmapArea(RESIZE_BITMAP_AREA); + Palette.Builder paletteBuilder = generateArtworkPaletteBuilder(bitmap); Palette palette = paletteBuilder.generate(); - backgroundColor = findBackgroundColorAndFilter(palette); + Palette.Swatch backgroundSwatch = findBackgroundSwatch(palette); + backgroundColor = backgroundSwatch.getRgb(); // we want most of the full region again, slightly shifted to the right float textColorStartWidthFraction = 0.4f; paletteBuilder.setRegion((int) (bitmap.getWidth() * textColorStartWidthFraction), 0, bitmap.getWidth(), bitmap.getHeight()); - if (mFilteredBackgroundHsl != null) { + // We're not filtering on white or black + if (!isWhiteOrBlack(backgroundSwatch.getHsl())) { + final float backgroundHue = backgroundSwatch.getHsl()[0]; paletteBuilder.addFilter((rgb, hsl) -> { // at least 10 degrees hue difference - float diff = Math.abs(hsl[0] - mFilteredBackgroundHsl[0]); + float diff = Math.abs(hsl[0] - backgroundHue); return diff > 10 && diff < 350; }); } @@ -244,18 +241,31 @@ public class MediaNotificationProcessor { && (swatch.getPopulation() / (float) RESIZE_BITMAP_AREA > MINIMUM_IMAGE_FRACTION); } - private int findBackgroundColorAndFilter(Palette palette) { + /** + * Finds an appropriate background swatch from media artwork. + * + * @param artwork Media artwork + * @return Swatch that should be used as the background of the media notification. + */ + public static Palette.Swatch findBackgroundSwatch(Bitmap artwork) { + return findBackgroundSwatch(generateArtworkPaletteBuilder(artwork).generate()); + } + + /** + * Finds an appropriate background swatch from the palette of media artwork. + * + * @param palette Artwork palette, should be obtained from {@link generateArtworkPaletteBuilder} + * @return Swatch that should be used as the background of the media notification. + */ + private static Palette.Swatch findBackgroundSwatch(Palette palette) { // by default we use the dominant palette Palette.Swatch dominantSwatch = palette.getDominantSwatch(); if (dominantSwatch == null) { - // We're not filtering on white or black - mFilteredBackgroundHsl = null; - return Color.WHITE; + return new Palette.Swatch(Color.WHITE, 100); } if (!isWhiteOrBlack(dominantSwatch.getHsl())) { - mFilteredBackgroundHsl = dominantSwatch.getHsl(); - return dominantSwatch.getRgb(); + return dominantSwatch; } // Oh well, we selected black or white. Lets look at the second color! List<Palette.Swatch> swatches = palette.getSwatches(); @@ -270,38 +280,51 @@ public class MediaNotificationProcessor { } } if (second == null) { - // We're not filtering on white or black - mFilteredBackgroundHsl = null; - return dominantSwatch.getRgb(); + return dominantSwatch; } if (dominantSwatch.getPopulation() / highestNonWhitePopulation > POPULATION_FRACTION_FOR_WHITE_OR_BLACK) { // The dominant swatch is very dominant, lets take it! // We're not filtering on white or black - mFilteredBackgroundHsl = null; - return dominantSwatch.getRgb(); + return dominantSwatch; } else { - mFilteredBackgroundHsl = second.getHsl(); - return second.getRgb(); + return second; } } - private boolean isWhiteOrBlack(float[] hsl) { - return isBlack(hsl) || isWhite(hsl); + /** + * Generate a palette builder for media artwork. + * + * For producing a smooth background transition, the palette is extracted from only the left + * side of the artwork. + * + * @param artwork Media artwork + * @return Builder that generates the {@link Palette} for the media artwork. + */ + private static Palette.Builder generateArtworkPaletteBuilder(Bitmap artwork) { + // for the background we only take the left side of the image to ensure + // a smooth transition + return Palette.from(artwork) + .setRegion(0, 0, artwork.getWidth() / 2, artwork.getHeight()) + .clearFilters() // we want all colors, red / white / black ones too! + .resizeBitmapArea(RESIZE_BITMAP_AREA); } + private static boolean isWhiteOrBlack(float[] hsl) { + return isBlack(hsl) || isWhite(hsl); + } /** * @return true if the color represents a color which is close to black. */ - private boolean isBlack(float[] hslColor) { + private static boolean isBlack(float[] hslColor) { return hslColor[2] <= BLACK_MAX_LIGHTNESS; } /** * @return true if the color represents a color which is close to white. */ - private boolean isWhite(float[] hslColor) { + private static boolean isWhite(float[] hslColor) { return hslColor[2] >= WHITE_MIN_LIGHTNESS; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java index b81d81438ea3..ad745f5ab276 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java @@ -542,6 +542,12 @@ public class NotificationContentView extends FrameLayout { } @Override + public void onViewAdded(View child) { + super.onViewAdded(child); + child.setTag(R.id.row_tag_for_content_view, mContainingNotification); + } + + @Override protected void onVisibilityChanged(View changedView, int visibility) { super.onVisibilityChanged(changedView, visibility); updateVisibility(); @@ -1893,4 +1899,8 @@ public class NotificationContentView extends FrameLayout { } pw.println(); } + + public RemoteInputView getExpandedRemoteInput() { + return mExpandedRemoteInput; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java index 1f8ca37315bf..d49f1685e34a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java @@ -40,8 +40,11 @@ import android.graphics.drawable.Drawable; import android.metrics.LogMaker; import android.os.Handler; import android.os.RemoteException; +import android.provider.Settings; import android.service.notification.StatusBarNotification; import android.text.TextUtils; +import android.transition.AutoTransition; +import android.transition.TransitionManager; import android.util.AttributeSet; import android.util.Log; import android.view.View; @@ -88,6 +91,8 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G // standard controls private static final int ACTION_ALERT = 5; + private static final int BUTTON_ANIM_TIME_MS = 200; + private INotificationManager mINotificationManager; private PackageManager mPm; private MetricsLogger mMetricsLogger; @@ -102,6 +107,8 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private boolean mWasShownHighPriority; private boolean mShowOnLockscreen; private boolean mShowInStatusBar; + private boolean mPressedApply; + /** * The last importance level chosen by the user. Null if the user has not chosen an importance * level; non-null once the user takes an action which indicates an explicit preference. @@ -132,7 +139,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private OnClickListener mOnAlert = v -> { mExitReason = NotificationCounters.BLOCKING_HELPER_KEEP_SHOWING; mChosenImportance = IMPORTANCE_DEFAULT; - setImportanceSummary(ACTION_ALERT); + setImportanceSummary(ACTION_ALERT, true); updateButtons(ACTION_ALERT); }; @@ -140,12 +147,13 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private OnClickListener mOnSilent = v -> { mExitReason = NotificationCounters.BLOCKING_HELPER_DELIVER_SILENTLY; mChosenImportance = IMPORTANCE_LOW; - setImportanceSummary(ACTION_TOGGLE_SILENT); + setImportanceSummary(ACTION_TOGGLE_SILENT, true); updateButtons(ACTION_TOGGLE_SILENT); }; // used by standard ui private OnClickListener mOnDismissSettings = v -> { + mPressedApply = true; closeControls(v); }; @@ -294,7 +302,8 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G mShowInStatusBar = !mINotificationManager.shouldHideSilentStatusIcons( mContext.getPackageName()); - // TODO: b/128445911 use show on lockscreen setting + mShowOnLockscreen = Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0) == 1; bindHeader(); bindChannelDetails(); @@ -334,6 +343,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G findViewById(R.id.non_configurable_multichannel_text).setVisibility(GONE); findViewById(R.id.interruptiveness_settings).setVisibility(GONE); ((TextView) findViewById(R.id.done)).setText(R.string.inline_done_button); + findViewById(R.id.turn_off_notifications).setVisibility(GONE); } else if (mNumUniqueChannelsInRow > 1) { findViewById(R.id.non_configurable_text).setVisibility(GONE); findViewById(R.id.interruptiveness_settings).setVisibility(GONE); @@ -360,10 +370,10 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G if (mWasShownHighPriority) { updateButtons(ACTION_ALERT); - setImportanceSummary(ACTION_ALERT); + setImportanceSummary(ACTION_ALERT, false); } else { updateButtons(ACTION_TOGGLE_SILENT); - setImportanceSummary(ACTION_TOGGLE_SILENT); + setImportanceSummary(ACTION_TOGGLE_SILENT, false); } } @@ -484,14 +494,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G } } - private boolean hasImportanceChanged() { - return mSingleNotificationChannel != null - && mChosenImportance != null - && (mStartingChannelImportance == IMPORTANCE_UNSPECIFIED - || (mWasShownHighPriority && mChosenImportance < IMPORTANCE_DEFAULT) - || (!mWasShownHighPriority && mChosenImportance >= IMPORTANCE_DEFAULT)); - } - private void saveImportance() { if (!mIsNonblockable || mExitReason != NotificationCounters.BLOCKING_HELPER_STOP_NOTIFICATIONS) { @@ -526,8 +528,8 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G } private void updateButtons(int blockState) { - TextView silence = findViewById(R.id.silence); - TextView alert = findViewById(R.id.alert); + View silence = findViewById(R.id.silence); + View alert = findViewById(R.id.alert); TextView done = findViewById(R.id.done); switch (blockState) { case ACTION_TOGGLE_SILENT: @@ -549,22 +551,28 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G } } - private void updateButtons(TextView selected, TextView unselected) { + private void updateButtons(View selected, View unselected) { selected.setBackground(mSelectedBackground); selected.setSelected(true); - selected.setTextAppearance( - R.style.TextAppearance_NotificationImportanceButton_Selected); unselected.setBackground(mUnselectedBackground); unselected.setSelected(false); - unselected.setTextAppearance( - R.style.TextAppearance_NotificationImportanceButton_Unselected); } - void setImportanceSummary(int blockState) { - TextView view = findViewById(R.id.description); + void setImportanceSummary(int blockState, boolean userTriggered) { + if (userTriggered) { + AutoTransition transition = new AutoTransition(); + transition.setDuration(BUTTON_ANIM_TIME_MS); + TransitionManager.beginDelayedTransition(this, transition); + } if (blockState == ACTION_ALERT) { + TextView view = findViewById(R.id.alert_summary); + view.setVisibility(VISIBLE); + findViewById(R.id.silence_summary).setVisibility(GONE); view.setText(R.string.notification_channel_summary_default); } else { + TextView view = findViewById(R.id.silence_summary); + view.setVisibility(VISIBLE); + findViewById(R.id.alert_summary).setVisibility(GONE); if (mShowInStatusBar) { if (mShowOnLockscreen) { view.setText(R.string.notification_channel_summary_low_status_lock); @@ -742,12 +750,12 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G @Override public boolean willBeRemoved() { - return hasImportanceChanged(); + return false; } @Override public boolean shouldBeSaved() { - return hasImportanceChanged(); + return mPressedApply; } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java index ef7d20c11c56..73f7732fe4ab 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java @@ -42,7 +42,6 @@ import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.statusbar.AlphaOptimizedImageView; -import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.row.NotificationGuts.GutsContent; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; @@ -80,7 +79,7 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl private OnMenuEventListener mMenuListener; private boolean mDismissRtl; private boolean mIsForeground; - private final boolean mIsUsingNewInterruptionModel; + private final boolean mIsUsingBidirectionalSwipe; private ValueAnimator mFadeAnimator; private boolean mAnimating; @@ -113,12 +112,19 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl private boolean mIsUserTouching; public NotificationMenuRow(Context context) { + //TODO: (b/131242807) not using bidirectional swipe for now + this(context, false); + } + + // Only needed for testing until we want to turn bidirectional swipe back on + @VisibleForTesting + NotificationMenuRow(Context context, boolean isUsingBidirectionalSwipe) { mContext = context; mShouldShowMenu = context.getResources().getBoolean(R.bool.config_showNotificationGear); mHandler = new Handler(Looper.getMainLooper()); mLeftMenuItems = new ArrayList<>(); mRightMenuItems = new ArrayList<>(); - mIsUsingNewInterruptionModel = NotificationUtils.useNewInterruptionModel(mContext); + mIsUsingBidirectionalSwipe = isUsingBidirectionalSwipe; } @Override @@ -255,13 +261,13 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl mSnoozeItem = createSnoozeItem(mContext); } mAppOpsItem = createAppOpsItem(mContext); - if (mIsUsingNewInterruptionModel) { + if (mIsUsingBidirectionalSwipe) { mInfoItem = createInfoItem(mContext, !mParent.getEntry().isHighPriority()); } else { mInfoItem = createInfoItem(mContext); } - if (!mIsUsingNewInterruptionModel) { + if (!mIsUsingBidirectionalSwipe) { if (!isForeground) { mRightMenuItems.add(mSnoozeItem); } @@ -618,12 +624,12 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl @Override public boolean shouldShowGutsOnSnapOpen() { - return mIsUsingNewInterruptionModel; + return mIsUsingBidirectionalSwipe; } @Override public MenuItem menuItemToExposeOnSnap() { - return mIsUsingNewInterruptionModel ? mInfoItem : null; + return mIsUsingBidirectionalSwipe ? mInfoItem : null; } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index ebda5852faea..7a9da6bf9ab7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -6355,13 +6355,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd @Override public boolean canChildBeDismissedInDirection(View v, boolean isRightOrDown) { - boolean isValidDirection; - if (NotificationUtils.useNewInterruptionModel(mContext)) { - isValidDirection = mDismissRtl ? !isRightOrDown : isRightOrDown; - } else { - isValidDirection = true; - } - return isValidDirection && canChildBeDismissed(v); + //TODO: b/131242807 for why this doesn't do anything with direction + return canChildBeDismissed(v); } }; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java index fdf8ccee3f62..5912cd7b6433 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java @@ -95,7 +95,8 @@ public class AutoHideController implements CommandQueue.Callbacks { @Override public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis, - int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) { + int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds, + boolean navbarColorManagedByIme) { if (displayId != mDisplayId) { return; } @@ -119,7 +120,7 @@ public class AutoHideController implements CommandQueue.Callbacks { if (mSystemUiVisibility != newVal) { mCommandQueue.setSystemUiVisibility(mDisplayId, mSystemUiVisibility, fullscreenStackVis, dockedStackVis, mask, fullscreenStackBounds, - dockedStackBounds); + dockedStackBounds, navbarColorManagedByIme); } notifySystemUiVisibilityChanged(mSystemUiVisibility); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java index b590ca7f4df0..b0b656a1a951 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java @@ -79,6 +79,9 @@ public class LightBarController implements BatteryController.BatteryStateChangeC private final Rect mLastDockedBounds = new Rect(); private boolean mQsCustomizing; + private boolean mDirectReplying; + private boolean mNavbarColorManagedByIme; + @Inject public LightBarController(Context ctx, DarkIconDispatcher darkIconDispatcher, BatteryController batteryController) { @@ -100,7 +103,7 @@ public class LightBarController implements BatteryController.BatteryStateChangeC public void onSystemUiVisibilityChanged(int fullscreenStackVis, int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds, boolean sbModeChanged, - int statusBarMode) { + int statusBarMode, boolean navbarColorManagedByIme) { int oldFullscreen = mFullscreenStackVisibility; int newFullscreen = (oldFullscreen & ~mask) | (fullscreenStackVis & mask); int diffFullscreen = newFullscreen ^ oldFullscreen; @@ -122,12 +125,13 @@ public class LightBarController implements BatteryController.BatteryStateChangeC mFullscreenStackVisibility = newFullscreen; mDockedStackVisibility = newDocked; mLastStatusBarMode = statusBarMode; + mNavbarColorManagedByIme = navbarColorManagedByIme; mLastFullscreenBounds.set(fullscreenStackBounds); mLastDockedBounds.set(dockedStackBounds); } public void onNavigationVisibilityChanged(int vis, int mask, boolean nbModeChanged, - int navigationBarMode) { + int navigationBarMode, boolean navbarColorManagedByIme) { int oldVis = mSystemUiVisibility; int newVis = (oldVis & ~mask) | (vis & mask); int diffVis = newVis ^ oldVis; @@ -136,21 +140,24 @@ public class LightBarController implements BatteryController.BatteryStateChangeC boolean last = mNavigationLight; mHasLightNavigationBar = isLight(vis, navigationBarMode, View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR); - mNavigationLight = mHasLightNavigationBar && !mForceDarkForScrim && !mQsCustomizing; + mNavigationLight = mHasLightNavigationBar + && (mDirectReplying && mNavbarColorManagedByIme || !mForceDarkForScrim) + && !mQsCustomizing; if (mNavigationLight != last) { updateNavigation(); } } mSystemUiVisibility = newVis; mLastNavigationBarMode = navigationBarMode; + mNavbarColorManagedByIme = navbarColorManagedByIme; } private void reevaluate() { onSystemUiVisibilityChanged(mFullscreenStackVisibility, mDockedStackVisibility, 0 /* mask */, mLastFullscreenBounds, mLastDockedBounds, - true /* sbModeChange*/, mLastStatusBarMode); + true /* sbModeChange*/, mLastStatusBarMode, mNavbarColorManagedByIme); onNavigationVisibilityChanged(mSystemUiVisibility, 0 /* mask */, true /* nbModeChanged */, - mLastNavigationBarMode); + mLastNavigationBarMode, mNavbarColorManagedByIme); } public void setQsCustomizing(boolean customizing) { @@ -159,6 +166,16 @@ public class LightBarController implements BatteryController.BatteryStateChangeC reevaluate(); } + /** + * Sets whether the direct-reply is in use or not. + * @param directReplying {@code true} when the direct-reply is in-use. + */ + public void setDirectReplying(boolean directReplying) { + if (mDirectReplying == directReplying) return; + mDirectReplying = directReplying; + reevaluate(); + } + public void setScrimState(ScrimState scrimState, float scrimBehindAlpha, GradientColors scrimInFrontColor) { boolean forceDarkForScrimLast = mForceDarkForScrim; @@ -260,7 +277,9 @@ public class LightBarController implements BatteryController.BatteryStateChangeC pw.print(" mLastNavigationBarMode="); pw.println(mLastNavigationBarMode); pw.print(" mForceDarkForScrim="); pw.print(mForceDarkForScrim); - pw.print(" mQsCustomizing="); pw.println(mQsCustomizing); + pw.print(" mQsCustomizing="); pw.print(mQsCustomizing); + pw.print(" mDirectReplying="); pw.println(mDirectReplying); + pw.print(" mNavbarColorManagedByIme="); pw.println(mNavbarColorManagedByIme); pw.println(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java index 586e82c612c8..3b3bd218a8e6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.phone; +import static com.android.systemui.Dependency.MAIN_HANDLER_NAME; import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT; import android.content.Context; @@ -27,14 +28,18 @@ import android.graphics.drawable.Animatable2; import android.graphics.drawable.AnimatedVectorDrawable; import android.graphics.drawable.Drawable; import android.hardware.biometrics.BiometricSourceType; +import android.os.Handler; import android.util.AttributeSet; import android.view.ViewGroup; import android.view.accessibility.AccessibilityNodeInfo; +import androidx.annotation.Nullable; + import com.android.internal.graphics.ColorUtils; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.R; +import com.android.systemui.dock.DockManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.KeyguardAffordanceView; import com.android.systemui.statusbar.policy.AccessibilityController; @@ -62,6 +67,8 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange private final UnlockMethodCache mUnlockMethodCache; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private final AccessibilityController mAccessibilityController; + private final DockManager mDockManager; + private final Handler mMainHandler; private int mLastState = 0; private boolean mTransientBiometricsError; @@ -72,13 +79,28 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange private boolean mPulsing; private boolean mDozing; private boolean mBouncerVisible; + private boolean mDocked; private boolean mLastDozing; private boolean mLastPulsing; private boolean mLastBouncerVisible; private int mIconColor; + private float mDozeAmount; + private int mIconRes; + private boolean mWasPulsingOnThisFrame; private final Runnable mDrawOffTimeout = () -> update(true /* forceUpdate */); - private float mDozeAmount; + private final DockManager.DockEventListener mDockEventListener = + new DockManager.DockEventListener() { + @Override + public void onEvent(int event) { + boolean docked = event == DockManager.STATE_DOCKED + || event == DockManager.STATE_DOCKED_HIDE; + if (docked != mDocked) { + mDocked = docked; + update(true /* force */); + } + } + }; private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() { @@ -115,7 +137,9 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange public LockIcon(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs, StatusBarStateController statusBarStateController, ConfigurationController configurationController, - AccessibilityController accessibilityController) { + AccessibilityController accessibilityController, + @Nullable DockManager dockManager, + @Named(MAIN_HANDLER_NAME) Handler mainHandler) { super(context, attrs); mContext = context; mUnlockMethodCache = UnlockMethodCache.getInstance(context); @@ -123,6 +147,8 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange mAccessibilityController = accessibilityController; mConfigurationController = configurationController; mStatusBarStateController = statusBarStateController; + mDockManager = dockManager; + mMainHandler = mainHandler; } @Override @@ -132,6 +158,9 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange mConfigurationController.addCallback(this); mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback); mUnlockMethodCache.addListener(this); + if (mDockManager != null) { + mDockManager.addListener(mDockEventListener); + } onThemeChanged(); } @@ -142,6 +171,9 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange mConfigurationController.removeCallback(this); mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback); mUnlockMethodCache.removeListener(this); + if (mDockManager != null) { + mDockManager.removeListener(mDockEventListener); + } } @Override @@ -189,39 +221,36 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange mPulsing, mLastDozing, mDozing, mBouncerVisible); boolean isAnim = iconAnimRes != -1; - Drawable icon; - if (isAnim) { - // Load the animation resource. - icon = mContext.getDrawable(iconAnimRes); - } else { - // Load the static icon resource based on the current state. - icon = getIconForState(state); - } - - final AnimatedVectorDrawable animation = icon instanceof AnimatedVectorDrawable - ? (AnimatedVectorDrawable) icon - : null; - setImageDrawable(icon, false); - updateDarkTint(); - if (mIsFaceUnlockState) { - announceForAccessibility(getContext().getString( - R.string.accessibility_scanning_face)); - } + int iconRes = isAnim ? iconAnimRes : getIconForState(state); + if (iconRes != mIconRes) { + mIconRes = iconRes; + + Drawable icon = mContext.getDrawable(iconRes); + final AnimatedVectorDrawable animation = icon instanceof AnimatedVectorDrawable + ? (AnimatedVectorDrawable) icon + : null; + setImageDrawable(icon, false); + if (mIsFaceUnlockState) { + announceForAccessibility(getContext().getString( + R.string.accessibility_scanning_face)); + } - if (animation != null && isAnim) { - animation.forceAnimationOnUI(); - animation.clearAnimationCallbacks(); - animation.registerAnimationCallback(new Animatable2.AnimationCallback() { - @Override - public void onAnimationEnd(Drawable drawable) { - if (getDrawable() == animation && state == getState() - && doesAnimationLoop(iconAnimRes)) { - animation.start(); + if (animation != null && isAnim) { + animation.forceAnimationOnUI(); + animation.clearAnimationCallbacks(); + animation.registerAnimationCallback(new Animatable2.AnimationCallback() { + @Override + public void onAnimationEnd(Drawable drawable) { + if (getDrawable() == animation && state == getState() + && doesAnimationLoop(iconAnimRes)) { + animation.start(); + } } - } - }); - animation.start(); + }); + animation.start(); + } } + updateDarkTint(); if (isAnim && !mLastScreenOn) { removeCallbacks(mDrawOffTimeout); @@ -237,7 +266,8 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange mLastBouncerVisible = mBouncerVisible; } - setVisibility(mDozing && !mPulsing ? INVISIBLE : VISIBLE); + boolean invisible = mDozing && (!mPulsing || mDocked); + setVisibility(invisible ? INVISIBLE : VISIBLE); updateClickability(); } @@ -274,7 +304,7 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange } } - private Drawable getIconForState(int state) { + private int getIconForState(int state) { int iconRes; switch (state) { case STATE_LOCKED: @@ -292,25 +322,27 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange throw new IllegalArgumentException(); } - return mContext.getDrawable(iconRes); + return iconRes; } private boolean doesAnimationLoop(int resourceId) { return resourceId == com.android.internal.R.anim.lock_scanning; } - private static int getAnimationResForTransition(int oldState, int newState, + private int getAnimationResForTransition(int oldState, int newState, boolean wasPulsing, boolean pulsing, boolean wasDozing, boolean dozing, boolean bouncerVisible) { // Never animate when screen is off - if (dozing && !pulsing) { + if (dozing && !pulsing && !mWasPulsingOnThisFrame) { return -1; } boolean isError = oldState != STATE_BIOMETRICS_ERROR && newState == STATE_BIOMETRICS_ERROR; boolean justUnlocked = oldState != STATE_LOCK_OPEN && newState == STATE_LOCK_OPEN; boolean justLocked = oldState == STATE_LOCK_OPEN && newState == STATE_LOCKED; + boolean nowPulsing = !wasPulsing && pulsing; + boolean turningOn = wasDozing && !dozing && !mWasPulsingOnThisFrame; if (isError) { return com.android.internal.R.anim.lock_to_error; @@ -320,7 +352,7 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange return com.android.internal.R.anim.lock_lock; } else if (newState == STATE_SCANNING_FACE && bouncerVisible) { return com.android.internal.R.anim.lock_scanning; - } else if (!wasPulsing && pulsing && newState != STATE_LOCK_OPEN) { + } else if ((nowPulsing || turningOn) && newState != STATE_LOCK_OPEN) { return com.android.internal.R.anim.lock_in; } return -1; @@ -351,6 +383,12 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange */ public void setPulsing(boolean pulsing) { mPulsing = pulsing; + if (!mPulsing) { + mWasPulsingOnThisFrame = true; + mMainHandler.post(() -> { + mWasPulsingOnThisFrame = false; + }); + } update(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java index 79c7ab1d2b8c..4603ba6af89f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java @@ -23,6 +23,7 @@ import android.graphics.Canvas;; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Rect; +import android.os.SystemClock; import android.os.VibrationEffect; import android.util.MathUtils; import android.view.ContextThemeWrapper; @@ -51,6 +52,11 @@ public class NavigationBarEdgePanel extends View { private static final long DISAPPEAR_ARROW_ANIMATION_DURATION_MS = 100; /** + * The minimum time required since the first vibration effect to receive a second one + */ + private static final int MIN_TIME_BETWEEN_EFFECTS_MS = 120; + + /** * The size of the protection of the arrow in px. Only used if this is not background protected */ private static final int PROTECTION_WIDTH_PX = 2; @@ -182,6 +188,7 @@ public class NavigationBarEdgePanel extends View { private int mArrowStartColor; private int mCurrentArrowColor; private float mDisappearAmount; + private long mVibrationTime; private DynamicAnimation.OnAnimationEndListener mSetGoneEndListener = new DynamicAnimation.OnAnimationEndListener() { @@ -394,7 +401,7 @@ public class NavigationBarEdgePanel extends View { case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: { if (mTriggerBack) { - triggerBackAnimation(); + triggerBack(); } else { if (mTranslationAnimation.isRunning()) { mTranslationAnimation.addEndListener(mSetGoneEndListener); @@ -521,8 +528,10 @@ public class NavigationBarEdgePanel extends View { return mCurrentTranslation; } - private void triggerBackAnimation() { - + private void triggerBack() { + if (SystemClock.uptimeMillis() - mVibrationTime >= MIN_TIME_BETWEEN_EFFECTS_MS) { + mVibratorHelper.vibrate(VibrationEffect.EFFECT_CLICK); + } mVelocityTracker.computeCurrentVelocity(1000); // Only do the extra translation if we're not already flinging boolean doExtraTranslation = Math.abs(mVelocityTracker.getXVelocity()) < 1000; @@ -577,6 +586,7 @@ public class NavigationBarEdgePanel extends View { setCurrentTranslation(0); mPreviousTouchTranslation = 0; mTotalTouchDelta = 0; + mVibrationTime = 0; setDesiredVerticalTransition(0, false /* animated */); } @@ -599,6 +609,7 @@ public class NavigationBarEdgePanel extends View { if (!mDragSlopPassed && touchTranslation > mSwipeThreshold) { mDragSlopPassed = true; mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK); + mVibrationTime = SystemClock.uptimeMillis(); // Let's show the arrow and animate it in! mDisappearAmount = 0.0f; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java index de57066d80ba..8aa4f0382649 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java @@ -24,11 +24,9 @@ import static android.app.StatusBarManager.windowStateToString; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON; import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener; -import static com.android.systemui.shared.system.NavigationBarCompat.InteractionType; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN; -import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE; @@ -197,12 +195,6 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback } @Override - public void onInteractionFlagsChanged(@InteractionType int flags) { - mNavigationBarView.updateStates(); - updateScreenPinningGestures(); - } - - @Override public void startAssistant(Bundle bundle) { mAssistManager.startAssist(bundle); } @@ -335,7 +327,10 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback filter.addAction(Intent.ACTION_USER_SWITCHED); getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null); notifyNavigationBarScreenOn(); + mOverviewProxyService.addCallback(mOverviewProxyListener); + mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_NAV_BAR_HIDDEN, + !isNavBarWindowVisible(), mDisplayId); // Currently there is no accelerometer sensor on non-default display. if (mIsOnDefaultDisplay) { @@ -454,13 +449,6 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback } @Override - public void topAppWindowChanged(int displayId, boolean showMenu) { - if (displayId == mDisplayId && mNavigationBarView != null) { - mNavigationBarView.setMenuVisibility(showMenu); - } - } - - @Override public void setWindowState( int displayId, @WindowType int window, @WindowVisibleState int state) { if (displayId == mDisplayId @@ -471,7 +459,7 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback if (DEBUG_WINDOW_STATE) Log.d(TAG, "Navigation bar " + windowStateToString(state)); mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_NAV_BAR_HIDDEN, - !isNavBarWindowVisible()); + !isNavBarWindowVisible(), mDisplayId); mNavigationBarView.getRotateSuggestionButton() .onNavigationBarWindowVisibilityChange(isNavBarWindowVisible()); } @@ -512,12 +500,13 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback mAutoHideController.touchAutoHide(); mLightBarController.onNavigationVisibilityChanged(mSystemUiVisibility, 0 /* mask */, - true /* nbModeChanged */, mNavigationBarMode); + true /* nbModeChanged */, mNavigationBarMode, false /* navbarColorManagedByIme */); } @Override public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis, - int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) { + int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds, + boolean navbarColorManagedByIme) { if (displayId != mDisplayId) { return; } @@ -545,7 +534,7 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback } } mLightBarController.onNavigationVisibilityChanged( - vis, mask, nbModeChanged, mNavigationBarMode); + vis, mask, nbModeChanged, mNavigationBarMode, navbarColorManagedByIme); } private @TransitionMode int computeBarMode(int oldVis, int newVis) { @@ -831,7 +820,6 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback activityManager.stopSystemLockTaskMode(); // When exiting refresh disabled flags. mNavigationBarView.updateNavButtonIcons(); - mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_SCREEN_PINNING, false); } } @@ -883,9 +871,10 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback boolean clickable = (flags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0; boolean longClickable = (flags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0; mNavigationBarView.setAccessibilityButtonState(clickable, longClickable); - mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_A11Y_BUTTON_CLICKABLE, clickable); mOverviewProxyService.setSystemUiStateFlag( - SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE, longClickable); + SYSUI_STATE_A11Y_BUTTON_CLICKABLE, clickable, mDisplayId); + mOverviewProxyService.setSystemUiStateFlag( + SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE, longClickable, mDisplayId); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index 4333200e702a..831d882d68a7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -19,7 +19,6 @@ package com.android.systemui.statusbar.phone; import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON; -import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_SHOW_OVERVIEW_BUTTON; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE; @@ -269,8 +268,6 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav // Set up the context group of buttons mContextualButtonGroup = new ContextualButtonGroup(R.id.menu_container); - final ContextualButton menuButton = new ContextualButton(R.id.menu, - R.drawable.ic_sysbar_menu); final ContextualButton imeSwitcherButton = new ContextualButton(R.id.ime_switcher, R.drawable.ic_ime_switcher_default); final RotationContextButton rotateSuggestionButton = new RotationContextButton( @@ -279,7 +276,6 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav final ContextualButton accessibilityButton = new ContextualButton(R.id.accessibility_button, R.drawable.ic_sysbar_accessibility_button); - mContextualButtonGroup.addButton(menuButton); mContextualButtonGroup.addButton(imeSwitcherButton); if (!isGesturalMode) { mContextualButtonGroup.addButton(rotateSuggestionButton); @@ -307,7 +303,6 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav mButtonDispatchers.put(R.id.home, new ButtonDispatcher(R.id.home)); mButtonDispatchers.put(R.id.home_handle, new ButtonDispatcher(R.id.home_handle)); mButtonDispatchers.put(R.id.recent_apps, new ButtonDispatcher(R.id.recent_apps)); - mButtonDispatchers.put(R.id.menu, menuButton); mButtonDispatchers.put(R.id.ime_switcher, imeSwitcherButton); mButtonDispatchers.put(R.id.accessibility_button, accessibilityButton); mButtonDispatchers.put(R.id.rotate_suggestion, rotateSuggestionButton); @@ -407,10 +402,6 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav return mButtonDispatchers.get(R.id.recent_apps); } - public ButtonDispatcher getMenuButton() { - return mButtonDispatchers.get(R.id.menu); - } - public ButtonDispatcher getBackButton() { return mButtonDispatchers.get(R.id.back); } @@ -637,10 +628,6 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav // as they are used for exiting. final boolean pinningActive = ActivityManagerWrapper.getInstance().isScreenPinningActive(); if (mOverviewProxyService.isEnabled()) { - // Use interaction flags to show/hide navigation buttons but will be shown if required - // to exit screen pinning. - final int flags = mOverviewProxyService.getInteractionFlags(); - disableRecent |= (flags & FLAG_SHOW_OVERVIEW_BUTTON) == 0; if (pinningActive) { disableBack = disableHome = false; } @@ -727,10 +714,10 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav } } - public void onPanelExpandedChange(boolean expanded) { + public void onPanelExpandedChange() { updateSlippery(); mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED, - expanded); + mPanelView.isFullyExpanded(), getContext().getDisplayId()); } public void updateStates() { @@ -800,10 +787,6 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav } } - public void setMenuVisibility(final boolean show) { - mContextualButtonGroup.setButtonVisibility(R.id.menu, show); - } - public void setAccessibilityButtonState(final boolean visible, final boolean longClickable) { mLongClickableAccessibilityButton = longClickable; getAccessibilityButton().setLongClickable(longClickable); @@ -1159,16 +1142,14 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav visibilityToString(getCurrentView().getVisibility()), getCurrentView().getAlpha())); - pw.println(String.format(" disabled=0x%08x vertical=%s menu=%s darkIntensity=%.2f", + pw.println(String.format(" disabled=0x%08x vertical=%s darkIntensity=%.2f", mDisabledFlags, mIsVertical ? "true" : "false", - getMenuButton().isVisible() ? "true" : "false", getLightTransitionsController().getCurrentDarkIntensity())); dumpButton(pw, "back", getBackButton()); dumpButton(pw, "home", getHomeButton()); dumpButton(pw, "rcnt", getRecentsButton()); - dumpButton(pw, "menu", getMenuButton()); dumpButton(pw, "rota", getRotateSuggestionButton()); dumpButton(pw, "a11y", getAccessibilityButton()); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java index a00feeb6c116..fa2263fb97ad 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java @@ -17,8 +17,10 @@ package com.android.systemui.statusbar.phone; import static android.content.Intent.ACTION_OVERLAY_CHANGED; -import static android.content.Intent.ACTION_USER_SWITCHED; +import static android.os.UserHandle.USER_CURRENT; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON; +import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY; +import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY; import android.content.BroadcastReceiver; import android.content.Context; @@ -28,16 +30,21 @@ import android.content.om.IOverlayManager; import android.content.pm.PackageManager; import android.content.res.ApkAssets; import android.os.PatternMatcher; +import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; import android.util.Log; +import android.util.SparseBooleanArray; import com.android.systemui.Dumpable; +import com.android.systemui.UiOffloadThread; import com.android.systemui.shared.system.ActivityManagerWrapper; +import com.android.systemui.statusbar.policy.DeviceProvisionedController; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; import javax.inject.Inject; import javax.inject.Singleton; @@ -48,7 +55,7 @@ import javax.inject.Singleton; @Singleton public class NavigationModeController implements Dumpable { - private static final String TAG = NavigationModeController.class.getName(); + private static final String TAG = NavigationModeController.class.getSimpleName(); private static final boolean DEBUG = true; public interface ModeChangedListener { @@ -57,6 +64,10 @@ public class NavigationModeController implements Dumpable { private final Context mContext; private final IOverlayManager mOverlayManager; + private final DeviceProvisionedController mDeviceProvisionedController; + private final UiOffloadThread mUiOffloadThread; + + private SparseBooleanArray mRestoreGesturalNavBarMode = new SparseBooleanArray(); private int mMode = NAV_BAR_MODE_3BUTTON; private ArrayList<ModeChangedListener> mListeners = new ArrayList<>(); @@ -64,36 +75,90 @@ public class NavigationModeController implements Dumpable { private BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - context = getCurrentUserContext(); - int mode = getCurrentInteractionMode(context); - mMode = mode; - if (DEBUG) { - Log.e(TAG, "ACTION_OVERLAY_CHANGED: mode=" + mMode - + " contextUser=" + context.getUserId()); - dumpAssetPaths(context); - } - - for (int i = 0; i < mListeners.size(); i++) { - mListeners.get(i).onNavigationModeChanged(mode); + if (intent.getAction().equals(ACTION_OVERLAY_CHANGED)) { + if (DEBUG) { + Log.d(TAG, "ACTION_OVERLAY_CHANGED"); + } + updateCurrentInteractionMode(true /* notify */); } } }; + private final DeviceProvisionedController.DeviceProvisionedListener mDeviceProvisionedCallback = + new DeviceProvisionedController.DeviceProvisionedListener() { + @Override + public void onDeviceProvisionedChanged() { + if (DEBUG) { + Log.d(TAG, "onDeviceProvisionedChanged: " + + mDeviceProvisionedController.isDeviceProvisioned()); + } + // Once the device has been provisioned, check if we can restore gestural nav + restoreGesturalNavOverlayIfNecessary(); + } + + @Override + public void onUserSetupChanged() { + if (DEBUG) { + Log.d(TAG, "onUserSetupChanged: " + + mDeviceProvisionedController.isCurrentUserSetup()); + } + // Once the user has been setup, check if we can restore gestural nav + restoreGesturalNavOverlayIfNecessary(); + } + + @Override + public void onUserSwitched() { + if (DEBUG) { + Log.d(TAG, "onUserSwitched: " + + ActivityManagerWrapper.getInstance().getCurrentUserId()); + } + + // Update the nav mode for the current user + updateCurrentInteractionMode(true /* notify */); + + // When switching users, defer enabling the gestural nav overlay until the user + // is all set up + deferGesturalNavOverlayIfNecessary(); + } + }; + @Inject - public NavigationModeController(Context context) { + public NavigationModeController(Context context, + DeviceProvisionedController deviceProvisionedController, + UiOffloadThread uiOffloadThread) { mContext = context; mOverlayManager = IOverlayManager.Stub.asInterface( ServiceManager.getService(Context.OVERLAY_SERVICE)); + mUiOffloadThread = uiOffloadThread; + mDeviceProvisionedController = deviceProvisionedController; + mDeviceProvisionedController.addCallback(mDeviceProvisionedCallback); IntentFilter overlayFilter = new IntentFilter(ACTION_OVERLAY_CHANGED); overlayFilter.addDataScheme("package"); overlayFilter.addDataSchemeSpecificPart("android", PatternMatcher.PATTERN_LITERAL); mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, overlayFilter, null, null); - IntentFilter userFilter = new IntentFilter(ACTION_USER_SWITCHED); - mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, userFilter, null, null); + updateCurrentInteractionMode(false /* notify */); - mMode = getCurrentInteractionMode(getCurrentUserContext()); + // Check if we need to defer enabling gestural nav + deferGesturalNavOverlayIfNecessary(); + } + + public void updateCurrentInteractionMode(boolean notify) { + Context context = getCurrentUserContext(); + int mode = getCurrentInteractionMode(context); + mMode = mode; + if (DEBUG) { + Log.e(TAG, "updateCurrentInteractionMode: mode=" + mMode + + " contextUser=" + context.getUserId()); + dumpAssetPaths(context); + } + + if (notify) { + for (int i = 0; i < mListeners.size(); i++) { + mListeners.get(i).onNavigationModeChanged(mode); + } + } } public int addListener(ModeChangedListener listener) { @@ -129,10 +194,77 @@ public class NavigationModeController implements Dumpable { } } + private void deferGesturalNavOverlayIfNecessary() { + final int userId = mDeviceProvisionedController.getCurrentUser(); + mRestoreGesturalNavBarMode.put(userId, false); + if (mDeviceProvisionedController.isDeviceProvisioned() + && mDeviceProvisionedController.isCurrentUserSetup()) { + // User is already setup and device is provisioned, nothing to do + if (DEBUG) { + Log.d(TAG, "deferGesturalNavOverlayIfNecessary: device is provisioned and user is " + + "setup"); + } + return; + } + + ArrayList<String> defaultOverlays = new ArrayList<>(); + try { + defaultOverlays.addAll(Arrays.asList(mOverlayManager.getDefaultOverlayPackages())); + } catch (RemoteException e) { + Log.e(TAG, "deferGesturalNavOverlayIfNecessary: failed to fetch default overlays"); + } + if (!defaultOverlays.contains(NAV_BAR_MODE_GESTURAL_OVERLAY)) { + // No default gesture nav overlay + if (DEBUG) { + Log.d(TAG, "deferGesturalNavOverlayIfNecessary: no default gestural overlay, " + + "default=" + defaultOverlays); + } + return; + } + + // If the default is gestural, force-enable three button mode until the device is + // provisioned + setModeOverlay(NAV_BAR_MODE_3BUTTON_OVERLAY, USER_CURRENT); + mRestoreGesturalNavBarMode.put(userId, true); + if (DEBUG) { + Log.d(TAG, "deferGesturalNavOverlayIfNecessary: setting to 3 button mode"); + } + } + + private void restoreGesturalNavOverlayIfNecessary() { + if (DEBUG) { + Log.d(TAG, "restoreGesturalNavOverlayIfNecessary: needs restore=" + + mRestoreGesturalNavBarMode); + } + final int userId = mDeviceProvisionedController.getCurrentUser(); + if (mRestoreGesturalNavBarMode.get(userId)) { + // Restore the gestural state if necessary + setModeOverlay(NAV_BAR_MODE_GESTURAL_OVERLAY, USER_CURRENT); + mRestoreGesturalNavBarMode.put(userId, false); + } + } + + public void setModeOverlay(String overlayPkg, int userId) { + mUiOffloadThread.submit(() -> { + try { + mOverlayManager.setEnabledExclusiveInCategory(overlayPkg, userId); + } catch (RemoteException e) { + Log.e(TAG, "Failed to enable overlay " + overlayPkg + " for user " + userId); + } + }); + } + @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("NavigationModeController:"); pw.println(" mode=" + mMode); + String defaultOverlays = ""; + try { + defaultOverlays = String.join(", ", mOverlayManager.getDefaultOverlayPackages()); + } catch (RemoteException e) { + defaultOverlays = "failed_to_fetch"; + } + pw.println(" defaultOverlays=" + defaultOverlays); dumpAssetPaths(getCurrentUserContext()); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index 7b3ddf7cfaef..1194a1d7b3f2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -833,6 +833,11 @@ public class NotificationPanelView extends PanelView implements return false; } initDownStates(event); + // Do not let touches go to shade or QS if the bouncer is visible, + // but still let user swipe down to expand the panel, dismissing the bouncer. + if (mStatusBar.isBouncerShowing()) { + return true; + } if (mBar.panelEnabled() && mHeadsUpTouchHelper.onInterceptTouchEvent(event)) { mIsExpansionFromHeadsUp = true; MetricsLogger.count(mContext, COUNTER_PANEL_OPEN, 1); @@ -1000,6 +1005,13 @@ public class NotificationPanelView extends PanelView implements if (mBlockTouches || (mQs != null && mQs.isCustomizing())) { return false; } + + // Do not allow panel expansion if bouncer is scrimmed, otherwise user would be able to + // pull down QS or expand the shade. + if (mStatusBar.isBouncerShowingScrimmed()) { + return false; + } + initDownStates(event); // Make sure the next touch won't the blocked after the current ends. if (event.getAction() == MotionEvent.ACTION_UP diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index d5706e36bd21..27368deac847 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -44,6 +44,7 @@ import com.android.systemui.SysUiServiceProvider; import com.android.systemui.UiOffloadThread; import com.android.systemui.privacy.PrivacyItem; import com.android.systemui.privacy.PrivacyItemController; +import com.android.systemui.privacy.PrivacyType; import com.android.systemui.qs.tiles.DndTile; import com.android.systemui.qs.tiles.RotationLockTile; import com.android.systemui.statusbar.CommandQueue; @@ -229,9 +230,11 @@ public class PhoneStatusBarPolicy mIconController.setIconVisibility(mSlotDataSaver, false); // privacy items - mIconController.setIcon(mSlotMicrophone, R.drawable.stat_sys_mic_none, null); + mIconController.setIcon(mSlotMicrophone, R.drawable.stat_sys_mic_none, + PrivacyType.TYPE_MICROPHONE.getName(mContext)); mIconController.setIconVisibility(mSlotMicrophone, false); - mIconController.setIcon(mSlotCamera, R.drawable.stat_sys_camera, null); + mIconController.setIcon(mSlotCamera, R.drawable.stat_sys_camera, + PrivacyType.TYPE_CAMERA.getName(mContext)); mIconController.setIconVisibility(mSlotCamera, false); mIconController.setIcon(mSlotLocation, LOCATION_STATUS_ICON_ID, mContext.getString(R.string.accessibility_location_active)); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java index ce1d6384b7c0..68eba50984d6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java @@ -277,7 +277,7 @@ public class PhoneStatusBarView extends PanelBar { super.panelExpansionChanged(frac, expanded); updateScrimFraction(); if ((frac == 0 || frac == 1) && mBar.getNavigationBarView() != null) { - mBar.getNavigationBarView().onPanelExpandedChange(expanded); + mBar.getNavigationBarView().onPanelExpandedChange(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 93db82d8e119..1fc40b42c552 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -436,7 +436,6 @@ public class StatusBar extends SystemUI implements DemoMode, public void onUserSetupChanged() { final boolean userSetup = mDeviceProvisionedController.isUserSetup( mDeviceProvisionedController.getCurrentUser()); - // STOPSHIP(kozynski, b/129405675) Remove log Log.d(TAG, "mUserSetupObserver - DeviceProvisionedListener called for user " + mDeviceProvisionedController.getCurrentUser()); if (MULTIUSER_DEBUG) { @@ -593,6 +592,7 @@ public class StatusBar extends SystemUI implements DemoMode, updateScrimController(); }; private ActivityIntentHelper mActivityIntentHelper; + private ShadeController mShadeController; @Override public void onActiveStateChanged(int code, int uid, String packageName, boolean active) { @@ -710,8 +710,8 @@ public class StatusBar extends SystemUI implements DemoMode, setSystemUiVisibility(mDisplayId, result.mSystemUiVisibility, result.mFullscreenStackSysUiVisibility, result.mDockedStackSysUiVisibility, - 0xffffffff, result.mFullscreenStackBounds, result.mDockedStackBounds); - topAppWindowChanged(mDisplayId, result.mMenuVisible); + 0xffffffff, result.mFullscreenStackBounds, result.mDockedStackBounds, + result.mNavbarColorManagedByIme); // StatusBarManagerService has a back up of IME token and it's restored here. setImeWindowStatus(mDisplayId, result.mImeToken, result.mImeWindowVis, result.mImeBackDisposition, result.mShowImeSwitcher); @@ -725,11 +725,10 @@ public class StatusBar extends SystemUI implements DemoMode, if (DEBUG) { Log.d(TAG, String.format( - "init: icons=%d disabled=0x%08x lights=0x%08x menu=0x%08x imeButton=0x%08x", + "init: icons=%d disabled=0x%08x lights=0x%08x imeButton=0x%08x", numIcons, result.mDisabledFlags1, result.mSystemUiVisibility, - result.mMenuVisible ? 1 : 0, result.mImeWindowVis)); } @@ -1062,7 +1061,7 @@ public class StatusBar extends SystemUI implements DemoMode, final StatusBarRemoteInputCallback mStatusBarRemoteInputCallback = (StatusBarRemoteInputCallback) Dependency.get( NotificationRemoteInputManager.Callback.class); - final ShadeController shadeController = Dependency.get(ShadeController.class); + mShadeController = Dependency.get(ShadeController.class); final ActivityStarter activityStarter = Dependency.get(ActivityStarter.class); mNotificationActivityStarter = new StatusBarNotificationActivityStarter(mContext, @@ -1070,7 +1069,7 @@ public class StatusBar extends SystemUI implements DemoMode, mHeadsUpManager, activityStarter, mActivityLaunchAnimator, mBarService, mStatusBarStateController, mKeyguardManager, mDreamManager, mRemoteInputManager, mStatusBarRemoteInputCallback, mGroupManager, - mLockscreenUserManager, shadeController, mKeyguardMonitor, + mLockscreenUserManager, mShadeController, mKeyguardMonitor, mNotificationInterruptionStateProvider, mMetricsLogger, new LockPatternUtils(mContext), Dependency.get(MAIN_HANDLER), Dependency.get(BG_HANDLER), mActivityIntentHelper, mBubbleController); @@ -2077,7 +2076,8 @@ public class StatusBar extends SystemUI implements DemoMode, @Override // CommandQueue public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis, - int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) { + int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds, + boolean navbarColorManagedByIme) { if (displayId != mDisplayId) { return; } @@ -2114,7 +2114,8 @@ public class StatusBar extends SystemUI implements DemoMode, } } mLightBarController.onSystemUiVisibilityChanged(fullscreenStackVis, dockedStackVis, - mask, fullscreenStackBounds, dockedStackBounds, sbModeChanged, mStatusBarMode); + mask, fullscreenStackBounds, dockedStackBounds, sbModeChanged, mStatusBarMode, + navbarColorManagedByIme); } @Override @@ -2231,30 +2232,6 @@ public class StatusBar extends SystemUI implements DemoMode, return 0 == (mSystemUiVisibility & View.SYSTEM_UI_FLAG_LOW_PROFILE); } - public void setLightsOn(boolean on) { - Log.v(TAG, "setLightsOn(" + on + ")"); - if (on) { - setSystemUiVisibility(mDisplayId, 0, 0, 0, View.SYSTEM_UI_FLAG_LOW_PROFILE, - mLastFullscreenStackBounds, mLastDockedStackBounds); - } else { - setSystemUiVisibility(mDisplayId, View.SYSTEM_UI_FLAG_LOW_PROFILE, 0, 0, - View.SYSTEM_UI_FLAG_LOW_PROFILE, mLastFullscreenStackBounds, - mLastDockedStackBounds); - } - } - - @Override - public void topAppWindowChanged(int displayId, boolean showMenu) { - if (mDisplayId != displayId) return; - if (SPEW) { - Log.d(TAG, "display#" + displayId + ": " - + (showMenu ? "showing" : "hiding") + " the MENU button"); - } - - // See above re: lights-out policy for legacy apps. - if (showMenu) setLightsOn(true); - } - public static String viewInfo(View v) { return "[(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom() + ") " + v.getWidth() + "x" + v.getHeight() + "]"; @@ -3584,7 +3561,7 @@ public class StatusBar extends SystemUI implements DemoMode, // Notify overview proxy service of the new states Dependency.get(OverviewProxyService.class).setSystemUiStateFlag(SYSUI_STATE_BOUNCER_SHOWING, - isBouncerShowing()); + isBouncerShowing(), mContext.getDisplayId()); } /** @@ -4344,6 +4321,13 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void startPendingIntentDismissingKeyguard( final PendingIntent intent, @Nullable final Runnable intentSentUiThreadCallback) { + startPendingIntentDismissingKeyguard(intent, intentSentUiThreadCallback, null /* row */); + } + + @Override + public void startPendingIntentDismissingKeyguard( + final PendingIntent intent, @Nullable final Runnable intentSentUiThreadCallback, + View associatedView) { final boolean afterKeyguardGone = intent.isActivity() && mActivityIntentHelper.wouldLaunchResolverActivity(intent.getIntent(), mLockscreenUserManager.getCurrentUserId()); @@ -4351,7 +4335,8 @@ public class StatusBar extends SystemUI implements DemoMode, executeActionDismissingKeyguard(() -> { try { intent.send(null, 0, null, null, null, null, getActivityOptions( - null /* animationAdapter */)); + mActivityLaunchAnimator.getLaunchAnimation(associatedView, + mShadeController.isOccluded()))); } catch (PendingIntent.CanceledException e) { // the stack trace isn't very helpful here. // Just log the exception message. @@ -4428,6 +4413,13 @@ public class StatusBar extends SystemUI implements DemoMode, } /** + * @return Whether the security bouncer from Keyguard is showing. + */ + public boolean isBouncerShowingScrimmed() { + return isBouncerShowing() && mStatusBarKeyguardViewManager.bouncerNeedsScrimming(); + } + + /** * @return a PackageManger for userId or if userId is < 0 (USER_ALL etc) then * return PackageManager for mContext */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 002313c227c6..d350c3a7ae96 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -25,8 +25,6 @@ import android.content.Context; import android.content.res.ColorStateList; import android.os.Bundle; import android.os.SystemClock; -import android.transition.Fade; -import android.transition.TransitionManager; import android.util.StatsLog; import android.view.KeyEvent; import android.view.View; @@ -40,17 +38,15 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.keyguard.ViewMediatorCallback; import com.android.settingslib.animation.AppearAnimationUtils; -import com.android.settingslib.animation.DisappearAnimationUtils; import com.android.systemui.DejankUtils; import com.android.systemui.Dependency; -import com.android.systemui.Interpolators; -import com.android.systemui.SysUiServiceProvider; import com.android.systemui.SystemUIFactory; import com.android.systemui.dock.DockManager; import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.CrossFadeHelper; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.StatusBarState; @@ -158,7 +154,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb private boolean mLastPulsing; private int mLastBiometricMode; private boolean mGoingToSleepVisibleNotOccluded; - private int mLastLockVisibility = -1; + private boolean mLastLockVisible; private OnDismissAction mAfterKeyguardGoneAction; private final ArrayList<Runnable> mAfterKeyguardGoneRunnables = new ArrayList<>(); @@ -195,9 +191,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb KeyguardUpdateMonitor.getInstance(context).registerCallback(mUpdateMonitorCallback); mStatusBarStateController.addCallback(this); Dependency.get(ConfigurationController.class).addCallback(this); - mLastGesturalNav = QuickStepContract.isGesturalMode( + mGesturalNav = QuickStepContract.isGesturalMode( Dependency.get(NavigationModeController.class).addListener(this)); - mDockManager = SysUiServiceProvider.getComponent(context, DockManager.class); + mDockManager = Dependency.get(DockManager.class); if (mDockManager != null) { mDockManager.addListener(mDockEventListener); mIsDocked = mDockManager.isDocked(); @@ -213,6 +209,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mStatusBar = statusBar; mContainer = container; mLockIconContainer = lockIconContainer; + if (mLockIconContainer != null) { + mLastLockVisible = mLockIconContainer.getVisibility() == View.VISIBLE; + } mBiometricUnlockController = biometricUnlockController; mBouncer = SystemUIFactory.getInstance().createKeyguardBouncer(mContext, mViewMediatorCallback, mLockPatternUtils, container, dismissCallbackRegistry, @@ -261,21 +260,20 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } boolean keyguardWithoutQs = mStatusBarStateController.getState() == StatusBarState.KEYGUARD && !mNotificationPanelView.isQsExpanded(); - int lockVisibility = (mBouncer.isShowing() || keyguardWithoutQs) - && !mBouncer.isAnimatingAway() ? View.VISIBLE : View.INVISIBLE; - - if (mLastLockVisibility != lockVisibility) { - mLastLockVisibility = lockVisibility; - - Fade transition = new Fade(); - boolean appearing = lockVisibility == View.VISIBLE; - transition.setDuration(appearing ? AppearAnimationUtils.DEFAULT_APPEAR_DURATION - : DisappearAnimationUtils.DEFAULT_APPEAR_DURATION / 2); - transition.setInterpolator(appearing ? Interpolators.ALPHA_IN - : Interpolators.ALPHA_OUT); - TransitionManager.beginDelayedTransition((ViewGroup) mLockIconContainer.getParent(), - transition); - mLockIconContainer.setVisibility(lockVisibility); + boolean lockVisible = (mBouncer.isShowing() || keyguardWithoutQs) + && !mBouncer.isAnimatingAway(); + + if (mLastLockVisible != lockVisible) { + mLastLockVisible = lockVisible; + if (lockVisible) { + CrossFadeHelper.fadeIn(mLockIconContainer, + AppearAnimationUtils.DEFAULT_APPEAR_DURATION /* duration */, + 0 /* delay */); + } else { + CrossFadeHelper.fadeOut(mLockIconContainer, + AppearAnimationUtils.DEFAULT_APPEAR_DURATION / 2 /* duration */, + 0 /* delay */, null /* runnable */); + } } } @@ -485,7 +483,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb // by a FLAG_DISMISS_KEYGUARD_ACTIVITY. reset(isOccluding /* hideBouncerWhenShowing*/); } - if (animate && !occluded && mShowing) { + if (animate && !occluded && mShowing && !mBouncer.isShowing()) { mStatusBar.animateKeyguardUnoccluding(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java index 471d5113a502..0865eb6ae7d2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java @@ -28,6 +28,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.IntentSender; +import android.os.Handler; import android.os.RemoteException; import android.os.UserHandle; import android.view.View; @@ -65,6 +66,7 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks, private final ActivityStarter mActivityStarter = Dependency.get(ActivityStarter.class); private final Context mContext; private final ActivityIntentHelper mActivityIntentHelper; + private final NotificationGroupManager mGroupManager; private View mPendingWorkRemoteInputView; private View mPendingRemoteInputView; private final ShadeController mShadeController = Dependency.get(ShadeController.class); @@ -72,11 +74,12 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks, private final CommandQueue mCommandQueue; private int mDisabled2; protected BroadcastReceiver mChallengeReceiver = new ChallengeReceiver(); + private Handler mMainHandler = new Handler(); /** */ @Inject - public StatusBarRemoteInputCallback(Context context) { + public StatusBarRemoteInputCallback(Context context, NotificationGroupManager groupManager) { mContext = context; mContext.registerReceiverAsUser(mChallengeReceiver, UserHandle.ALL, new IntentFilter(ACTION_DEVICE_LOCKED_CHANGED), null, null); @@ -85,15 +88,15 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks, mCommandQueue = getComponent(context, CommandQueue.class); mCommandQueue.addCallback(this); mActivityIntentHelper = new ActivityIntentHelper(mContext); + mGroupManager = groupManager; } @Override public void onStateChanged(int state) { if (state == StatusBarState.SHADE && mStatusBarStateController.leaveOpenOnKeyguardHide()) { if (!mStatusBarStateController.isKeyguardRequested()) { - if (mPendingRemoteInputView != null - && mPendingRemoteInputView.isAttachedToWindow()) { - mPendingRemoteInputView.post(mPendingRemoteInputView::callOnClick); + if (mPendingRemoteInputView != null) { + mMainHandler.post(mPendingRemoteInputView::callOnClick); } mPendingRemoteInputView = null; } @@ -159,6 +162,10 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks, if (mKeyguardMonitor.isShowing()) { onLockedRemoteInput(row, clickedView); } else { + if (row.isChildInGroup() && !row.areChildrenExpanded()) { + // The group isn't expanded, let's make sure it's visible! + mGroupManager.toggleGroupExpansion(row.getStatusBarNotification()); + } row.setUserExpanded(true); row.getPrivateLayout().setOnExpandedVisibleListener(clickedView::performClick); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java index 9c7a1e34aeb7..3fc9b44f3850 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java @@ -212,13 +212,15 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC @Nullable private String generateTimeRemainingString() { - if (mEstimate == null) { - return null; - } + synchronized (mFetchCallbacks) { + if (mEstimate == null) { + return null; + } - String percentage = NumberFormat.getPercentInstance().format((double) mLevel / 100.0); - return PowerUtil.getBatteryRemainingShortStringFormatted( - mContext, mEstimate.getEstimateMillis()); + String percentage = NumberFormat.getPercentInstance().format((double) mLevel / 100.0); + return PowerUtil.getBatteryRemainingShortStringFormatted( + mContext, mEstimate.getEstimateMillis()); + } } private void updateEstimateInBackground() { @@ -230,9 +232,11 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC mFetchingEstimate = true; Dependency.get(Dependency.BG_HANDLER).post(() -> { // Only fetch the estimate if they are enabled - mEstimate = null; - if (mEstimates.isHybridNotificationEnabled()) { - updateEstimate(); + synchronized (mFetchCallbacks) { + mEstimate = null; + if (mEstimates.isHybridNotificationEnabled()) { + updateEstimate(); + } } mFetchingEstimate = false; Dependency.get(Dependency.MAIN_HANDLER).post(this::notifyEstimateFetchCallbacks); @@ -240,9 +244,8 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC } private void notifyEstimateFetchCallbacks() { - String estimate = generateTimeRemainingString(); - synchronized (mFetchCallbacks) { + String estimate = generateTimeRemainingString(); for (EstimateFetchCompletion completion : mFetchCallbacks) { completion.onBatteryRemainingEstimateRetrieved(estimate); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java index db2523e40ded..98ab3e1b16fd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java @@ -61,7 +61,6 @@ public class DeviceProvisionedControllerImpl extends CurrentUserTracker implemen mSettingsObserver = new ContentObserver(mainHandler) { @Override public void onChange(boolean selfChange, Uri uri, int userId) { - // STOPSHIP(kozynski, b/129405675) Remove log Log.d(TAG, "Setting change: " + uri); if (mUserSetupUri.equals(uri)) { notifySetupChanged(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java index 9923ec9d8a75..41fa34608ee8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java @@ -56,7 +56,7 @@ import com.android.systemui.R; import com.android.systemui.bubbles.BubbleController; import com.android.systemui.plugins.statusbar.phone.NavBarButtonProvider.ButtonInterface; import com.android.systemui.recents.OverviewProxyService; -import com.android.systemui.shared.system.NavigationBarCompat; +import com.android.systemui.shared.system.QuickStepContract; public class KeyButtonView extends ImageView implements ButtonInterface { private static final String TAG = KeyButtonView.class.getSimpleName(); @@ -244,11 +244,11 @@ public class KeyButtonView extends ImageView implements ButtonInterface { y = (int)ev.getRawY(); boolean exceededTouchSlopX = Math.abs(x - mTouchDownX) > (mIsVertical - ? NavigationBarCompat.getQuickScrubTouchSlopPx() - : NavigationBarCompat.getQuickStepTouchSlopPx()); + ? QuickStepContract.getQuickScrubTouchSlopPx() + : QuickStepContract.getQuickStepTouchSlopPx()); boolean exceededTouchSlopY = Math.abs(y - mTouchDownY) > (mIsVertical - ? NavigationBarCompat.getQuickStepTouchSlopPx() - : NavigationBarCompat.getQuickScrubTouchSlopPx()); + ? QuickStepContract.getQuickStepTouchSlopPx() + : QuickStepContract.getQuickScrubTouchSlopPx()); if (exceededTouchSlopX || exceededTouchSlopY) { // When quick step is enabled, prevent animating the ripple triggered by // setPressed and decide to run it on touch up diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java index 11e5625f8538..c398ad82e0a9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java @@ -94,8 +94,7 @@ public class MobileSignalController extends SignalController< mPhone = phone; mDefaults = defaults; mSubscriptionInfo = info; - mPhoneStateListener = new MobilePhoneStateListener(info.getSubscriptionId(), - receiverLooper); + mPhoneStateListener = new MobilePhoneStateListener(receiverLooper); mNetworkNameSeparator = getStringIfExists(R.string.status_bar_network_name_separator); mNetworkNameDefault = getStringIfExists( com.android.internal.R.string.lockscreen_carrier_default); @@ -574,8 +573,8 @@ public class MobileSignalController extends SignalController< } class MobilePhoneStateListener extends PhoneStateListener { - public MobilePhoneStateListener(int subId, Looper looper) { - super(subId, looper); + public MobilePhoneStateListener(Looper looper) { + super(looper); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java index faf63c838259..950f07d5cffa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -662,8 +662,9 @@ public class NetworkControllerImpl extends BroadcastReceiver cachedControllers.remove(subId); } else { MobileSignalController controller = new MobileSignalController(mContext, mConfig, - mHasMobileDataFeature, mPhone, mCallbackHandler, - this, subscriptions.get(i), mSubDefaults, mReceiverHandler.getLooper()); + mHasMobileDataFeature, mPhone.createForSubscriptionId(subId), + mCallbackHandler, this, subscriptions.get(i), + mSubDefaults, mReceiverHandler.getLooper()); controller.setUserSetupComplete(mUserSetup); mMobileSignalControllers.put(subId, controller); if (subscriptions.get(i).getSimSlotIndex() == 0) { @@ -1049,7 +1050,8 @@ public class NetworkControllerImpl extends BroadcastReceiver SubscriptionInfo info = new SubscriptionInfo(id, "", simSlotIndex, "", "", 0, 0, "", 0, null, null, null, "", false, null, null); MobileSignalController controller = new MobileSignalController(mContext, - mConfig, mHasMobileDataFeature, mPhone, mCallbackHandler, this, info, + mConfig, mHasMobileDataFeature, + mPhone.createForSubscriptionId(info.getSubscriptionId()), mCallbackHandler, this, info, mSubDefaults, mReceiverHandler.getLooper()); mMobileSignalControllers.put(id, controller); controller.getState().userSetup = true; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java index c1950a21d10f..43795dc08c91 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java @@ -63,6 +63,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntry.EditedSuggestionInfo; import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; +import com.android.systemui.statusbar.phone.LightBarController; import java.util.function.Consumer; @@ -516,10 +517,12 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene private final Drawable mBackground; private RemoteInputView mRemoteInputView; boolean mShowImeOnInputConnection; + private LightBarController mLightBarController; public RemoteEditText(Context context, AttributeSet attrs) { super(context, attrs); mBackground = getBackground(); + mLightBarController = Dependency.get(LightBarController.class); } private void defocusIfNeeded(boolean animate) { @@ -558,6 +561,9 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene if (!focused) { defocusIfNeeded(true /* animate */); } + if (!mRemoteInputView.mRemoved) { + mLightBarController.setDirectReplying(focused); + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java index f02b5441a737..655c29cbf687 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java @@ -93,10 +93,10 @@ public final class SmartReplyConstants { } private void registerDeviceConfigListener() { - DeviceConfig.addOnPropertyChangedListener( + DeviceConfig.addOnPropertiesChangedListener( DeviceConfig.NAMESPACE_SYSTEMUI, this::postToHandler, - this::onDeviceConfigPropertyChanged); + (properties) -> onDeviceConfigPropertiesChanged(properties.getNamespace())); } private void postToHandler(Runnable r) { @@ -104,10 +104,10 @@ public final class SmartReplyConstants { } @VisibleForTesting - void onDeviceConfigPropertyChanged(String namespace, String name, String value) { + void onDeviceConfigPropertiesChanged(String namespace) { if (!DeviceConfig.NAMESPACE_SYSTEMUI.equals(namespace)) { Log.e(TAG, "Received update from DeviceConfig for unrelated namespace: " - + namespace + " " + name + "=" + value); + + namespace); return; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java index 0f7a0f09b2e1..640f0f0cc3f6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java @@ -349,7 +349,7 @@ public class SmartReplyView extends ViewGroup { smartReplyController.smartActionClicked( entry, actionIndex, action, smartActions.fromAssistant); headsUpManager.removeNotification(entry.key, true); - }); + }, entry.getRow()); if (useDelayedOnClickListener) { onClickListener = new DelayedOnClickListener(onClickListener, smartReplyView.mConstants.getOnClickInitDelay()); diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java index 8380b19f7009..89aa7979e7d8 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java @@ -68,7 +68,8 @@ public class ThemeOverlayController extends SystemUI { mThemeManager = new ThemeOverlayManager( mContext.getSystemService(OverlayManager.class), AsyncTask.THREAD_POOL_EXECUTOR, - mContext.getString(R.string.launcher_overlayable_package)); + mContext.getString(R.string.launcher_overlayable_package), + mContext.getString(R.string.themepicker_overlayable_package)); final Handler bgHandler = Dependency.get(Dependency.BG_HANDLER); final IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_USER_SWITCHED); diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayManager.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayManager.java index 27e3b2b438bf..930016be2d22 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayManager.java +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayManager.java @@ -64,6 +64,9 @@ class ThemeOverlayManager { @VisibleForTesting static final String OVERLAY_CATEGORY_ICON_LAUNCHER = "android.theme.customization.icon_pack.launcher"; + @VisibleForTesting + static final String OVERLAY_CATEGORY_ICON_THEME_PICKER = + "android.theme.customization.icon_pack.themepicker"; /* * All theme customization categories used by the system, in order that they should be applied, @@ -76,7 +79,8 @@ class ThemeOverlayManager { OVERLAY_CATEGORY_COLOR, OVERLAY_CATEGORY_ICON_ANDROID, OVERLAY_CATEGORY_ICON_SYSUI, - OVERLAY_CATEGORY_ICON_SETTINGS); + OVERLAY_CATEGORY_ICON_SETTINGS, + OVERLAY_CATEGORY_ICON_THEME_PICKER); /* Categories that need to applied to the current user as well as the system user. */ @VisibleForTesting @@ -94,12 +98,14 @@ class ThemeOverlayManager { private final OverlayManager mOverlayManager; private final Executor mExecutor; private final String mLauncherPackage; + private final String mThemePickerPackage; ThemeOverlayManager(OverlayManager overlayManager, Executor executor, - String launcherPackage) { + String launcherPackage, String themePickerPackage) { mOverlayManager = overlayManager; mExecutor = executor; mLauncherPackage = launcherPackage; + mThemePickerPackage = themePickerPackage; mTargetPackageToCategories.put(ANDROID_PACKAGE, Sets.newHashSet( OVERLAY_CATEGORY_COLOR, OVERLAY_CATEGORY_FONT, OVERLAY_CATEGORY_SHAPE, OVERLAY_CATEGORY_ICON_ANDROID)); @@ -109,6 +115,8 @@ class ThemeOverlayManager { Sets.newHashSet(OVERLAY_CATEGORY_ICON_SETTINGS)); mTargetPackageToCategories.put(mLauncherPackage, Sets.newHashSet(OVERLAY_CATEGORY_ICON_LAUNCHER)); + mTargetPackageToCategories.put(mThemePickerPackage, + Sets.newHashSet(OVERLAY_CATEGORY_ICON_THEME_PICKER)); mCategoryToTargetPackage.put(OVERLAY_CATEGORY_COLOR, ANDROID_PACKAGE); mCategoryToTargetPackage.put(OVERLAY_CATEGORY_FONT, ANDROID_PACKAGE); mCategoryToTargetPackage.put(OVERLAY_CATEGORY_SHAPE, ANDROID_PACKAGE); @@ -116,6 +124,7 @@ class ThemeOverlayManager { mCategoryToTargetPackage.put(OVERLAY_CATEGORY_ICON_SYSUI, SYSUI_PACKAGE); mCategoryToTargetPackage.put(OVERLAY_CATEGORY_ICON_SETTINGS, SETTINGS_PACKAGE); mCategoryToTargetPackage.put(OVERLAY_CATEGORY_ICON_LAUNCHER, mLauncherPackage); + mCategoryToTargetPackage.put(OVERLAY_CATEGORY_ICON_THEME_PICKER, mThemePickerPackage); } /** diff --git a/packages/SystemUI/tests/res/values/overlayable_icons_test.xml b/packages/SystemUI/tests/res/values/overlayable_icons_test.xml index 5cc7976d3e60..4dc8d45a9d53 100644 --- a/packages/SystemUI/tests/res/values/overlayable_icons_test.xml +++ b/packages/SystemUI/tests/res/values/overlayable_icons_test.xml @@ -42,7 +42,6 @@ <item>@drawable/ic_power_low</item> <item>@drawable/ic_power_saver</item> <item>@drawable/ic_qs_bluetooth_connecting</item> - <item>@drawable/ic_qs_bluetooth_connecting</item> <item>@drawable/ic_qs_cancel</item> <item>@drawable/ic_qs_no_sim</item> <item>@drawable/ic_qs_wifi_0</item> diff --git a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java index 212c8f52a19e..e312990c7f8e 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java @@ -189,10 +189,6 @@ public class CarrierTextControllerTest extends SysuiTestCase { when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn(IccCardConstants.State.READY); when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list); - // STOPSHIP(b/130246708) This line makes sure that SubscriptionManager provides the - // same answer as KeyguardUpdateMonitor. Remove when this is addressed - when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(list); - mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor = @@ -217,10 +213,6 @@ public class CarrierTextControllerTest extends SysuiTestCase { when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn(IccCardConstants.State.READY); when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list); - // STOPSHIP(b/130246708) This line makes sure that SubscriptionManager provides the - // same answer as KeyguardUpdateMonitor. Remove when this is addressed - when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(list); - mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor = @@ -243,11 +235,6 @@ public class CarrierTextControllerTest extends SysuiTestCase { when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn( new ArrayList<>()); - // STOPSHIP(b/130246708) This line makes sure that SubscriptionManager provides the - // same answer as KeyguardUpdateMonitor. Remove when this is addressed - when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn( - new ArrayList<>()); - ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor = ArgumentCaptor.forClass( CarrierTextController.CarrierTextCallbackInfo.class); @@ -271,10 +258,6 @@ public class CarrierTextControllerTest extends SysuiTestCase { when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn(IccCardConstants.State.READY); when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list); - // STOPSHIP(b/130246708) This line makes sure that SubscriptionManager provides the - // same answer as KeyguardUpdateMonitor. Remove when this is addressed - when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(list); - mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor = @@ -300,10 +283,6 @@ public class CarrierTextControllerTest extends SysuiTestCase { .thenReturn(IccCardConstants.State.NOT_READY); when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list); - // STOPSHIP(b/130246708) This line makes sure that SubscriptionManager provides the - // same answer as KeyguardUpdateMonitor. Remove when this is addressed - when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(list); - mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor = @@ -329,10 +308,6 @@ public class CarrierTextControllerTest extends SysuiTestCase { .thenReturn(IccCardConstants.State.READY); when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list); - // STOPSHIP(b/130246708) This line makes sure that SubscriptionManager provides the - // same answer as KeyguardUpdateMonitor. Remove when this is addressed - when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(list); - mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor = @@ -361,10 +336,6 @@ public class CarrierTextControllerTest extends SysuiTestCase { when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list); mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); - // STOPSHIP(b/130246708) This line makes sure that SubscriptionManager provides the - // same answer as KeyguardUpdateMonitor. Remove when this is addressed - when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(list); - ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor = ArgumentCaptor.forClass( CarrierTextController.CarrierTextCallbackInfo.class); @@ -385,12 +356,9 @@ public class CarrierTextControllerTest extends SysuiTestCase { list.add(TEST_SUBSCRIPTION_2); when(mKeyguardUpdateMonitor.getSimState(anyInt())) .thenReturn(IccCardConstants.State.READY); - when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list); + mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); mCarrierTextController.updateDisplayOpportunisticSubscriptionCarrierText(); - - // STOPSHIP(b/130246708) This line makes sure that SubscriptionManager provides the - // same answer as KeyguardUpdateMonitor. Remove when this is addressed when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(list); ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor = diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java index 76f1684aae7a..3330d1e6d0a8 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java @@ -84,7 +84,6 @@ public final class ClockManagerTest extends SysuiTestCase { when(mMockInjectionInflationController.injectable(any())).thenReturn(inflater); mFakeDockManager = new DockManagerFake(); - getContext().putComponent(DockManager.class, mFakeDockManager); mCurrentUser = new MutableLiveData<>(); mCurrentUser.setValue(MAIN_USER_ID); @@ -92,7 +91,7 @@ public final class ClockManagerTest extends SysuiTestCase { mClockManager = new ClockManager(getContext(), mMockInjectionInflationController, mMockPluginManager, mMockColorExtractor, mMockContentResolver, - mMockCurrentUserObserable, mMockSettingsWrapper); + mMockCurrentUserObserable, mMockSettingsWrapper, mFakeDockManager); mClockManager.addOnClockChangedListener(mMockListener1); mClockManager.addOnClockChangedListener(mMockListener2); diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java new file mode 100644 index 000000000000..d6dac2f36ba1 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java @@ -0,0 +1,707 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.bubbles; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.Notification; +import android.app.PendingIntent; +import android.graphics.drawable.Icon; +import android.os.UserHandle; +import android.service.notification.StatusBarNotification; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.bubbles.BubbleData.TimeSource; +import com.android.systemui.statusbar.NotificationTestHelper; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; + +import com.google.common.collect.ImmutableList; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +public class BubbleDataTest extends SysuiTestCase { + + private NotificationEntry mEntryA1; + private NotificationEntry mEntryA2; + private NotificationEntry mEntryA3; + private NotificationEntry mEntryB1; + private NotificationEntry mEntryB2; + private NotificationEntry mEntryB3; + private NotificationEntry mEntryC1; + + private Bubble mBubbleA1; + private Bubble mBubbleA2; + private Bubble mBubbleA3; + private Bubble mBubbleB1; + private Bubble mBubbleB2; + private Bubble mBubbleB3; + private Bubble mBubbleC1; + + private BubbleData mBubbleData; + + @Mock + private TimeSource mTimeSource; + @Mock + private BubbleData.Listener mListener; + @Mock + private PendingIntent mExpandIntent; + @Mock + private PendingIntent mDeleteIntent; + + private NotificationTestHelper mNotificationTestHelper; + + @Before + public void setUp() throws Exception { + mNotificationTestHelper = new NotificationTestHelper(mContext); + MockitoAnnotations.initMocks(this); + + mEntryA1 = createBubbleEntry(1, "a1", "package.a"); + mEntryA2 = createBubbleEntry(1, "a2", "package.a"); + mEntryA3 = createBubbleEntry(1, "a3", "package.a"); + mEntryB1 = createBubbleEntry(1, "b1", "package.b"); + mEntryB2 = createBubbleEntry(1, "b2", "package.b"); + mEntryB3 = createBubbleEntry(1, "b3", "package.b"); + mEntryC1 = createBubbleEntry(1, "c1", "package.c"); + + mBubbleA1 = new Bubble(mEntryA1); + mBubbleA2 = new Bubble(mEntryA2); + mBubbleA3 = new Bubble(mEntryA3); + mBubbleB1 = new Bubble(mEntryB1); + mBubbleB2 = new Bubble(mEntryB2); + mBubbleB3 = new Bubble(mEntryB3); + mBubbleC1 = new Bubble(mEntryC1); + + mBubbleData = new BubbleData(getContext()); + + // Used by BubbleData to set lastAccessedTime + when(mTimeSource.currentTimeMillis()).thenReturn(1000L); + mBubbleData.setTimeSource(mTimeSource); + } + + private NotificationEntry createBubbleEntry(int userId, String notifKey, String packageName) { + return createBubbleEntry(userId, notifKey, packageName, 1000); + } + + private void setPostTime(NotificationEntry entry, long postTime) { + when(entry.notification.getPostTime()).thenReturn(postTime); + } + + private void setOngoing(NotificationEntry entry, boolean ongoing) { + if (ongoing) { + entry.notification.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE; + } else { + entry.notification.getNotification().flags &= ~Notification.FLAG_FOREGROUND_SERVICE; + } + } + + /** + * No ExpandableNotificationRow is required to test BubbleData. This setup is all that is + * required for BubbleData functionality and verification. NotificationTestHelper is used only + * as a convenience to create a Notification w/BubbleMetadata. + */ + private NotificationEntry createBubbleEntry(int userId, String notifKey, String packageName, + long postTime) { + // BubbleMetadata + Notification.BubbleMetadata bubbleMetadata = new Notification.BubbleMetadata.Builder() + .setIntent(mExpandIntent) + .setDeleteIntent(mDeleteIntent) + .setIcon(Icon.createWithResource("", 0)) + .build(); + // Notification -> BubbleMetadata + Notification notification = mNotificationTestHelper.createNotification(false, + null /* groupKey */, bubbleMetadata); + + // StatusBarNotification + StatusBarNotification sbn = mock(StatusBarNotification.class); + when(sbn.getKey()).thenReturn(notifKey); + when(sbn.getUser()).thenReturn(new UserHandle(userId)); + when(sbn.getPackageName()).thenReturn(packageName); + when(sbn.getPostTime()).thenReturn(postTime); + when(sbn.getNotification()).thenReturn(notification); + + // NotificationEntry -> StatusBarNotification -> Notification -> BubbleMetadata + return new NotificationEntry(sbn); + } + + private void sendUpdatedEntryAtTime(NotificationEntry entry, long postTime) { + setPostTime(entry, postTime); + mBubbleData.notificationEntryUpdated(entry); + } + + private void changeExpandedStateAtTime(boolean shouldBeExpanded, long time) { + when(mTimeSource.currentTimeMillis()).thenReturn(time); + mBubbleData.setExpanded(shouldBeExpanded); + } + + @Test + public void testAddBubble() { + // Setup + mBubbleData.setListener(mListener); + + // Test + setPostTime(mEntryA1, 1000); + mBubbleData.notificationEntryUpdated(mEntryA1); + + // Verify + verify(mListener).onBubbleAdded(eq(mBubbleA1)); + verify(mListener).onSelectionChanged(eq(mBubbleA1)); + verify(mListener).apply(); + } + + @Test + public void testRemoveBubble() { + // Setup + mBubbleData.notificationEntryUpdated(mEntryA1); + mBubbleData.notificationEntryUpdated(mEntryA2); + mBubbleData.notificationEntryUpdated(mEntryA3); + mBubbleData.setListener(mListener); + + // Test + mBubbleData.notificationEntryRemoved(mEntryA1, BubbleController.DISMISS_USER_GESTURE); + + // Verify + verify(mListener).onBubbleRemoved(eq(mBubbleA1), eq(BubbleController.DISMISS_USER_GESTURE)); + verify(mListener).onSelectionChanged(eq(mBubbleA2)); + verify(mListener).apply(); + } + + @Test + public void test_collapsed_addBubble_atMaxBubbles_expiresLeastActive() { + // Given + sendUpdatedEntryAtTime(mEntryA1, 1000); + sendUpdatedEntryAtTime(mEntryA2, 2000); + sendUpdatedEntryAtTime(mEntryA3, 3000); + sendUpdatedEntryAtTime(mEntryB1, 4000); + sendUpdatedEntryAtTime(mEntryB2, 5000); + assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleA1); + + // When + sendUpdatedEntryAtTime(mEntryC1, 6000); + + // Then + // A2 is removed. A1 is oldest but is the selected bubble. + assertThat(mBubbleData.getBubbles()).doesNotContain(mBubbleA2); + } + + @Test + public void test_collapsed_expand_whenEmpty_doesNothing() { + assertThat(mBubbleData.hasBubbles()).isFalse(); + changeExpandedStateAtTime(true, 2000L); + + verify(mListener, never()).onExpandedChanged(anyBoolean()); + verify(mListener, never()).apply(); + } + + // New bubble while stack is collapsed + @Test + public void test_collapsed_addBubble() { + // Given + assertThat(mBubbleData.hasBubbles()).isFalse(); + assertThat(mBubbleData.isExpanded()).isFalse(); + + // When + sendUpdatedEntryAtTime(mEntryA1, 1000); + sendUpdatedEntryAtTime(mEntryB1, 2000); + sendUpdatedEntryAtTime(mEntryB2, 3000); + sendUpdatedEntryAtTime(mEntryA2, 4000); + + // Then + // New bubbles move to front when collapsed, bringing bubbles from the same app along + assertThat(mBubbleData.getBubbles()).isEqualTo( + ImmutableList.of(mBubbleA2, mBubbleA1, mBubbleB2, mBubbleB1)); + } + + // New bubble while collapsed with ongoing bubble present + @Test + public void test_collapsed_addBubble_withOngoing() { + // Given + assertThat(mBubbleData.hasBubbles()).isFalse(); + assertThat(mBubbleData.isExpanded()).isFalse(); + + // When + setOngoing(mEntryA1, true); + setPostTime(mEntryA1, 1000); + mBubbleData.notificationEntryUpdated(mEntryA1); + setPostTime(mEntryB1, 2000); + mBubbleData.notificationEntryUpdated(mEntryB1); + setPostTime(mEntryB2, 3000); + mBubbleData.notificationEntryUpdated(mEntryB2); + setPostTime(mEntryA2, 4000); + mBubbleData.notificationEntryUpdated(mEntryA2); + + // Then + // New bubbles move to front, but stay behind any ongoing bubbles. + assertThat(mBubbleData.getBubbles()).isEqualTo( + ImmutableList.of(mBubbleA1, mBubbleA2, mBubbleB2, mBubbleB1)); + } + + // Remove the selected bubble (middle bubble), while the stack is collapsed. + @Test + public void test_collapsed_removeBubble_selected() { + // Given + assertThat(mBubbleData.hasBubbles()).isFalse(); + assertThat(mBubbleData.isExpanded()).isFalse(); + + setPostTime(mEntryA1, 1000); + mBubbleData.notificationEntryUpdated(mEntryA1); + + setPostTime(mEntryB1, 2000); + mBubbleData.notificationEntryUpdated(mEntryB1); + + setPostTime(mEntryB2, 3000); + mBubbleData.notificationEntryUpdated(mEntryB2); + + setPostTime(mEntryA2, 4000); + mBubbleData.notificationEntryUpdated(mEntryA2); + + mBubbleData.setSelectedBubble(mBubbleB2); + assertThat(mBubbleData.getBubbles()).isEqualTo( + ImmutableList.of(mBubbleA2, mBubbleA1, mBubbleB2, mBubbleB1)); + + // When + mBubbleData.notificationEntryRemoved(mEntryB2, BubbleController.DISMISS_USER_GESTURE); + + // Then + // (Selection remains in the same position) + assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleB1); + } + + // Remove the selected bubble (last bubble), while the stack is collapsed. + @Test + public void test_collapsed_removeSelectedBubble_inLastPosition() { + // Given + assertThat(mBubbleData.hasBubbles()).isFalse(); + assertThat(mBubbleData.isExpanded()).isFalse(); + + sendUpdatedEntryAtTime(mEntryA1, 1000); + sendUpdatedEntryAtTime(mEntryB1, 2000); + sendUpdatedEntryAtTime(mEntryB2, 3000); + sendUpdatedEntryAtTime(mEntryA2, 4000); + + mBubbleData.setSelectedBubble(mBubbleB1); + assertThat(mBubbleData.getBubbles()).isEqualTo( + ImmutableList.of(mBubbleA2, mBubbleA1, mBubbleB2, mBubbleB1)); + + // When + mBubbleData.notificationEntryRemoved(mEntryB1, BubbleController.DISMISS_USER_GESTURE); + + // Then + // (Selection is forced to move to previous) + assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleB2); + } + + @Test + public void test_collapsed_addBubble_ongoing() { + // Given + assertThat(mBubbleData.hasBubbles()).isFalse(); + assertThat(mBubbleData.isExpanded()).isFalse(); + + // When + setPostTime(mEntryA1, 1000); + mBubbleData.notificationEntryUpdated(mEntryA1); + + setPostTime(mEntryB1, 2000); + mBubbleData.notificationEntryUpdated(mEntryB1); + + setPostTime(mEntryB2, 3000); + setOngoing(mEntryB2, true); + mBubbleData.notificationEntryUpdated(mEntryB2); + + setPostTime(mEntryA2, 4000); + mBubbleData.notificationEntryUpdated(mEntryA2); + + // Then + // New bubbles move to front, but stay behind any ongoing bubbles. + // Does not break grouping. (A2 is inserted after B1, even though it's newer). + assertThat(mBubbleData.getBubbles()).isEqualTo( + ImmutableList.of(mBubbleB2, mBubbleB1, mBubbleA2, mBubbleA1)); + } + + @Test + public void test_collapsed_removeBubble() { + // Given + assertThat(mBubbleData.hasBubbles()).isFalse(); + assertThat(mBubbleData.isExpanded()).isFalse(); + + sendUpdatedEntryAtTime(mEntryA1, 1000); + sendUpdatedEntryAtTime(mEntryB1, 2000); + sendUpdatedEntryAtTime(mEntryB2, 3000); + sendUpdatedEntryAtTime(mEntryA2, 4000); + + // When + mBubbleData.notificationEntryRemoved(mEntryB2, BubbleController.DISMISS_USER_GESTURE); + + // Then + assertThat(mBubbleData.getBubbles()).isEqualTo( + ImmutableList.of(mBubbleA2, mBubbleA1, mBubbleB1)); + } + + @Test + public void test_collapsed_updateBubble() { + // Given + assertThat(mBubbleData.hasBubbles()).isFalse(); + assertThat(mBubbleData.isExpanded()).isFalse(); + + sendUpdatedEntryAtTime(mEntryA1, 1000); + sendUpdatedEntryAtTime(mEntryB1, 2000); + sendUpdatedEntryAtTime(mEntryB2, 3000); + sendUpdatedEntryAtTime(mEntryA2, 4000); + + assertThat(mBubbleData.getBubbles()).isEqualTo( + ImmutableList.of(mBubbleA2, mBubbleA1, mBubbleB2, mBubbleB1)); + + // When + sendUpdatedEntryAtTime(mEntryB2, 5000); + + // Then + assertThat(mBubbleData.getBubbles()).isEqualTo( + ImmutableList.of(mBubbleB2, mBubbleB1, mBubbleA2, mBubbleA1)); + } + + @Test + public void test_collapsed_updateBubble_withOngoing() { + // Given + assertThat(mBubbleData.hasBubbles()).isFalse(); + assertThat(mBubbleData.isExpanded()).isFalse(); + + setPostTime(mEntryA1, 1000); + mBubbleData.notificationEntryUpdated(mEntryA1); + + setPostTime(mEntryB1, 2000); + mBubbleData.notificationEntryUpdated(mEntryB1); + + setPostTime(mEntryB2, 3000); + mBubbleData.notificationEntryUpdated(mEntryB2); + + setOngoing(mEntryA2, true); + setPostTime(mEntryA2, 4000); + mBubbleData.notificationEntryUpdated(mEntryA2); + + assertThat(mBubbleData.getBubbles()).isEqualTo( + ImmutableList.of(mBubbleA2, mBubbleA1, mBubbleB2, mBubbleB1)); + + // When + setPostTime(mEntryB1, 5000); + mBubbleData.notificationEntryUpdated(mEntryB1); + + // Then + // A2 remains in first position, due to being ongoing. B1 moves before B2, Group A + // remains before group B. + assertThat(mBubbleData.getBubbles()).isEqualTo( + ImmutableList.of(mBubbleA2, mBubbleA1, mBubbleB1, mBubbleB2)); + } + + @Test + public void test_collapse_afterUpdateWhileExpanded() { + // Given + assertThat(mBubbleData.hasBubbles()).isFalse(); + assertThat(mBubbleData.isExpanded()).isFalse(); + + sendUpdatedEntryAtTime(mEntryA1, 1000); + sendUpdatedEntryAtTime(mEntryB1, 2000); + sendUpdatedEntryAtTime(mEntryB2, 3000); + sendUpdatedEntryAtTime(mEntryA2, 4000); + + assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleA1); + + changeExpandedStateAtTime(true, 5000L); + assertThat(mBubbleData.getBubbles()).isEqualTo( + ImmutableList.of(mBubbleA2, mBubbleA1, mBubbleB2, mBubbleB1)); + + sendUpdatedEntryAtTime(mEntryB1, 6000); + + // (No reordering while expanded) + assertThat(mBubbleData.getBubbles()).isEqualTo( + ImmutableList.of(mBubbleA2, mBubbleA1, mBubbleB2, mBubbleB1)); + + // When + changeExpandedStateAtTime(false, 7000L); + + // Then + // A1 moves to front on collapse, since it is the selected bubble (and most recently + // accessed). + // A2 moves next to A1 to maintain grouping. + // B1 moves in front of B2, since it received an update while expanded + assertThat(mBubbleData.getBubbles()).isEqualTo( + ImmutableList.of(mBubbleA1, mBubbleA2, mBubbleB1, mBubbleB2)); + } + + @Test + public void test_collapse_afterUpdateWhileExpanded_withOngoing() { + // Given + assertThat(mBubbleData.hasBubbles()).isFalse(); + assertThat(mBubbleData.isExpanded()).isFalse(); + + sendUpdatedEntryAtTime(mEntryA1, 1000); + sendUpdatedEntryAtTime(mEntryB1, 2000); + + setOngoing(mEntryB2, true); + sendUpdatedEntryAtTime(mEntryB2, 3000); + + sendUpdatedEntryAtTime(mEntryA2, 4000); + + assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleA1); + + changeExpandedStateAtTime(true, 5000L); + assertThat(mBubbleData.getBubbles()).isEqualTo( + ImmutableList.of(mBubbleB2, mBubbleB1, mBubbleA2, mBubbleA1)); + + sendUpdatedEntryAtTime(mEntryA1, 6000); + + // No reordering if expanded + assertThat(mBubbleData.getBubbles()).isEqualTo( + ImmutableList.of(mBubbleB2, mBubbleB1, mBubbleA2, mBubbleA1)); + + // When + changeExpandedStateAtTime(false, 7000L); + + // Then + // B2 remains in first position because it is ongoing. + // B1 remains grouped with B2 + // A1 moves in front of A2, since it is more recently updated (and is selected). + // B1 moves in front of B2, since it has more recent activity. + assertThat(mBubbleData.getBubbles()).isEqualTo( + ImmutableList.of(mBubbleB2, mBubbleB1, mBubbleA1, mBubbleA2)); + } + + @Test + public void test_collapsed_removeLastBubble_clearsSelectedBubble() { + // Given + assertThat(mBubbleData.hasBubbles()).isFalse(); + assertThat(mBubbleData.isExpanded()).isFalse(); + + sendUpdatedEntryAtTime(mEntryA1, 1000); + sendUpdatedEntryAtTime(mEntryB1, 2000); + sendUpdatedEntryAtTime(mEntryB2, 3000); + sendUpdatedEntryAtTime(mEntryA2, 4000); + + assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleA1); + + mBubbleData.notificationEntryRemoved(mEntryA1, BubbleController.DISMISS_USER_GESTURE); + mBubbleData.notificationEntryRemoved(mEntryB1, BubbleController.DISMISS_USER_GESTURE); + mBubbleData.notificationEntryRemoved(mEntryB2, BubbleController.DISMISS_USER_GESTURE); + mBubbleData.notificationEntryRemoved(mEntryA2, BubbleController.DISMISS_USER_GESTURE); + + assertThat(mBubbleData.getSelectedBubble()).isNull(); + } + + @Test + public void test_expanded_addBubble_atMaxBubbles_expiresLeastActive() { + // Given + sendUpdatedEntryAtTime(mEntryA1, 1000); + assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleA1); + + changeExpandedStateAtTime(true, 2000L); + assertThat(mBubbleData.getSelectedBubble().getLastActivity()).isEqualTo(2000); + + sendUpdatedEntryAtTime(mEntryA2, 3000); + sendUpdatedEntryAtTime(mEntryA3, 4000); + sendUpdatedEntryAtTime(mEntryB1, 5000); + sendUpdatedEntryAtTime(mEntryB2, 6000); + sendUpdatedEntryAtTime(mEntryB3, 7000); + + + // Then + // A1 would be removed, but it is selected and expanded, so it should not go away. + // Instead, fall through to removing A2 (the next oldest). + assertThat(mBubbleData.getBubbles()).doesNotContain(mEntryA2); + } + + @Test + public void test_expanded_removeLastBubble_collapsesStack() { + // Given + setPostTime(mEntryA1, 1000); + mBubbleData.notificationEntryUpdated(mEntryA1); + + setPostTime(mEntryB1, 2000); + mBubbleData.notificationEntryUpdated(mEntryB1); + + setPostTime(mEntryB2, 3000); + mBubbleData.notificationEntryUpdated(mEntryC1); + + mBubbleData.setExpanded(true); + + mBubbleData.notificationEntryRemoved(mEntryA1, BubbleController.DISMISS_USER_GESTURE); + mBubbleData.notificationEntryRemoved(mEntryB1, BubbleController.DISMISS_USER_GESTURE); + mBubbleData.notificationEntryRemoved(mEntryC1, BubbleController.DISMISS_USER_GESTURE); + + assertThat(mBubbleData.isExpanded()).isFalse(); + assertThat(mBubbleData.getSelectedBubble()).isNull(); + } + + // Bubbles do not reorder while expanded + @Test + public void test_expanded_selection_collapseToTop() { + // Given + assertThat(mBubbleData.hasBubbles()).isFalse(); + assertThat(mBubbleData.isExpanded()).isFalse(); + + sendUpdatedEntryAtTime(mEntryA1, 1000); + sendUpdatedEntryAtTime(mEntryA2, 2000); + sendUpdatedEntryAtTime(mEntryB1, 3000); + + assertThat(mBubbleData.getBubbles()).isEqualTo( + ImmutableList.of(mBubbleB1, mBubbleA2, mBubbleA1)); + + changeExpandedStateAtTime(true, 4000L); + + // regrouping only happens when collapsed (after new or update) or expanded->collapsed + assertThat(mBubbleData.getBubbles()).isEqualTo( + ImmutableList.of(mBubbleB1, mBubbleA2, mBubbleA1)); + + changeExpandedStateAtTime(false, 6000L); + + // A1 is still selected and it's lastAccessed time has been updated + // on collapse, sorting is applied, keeping the selected bubble at the front + assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleA1); + assertThat(mBubbleData.getBubbles()).isEqualTo( + ImmutableList.of(mBubbleA1, mBubbleA2, mBubbleB1)); + } + + // New bubble from new app while stack is expanded + @Test + public void test_expanded_addBubble_newApp() { + // Given + sendUpdatedEntryAtTime(mEntryA1, 1000); + sendUpdatedEntryAtTime(mEntryA2, 2000); + sendUpdatedEntryAtTime(mEntryA3, 3000); + sendUpdatedEntryAtTime(mEntryB1, 4000); + sendUpdatedEntryAtTime(mEntryB2, 5000); + + assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleA1); + + changeExpandedStateAtTime(true, 6000L); + + assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleA1); + assertThat(mBubbleData.getSelectedBubble().getLastActivity()).isEqualTo(6000L); + + // regrouping only happens when collapsed (after new or update) or expanded->collapsed + assertThat(mBubbleData.getBubbles()).isEqualTo( + ImmutableList.of(mBubbleB2, mBubbleB1, mBubbleA3, mBubbleA2, mBubbleA1)); + + // When + sendUpdatedEntryAtTime(mEntryC1, 7000); + + // Then + // A2 is expired. A1 was oldest, but lastActivityTime is reset when expanded, since A1 is + // selected. + // C1 is added at the end since bubbles are expanded. + assertThat(mBubbleData.getBubbles()).isEqualTo( + ImmutableList.of(mBubbleB2, mBubbleB1, mBubbleA3, mBubbleA1, mBubbleC1)); + } + + // New bubble from existing app while stack is expanded + @Test + public void test_expanded_addBubble_existingApp() { + // Given + sendUpdatedEntryAtTime(mEntryB1, 1000); + sendUpdatedEntryAtTime(mEntryB2, 2000); + sendUpdatedEntryAtTime(mEntryA1, 3000); + sendUpdatedEntryAtTime(mEntryA2, 4000); + sendUpdatedEntryAtTime(mEntryA3, 5000); + + assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleB1); + + changeExpandedStateAtTime(true, 6000L); + + // B1 is first (newest, since it's just been expanded and is selected) + assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleB1); + assertThat(mBubbleData.getSelectedBubble().getLastActivity()).isEqualTo(6000L); + + // regrouping only happens when collapsed (after new or update) or while collapsing + assertThat(mBubbleData.getBubbles()).isEqualTo( + ImmutableList.of(mBubbleA3, mBubbleA2, mBubbleA1, mBubbleB2, mBubbleB1)); + + // When + sendUpdatedEntryAtTime(mEntryB3, 7000); + + // Then + // (B2 is expired, B1 was oldest, but it's lastActivityTime is updated at the point when + // the stack was expanded, since it is the selected bubble. + assertThat(mBubbleData.getBubbles()).isEqualTo( + ImmutableList.of(mBubbleA3, mBubbleA2, mBubbleA1, mBubbleB3, mBubbleB1)); + } + + // Updated bubble from existing app while stack is expanded + @Test + public void test_expanded_updateBubble_existingApp() { + sendUpdatedEntryAtTime(mEntryA1, 1000); + sendUpdatedEntryAtTime(mEntryA2, 2000); + sendUpdatedEntryAtTime(mEntryB1, 3000); + sendUpdatedEntryAtTime(mEntryB2, 4000); + + assertThat(mBubbleData.getBubbles()).isEqualTo( + ImmutableList.of(mBubbleB2, mBubbleB1, mBubbleA2, mBubbleA1)); + mBubbleData.setExpanded(true); + + sendUpdatedEntryAtTime(mEntryA1, 5000); + + // Does not reorder while expanded (for an update). + assertThat(mBubbleData.getBubbles()).isEqualTo( + ImmutableList.of(mBubbleB2, mBubbleB1, mBubbleA2, mBubbleA1)); + } + + @Test + public void test_expanded_updateBubble() { + // Given + assertThat(mBubbleData.hasBubbles()).isFalse(); + assertThat(mBubbleData.isExpanded()).isFalse(); + + setPostTime(mEntryA1, 1000); + mBubbleData.notificationEntryUpdated(mEntryA1); + + setPostTime(mEntryB1, 2000); + mBubbleData.notificationEntryUpdated(mEntryB1); + + setPostTime(mEntryB2, 3000); + mBubbleData.notificationEntryUpdated(mEntryB2); + + setPostTime(mEntryA2, 4000); + mBubbleData.notificationEntryUpdated(mEntryA2); + + mBubbleData.setExpanded(true); + assertThat(mBubbleData.getBubbles()).isEqualTo( + ImmutableList.of(mBubbleA2, mBubbleA1, mBubbleB2, mBubbleB1)); + + // When + setPostTime(mEntryB1, 5000); + mBubbleData.notificationEntryUpdated(mEntryB1); + + // Then + // B1 remains in the same place due to being expanded + assertThat(mBubbleData.getBubbles()).isEqualTo( + ImmutableList.of(mBubbleA2, mBubbleA1, mBubbleB2, mBubbleB1)); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java index df014a49e5cb..af2de1be1d57 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java @@ -73,8 +73,6 @@ public class DozeDockHandlerTest extends SysuiTestCase { doReturn(false).when(mConfig).alwaysOnEnabled(anyInt()); mDockManagerFake = spy(new DockManagerFake()); - mContext.putComponent(DockManager.class, mDockManagerFake); - mDockHandler = new DozeDockHandler(mContext, mMachine, mHost, mConfig, Handler.createAsync(Looper.myLooper()), mDockManagerFake); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java index 1ac6bef1c7a5..6979fd811817 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java @@ -38,7 +38,6 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; -import com.android.systemui.dock.DockManager; import com.android.systemui.dock.DockManagerFake; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.util.wakelock.WakeLock; @@ -84,7 +83,6 @@ public class DozeTriggersTest extends SysuiTestCase { mSensors = new FakeSensorManager(mContext); mWakeLock = new WakeLockFake(); mDockManagerFake = spy(new DockManagerFake()); - mContext.putComponent(DockManager.class, mDockManagerFake); mTriggers = new DozeTriggers(mContext, mMachine, mHost, mAlarmManager, mConfig, mParameters, mSensors, Handler.createAsync(Looper.myLooper()), mWakeLock, true, diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsColumnLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsColumnLayoutTest.java new file mode 100644 index 000000000000..16d665cbafcd --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsColumnLayoutTest.java @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.globalactions; + +import static junit.framework.Assert.assertEquals; + +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.testing.AndroidTestingRunner; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.util.leak.RotationUtils; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Tests for {@link ListGridLayout}. + */ +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class GlobalActionsColumnLayoutTest extends SysuiTestCase { + + private GlobalActionsColumnLayout mColumnLayout; + + @Before + public void setUp() throws Exception { + mColumnLayout = spy((GlobalActionsColumnLayout) + LayoutInflater.from(mContext).inflate(R.layout.global_actions_column, null)); + } + + @Test + public void testShouldReverseListItems() { + doReturn(View.LAYOUT_DIRECTION_LTR).when(mColumnLayout).getCurrentLayoutDirection(); + + doReturn(RotationUtils.ROTATION_LANDSCAPE).when(mColumnLayout).getCurrentRotation(); + assertEquals(false, mColumnLayout.shouldReverseListItems()); + + doReturn(RotationUtils.ROTATION_NONE).when(mColumnLayout).getCurrentRotation(); + assertEquals(false, mColumnLayout.shouldReverseListItems()); + + doReturn(RotationUtils.ROTATION_SEASCAPE).when(mColumnLayout).getCurrentRotation(); + assertEquals(true, mColumnLayout.shouldReverseListItems()); + + doReturn(View.LAYOUT_DIRECTION_RTL).when(mColumnLayout).getCurrentLayoutDirection(); + + doReturn(RotationUtils.ROTATION_LANDSCAPE).when(mColumnLayout).getCurrentRotation(); + assertEquals(true, mColumnLayout.shouldReverseListItems()); + + doReturn(RotationUtils.ROTATION_NONE).when(mColumnLayout).getCurrentRotation(); + assertEquals(false, mColumnLayout.shouldReverseListItems()); + + doReturn(RotationUtils.ROTATION_SEASCAPE).when(mColumnLayout).getCurrentRotation(); + assertEquals(false, mColumnLayout.shouldReverseListItems()); + } + + @Test + public void testGetAnimationOffsetX() { + doReturn(50f).when(mColumnLayout).getAnimationDistance(); + + doReturn(RotationUtils.ROTATION_NONE).when(mColumnLayout).getCurrentRotation(); + assertEquals(50f, mColumnLayout.getAnimationOffsetX(), .01); + + doReturn(RotationUtils.ROTATION_LANDSCAPE).when(mColumnLayout).getCurrentRotation(); + assertEquals(0, mColumnLayout.getAnimationOffsetX(), .01); + + doReturn(RotationUtils.ROTATION_SEASCAPE).when(mColumnLayout).getCurrentRotation(); + assertEquals(0, mColumnLayout.getAnimationOffsetX(), .01); + } + + @Test + public void testGetAnimationOffsetY() { + doReturn(50f).when(mColumnLayout).getAnimationDistance(); + + doReturn(RotationUtils.ROTATION_NONE).when(mColumnLayout).getCurrentRotation(); + assertEquals(0, mColumnLayout.getAnimationOffsetY(), .01); + + doReturn(RotationUtils.ROTATION_LANDSCAPE).when(mColumnLayout).getCurrentRotation(); + assertEquals(-50f, mColumnLayout.getAnimationOffsetY(), .01); + + doReturn(RotationUtils.ROTATION_SEASCAPE).when(mColumnLayout).getCurrentRotation(); + assertEquals(50f, mColumnLayout.getAnimationOffsetY(), .01); + } + + @Test + public void testSnapToPowerButton_portrait() { + doReturn(RotationUtils.ROTATION_NONE).when(mColumnLayout).getCurrentRotation(); + doReturn(50).when(mColumnLayout).getPowerButtonOffsetDistance(); + + mColumnLayout.snapToPowerButton(); + assertEquals(Gravity.TOP | Gravity.RIGHT, mColumnLayout.getGravity()); + assertEquals(50, mColumnLayout.getPaddingTop(), .01); + } + + @Test + public void testCenterAlongEdge_portrait() { + doReturn(RotationUtils.ROTATION_NONE).when(mColumnLayout).getCurrentRotation(); + + mColumnLayout.centerAlongEdge(); + assertEquals(Gravity.CENTER_VERTICAL | Gravity.RIGHT, mColumnLayout.getGravity()); + assertEquals(0, mColumnLayout.getPaddingTop(), .01); + } + + @Test + public void testUpdateSnap_initialState() { + doReturn(false).when(mColumnLayout).shouldSnapToPowerButton(); + + mColumnLayout.updateSnap(); // should do nothing, since snap has not changed from init state + + verify(mColumnLayout, times(0)).snapToPowerButton(); + verify(mColumnLayout, times(0)).centerAlongEdge(); + } + + @Test + public void testUpdateSnap_snapThenSnap() { + doReturn(true).when(mColumnLayout).shouldSnapToPowerButton(); + + mColumnLayout.updateSnap(); // should snap to power button + + verify(mColumnLayout, times(1)).snapToPowerButton(); + verify(mColumnLayout, times(0)).centerAlongEdge(); + + mColumnLayout.updateSnap(); // should do nothing, since this is the same state as last time + + verify(mColumnLayout, times(1)).snapToPowerButton(); + verify(mColumnLayout, times(0)).centerAlongEdge(); + } + + @Test + public void testUpdateSnap_snapThenCenter() { + doReturn(true).when(mColumnLayout).shouldSnapToPowerButton(); + + mColumnLayout.updateSnap(); // should snap to power button + + verify(mColumnLayout, times(1)).snapToPowerButton(); + verify(mColumnLayout, times(0)).centerAlongEdge(); + + doReturn(false).when(mColumnLayout).shouldSnapToPowerButton(); + + mColumnLayout.updateSnap(); // should center to edge + + verify(mColumnLayout, times(1)).snapToPowerButton(); + verify(mColumnLayout, times(1)).centerAlongEdge(); + } + + @Test + public void testShouldSnapToPowerButton_vertical() { + doReturn(RotationUtils.ROTATION_NONE).when(mColumnLayout).getCurrentRotation(); + doReturn(300).when(mColumnLayout).getPowerButtonOffsetDistance(); + doReturn(1000).when(mColumnLayout).getMeasuredHeight(); + View wrapper = spy(new View(mContext, null)); + doReturn(wrapper).when(mColumnLayout).getWrapper(); + doReturn(500).when(wrapper).getMeasuredHeight(); + + assertEquals(true, mColumnLayout.shouldSnapToPowerButton()); + + doReturn(600).when(mColumnLayout).getMeasuredHeight(); + + assertEquals(false, mColumnLayout.shouldSnapToPowerButton()); + } + + @Test + public void testShouldSnapToPowerButton_horizontal() { + doReturn(RotationUtils.ROTATION_LANDSCAPE).when(mColumnLayout).getCurrentRotation(); + doReturn(300).when(mColumnLayout).getPowerButtonOffsetDistance(); + doReturn(1000).when(mColumnLayout).getMeasuredWidth(); + View wrapper = spy(new View(mContext, null)); + doReturn(wrapper).when(mColumnLayout).getWrapper(); + doReturn(500).when(wrapper).getMeasuredWidth(); + + assertEquals(true, mColumnLayout.shouldSnapToPowerButton()); + + doReturn(600).when(mColumnLayout).getMeasuredWidth(); + + assertEquals(false, mColumnLayout.shouldSnapToPowerButton()); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsGridLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsGridLayoutTest.java index 3c52e9d86c15..a396f3e8bf46 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsGridLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsGridLayoutTest.java @@ -18,12 +18,8 @@ package com.android.systemui.globalactions; import static junit.framework.Assert.assertEquals; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; import android.testing.AndroidTestingRunner; import android.view.LayoutInflater; @@ -32,7 +28,6 @@ import android.view.ViewGroup; import androidx.test.filters.SmallTest; -import com.android.systemui.MultiListLayout; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.util.leak.RotationUtils; @@ -49,61 +44,12 @@ import org.junit.runner.RunWith; public class GlobalActionsGridLayoutTest extends SysuiTestCase { private GlobalActionsGridLayout mGridLayout; - private TestAdapter mAdapter; private ListGridLayout mListGrid; - private class TestAdapter extends MultiListLayout.MultiListAdapter { - @Override - public void onClickItem(int index) { } - - @Override - public boolean onLongClickItem(int index) { - return true; - } - - @Override - public int countSeparatedItems() { - return -1; - } - - @Override - public int countListItems() { - return -1; - } - - @Override - public boolean shouldBeSeparated(int position) { - return false; - } - - @Override - public int getCount() { - return countSeparatedItems() + countListItems(); - } - - @Override - public Object getItem(int position) { - return null; - } - - @Override - public long getItemId(int position) { - return -1; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - return null; - } - } - - @Before public void setUp() throws Exception { mGridLayout = spy((GlobalActionsGridLayout) LayoutInflater.from(mContext).inflate(R.layout.global_actions_grid, null)); - mAdapter = spy(new TestAdapter()); - mGridLayout.setAdapter(mAdapter); mListGrid = spy(mGridLayout.getListView()); doReturn(mListGrid).when(mGridLayout).getListView(); } @@ -122,7 +68,7 @@ public class GlobalActionsGridLayoutTest extends SysuiTestCase { @Test public void testShouldReverseListItems() { - doReturn(View.LAYOUT_DIRECTION_LTR).when(mGridLayout).getLayoutDirection(); + doReturn(View.LAYOUT_DIRECTION_LTR).when(mGridLayout).getCurrentLayoutDirection(); doReturn(RotationUtils.ROTATION_LANDSCAPE).when(mGridLayout).getCurrentRotation(); assertEquals(false, mGridLayout.shouldReverseListItems()); @@ -133,7 +79,7 @@ public class GlobalActionsGridLayoutTest extends SysuiTestCase { doReturn(RotationUtils.ROTATION_SEASCAPE).when(mGridLayout).getCurrentRotation(); assertEquals(true, mGridLayout.shouldReverseListItems()); - doReturn(View.LAYOUT_DIRECTION_RTL).when(mGridLayout).getLayoutDirection(); + doReturn(View.LAYOUT_DIRECTION_RTL).when(mGridLayout).getCurrentLayoutDirection(); doReturn(RotationUtils.ROTATION_LANDSCAPE).when(mGridLayout).getCurrentRotation(); assertEquals(true, mGridLayout.shouldReverseListItems()); @@ -185,123 +131,26 @@ public class GlobalActionsGridLayoutTest extends SysuiTestCase { assertEquals(0f, mGridLayout.getAnimationOffsetY(), .01); } - @Test(expected = IllegalStateException.class) - public void testOnUpdateList_noAdapter() { - mGridLayout.setAdapter(null); - mGridLayout.updateList(); - } - @Test - public void testOnUpdateList_noItems() { - doReturn(0).when(mAdapter).countSeparatedItems(); - doReturn(0).when(mAdapter).countListItems(); - mGridLayout.updateList(); - - ViewGroup separatedView = mGridLayout.getSeparatedView(); - ListGridLayout listView = mGridLayout.getListView(); - - assertEquals(0, separatedView.getChildCount()); - assertEquals(View.GONE, separatedView.getVisibility()); - - verify(mListGrid, times(0)).addItem(any()); - } - - @Test - public void testOnUpdateList_resizesFirstSeparatedItem() { - doReturn(1).when(mAdapter).countSeparatedItems(); - doReturn(0).when(mAdapter).countListItems(); + public void testUpdateSeparatedItemSize() { View firstView = new View(mContext, null); View secondView = new View(mContext, null); - doReturn(firstView).when(mAdapter).getView(eq(0), any(), any()); - doReturn(true).when(mAdapter).shouldBeSeparated(0); + ViewGroup separatedView = mGridLayout.getSeparatedView(); + separatedView.addView(firstView); - mGridLayout.updateList(); + mGridLayout.updateSeparatedItemSize(); ViewGroup.LayoutParams childParams = firstView.getLayoutParams(); assertEquals(ViewGroup.LayoutParams.MATCH_PARENT, childParams.width); assertEquals(ViewGroup.LayoutParams.MATCH_PARENT, childParams.height); - doReturn(2).when(mAdapter).countSeparatedItems(); - doReturn(secondView).when(mAdapter).getView(eq(1), any(), any()); - doReturn(true).when(mAdapter).shouldBeSeparated(1); + separatedView.addView(secondView); - mGridLayout.updateList(); + mGridLayout.updateSeparatedItemSize(); childParams = firstView.getLayoutParams(); assertEquals(ViewGroup.LayoutParams.WRAP_CONTENT, childParams.width); assertEquals(ViewGroup.LayoutParams.WRAP_CONTENT, childParams.height); - - - } - - @Test - public void testOnUpdateList_onlySeparatedItems() { - doReturn(1).when(mAdapter).countSeparatedItems(); - doReturn(0).when(mAdapter).countListItems(); - View testView = new View(mContext, null); - doReturn(testView).when(mAdapter).getView(eq(0), any(), any()); - doReturn(true).when(mAdapter).shouldBeSeparated(0); - - mGridLayout.updateList(); - - verify(mListGrid, times(0)).addItem(any()); - } - - @Test - public void testOnUpdateList_oneSeparatedOneList() { - doReturn(1).when(mAdapter).countSeparatedItems(); - doReturn(1).when(mAdapter).countListItems(); - View view1 = new View(mContext, null); - View view2 = new View(mContext, null); - - doReturn(view1).when(mAdapter).getView(eq(0), any(), any()); - doReturn(true).when(mAdapter).shouldBeSeparated(0); - - doReturn(view2).when(mAdapter).getView(eq(1), any(), any()); - doReturn(false).when(mAdapter).shouldBeSeparated(1); - - mGridLayout.updateList(); - - ViewGroup separatedView = mGridLayout.getSeparatedView(); - - assertEquals(1, separatedView.getChildCount()); - assertEquals(View.VISIBLE, separatedView.getVisibility()); - assertEquals(view1, separatedView.getChildAt(0)); - - verify(mListGrid, times(1)).addItem(view2); - } - - @Test - public void testOnUpdateList_fourInList() { - doReturn(0).when(mAdapter).countSeparatedItems(); - doReturn(4).when(mAdapter).countListItems(); - View view1 = new View(mContext, null); - View view2 = new View(mContext, null); - View view3 = new View(mContext, null); - View view4 = new View(mContext, null); - - doReturn(view1).when(mAdapter).getView(eq(0), any(), any()); - doReturn(false).when(mAdapter).shouldBeSeparated(0); - - doReturn(view2).when(mAdapter).getView(eq(1), any(), any()); - doReturn(false).when(mAdapter).shouldBeSeparated(1); - - doReturn(view3).when(mAdapter).getView(eq(2), any(), any()); - doReturn(false).when(mAdapter).shouldBeSeparated(2); - - doReturn(view4).when(mAdapter).getView(eq(3), any(), any()); - doReturn(false).when(mAdapter).shouldBeSeparated(3); - - mGridLayout.updateList(); - - ViewGroup separatedView = mGridLayout.getSeparatedView(); - assertEquals(0, separatedView.getChildCount()); - assertEquals(View.GONE, separatedView.getVisibility()); - - verify(mListGrid, times(1)).addItem(view1); - verify(mListGrid, times(1)).addItem(view2); - verify(mListGrid, times(1)).addItem(view3); - verify(mListGrid, times(1)).addItem(view4); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsLayoutTest.java new file mode 100644 index 000000000000..16dcd659b3f9 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsLayoutTest.java @@ -0,0 +1,303 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.globalactions; + +import static junit.framework.Assert.assertEquals; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; + +import android.content.Context; +import android.testing.AndroidTestingRunner; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.MultiListLayout; +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; + +/** + * Tests for {@link ListGridLayout}. + */ +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class GlobalActionsLayoutTest extends SysuiTestCase { + + private TestLayout mLayout; + private TestAdapter mAdapter; + + private class TestAdapter extends MultiListLayout.MultiListAdapter { + @Override + public void onClickItem(int index) { } + + @Override + public boolean onLongClickItem(int index) { + return true; + } + + @Override + public int countSeparatedItems() { + return -1; + } + + @Override + public int countListItems() { + return -1; + } + + @Override + public boolean shouldBeSeparated(int position) { + return false; + } + + @Override + public int getCount() { + return countSeparatedItems() + countListItems(); + } + + @Override + public Object getItem(int position) { + return null; + } + + @Override + public long getItemId(int position) { + return -1; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + return null; + } + } + + private class TestLayout extends GlobalActionsLayout { + ArrayList<View> mSeparatedViews = new ArrayList<>(); + ArrayList<View> mListViews = new ArrayList<>(); + boolean mSeparatedViewVisible = false; + + TestLayout(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected boolean shouldReverseListItems() { + return false; + } + + @Override + public float getAnimationOffsetX() { + return 0; + } + + @Override + public float getAnimationOffsetY() { + return 0; + } + + @Override + protected void addToListView(View v, boolean reverse) { + if (reverse) { + mListViews.add(0, v); + } else { + mListViews.add(v); + } + } + + @Override + protected void addToSeparatedView(View v, boolean reverse) { + if (reverse) { + mSeparatedViews.add(0, v); + } else { + mSeparatedViews.add(v); + } + } + + @Override + protected void setSeparatedViewVisibility(boolean visible) { + mSeparatedViewVisible = visible; + } + } + + @Before + public void setUp() throws Exception { + mLayout = spy(new TestLayout(mContext, null)); + mAdapter = spy(new TestAdapter()); + mLayout.setAdapter(mAdapter); + } + + @Test(expected = IllegalStateException.class) + public void testOnUpdateList_noAdapter() { + mLayout.setAdapter(null); + mLayout.updateList(); + } + + @Test + public void testOnUpdateList_noItems() { + doReturn(0).when(mAdapter).countSeparatedItems(); + doReturn(0).when(mAdapter).countListItems(); + mLayout.updateList(); + + assertEquals(0, mLayout.mSeparatedViews.size()); + assertEquals(0, mLayout.mListViews.size()); + + assertEquals(false, mLayout.mSeparatedViewVisible); + } + + @Test + public void testOnUpdateList_oneSeparatedOneList() { + doReturn(1).when(mAdapter).countSeparatedItems(); + doReturn(1).when(mAdapter).countListItems(); + View view1 = new View(mContext, null); + View view2 = new View(mContext, null); + + doReturn(view1).when(mAdapter).getView(eq(0), any(), any()); + doReturn(true).when(mAdapter).shouldBeSeparated(0); + + doReturn(view2).when(mAdapter).getView(eq(1), any(), any()); + doReturn(false).when(mAdapter).shouldBeSeparated(1); + + mLayout.updateList(); + + assertEquals(1, mLayout.mSeparatedViews.size()); + assertEquals(1, mLayout.mListViews.size()); + assertEquals(view1, mLayout.mSeparatedViews.get(0)); + assertEquals(view2, mLayout.mListViews.get(0)); + } + + + @Test + public void testOnUpdateList_twoSeparatedItems() { + doReturn(2).when(mAdapter).countSeparatedItems(); + doReturn(0).when(mAdapter).countListItems(); + View view1 = new View(mContext, null); + View view2 = new View(mContext, null); + + doReturn(view1).when(mAdapter).getView(eq(0), any(), any()); + doReturn(true).when(mAdapter).shouldBeSeparated(0); + doReturn(view2).when(mAdapter).getView(eq(1), any(), any()); + doReturn(true).when(mAdapter).shouldBeSeparated(1); + + mLayout.updateList(); + + assertEquals(2, mLayout.mSeparatedViews.size()); + assertEquals(0, mLayout.mListViews.size()); + + assertEquals(view1, mLayout.mSeparatedViews.get(0)); + assertEquals(view2, mLayout.mSeparatedViews.get(1)); + + // if separated view has items in it, should be made visible + assertEquals(true, mLayout.mSeparatedViewVisible); + } + + @Test + public void testOnUpdateList_twoSeparatedItems_reverse() { + doReturn(2).when(mAdapter).countSeparatedItems(); + doReturn(0).when(mAdapter).countListItems(); + doReturn(true).when(mLayout).shouldReverseListItems(); + View view1 = new View(mContext, null); + View view2 = new View(mContext, null); + + doReturn(view1).when(mAdapter).getView(eq(0), any(), any()); + doReturn(true).when(mAdapter).shouldBeSeparated(0); + + doReturn(view2).when(mAdapter).getView(eq(1), any(), any()); + doReturn(true).when(mAdapter).shouldBeSeparated(1); + + mLayout.updateList(); + + assertEquals(2, mLayout.mSeparatedViews.size()); + assertEquals(0, mLayout.mListViews.size()); + + // separated view items are not reversed in current implementation, and this is intentional! + assertEquals(view1, mLayout.mSeparatedViews.get(0)); + assertEquals(view2, mLayout.mSeparatedViews.get(1)); + } + + @Test + public void testOnUpdateList_fourInList() { + doReturn(0).when(mAdapter).countSeparatedItems(); + doReturn(4).when(mAdapter).countListItems(); + View view1 = new View(mContext, null); + View view2 = new View(mContext, null); + View view3 = new View(mContext, null); + View view4 = new View(mContext, null); + + doReturn(view1).when(mAdapter).getView(eq(0), any(), any()); + doReturn(false).when(mAdapter).shouldBeSeparated(0); + + doReturn(view2).when(mAdapter).getView(eq(1), any(), any()); + doReturn(false).when(mAdapter).shouldBeSeparated(1); + + doReturn(view3).when(mAdapter).getView(eq(2), any(), any()); + doReturn(false).when(mAdapter).shouldBeSeparated(2); + + doReturn(view4).when(mAdapter).getView(eq(3), any(), any()); + doReturn(false).when(mAdapter).shouldBeSeparated(3); + + mLayout.updateList(); + + assertEquals(0, mLayout.mSeparatedViews.size()); + assertEquals(4, mLayout.mListViews.size()); + assertEquals(view1, mLayout.mListViews.get(0)); + assertEquals(view2, mLayout.mListViews.get(1)); + assertEquals(view3, mLayout.mListViews.get(2)); + assertEquals(view4, mLayout.mListViews.get(3)); + } + + @Test + public void testOnUpdateList_fourInList_reverse() { + doReturn(0).when(mAdapter).countSeparatedItems(); + doReturn(4).when(mAdapter).countListItems(); + doReturn(true).when(mLayout).shouldReverseListItems(); + View view1 = new View(mContext, null); + View view2 = new View(mContext, null); + View view3 = new View(mContext, null); + View view4 = new View(mContext, null); + + doReturn(view1).when(mAdapter).getView(eq(0), any(), any()); + doReturn(false).when(mAdapter).shouldBeSeparated(0); + + doReturn(view2).when(mAdapter).getView(eq(1), any(), any()); + doReturn(false).when(mAdapter).shouldBeSeparated(1); + + doReturn(view3).when(mAdapter).getView(eq(2), any(), any()); + doReturn(false).when(mAdapter).shouldBeSeparated(2); + + doReturn(view4).when(mAdapter).getView(eq(3), any(), any()); + doReturn(false).when(mAdapter).shouldBeSeparated(3); + + mLayout.updateList(); + + assertEquals(0, mLayout.mSeparatedViews.size()); + assertEquals(4, mLayout.mListViews.size()); + assertEquals(view1, mLayout.mListViews.get(3)); + assertEquals(view2, mLayout.mListViews.get(2)); + assertEquals(view3, mLayout.mListViews.get(1)); + assertEquals(view4, mLayout.mListViews.get(0)); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java index c2f55e2f9b99..b049632942cf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java @@ -112,33 +112,19 @@ public class CommandQueueTest extends SysuiTestCase { @Test public void testSetSystemUiVisibility() { Rect r = new Rect(); - mCommandQueue.setSystemUiVisibility(DEFAULT_DISPLAY, 1, 2, 3, 4, null, r); + mCommandQueue.setSystemUiVisibility(DEFAULT_DISPLAY, 1, 2, 3, 4, null, r, false); waitForIdleSync(); verify(mCallbacks).setSystemUiVisibility(eq(DEFAULT_DISPLAY), eq(1), eq(2), eq(3), eq(4), - eq(null), eq(r)); + eq(null), eq(r), eq(false)); } @Test public void testSetSystemUiVisibilityForSecondaryDisplay() { Rect r = new Rect(); - mCommandQueue.setSystemUiVisibility(SECONDARY_DISPLAY, 1, 2, 3, 4, null, r); + mCommandQueue.setSystemUiVisibility(SECONDARY_DISPLAY, 1, 2, 3, 4, null, r, false); waitForIdleSync(); verify(mCallbacks).setSystemUiVisibility(eq(SECONDARY_DISPLAY), eq(1), eq(2), eq(3), eq(4), - eq(null), eq(r)); - } - - @Test - public void testTopAppWindowChanged() { - mCommandQueue.topAppWindowChanged(DEFAULT_DISPLAY, true); - waitForIdleSync(); - verify(mCallbacks).topAppWindowChanged(eq(DEFAULT_DISPLAY), eq(true)); - } - - @Test - public void testTopAppWindowChangedForSecondaryDisplay() { - mCommandQueue.topAppWindowChanged(SECONDARY_DISPLAY, true); - waitForIdleSync(); - verify(mCallbacks).topAppWindowChanged(eq(SECONDARY_DISPLAY), eq(true)); + eq(null), eq(r), eq(false)); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java index e4b90c54d5b3..028fd7afd945 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java @@ -234,7 +234,7 @@ public class NotificationTestHelper { * @param bubbleMetadata the bubble metadata to use for this notification if it exists. * @return a notification that is in the group specified or standalone if unspecified */ - private Notification createNotification(boolean isGroupSummary, + public Notification createNotification(boolean isGroupSummary, @Nullable String groupKey, @Nullable BubbleMetadata bubbleMetadata) { Notification publicVersion = new Notification.Builder(mContext).setSmallIcon( R.drawable.ic_person) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java index b03abecbc59b..7eeae67c9fdf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.notification; +import static com.google.common.truth.Truth.assertThat; + import static org.junit.Assert.assertNotSame; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; @@ -24,17 +26,22 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; +import android.annotation.Nullable; import android.app.Notification; import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.drawable.Drawable; import android.test.suitebuilder.annotation.SmallTest; import android.widget.RemoteViews; +import androidx.palette.graphics.Palette; import androidx.test.runner.AndroidJUnit4; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -43,9 +50,18 @@ import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) public class MediaNotificationProcessorTest extends SysuiTestCase { + private static final int BITMAP_WIDTH = 10; + private static final int BITMAP_HEIGHT = 10; + + /** + * Color tolerance is borrowed from the AndroidX test utilities for Palette. + */ + private static final int COLOR_TOLERANCE = 8; + private MediaNotificationProcessor mProcessor; private Bitmap mBitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); private ImageGradientColorizer mColorizer; + @Nullable private Bitmap mArtwork; @Before public void setUp() { @@ -53,6 +69,14 @@ public class MediaNotificationProcessorTest extends SysuiTestCase { mProcessor = new MediaNotificationProcessor(getContext(), getContext(), mColorizer); } + @After + public void tearDown() { + if (mArtwork != null) { + mArtwork.recycle(); + mArtwork = null; + } + } + @Test public void testColorizedWithLargeIcon() { Notification.Builder builder = new Notification.Builder(getContext()).setSmallIcon( @@ -100,6 +124,36 @@ public class MediaNotificationProcessorTest extends SysuiTestCase { assertNotSame(contentView, remoteViews); } + @Test + public void findBackgroundSwatch_white() { + // Given artwork that is completely white. + mArtwork = Bitmap.createBitmap(BITMAP_WIDTH, BITMAP_HEIGHT, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(mArtwork); + canvas.drawColor(Color.WHITE); + // WHEN the background swatch is computed + Palette.Swatch swatch = MediaNotificationProcessor.findBackgroundSwatch(mArtwork); + // THEN the swatch color is white + assertCloseColors(swatch.getRgb(), Color.WHITE); + } + + @Test + public void findBackgroundSwatch_red() { + // Given artwork that is completely red. + mArtwork = Bitmap.createBitmap(BITMAP_WIDTH, BITMAP_HEIGHT, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(mArtwork); + canvas.drawColor(Color.RED); + // WHEN the background swatch is computed + Palette.Swatch swatch = MediaNotificationProcessor.findBackgroundSwatch(mArtwork); + // THEN the swatch color is red + assertCloseColors(swatch.getRgb(), Color.RED); + } + + static void assertCloseColors(int expected, int actual) { + assertThat((float) Color.red(expected)).isWithin(COLOR_TOLERANCE).of(Color.red(actual)); + assertThat((float) Color.green(expected)).isWithin(COLOR_TOLERANCE).of(Color.green(actual)); + assertThat((float) Color.blue(expected)).isWithin(COLOR_TOLERANCE).of(Color.blue(actual)); + } + public static class TestableColorizer extends ImageGradientColorizer { private final Bitmap mBitmap; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java index 25995eb59d80..7bd25808a9c5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java @@ -891,7 +891,31 @@ public class NotificationInfoTest extends SysuiTestCase { } @Test - public void testWillBeRemovedReturnsFalseBeforeBind() throws Exception { + public void testCloseControls_withoutHittingApply() throws Exception { + mNotificationChannel.setImportance(IMPORTANCE_LOW); + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, + (Runnable saveImportance, StatusBarNotification sbn) -> { + saveImportance.run(); + }, null, null, true, false, IMPORTANCE_LOW, false + ); + + mNotificationInfo.findViewById(R.id.alert).performClick(); + + assertFalse(mNotificationInfo.shouldBeSaved()); + } + + @Test + public void testWillBeRemovedReturnsFalse() throws Exception { + assertFalse(mNotificationInfo.willBeRemoved()); + + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, + (Runnable saveImportance, StatusBarNotification sbn) -> { + saveImportance.run(); + }, null, null, true, false, IMPORTANCE_LOW, false + ); + assertFalse(mNotificationInfo.willBeRemoved()); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java index c62a802e25e4..43ea92f13c8d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java @@ -112,11 +112,8 @@ public class NotificationMenuRowTest extends LeakCheckedTest { } @Test - public void testNoAppOpsInSlowSwipe_newInterruptionModel() { - Settings.Secure.putInt(mContext.getContentResolver(), - NOTIFICATION_NEW_INTERRUPTION_MODEL, 1); - - NotificationMenuRow row = new NotificationMenuRow(mContext); + public void testNoAppOpsInSlowSwipe_biDirectionalSwipe() { + NotificationMenuRow row = new NotificationMenuRow(mContext, true); row.createMenu(mRow, null); ViewGroup container = (ViewGroup) row.getMenuView(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoHideControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoHideControllerTest.java index 1b34a7584994..f614354a7691 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoHideControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoHideControllerTest.java @@ -70,7 +70,7 @@ public class AutoHideControllerTest extends SysuiTestCase { @Test public void testSetSystemUiVisibilityEarlyReturnWithDifferentDisplay() { - mAutoHideController.setSystemUiVisibility(1, 1, 2, 3, 4, null, new Rect()); + mAutoHideController.setSystemUiVisibility(1, 1, 2, 3, 4, null, new Rect(), false); verify(mAutoHideController, never()).notifySystemUiVisibilityChanged(anyInt()); } @@ -78,7 +78,8 @@ public class AutoHideControllerTest extends SysuiTestCase { @Test public void testSetSystemUiVisibilityEarlyReturnWithSameVisibility() { mAutoHideController - .setSystemUiVisibility(DEFAULT_DISPLAY, View.VISIBLE, 2, 3, 4, null, new Rect()); + .setSystemUiVisibility( + DEFAULT_DISPLAY, View.VISIBLE, 2, 3, 4, null, new Rect(), false); verify(mAutoHideController, never()).notifySystemUiVisibilityChanged(anyInt()); } @@ -92,7 +93,7 @@ public class AutoHideControllerTest extends SysuiTestCase { View.SYSTEM_UI_FLAG_FULLSCREEN | View.STATUS_BAR_UNHIDE; mAutoHideController.setSystemUiVisibility( - DEFAULT_DISPLAY, expectedStatus, 2, 3, FULL_MASK, null, new Rect()); + DEFAULT_DISPLAY, expectedStatus, 2, 3, FULL_MASK, null, new Rect(), false); assertEquals("System UI visibility should not be changed", expectedStatus, mAutoHideController.mSystemUiVisibility); @@ -109,7 +110,7 @@ public class AutoHideControllerTest extends SysuiTestCase { mAutoHideController.setSystemUiVisibility( DEFAULT_DISPLAY, View.STATUS_BAR_UNHIDE | View.NAVIGATION_BAR_UNHIDE, - 2, 3, FULL_MASK, null, new Rect()); + 2, 3, FULL_MASK, null, new Rect(), false); int expectedStatus = View.VISIBLE; assertEquals(expectedStatus, mAutoHideController.mSystemUiVisibility); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index d09cea59b9c5..f50cf5a70cdc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.phone; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; @@ -28,7 +29,6 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; -import android.view.View; import android.view.ViewGroup; import androidx.test.filters.SmallTest; @@ -39,7 +39,6 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.plugins.ActivityStarter.OnDismissAction; import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.statusbar.StatusBarState; import org.junit.Before; import org.junit.Test; @@ -199,26 +198,14 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { } @Test - public void onQsExpansionChanged_lockVisibleOnlyWhenCollapsed() { - when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD); - mStatusBarKeyguardViewManager.onQsExpansionChanged(0); - verify(mLockIconContainer).setVisibility(eq(View.VISIBLE)); - - reset(mNotificationPanelView); - when(mNotificationPanelView.isQsExpanded()).thenReturn(true); - mStatusBarKeyguardViewManager.onQsExpansionChanged(1f); - verify(mLockIconContainer).setVisibility(eq(View.INVISIBLE)); - } + public void setOccluded_animatesPanelExpansion_onlyIfBouncerHidden() { + mStatusBarKeyguardViewManager.setOccluded(false /* occluded */, true /* animated */); + verify(mStatusBar).animateKeyguardUnoccluding(); - @Test - public void onQsExpansionChanged_lockInvisibleWhenAnimatingAway() { when(mBouncer.isShowing()).thenReturn(true); - mStatusBarKeyguardViewManager.onQsExpansionChanged(0); - verify(mLockIconContainer).setVisibility(eq(View.VISIBLE)); - - when(mBouncer.isAnimatingAway()).thenReturn(true); - mStatusBarKeyguardViewManager.onQsExpansionChanged(0f); - verify(mLockIconContainer).setVisibility(eq(View.INVISIBLE)); + clearInvocations(mStatusBar); + mStatusBarKeyguardViewManager.setOccluded(false /* occluded */, true /* animated */); + verify(mStatusBar, never()).animateKeyguardUnoccluding(); } private class TestableStatusBarKeyguardViewManager extends StatusBarKeyguardViewManager { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java index a88a59540161..a97832f94924 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java @@ -70,7 +70,8 @@ public class StatusBarRemoteInputCallbackTest extends SysuiTestCase { mNotificationLockscreenUserManager); mDependency.putComponent(CommandQueue.class, mock(CommandQueue.class)); - mRemoteInputCallback = spy(new StatusBarRemoteInputCallback(mContext)); + mRemoteInputCallback = spy(new StatusBarRemoteInputCallback(mContext, + mock(NotificationGroupManager.class))); mRemoteInputCallback.mChallengeReceiver = mRemoteInputCallback.new ChallengeReceiver(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java index ce5bfced961b..616b46a6d316 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java @@ -130,6 +130,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase { when(mMockCm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)).thenReturn(true); when(mMockCm.getDefaultNetworkCapabilitiesForUser(0)).thenReturn( new NetworkCapabilities[] { mNetCapabilities }); + when(mMockTm.createForSubscriptionId(anyInt())).thenReturn(mMockTm); mSignalStrength = mock(SignalStrength.class); mServiceState = mock(ServiceState.class); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java index 8bf1606d5b7b..bc468bf2fb82 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java @@ -42,6 +42,7 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.NotificationTestHelper; import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; +import com.android.systemui.statusbar.phone.LightBarController; import com.android.systemui.util.Assert; import org.junit.After; @@ -67,6 +68,7 @@ public class RemoteInputViewTest extends SysuiTestCase { @Mock private RemoteInputController mController; @Mock private ShortcutManager mShortcutManager; @Mock private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler; + @Mock private LightBarController mLightBarController; private BlockingQueueIntentReceiver mReceiver; private RemoteInputView mView; @@ -77,6 +79,8 @@ public class RemoteInputViewTest extends SysuiTestCase { mDependency.injectTestDependency(RemoteInputQuickSettingsDisabler.class, mRemoteInputQuickSettingsDisabler); + mDependency.injectTestDependency(LightBarController.class, + mLightBarController); mReceiver = new BlockingQueueIntentReceiver(); mContext.registerReceiver(mReceiver, new IntentFilter(TEST_ACTION), null, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java index fb2b7dced7c2..c761a44170c3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java @@ -229,8 +229,7 @@ public class SmartReplyConstantsTest extends SysuiTestCase { } private void triggerConstantsOnChange() { - mConstants.onDeviceConfigPropertyChanged(DeviceConfig.NAMESPACE_SYSTEMUI, - "" /* name */, "" /* value */); + mConstants.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI); } private void resetAllDeviceConfigFlags() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java index 01f3c923832f..8c5fac47885f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java @@ -634,7 +634,8 @@ public class SmartReplyViewTest extends SysuiTestCase { mView.getChildAt(2).performClick(); - verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any(), any()); + verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any(), any(), + any()); } @Test @@ -645,7 +646,7 @@ public class SmartReplyViewTest extends SysuiTestCase { mView.getChildAt(2).performClick(); - verify(mActivityStarter, never()).startPendingIntentDismissingKeyguard(any(), any()); + verify(mActivityStarter, never()).startPendingIntentDismissingKeyguard(any(), any(), any()); } @Test @@ -657,7 +658,8 @@ public class SmartReplyViewTest extends SysuiTestCase { Thread.sleep(delayMs); mView.getChildAt(2).performClick(); - verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any(), any()); + verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any(), any(), + any()); } @Test @@ -668,7 +670,8 @@ public class SmartReplyViewTest extends SysuiTestCase { mView.getChildAt(2).performClick(); - verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any(), any()); + verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any(), any(), + any()); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayManagerTest.java index 4659afc8e1c2..c99deb606459 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayManagerTest.java @@ -22,6 +22,7 @@ import static com.android.systemui.theme.ThemeOverlayManager.OVERLAY_CATEGORY_IC import static com.android.systemui.theme.ThemeOverlayManager.OVERLAY_CATEGORY_ICON_LAUNCHER; import static com.android.systemui.theme.ThemeOverlayManager.OVERLAY_CATEGORY_ICON_SETTINGS; import static com.android.systemui.theme.ThemeOverlayManager.OVERLAY_CATEGORY_ICON_SYSUI; +import static com.android.systemui.theme.ThemeOverlayManager.OVERLAY_CATEGORY_ICON_THEME_PICKER; import static com.android.systemui.theme.ThemeOverlayManager.OVERLAY_CATEGORY_SHAPE; import static com.android.systemui.theme.ThemeOverlayManager.SETTINGS_PACKAGE; import static com.android.systemui.theme.ThemeOverlayManager.SYSTEM_USER_CATEGORIES; @@ -74,6 +75,7 @@ public class ThemeOverlayManagerTest extends SysuiTestCase { } } + private static final String THEMEPICKER_PACKAGE = "com.android.wallpaper"; private static final String LAUNCHER_PACKAGE = "com.android.launcher3"; private static final UserHandle TEST_USER = UserHandle.of(5); private static final Set<UserHandle> TEST_USER_HANDLES = Sets.newHashSet(TEST_USER); @@ -87,7 +89,7 @@ public class ThemeOverlayManagerTest extends SysuiTestCase { public void setup() throws Exception { MockitoAnnotations.initMocks(this); mManager = new ThemeOverlayManager(mOverlayManager, MoreExecutors.directExecutor(), - LAUNCHER_PACKAGE); + LAUNCHER_PACKAGE, THEMEPICKER_PACKAGE); when(mOverlayManager.getOverlayInfosForTarget(ANDROID_PACKAGE, UserHandle.SYSTEM)) .thenReturn(Lists.newArrayList( createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_COLOR, @@ -124,6 +126,12 @@ public class ThemeOverlayManagerTest extends SysuiTestCase { LAUNCHER_PACKAGE, OVERLAY_CATEGORY_ICON_LAUNCHER, false), createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_ICON_LAUNCHER, LAUNCHER_PACKAGE, OVERLAY_CATEGORY_ICON_LAUNCHER, true))); + when(mOverlayManager.getOverlayInfosForTarget(THEMEPICKER_PACKAGE, UserHandle.SYSTEM)) + .thenReturn(Lists.newArrayList( + createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_ICON_THEME_PICKER, + THEMEPICKER_PACKAGE, OVERLAY_CATEGORY_ICON_THEME_PICKER, false), + createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_ICON_THEME_PICKER, + THEMEPICKER_PACKAGE, OVERLAY_CATEGORY_ICON_THEME_PICKER, true))); } @Test @@ -222,6 +230,8 @@ public class ThemeOverlayManagerTest extends SysuiTestCase { verify(mOverlayManager, never()).getOverlayInfosForTarget(SYSUI_PACKAGE, UserHandle.SYSTEM); verify(mOverlayManager, never()).getOverlayInfosForTarget(LAUNCHER_PACKAGE, UserHandle.SYSTEM); + verify(mOverlayManager, never()).getOverlayInfosForTarget(THEMEPICKER_PACKAGE, + UserHandle.SYSTEM); } private static OverlayInfo createOverlayInfo(String packageName, String targetPackageName, diff --git a/packages/overlays/AccentColorBlackOverlay/Android.mk b/packages/overlays/AccentColorBlackOverlay/Android.mk index b81ae5bbe3fa..a689defe5b6b 100644 --- a/packages/overlays/AccentColorBlackOverlay/Android.mk +++ b/packages/overlays/AccentColorBlackOverlay/Android.mk @@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_RRO_THEME := AccentColorBlack -LOCAL_CERTIFICATE := platform + LOCAL_PRODUCT_MODULE := true LOCAL_SRC_FILES := $(call all-subdir-java-files) diff --git a/packages/overlays/AccentColorCinnamonOverlay/Android.mk b/packages/overlays/AccentColorCinnamonOverlay/Android.mk index d53c1143cd33..3a6cbe3a182a 100644 --- a/packages/overlays/AccentColorCinnamonOverlay/Android.mk +++ b/packages/overlays/AccentColorCinnamonOverlay/Android.mk @@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_RRO_THEME := AccentColorCinnamon -LOCAL_CERTIFICATE := platform + LOCAL_PRODUCT_MODULE := true LOCAL_SRC_FILES := $(call all-subdir-java-files) diff --git a/packages/overlays/AccentColorGreenOverlay/Android.mk b/packages/overlays/AccentColorGreenOverlay/Android.mk index db92157c8fdf..d96dbe17b103 100644 --- a/packages/overlays/AccentColorGreenOverlay/Android.mk +++ b/packages/overlays/AccentColorGreenOverlay/Android.mk @@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_RRO_THEME := AccentColorGreen -LOCAL_CERTIFICATE := platform + LOCAL_PRODUCT_MODULE := true LOCAL_SRC_FILES := $(call all-subdir-java-files) diff --git a/packages/overlays/AccentColorOceanOverlay/Android.mk b/packages/overlays/AccentColorOceanOverlay/Android.mk index a28fc7227a8c..cf0c6b310f02 100644 --- a/packages/overlays/AccentColorOceanOverlay/Android.mk +++ b/packages/overlays/AccentColorOceanOverlay/Android.mk @@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_RRO_THEME := AccentColorOcean -LOCAL_CERTIFICATE := platform + LOCAL_PRODUCT_MODULE := true LOCAL_SRC_FILES := $(call all-subdir-java-files) diff --git a/packages/overlays/AccentColorOrchidOverlay/Android.mk b/packages/overlays/AccentColorOrchidOverlay/Android.mk index c635890d02cc..fc55befef71e 100644 --- a/packages/overlays/AccentColorOrchidOverlay/Android.mk +++ b/packages/overlays/AccentColorOrchidOverlay/Android.mk @@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_RRO_THEME := AccentColorOrchid -LOCAL_CERTIFICATE := platform + LOCAL_PRODUCT_MODULE := true LOCAL_SRC_FILES := $(call all-subdir-java-files) diff --git a/packages/overlays/AccentColorPurpleOverlay/Android.mk b/packages/overlays/AccentColorPurpleOverlay/Android.mk index d7dc4978e2ca..3a28efa2f820 100644 --- a/packages/overlays/AccentColorPurpleOverlay/Android.mk +++ b/packages/overlays/AccentColorPurpleOverlay/Android.mk @@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_RRO_THEME := AccentColorPurple -LOCAL_CERTIFICATE := platform + LOCAL_PRODUCT_MODULE := true LOCAL_SRC_FILES := $(call all-subdir-java-files) diff --git a/packages/overlays/AccentColorSpaceOverlay/Android.mk b/packages/overlays/AccentColorSpaceOverlay/Android.mk index a0edb96ac23e..78cbf7325dee 100644 --- a/packages/overlays/AccentColorSpaceOverlay/Android.mk +++ b/packages/overlays/AccentColorSpaceOverlay/Android.mk @@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_RRO_THEME := AccentColorSpace -LOCAL_CERTIFICATE := platform + LOCAL_PRODUCT_MODULE := true LOCAL_SRC_FILES := $(call all-subdir-java-files) diff --git a/packages/overlays/Android.mk b/packages/overlays/Android.mk index b9b3a615650f..2675e900fd6d 100644 --- a/packages/overlays/Android.mk +++ b/packages/overlays/Android.mk @@ -32,14 +32,17 @@ LOCAL_REQUIRED_MODULES := \ IconPackCircularLauncherOverlay \ IconPackCircularSettingsOverlay \ IconPackCircularSystemUIOverlay \ + IconPackCircularThemePickerOverlay \ IconPackFilledAndroidOverlay \ IconPackFilledLauncherOverlay \ IconPackFilledSettingsOverlay \ IconPackFilledSystemUIOverlay \ + IconPackFilledThemePickerOverlay \ IconPackRoundedAndroidOverlay \ IconPackRoundedLauncherOverlay \ IconPackRoundedSettingsOverlay \ IconPackRoundedSystemUIOverlay \ + IconPackRoundedThemePickerUIOverlay \ IconShapeRoundedRectOverlay \ IconShapeSquareOverlay \ IconShapeSquircleOverlay \ diff --git a/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.mk index bf2b6312d68f..b73aea320d60 100644 --- a/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.mk +++ b/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.mk @@ -2,7 +2,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_RRO_THEME := DisplayCutoutEmulationCorner -LOCAL_CERTIFICATE := platform + LOCAL_PRODUCT_MODULE := true diff --git a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.mk index 70429064ec2b..8ca2dad25f3f 100644 --- a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.mk +++ b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.mk @@ -2,7 +2,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_RRO_THEME := DisplayCutoutEmulationDouble -LOCAL_CERTIFICATE := platform + LOCAL_PRODUCT_MODULE := true diff --git a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.mk index ae69e1137e60..7458cb5db9fb 100644 --- a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.mk +++ b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.mk @@ -2,7 +2,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_RRO_THEME := DisplayCutoutEmulationNarrow -LOCAL_CERTIFICATE := platform + LOCAL_PRODUCT_MODULE := true diff --git a/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.mk index 7dcadfbd4708..1a405e2275c6 100644 --- a/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.mk +++ b/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.mk @@ -2,7 +2,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_RRO_THEME := DisplayCutoutEmulationTall -LOCAL_CERTIFICATE := platform + LOCAL_PRODUCT_MODULE := true diff --git a/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.mk index 3f7be73aa5fa..3ebc540852c8 100644 --- a/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.mk +++ b/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.mk @@ -2,7 +2,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_RRO_THEME := DisplayCutoutEmulationWide -LOCAL_CERTIFICATE := platform + LOCAL_PRODUCT_MODULE := true diff --git a/packages/overlays/FontNotoSerifSourceOverlay/Android.mk b/packages/overlays/FontNotoSerifSourceOverlay/Android.mk index 6f3c4f7cfebd..f4eedaf377f9 100644 --- a/packages/overlays/FontNotoSerifSourceOverlay/Android.mk +++ b/packages/overlays/FontNotoSerifSourceOverlay/Android.mk @@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_RRO_THEME := FontNotoSerifSource -LOCAL_CERTIFICATE := platform + LOCAL_PRODUCT_MODULE := true LOCAL_SRC_FILES := $(call all-subdir-java-files) diff --git a/packages/overlays/IconPackCircularAndroidOverlay/Android.mk b/packages/overlays/IconPackCircularAndroidOverlay/Android.mk index 60f525b65128..8f3baa5962dc 100644 --- a/packages/overlays/IconPackCircularAndroidOverlay/Android.mk +++ b/packages/overlays/IconPackCircularAndroidOverlay/Android.mk @@ -17,7 +17,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_RRO_THEME := IconPackCircularAndroid -LOCAL_CERTIFICATE := platform + LOCAL_PRODUCT_MODULE := true LOCAL_SRC_FILES := $(call all-subdir-java-files) diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_0.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_0.xml new file mode 100644 index 000000000000..1be546840e6d --- /dev/null +++ b/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_0.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> + <objectAnimator android:propertyName="fillAlpha" android:duration="150" + android:startOffset="0" android:valueFrom="0.3" + android:valueTo="0.3" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator" /> + <objectAnimator android:propertyName="fillAlpha" android:duration="17" + android:startOffset="150" android:valueFrom="0.3" + android:valueTo="1" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator" /> + <objectAnimator android:propertyName="fillAlpha" android:duration="483" + android:startOffset="167" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator" /> + <objectAnimator android:propertyName="fillAlpha" android:duration="17" + android:startOffset="650" android:valueFrom="1" + android:valueTo="0.3" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator" /> +</set> diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_1.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_1.xml new file mode 100644 index 000000000000..c9fd424f7ad3 --- /dev/null +++ b/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_1.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> + <objectAnimator android:propertyName="fillAlpha" android:duration="317" + android:startOffset="0" android:valueFrom="0.3" + android:valueTo="0.3" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator" /> + <objectAnimator android:propertyName="fillAlpha" android:duration="17" + android:startOffset="317" android:valueFrom="0.3" + android:valueTo="1" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator" /> + <objectAnimator android:propertyName="fillAlpha" android:duration="483" + android:startOffset="333" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator" /> + <objectAnimator android:propertyName="fillAlpha" android:duration="17" + android:startOffset="817" android:valueFrom="1" + android:valueTo="0.3" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator" /> +</set> diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_2.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_2.xml new file mode 100644 index 000000000000..b34d3088e87e --- /dev/null +++ b/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_2.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> + <objectAnimator android:propertyName="fillAlpha" android:duration="483" + android:startOffset="0" android:valueFrom="0.3" + android:valueTo="0.3" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator" /> + <objectAnimator android:propertyName="fillAlpha" android:duration="17" + android:startOffset="483" android:valueFrom="0.3" + android:valueTo="1" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator" /> + <objectAnimator android:propertyName="fillAlpha" android:duration="483" + android:startOffset="500" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator" /> + <objectAnimator android:propertyName="fillAlpha" android:duration="17" + android:startOffset="983" android:valueFrom="1" + android:valueTo="0.3" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator" /> +</set> diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_3.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_3.xml new file mode 100644 index 000000000000..9d2b3a4f8016 --- /dev/null +++ b/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_3.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> + <objectAnimator android:propertyName="fillAlpha" android:duration="650" + android:startOffset="0" android:valueFrom="0.3" + android:valueTo="0.3" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator" /> + <objectAnimator android:propertyName="fillAlpha" android:duration="17" + android:startOffset="650" android:valueFrom="0.3" + android:valueTo="1" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator" /> + <objectAnimator android:propertyName="fillAlpha" android:duration="483" + android:startOffset="667" android:valueFrom="1" android:valueTo="1" + android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator" /> + <objectAnimator android:propertyName="fillAlpha" android:duration="17" + android:startOffset="1150" android:valueFrom="1" + android:valueTo="0.3" android:valueType="floatType" + android:interpolator="@*android:interpolator/transient_interpolator" /> +</set> diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_4.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_4.xml new file mode 100644 index 000000000000..943893dfb361 --- /dev/null +++ b/packages/overlays/IconPackCircularAndroidOverlay/res/anim/ic_signal_wifi_transient_animation_4.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> + <objectAnimator android:propertyName="translateX" android:duration="1250" + android:startOffset="0" android:valueFrom="0" android:valueTo="1" + android:valueType="floatType"/> +</set>
\ No newline at end of file diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_bluetooth_transient_animation.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_bluetooth_transient_animation.xml new file mode 100644 index 000000000000..8ca452097c4d --- /dev/null +++ b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_bluetooth_transient_animation.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@*android:drawable/ic_bluetooth_transient_animation_drawable"> + <target android:name="_R_G_L_1_G_D_0_P_0" + android:animation="@*android:anim/ic_bluetooth_transient_animation_0"/> + <target android:name="_R_G_L_0_G_D_0_P_0" + android:animation="@*android:anim/ic_bluetooth_transient_animation_1"/> + <target android:name="time_group" + android:animation="@*android:anim/ic_bluetooth_transient_animation_2"/> +</animated-vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_bluetooth_transient_animation_drawable.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_bluetooth_transient_animation_drawable.xml new file mode 100644 index 000000000000..66ac8feff21b --- /dev/null +++ b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_bluetooth_transient_animation_drawable.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> + +<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" + android:width="24dp" android:viewportHeight="24" + android:viewportWidth="24"> + <group android:name="_R_G"> + <group android:name="_R_G_L_2_G" android:translateX="3.75" + android:translateY="1.75"> + <path android:name="_R_G_L_2_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M1.22 3.22 C0.93,3.51 0.93,3.99 1.22,4.28 C1.22,4.28 7.19,10.25 7.19,10.25 C7.19,10.25 1.22,16.22 1.22,16.22 C0.93,16.51 0.93,16.99 1.22,17.28 C1.37,17.43 1.56,17.5 1.75,17.5 C1.94,17.5 2.13,17.43 2.28,17.28 C2.28,17.28 7.25,12.31 7.25,12.31 C7.25,12.31 7.25,20.25 7.25,20.25 C7.25,20.25 8,20.25 8,20.25 C10.96,20.25 13.37,17.84 13.37,14.88 C13.37,12.91 12.31,11.19 10.73,10.25 C12.31,9.31 13.37,7.59 13.37,5.63 C13.37,2.66 10.96,0.25 8,0.25 C8,0.25 7.25,0.25 7.25,0.25 C7.25,0.25 7.25,8.19 7.25,8.19 C7.25,8.19 2.28,3.22 2.28,3.22 C1.99,2.93 1.51,2.93 1.22,3.22c M8.75 1.82 C10.52,2.17 11.87,3.75 11.87,5.63 C11.87,7.5 10.52,9.08 8.75,9.43 C8.75,9.43 8.75,1.82 8.75,1.82c M8.75 11.07 C10.52,11.42 11.87,13 11.87,14.88 C11.87,16.75 10.52,18.33 8.75,18.68 C8.75,18.68 8.75,11.07 8.75,11.07c "/> + </group> + <group android:name="_R_G_L_1_G" android:translateX="3.75" + android:translateY="1.75"> + <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M15.25 9.25 C14.7,9.25 14.25,9.7 14.25,10.25 C14.25,10.8 14.7,11.25 15.25,11.25 C15.8,11.25 16.25,10.8 16.25,10.25 C16.25,9.7 15.8,9.25 15.25,9.25c "/> + </group> + <group android:name="_R_G_L_0_G" android:translateX="3.75" + android:translateY="1.75"> + <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M0.25 10.25 C0.25,10.8 0.7,11.25 1.25,11.25 C1.8,11.25 2.25,10.8 2.25,10.25 C2.25,9.7 1.8,9.25 1.25,9.25 C0.7,9.25 0.25,9.7 0.25,10.25c "/> + </group> + </group> + <group android:name="time_group"/> +</vector> diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_hotspot_transient_animation.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_hotspot_transient_animation.xml new file mode 100644 index 000000000000..1317f66b4131 --- /dev/null +++ b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_hotspot_transient_animation.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@*android:drawable/ic_hotspot_transient_animation_drawable"> + <target android:name="_R_G_L_0_G_D_0_P_0" android:animation="@*android:anim/ic_hotspot_transient_animation_0"/> + <target android:name="_R_G_L_0_G_D_1_P_0" android:animation="@*android:anim/ic_hotspot_transient_animation_1"/> + <target android:name="_R_G_L_0_G_D_2_P_0" android:animation="@*android:anim/ic_hotspot_transient_animation_2"/> + <target android:name="time_group" android:animation="@*android:anim/ic_hotspot_transient_animation_3"/> +</animated-vector> diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_hotspot_transient_animation_drawable.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_hotspot_transient_animation_drawable.xml new file mode 100644 index 000000000000..81531957f24d --- /dev/null +++ b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_hotspot_transient_animation_drawable.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:width="24dp" android:viewportHeight="24" + android:viewportWidth="24"> + <group android:name="_R_G"> + <group android:name="_R_G_L_0_G" android:translateX="2" + android:translateY="1.552999999999999"> + <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M10 12.45 C9.17,12.45 8.5,11.77 8.5,10.95 C8.5,10.12 9.17,9.45 10,9.45 C10.83,9.45 11.5,10.12 11.5,10.95 C11.5,11.77 10.83,12.45 10,12.45c "/> + <path android:name="_R_G_L_0_G_D_1_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M6.47 15.23 C6.27,15.23 6.08,15.16 5.94,15.01 C4.85,13.93 4.25,12.48 4.25,10.95 C4.25,9.41 4.85,7.97 5.94,6.88 C8.18,4.64 11.82,4.64 14.07,6.88 C15.15,7.97 15.75,9.41 15.75,10.95 C15.75,12.48 15.15,13.93 14.07,15.01 C13.77,15.3 13.3,15.3 13.01,15.01 C12.71,14.72 12.71,14.24 13.01,13.95 C13.81,13.15 14.25,12.08 14.25,10.95 C14.25,9.81 13.81,8.74 13.01,7.94 C11.35,6.28 8.65,6.28 6.99,7.94 C6.19,8.75 5.75,9.81 5.75,10.95 C5.75,12.08 6.19,13.15 6.99,13.95 C7.29,14.25 7.29,14.72 6.99,15.01 C6.85,15.16 6.66,15.23 6.47,15.23c "/> + <path android:name="_R_G_L_0_G_D_2_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M16.36 18.06 C16.17,18.06 15.98,17.99 15.83,17.84 C15.54,17.55 15.54,17.07 15.83,16.78 C17.39,15.22 18.25,13.15 18.25,10.95 C18.25,8.74 17.39,6.67 15.83,5.11 C12.62,1.9 7.38,1.9 4.17,5.11 C2.61,6.67 1.75,8.74 1.75,10.95 C1.75,13.15 2.61,15.22 4.17,16.78 C4.46,17.07 4.46,17.55 4.17,17.84 C3.87,18.13 3.4,18.13 3.11,17.84 C1.26,16 0.25,13.55 0.25,10.95 C0.25,8.34 1.26,5.89 3.11,4.05 C6.91,0.25 13.09,0.25 16.89,4.05 C18.74,5.89 19.75,8.34 19.75,10.95 C19.75,13.55 18.74,16 16.89,17.84 C16.75,17.99 16.56,18.06 16.36,18.06c "/> + </group> + </group> + <group android:name="time_group"/> +</vector> diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation.xml new file mode 100644 index 000000000000..06755f6e3871 --- /dev/null +++ b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@*android:drawable/ic_signal_wifi_transient_animation_drawable"> + <target android:name="_R_G_L_0_G_D_0_P_0" android:animation="@*android:anim/ic_signal_wifi_transient_animation_0"/> + <target android:name="_R_G_L_0_G_D_1_P_0" android:animation="@*android:anim/ic_signal_wifi_transient_animation_1"/> + <target android:name="_R_G_L_0_G_D_2_P_0" android:animation="@*android:anim/ic_signal_wifi_transient_animation_2"/> + <target android:name="_R_G_L_0_G_D_3_P_0" android:animation="@*android:anim/ic_signal_wifi_transient_animation_3"/> + <target android:name="time_group" android:animation="@*android:anim/ic_signal_wifi_transient_animation_4"/> +</animated-vector> diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation_drawable.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation_drawable.xml new file mode 100644 index 000000000000..6e455133e6eb --- /dev/null +++ b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation_drawable.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportHeight="24" + android:viewportWidth="24"> + <group android:name="_R_G"> + <group android:name="_R_G_L_0_G" android:translateX="0.6440000000000001" + android:translateY="2.755000000000001"> + <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="0.3" android:fillType="nonZero" + android:pathData=" M12.86 15.75 C12.86,16.58 12.19,17.25 11.36,17.25 C10.53,17.25 9.86,16.58 9.86,15.75 C9.86,14.92 10.53,14.25 11.36,14.25 C12.19,14.25 12.86,14.92 12.86,15.75c "/> + <path android:name="_R_G_L_0_G_D_1_P_0" android:fillColor="#000000" + android:fillAlpha="0.3" android:fillType="nonZero" + android:pathData=" M15.61 12.25 C15.42,12.25 15.23,12.17 15.08,12.03 C14.09,11.04 12.76,10.5 11.36,10.5 C9.95,10.5 8.63,11.04 7.63,12.03 C7.34,12.32 6.86,12.32 6.57,12.02 C6.28,11.73 6.28,11.26 6.58,10.96 C7.86,9.7 9.55,9 11.36,9 C13.16,9 14.86,9.7 16.14,10.96 C16.43,11.25 16.43,11.73 16.14,12.02 C16,12.17 15.8,12.25 15.61,12.25c "/> + <path android:name="_R_G_L_0_G_D_2_P_0" android:fillColor="#000000" + android:fillAlpha="0.3" android:fillType="nonZero" + android:pathData=" M18.77 9.08 C18.58,9.08 18.39,9.01 18.24,8.86 C16.4,7.02 13.96,6 11.36,6 C8.75,6 6.31,7.02 4.47,8.86 C4.18,9.16 3.7,9.16 3.41,8.86 C3.12,8.57 3.11,8.1 3.41,7.8 C5.53,5.67 8.35,4.5 11.36,4.5 C14.36,4.5 17.18,5.67 19.31,7.8 C19.6,8.1 19.6,8.57 19.3,8.86 C19.16,9.01 18.97,9.08 18.77,9.08c "/> + <path android:name="_R_G_L_0_G_D_3_P_0" android:fillColor="#000000" + android:fillAlpha="0.3" android:fillType="nonZero" + android:pathData=" M21.96 5.89 C21.77,5.89 21.58,5.82 21.43,5.67 C18.74,2.98 15.16,1.5 11.36,1.5 C7.55,1.5 3.97,2.98 1.28,5.67 C0.99,5.97 0.51,5.97 0.22,5.67 C-0.07,5.38 -0.07,4.91 0.22,4.61 C3.19,1.64 7.15,0 11.36,0 C15.56,0 19.52,1.64 22.49,4.61 C22.78,4.91 22.78,5.38 22.49,5.67 C22.35,5.82 22.15,5.89 21.96,5.89c "/> + </group> + </group> + <group android:name="time_group"/> +</vector> diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/values/config.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/values/config.xml new file mode 100644 index 000000000000..ae5cc2bf9883 --- /dev/null +++ b/packages/overlays/IconPackCircularAndroidOverlay/res/values/config.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* + * Copyright 2019, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> + +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="config_batterymeterPerimeterPath" translatable="false"> + M 8,1 C 8,0.45 7.55,0 7,0 H 5 C 4.45,0 4,0.45 4,1 H 3 C 1.34,1 0,2.34 0,4 V 17 C 0,18.66 1.34,20 3,20 H 9 C 10.66,20 12,18.66 12,17 V 4 C 12,2.34 10.66,1 9,1 Z M 10.5,4 V 17 C 10.5,17.83 9.83,18.5 9,18.5 H 3 C 2.17,18.5 1.5,17.83 1.5,17 V 4 C 1.5,3.17 2.17,2.5 3,2.5 H 9 C 9.83,2.5 10.5,3.17 10.5,4 Z + </string> + <string name="config_batterymeterFillMask" translatable="false"> + M 10.5,4 V 17 C 10.5,17.83 9.83,18.5 9,18.5 H 3 C 2.17,18.5 1.5,17.83 1.5,17 V 4 C 1.5,3.17 2.17,2.5 3,2.5 H 9 C 9.83,2.5 10.5,3.17 10.5,4 Z + </string> + <string name="config_batterymeterBoltPath" translatable="false"> + M 8.08,9.5 H 7 V 5.99 C 7,5.73 6.65,5.64 6.53,5.87 L 3.7,11.13 C 3.61,11.3 3.73,11.5 3.92,11.5 H 5 V 15.01 C 5,15.27 5.35,15.36 5.47,15.13 L 8.3,9.87 C 8.39,9.7 8.27,9.5 8.08,9.5 Z + </string> + <string name="config_batterymeterPowersavePath" translatable="false"> + M 3.75,11.25 H 5.25 V 12.75 C 5.25,13.16 5.59,13.5 6,13.5 6.41,13.5 6.75,13.16 6.75,12.75 V 11.25 H 8.25 C 8.66,11.25 9,10.91 9,10.5 9,10.09 8.6601,9.75 8.25,9.75 H 6.75 V 8.25 C 6.75,7.84 6.41,7.5 6,7.5 5.59,7.5 5.25,7.84 5.25,8.25 V 9.75 H 3.75 C 3.34,9.75 3,10.09 3,10.5 3,10.91 3.34,11.25 3.75,11.25 Z + </string> +</resources> diff --git a/packages/overlays/IconPackCircularLauncherOverlay/Android.mk b/packages/overlays/IconPackCircularLauncherOverlay/Android.mk index a5277fa17be0..310bdef44b48 100644 --- a/packages/overlays/IconPackCircularLauncherOverlay/Android.mk +++ b/packages/overlays/IconPackCircularLauncherOverlay/Android.mk @@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_RRO_THEME := IconPackCircularLauncher -LOCAL_CERTIFICATE := platform + LOCAL_PRODUCT_MODULE := true LOCAL_SRC_FILES := $(call all-subdir-java-files) diff --git a/packages/overlays/IconPackCircularSettingsOverlay/Android.mk b/packages/overlays/IconPackCircularSettingsOverlay/Android.mk index ad7324dec699..d06732228b82 100644 --- a/packages/overlays/IconPackCircularSettingsOverlay/Android.mk +++ b/packages/overlays/IconPackCircularSettingsOverlay/Android.mk @@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_RRO_THEME := IconPackCircularSettings -LOCAL_CERTIFICATE := platform + LOCAL_PRODUCT_MODULE := true LOCAL_SRC_FILES := $(call all-subdir-java-files) diff --git a/packages/overlays/IconPackCircularSystemUIOverlay/Android.mk b/packages/overlays/IconPackCircularSystemUIOverlay/Android.mk index 711063dbf6ad..5e0dcbee8118 100644 --- a/packages/overlays/IconPackCircularSystemUIOverlay/Android.mk +++ b/packages/overlays/IconPackCircularSystemUIOverlay/Android.mk @@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_RRO_THEME := IconPackCircularSystemUI -LOCAL_CERTIFICATE := platform + LOCAL_PRODUCT_MODULE := true LOCAL_SRC_FILES := $(call all-subdir-java-files) diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/Android.mk b/packages/overlays/IconPackCircularThemePickerOverlay/Android.mk new file mode 100644 index 000000000000..412c26fc3da4 --- /dev/null +++ b/packages/overlays/IconPackCircularThemePickerOverlay/Android.mk @@ -0,0 +1,31 @@ +# +# Copyright 2019, The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_RRO_THEME := IconPackCircularThemePicker +LOCAL_CERTIFICATE := platform +LOCAL_PRODUCT_MODULE := true + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res + +LOCAL_PACKAGE_NAME := IconPackCircularThemePickerOverlay +LOCAL_SDK_VERSION := current + +include $(BUILD_RRO_PACKAGE) diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/AndroidManifest.xml b/packages/overlays/IconPackCircularThemePickerOverlay/AndroidManifest.xml new file mode 100644 index 000000000000..eae7de8f5284 --- /dev/null +++ b/packages/overlays/IconPackCircularThemePickerOverlay/AndroidManifest.xml @@ -0,0 +1,24 @@ +<!-- +/** + * Copyright (c) 2019, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.theme.icon_pack.circular.themepicker" + android:versionCode="1" + android:versionName="1.0"> + <overlay android:targetPackage="com.android.wallpaper" android:category="android.theme.customization.icon_pack.themepicker" android:priority="1"/> + <application android:label="Circular" android:hasCode="false"/> +</manifest> diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_add_24px.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_add_24px.xml new file mode 100644 index 000000000000..900aaa0371b4 --- /dev/null +++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_add_24px.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M20.25,11.25h-7.5v-7.5C12.75,3.34,12.41,3,12,3s-0.75,0.34-0.75,0.75v7.5h-7.5C3.34,11.25,3,11.59,3,12 s0.34,0.75,0.75,0.75h7.5v7.5c0,0.41,0.34,0.75,0.75,0.75s0.75-0.34,0.75-0.75v-7.5h7.5c0.41,0,0.75-0.34,0.75-0.75 S20.66,11.25,20.25,11.25z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_close_24px.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_close_24px.xml new file mode 100644 index 000000000000..ddfb980bea88 --- /dev/null +++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_close_24px.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M20.03,3.97c-0.29-0.29-0.77-0.29-1.06,0L12,10.94L5.03,3.97c-0.29-0.29-0.77-0.29-1.06,0s-0.29,0.77,0,1.06L10.94,12 l-6.97,6.97c-0.29,0.29-0.29,0.77,0,1.06c0.15,0.15,0.34,0.22,0.53,0.22s0.38-0.07,0.53-0.22L12,13.06l6.97,6.97 c0.15,0.15,0.34,0.22,0.53,0.22s0.38-0.07,0.53-0.22c0.29-0.29,0.29-0.77,0-1.06L13.06,12l6.97-6.97 C20.32,4.74,20.32,4.26,20.03,3.97z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_colorize_24px.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_colorize_24px.xml new file mode 100644 index 000000000000..f572af605268 --- /dev/null +++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_colorize_24px.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M17.59,3.59l-2.97,2.97l-2.09-2.09c-0.29-0.29-0.77-0.29-1.06,0c-0.29,0.29-0.29,0.77,0,1.06l1.5,1.5l-9.68,9.68 C3.11,16.89,3,17.15,3,17.41V21h3.59c0.27,0,0.52-0.11,0.71-0.29l9.68-9.68l1.5,1.5c0.15,0.15,0.34,0.22,0.53,0.22 c0.19,0,0.38-0.07,0.53-0.22c0.29-0.29,0.29-0.77,0-1.06l-2.09-2.09l2.97-2.97c0.78-0.78,0.78-2.05,0-2.83 C19.63,2.8,18.37,2.8,17.59,3.59z M6.38,19.5H4.5v-1.88l9.5-9.5L15.88,10L6.38,19.5z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_delete_24px.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_delete_24px.xml new file mode 100644 index 000000000000..a87186bf2966 --- /dev/null +++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_delete_24px.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M9,20h6c1.66,0,3-1.34,3-3V6h0.5c0.41,0,0.75-0.34,0.75-0.75S18.91,4.5,18.5,4.5H18h-3l-1-1h-4l-1,1H6H5.5 c-0.41,0-0.75,0.34-0.75,0.75S5.09,6,5.5,6H6v11C6,18.66,7.34,20,9,20z M16.5,6v11c0,0.83-0.67,1.5-1.5,1.5H9 c-0.83,0-1.5-0.67-1.5-1.5V6H16.5z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M13.97,16c0.41,0,0.75-0.34,0.75-0.75v-6.5c0-0.41-0.34-0.75-0.75-0.75s-0.75,0.34-0.75,0.75v6.5 C13.22,15.66,13.55,16,13.97,16z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M10,16c0.41,0,0.75-0.34,0.75-0.75v-6.5C10.75,8.34,10.41,8,10,8S9.25,8.34,9.25,8.75v6.5C9.25,15.66,9.59,16,10,16z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_font.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_font.xml new file mode 100644 index 000000000000..edaf3c7e0b42 --- /dev/null +++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_font.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M18,2H6C3.8,2,2,3.8,2,6v12c0,2.2,1.8,4,4,4h12c2.2,0,4-1.8,4-4V6C22,3.8,20.2,2,18,2z M20.5,18c0,1.38-1.12,2.5-2.5,2.5H6 c-1.38,0-2.5-1.12-2.5-2.5V6c0-1.38,1.12-2.5,2.5-2.5h12c1.38,0,2.5,1.12,2.5,2.5V18z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M10.69,6L6.2,18h2.5l1-2.87h4.59L15.3,18h2.5L13.29,6H10.69z M10.43,13.06l1.51-4.46h0.13l1.49,4.46H10.43z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_clock.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_clock.xml new file mode 100644 index 000000000000..2884d71cefea --- /dev/null +++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_clock.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M22,12c0-5.52-4.48-10-10-10C6.48,2,2,6.48,2,12s4.48,10,10,10C17.52,22,22,17.52,22,12z M12,20.5 c-4.69,0-8.5-3.81-8.5-8.5S7.31,3.5,12,3.5c4.69,0,8.5,3.81,8.5,8.5S16.69,20.5,12,20.5z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M12.75,11.69V5.78c0-0.41-0.34-0.75-0.75-0.75s-0.75,0.34-0.75,0.75v6.53l3.78,3.78c0.15,0.15,0.34,0.22,0.53,0.22 s0.38-0.07,0.53-0.22c0.29-0.29,0.29-0.77,0-1.06L12.75,11.69z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_grid.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_grid.xml new file mode 100644 index 000000000000..d50dbd4e0cac --- /dev/null +++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_grid.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M2.75,16.75C2.34,16.75,2,17.09,2,17.5s0.34,0.75,0.75,0.75h3v3C5.75,21.66,6.09,22,6.5,22s0.75-0.34,0.75-0.75v-3h4v3 c0,0.41,0.34,0.75,0.75,0.75s0.75-0.34,0.75-0.75v-3h4v3c0,0.41,0.34,0.75,0.75,0.75s0.75-0.34,0.75-0.75v-3h3 c0.41,0,0.75-0.34,0.75-0.75s-0.34-0.75-0.75-0.75h-3v-4h3c0.41,0,0.75-0.34,0.75-0.75s-0.34-0.75-0.75-0.75h-3v-4h3 C21.66,7.25,22,6.91,22,6.5s-0.34-0.75-0.75-0.75h-3v-3C18.25,2.34,17.91,2,17.5,2s-0.75,0.34-0.75,0.75v3h-4v-3 C12.75,2.34,12.41,2,12,2s-0.75,0.34-0.75,0.75v3h-4v-3C7.25,2.34,6.91,2,6.5,2S5.75,2.34,5.75,2.75v3h-3C2.34,5.75,2,6.09,2,6.5 s0.34,0.75,0.75,0.75h3v4h-3C2.34,11.25,2,11.59,2,12s0.34,0.75,0.75,0.75h3v4H2.75z M16.75,16.75h-4v-4h4V16.75z M16.75,7.25v4h-4 v-4H16.75z M7.25,7.25h4v4h-4V7.25z M7.25,12.75h4v4h-4V12.75z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_theme.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_theme.xml new file mode 100644 index 000000000000..7375bc931665 --- /dev/null +++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_theme.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M8,14h1v5c0,1.66,1.34,3,3,3s3-1.34,3-3v-5h1c1.66,0,3-1.34,3-3V2H5v9C5,12.66,6.34,14,8,14z M16,12.5h-2.5V14v5 c0,0.83-0.67,1.5-1.5,1.5s-1.5-0.67-1.5-1.5v-5v-1.5H8c-0.83,0-1.5-0.67-1.5-1.5v-0.5h11V11C17.5,11.83,16.83,12.5,16,12.5z M9,3.5 v3c0,0.41,0.34,0.75,0.75,0.75S10.5,6.91,10.5,6.5v-3h2.75v3c0,0.41,0.34,0.75,0.75,0.75s0.75-0.34,0.75-0.75v-3h2.75V9h-11V3.5H9z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_wallpaper.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_wallpaper.xml new file mode 100644 index 000000000000..bdb74424c82e --- /dev/null +++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_nav_wallpaper.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M 16 6.75 C 16.6903559373 6.75 17.25 7.30964406271 17.25 8 C 17.25 8.69035593729 16.6903559373 9.25 16 9.25 C 15.3096440627 9.25 14.75 8.69035593729 14.75 8 C 14.75 7.30964406271 15.3096440627 6.75 16 6.75 Z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M11,3.76c0-0.41-0.34-0.75-0.75-0.75H6.84C4.72,3.01,3,4.74,3,6.85v3.4C3,10.66,3.34,11,3.75,11s0.75-0.34,0.75-0.75v-3.4 c0-1.29,1.05-2.33,2.34-2.33h3.41C10.66,4.51,11,4.18,11,3.76z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M10.25,19.5H6.84c-1.29,0-2.34-1.05-2.34-2.34v-3.41C4.5,13.34,4.16,13,3.75,13S3,13.34,3,13.75v3.41 C3,19.28,4.72,21,6.84,21h3.41c0.41,0,0.75-0.34,0.75-0.75S10.66,19.5,10.25,19.5z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M20.25,13c-0.41,0-0.75,0.34-0.75,0.75v3.41c0,1.29-1.05,2.34-2.34,2.34h-3.41c-0.41,0-0.75,0.34-0.75,0.75 S13.34,21,13.75,21h3.41c2.12,0,3.84-1.72,3.84-3.84v-3.41C21,13.34,20.66,13,20.25,13z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M17.16,3h-3.41C13.34,3,13,3.34,13,3.75s0.34,0.75,0.75,0.75h3.41c1.29,0,2.34,1.05,2.34,2.34v3.41 c0,0.41,0.34,0.75,0.75,0.75S21,10.66,21,10.25V6.84C21,4.72,19.28,3,17.16,3z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M12.89,15.29l-1.41-1.82c-0.8-1.04-2.37-1.03-3.17,0.01l-2.7,3.51l12.83-0.01l-1.88-2.35c-0.79-0.99-2.29-1-3.1-0.03 L12.89,15.29z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_shapes_24px.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_shapes_24px.xml new file mode 100644 index 000000000000..b7e6bf9e9a12 --- /dev/null +++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_shapes_24px.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M19,9h-1.5v1.5H19c0.83,0,1.5,0.67,1.5,1.5v7c0,0.83-0.67,1.5-1.5,1.5h-8c-0.83,0-1.5-0.67-1.5-1.5v-1.5H8V19 c0,1.66,1.34,3,3,3h8c1.66,0,3-1.34,3-3v-7C22,10.34,20.66,9,19,9z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M9,16c3.87,0,7-3.13,7-7c0-3.87-3.13-7-7-7C5.13,2,2,5.13,2,9C2,12.87,5.13,16,9,16z M3.5,9c0-3.03,2.47-5.5,5.5-5.5 s5.5,2.47,5.5,5.5s-2.47,5.5-5.5,5.5S3.5,12.03,3.5,9z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_tune.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_tune.xml new file mode 100644 index 000000000000..9c8821152466 --- /dev/null +++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_tune.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="20dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="20dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M20.25,4.75h-2.4C17.55,4.02,16.84,3.5,16,3.5c-1.1,0-2,0.9-2,2c0,1.1,0.9,2,2,2c0.84,0,1.55-0.52,1.85-1.25h2.4 C20.66,6.25,21,5.91,21,5.5S20.66,4.75,20.25,4.75z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M11.98,4.75H3.75C3.34,4.75,3,5.09,3,5.5s0.34,0.75,0.75,0.75h8.23c0.01,0,0.01,0,0.02,0V4.75 C11.99,4.75,11.99,4.75,11.98,4.75z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M20.25,17.75h-5.4c-0.3-0.73-1.01-1.25-1.85-1.25c-1.1,0-2,0.9-2,2c0,1.1,0.9,2,2,2c0.84,0,1.55-0.52,1.85-1.25h5.4 c0.41,0,0.75-0.34,0.75-0.75S20.66,17.75,20.25,17.75z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M8.98,17.75H3.75C3.34,17.75,3,18.09,3,18.5s0.34,0.75,0.75,0.75h5.23c0.01,0,0.01,0,0.02,0v-1.49 C8.99,17.75,8.99,17.75,8.98,17.75z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M7,10c-0.84,0-1.55,0.52-1.85,1.25h-1.4C3.34,11.25,3,11.59,3,12s0.34,0.75,0.75,0.75h1.4C5.45,13.48,6.16,14,7,14 c1.1,0,2-0.9,2-2C9,10.9,8.1,10,7,10z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M20.25,11.25h-9.23c-0.01,0-0.01,0-0.02,0v1.49c0.01,0,0.01,0,0.02,0h9.23c0.41,0,0.75-0.34,0.75-0.75 S20.66,11.25,20.25,11.25z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_wifi_24px.xml b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_wifi_24px.xml new file mode 100644 index 000000000000..fde996584f57 --- /dev/null +++ b/packages/overlays/IconPackCircularThemePickerOverlay/res/drawable/ic_wifi_24px.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M 12 17 C 12.8284271247 17 13.5 17.6715728753 13.5 18.5 C 13.5 19.3284271247 12.8284271247 20 12 20 C 11.1715728753 20 10.5 19.3284271247 10.5 18.5 C 10.5 17.6715728753 11.1715728753 17 12 17 Z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M19.42,11.84c-0.19,0-0.38-0.07-0.53-0.22C17.05,9.77,14.6,8.75,12,8.75s-5.05,1.02-6.89,2.86 c-0.29,0.29-0.77,0.29-1.06,0c-0.29-0.29-0.29-0.77,0-1.06C6.17,8.43,9,7.25,12,7.25s5.83,1.17,7.95,3.3 c0.29,0.29,0.29,0.77,0,1.06C19.8,11.76,19.61,11.84,19.42,11.84z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M22.61,8.65c-0.19,0-0.38-0.07-0.53-0.22C19.38,5.74,15.81,4.25,12,4.25S4.62,5.74,1.92,8.43c-0.29,0.29-0.77,0.29-1.06,0 s-0.29-0.77,0-1.06C3.84,4.39,7.79,2.75,12,2.75s8.16,1.64,11.14,4.61c0.29,0.29,0.29,0.77,0,1.06 C22.99,8.57,22.8,8.65,22.61,8.65z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M16.25,15c-0.19,0-0.38-0.07-0.53-0.22c-1-0.99-2.32-1.53-3.73-1.53s-2.73,0.54-3.73,1.53c-0.29,0.29-0.77,0.29-1.06-0.01 s-0.29-0.77,0.01-1.06c1.28-1.27,2.98-1.96,4.78-1.96s3.5,0.7,4.78,1.96c0.29,0.29,0.3,0.77,0.01,1.06 C16.64,14.93,16.45,15,16.25,15z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackFilledAndroidOverlay/Android.mk b/packages/overlays/IconPackFilledAndroidOverlay/Android.mk index e0db3a27cdbe..3036f7df9f1e 100644 --- a/packages/overlays/IconPackFilledAndroidOverlay/Android.mk +++ b/packages/overlays/IconPackFilledAndroidOverlay/Android.mk @@ -17,7 +17,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_RRO_THEME := IconPackFilledAndroid -LOCAL_CERTIFICATE := platform + LOCAL_PRODUCT_MODULE := true LOCAL_SRC_FILES := $(call all-subdir-java-files) diff --git a/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_bluetooth_transient_animation.xml b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_bluetooth_transient_animation.xml new file mode 100644 index 000000000000..e884bd3bc249 --- /dev/null +++ b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_bluetooth_transient_animation.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@*android:drawable/ic_bluetooth_transient_animation_drawable"> + <target android:name="_R_G_L_1_G_D_0_P_0" android:animation="@*android:anim/ic_bluetooth_transient_animation_0" /> + <target android:name="_R_G_L_0_G_D_0_P_0" android:animation="@*android:anim/ic_bluetooth_transient_animation_1" /> + <target android:name="time_group" android:animation="@*android:anim/ic_bluetooth_transient_animation_2" /> +</animated-vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_bluetooth_transient_animation_drawable.xml b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_bluetooth_transient_animation_drawable.xml new file mode 100644 index 000000000000..79107d8f6214 --- /dev/null +++ b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_bluetooth_transient_animation_drawable.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" + android:width="24dp" android:viewportHeight="24" + android:viewportWidth="24"> + <group android:name="_R_G"> + <group android:name="_R_G_L_2_G" android:translateX="3.099" + android:translateY="1.6400000000000006"> + <path android:name="_R_G_L_2_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M10.41 10.36 C10.41,10.36 14.16,6.62 14.16,6.62 C14.57,6.21 14.57,5.55 14.16,5.14 C14.16,5.14 9.69,0.67 9.69,0.67 C9.69,0.67 9.66,0.64 9.66,0.64 C9.24,0.25 8.58,0.27 8.18,0.69 C8,0.88 7.9,1.14 7.9,1.4 C7.9,1.4 7.9,7.84 7.9,7.84 C7.9,7.84 3.85,3.79 3.85,3.79 C3.44,3.38 2.79,3.38 2.38,3.79 C1.97,4.2 1.97,4.85 2.38,5.26 C2.38,5.26 7.47,10.36 7.47,10.36 C7.47,10.36 2.38,15.45 2.38,15.45 C1.97,15.86 1.97,16.51 2.38,16.92 C2.79,17.33 3.44,17.33 3.85,16.92 C3.85,16.92 7.9,12.87 7.9,12.87 C7.9,12.87 7.9,19.32 7.9,19.32 C7.9,19.89 8.37,20.36 8.94,20.36 C9.2,20.36 9.46,20.26 9.65,20.08 C9.65,20.08 9.7,20.03 9.7,20.03 C9.7,20.03 14.16,15.57 14.16,15.57 C14.57,15.16 14.57,14.5 14.16,14.09 C14.16,14.09 10.41,10.36 10.41,10.36c M9.89 3.73 C9.89,3.73 12.04,5.88 12.04,5.88 C12.04,5.88 9.89,8.03 9.89,8.03 C9.89,8.03 9.89,3.73 9.89,3.73c M9.89 16.98 C9.89,16.98 9.89,12.68 9.89,12.68 C9.89,12.68 12.04,14.83 12.04,14.83 C12.04,14.83 9.89,16.98 9.89,16.98c "/> + </group> + <group android:name="_R_G_L_1_G" android:translateX="3.099" + android:translateY="1.6400000000000006"> + <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M16.96 9.3 C16.95,9.3 16.95,9.29 16.95,9.28 C16.36,8.71 15.42,8.72 14.84,9.3 C14.84,9.3 14.83,9.31 14.83,9.31 C14.25,9.9 14.25,10.84 14.84,11.42 C15.42,12.01 16.37,12.01 16.96,11.42 C17.55,10.84 17.55,9.89 16.96,9.3c "/> + </group> + <group android:name="_R_G_L_0_G" android:translateX="3.099" + android:translateY="1.6400000000000006"> + <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M2.96 9.3 C2.96,9.3 2.95,9.29 2.95,9.29 C2.36,8.71 1.42,8.71 0.84,9.3 C0.84,9.3 0.83,9.31 0.83,9.31 C0.25,9.9 0.25,10.84 0.84,11.42 C1.42,12.01 2.37,12.01 2.96,11.42 C3.55,10.83 3.55,9.89 2.96,9.3c "/> + </group> + </group> + <group android:name="time_group"/> +</vector> diff --git a/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_hotspot_transient_animation.xml b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_hotspot_transient_animation.xml new file mode 100644 index 000000000000..54738c083ca3 --- /dev/null +++ b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_hotspot_transient_animation.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@*android:drawable/ic_hotspot_transient_animation_drawable"> + <target android:name="_R_G_L_0_G_D_0_P_0" android:animation="@*android:anim/ic_hotspot_transient_animation_2"/> + <target android:name="_R_G_L_0_G_D_1_P_0" android:animation="@*android:anim/ic_hotspot_transient_animation_1"/> + <target android:name="_R_G_L_0_G_D_2_P_0" android:animation="@*android:anim/ic_hotspot_transient_animation_0"/> + <target android:name="time_group" android:animation="@*android:anim/ic_hotspot_transient_animation_3"/> +</animated-vector> diff --git a/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_hotspot_transient_animation_drawable.xml b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_hotspot_transient_animation_drawable.xml new file mode 100644 index 000000000000..737b5222c181 --- /dev/null +++ b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_hotspot_transient_animation_drawable.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" + android:width="24dp" android:viewportHeight="24" + android:viewportWidth="24"> + <group android:name="_R_G"> + <group android:name="_R_G_L_0_G" android:translateX="1.545" + android:translateY="2.1449999999999996"> + <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M9.31 0.92 C4.67,1.43 0.92,5.26 0.5,9.92 C0.25,12.64 1.1,15.17 2.65,17.1 C3.02,17.56 3.71,17.6 4.13,17.18 C4.49,16.82 4.52,16.25 4.21,15.86 C2.81,14.11 2.12,11.76 2.61,9.25 C3.22,6.12 5.75,3.6 8.89,3 C14,2.04 18.45,5.92 18.45,10.86 C18.45,12.75 17.79,14.48 16.69,15.86 C16.37,16.25 16.41,16.81 16.77,17.17 C16.77,17.17 16.77,17.17 16.77,17.17 C17.19,17.59 17.89,17.56 18.26,17.09 C19.64,15.39 20.45,13.22 20.45,10.86 C20.45,4.96 15.34,0.25 9.31,0.92c "/> + <path android:name="_R_G_L_0_G_D_1_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M13.91 14.31 C13.91,14.31 13.92,14.32 13.92,14.32 C14.35,14.75 15.08,14.71 15.42,14.21 C16.07,13.25 16.45,12.1 16.45,10.85 C16.45,7.11 13.01,4.16 9.12,5 C6.88,5.49 5.08,7.3 4.6,9.54 C4.22,11.28 4.61,12.92 5.48,14.2 C5.83,14.71 6.56,14.75 6.99,14.32 C6.99,14.32 7,14.31 7,14.31 C7.34,13.97 7.37,13.43 7.1,13.03 C6.6,12.26 6.36,11.32 6.49,10.29 C6.73,8.55 8.16,7.13 9.9,6.89 C12.36,6.56 14.46,8.46 14.46,10.85 C14.46,11.66 14.22,12.4 13.81,13.02 C13.54,13.43 13.57,13.97 13.91,14.31c "/> + <path android:name="_R_G_L_0_G_D_2_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M12.46 10.86 C12.46,11.96 11.56,12.86 10.46,12.86 C9.35,12.86 8.46,11.96 8.46,10.86 C8.46,9.75 9.35,8.86 10.46,8.86 C11.56,8.86 12.46,9.75 12.46,10.86c "/> + </group> + </group> + <group android:name="time_group"/> +</vector> diff --git a/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation.xml b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation.xml new file mode 100644 index 000000000000..49d235b03be2 --- /dev/null +++ b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@*android:drawable/ic_signal_wifi_transient_animation_drawable"> + <target android:name="_R_G_L_7_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_0"/> + <target android:name="_R_G_L_6_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_1"/> + <target android:name="_R_G_L_6_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_2"/> + <target android:name="_R_G_L_5_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_1"/> + <target android:name="_R_G_L_5_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_2"/> + <target android:name="_R_G_L_4_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_3"/> + <target android:name="_R_G_L_4_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_4"/> + <target android:name="_R_G_L_3_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_3"/> + <target android:name="_R_G_L_3_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_4"/> + <target android:name="_R_G_L_2_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_5"/> + <target android:name="_R_G_L_2_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_6"/> + <target android:name="_R_G_L_1_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_5"/> + <target android:name="_R_G_L_1_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_6"/> + <target android:name="_R_G_L_0_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_7"/> + <target android:name="time_group" android:animation="@*android:anim/ic_signal_wifi_transient_animation_8"/> +</animated-vector> diff --git a/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation_drawable.xml b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation_drawable.xml new file mode 100644 index 000000000000..9fcb4795ad68 --- /dev/null +++ b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation_drawable.xml @@ -0,0 +1,72 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> + +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" android:width="24dp" android:viewportHeight="24" + android:viewportWidth="24"> + <group android:name="_R_G"> + <group android:name="_R_G_L_7_G" android:translateX="0.10000000000000142" + android:translateY="3"> + <path android:name="_R_G_L_7_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="0.3" android:fillType="nonZero" + android:pathData=" M23.56 5.11 C23.95,4.63 23.85,3.92 23.34,3.57 C21.57,2.36 17.45,0 11.9,0 C6.34,0 2.23,2.36 0.46,3.57 C-0.05,3.92 -0.15,4.63 0.23,5.11 C0.23,5.11 11.06,18.6 11.06,18.6 C11.48,19.13 12.29,19.13 12.72,18.6 C12.72,18.6 23.56,5.11 23.56,5.11c "/> + </group> + <group android:name="_R_G_L_6_G" android:translateX="0.10000000000000142" + android:translateY="3"> + <path android:name="_R_G_L_6_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="0.3" android:fillType="nonZero" + android:pathData=" M23.56 5.11 C23.95,4.63 23.85,3.92 23.34,3.57 C21.57,2.36 17.45,0 11.9,0 C6.34,0 2.23,2.36 0.46,3.57 C-0.05,3.92 -0.15,4.63 0.23,5.11 C0.23,5.11 11.06,18.6 11.06,18.6 C11.48,19.13 12.29,19.13 12.72,18.6 C12.72,18.6 23.56,5.11 23.56,5.11c "/> + </group> + <group android:name="_R_G_L_5_G" android:translateX="5.81" + android:translateY="12.747"> + <path android:name="_R_G_L_5_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M7.01 8.85 C7.01,8.85 12.12,2.5 12.12,2.5 C10.48,1.05 8.37,0.25 6.19,0.25 C3.91,0.25 1.84,1.1 0.25,2.5 C0.25,2.5 5.35,8.85 5.35,8.85 C5.78,9.38 6.58,9.38 7.01,8.85c "/> + </group> + <group android:name="_R_G_L_4_G" android:translateX="0.10000000000000142" + android:translateY="3"> + <path android:name="_R_G_L_4_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="0.3" android:fillType="nonZero" + android:pathData=" M23.56 5.11 C23.95,4.63 23.85,3.92 23.34,3.57 C21.57,2.36 17.45,0 11.9,0 C6.34,0 2.23,2.36 0.46,3.57 C-0.05,3.92 -0.15,4.63 0.23,5.11 C0.23,5.11 11.06,18.6 11.06,18.6 C11.48,19.13 12.29,19.13 12.72,18.6 C12.72,18.6 23.56,5.11 23.56,5.11c "/> + </group> + <group android:name="_R_G_L_3_G" android:translateX="3.9290000000000003" + android:translateY="9.75"> + <path android:name="_R_G_L_3_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M8.89 11.85 C8.89,11.85 15.88,3.15 15.88,3.15 C13.78,1.35 11.06,0.25 8.07,0.25 C5.08,0.25 2.35,1.35 0.25,3.16 C0.25,3.16 7.23,11.86 7.23,11.86 C7.66,12.38 8.46,12.38 8.89,11.85c "/> + </group> + <group android:name="_R_G_L_2_G" android:translateX="0.10000000000000142" + android:translateY="3"> + <path android:name="_R_G_L_2_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="0.3" android:fillType="nonZero" + android:pathData=" M23.56 5.11 C23.95,4.63 23.85,3.92 23.34,3.57 C21.57,2.36 17.45,0 11.9,0 C6.34,0 2.23,2.36 0.46,3.57 C-0.05,3.92 -0.15,4.63 0.23,5.11 C0.23,5.11 11.06,18.6 11.06,18.6 C11.48,19.13 12.29,19.13 12.72,18.6 C12.72,18.6 23.56,5.11 23.56,5.11c "/> + </group> + <group android:name="_R_G_L_1_G" android:translateX="2.6799999999999997" + android:translateY="7.748"> + <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M10.14 13.85 C10.14,13.85 18.39,3.59 18.39,3.59 C15.86,1.43 12.65,0.25 9.32,0.25 C5.86,0.25 2.69,1.51 0.25,3.6 C0.25,3.6 8.48,13.86 8.48,13.86 C8.91,14.38 9.71,14.38 10.14,13.85c "/> + </group> + <group android:name="_R_G_L_0_G" android:translateX="-0.3000000000000007" + android:translateY="2.75"> + <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M12.3 0.25 C6.74,0.25 2.63,2.61 0.86,3.82 C0.35,4.17 0.25,4.88 0.63,5.36 C0.63,5.36 11.46,18.85 11.46,18.85 C11.88,19.38 12.69,19.38 13.12,18.85 C13.12,18.85 23.96,5.36 23.96,5.36 C24.35,4.88 24.25,4.17 23.74,3.82 C21.97,2.61 17.85,0.25 12.3,0.25c "/> + </group> + </group> + <group android:name="time_group"/> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackFilledAndroidOverlay/res/values/config.xml b/packages/overlays/IconPackFilledAndroidOverlay/res/values/config.xml new file mode 100644 index 000000000000..6b59b6265c54 --- /dev/null +++ b/packages/overlays/IconPackFilledAndroidOverlay/res/values/config.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2019, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="config_batterymeterPerimeterPath" translatable="false"> + M 4,0 V 2 H 2.33 C 1.6,2 1,2.6 1,3.33 V 18.66 C 1,19.4 1.6,20 2.33,20 H 9.66 C 10.4,20 11,19.4 11,18.67 V 3.33 C 11,2.6 10.4,2 9.67,2 H 8 V 0 Z + </string> + <string name="config_batterymeterErrorPerimeterPath" translatable="false"> + M 3.5,0 V 0.5 1.5 H 2.3301 C 1.3261,1.5 0.5,2.3261 0.5,3.3301 V 18.16 C 0.5,19.17 1.3261,20 2.3301,20 H 9.6602 C 10.67,20 11.5,19.174 11.5,18.17 V 3.3301 C 11.5,2.3261 10.674,1.5 9.6699,1.5 H 8.5 V 0 Z M 9.1698,2.9999 C 9.6259,2.9999 9.9999,3.374 9.9999,3.83 V 17.67 C 9.9999,18.126 9.6299,18.5 9.1601,18.5 H 2.83 C 2.3741,18.5 2,18.13 2,17.66 V 3.83 C 2,3.374 2.3741,2.9999 2.83,2.9999 Z + </string> + <string name="config_batterymeterFillMask" translatable="false"> + M 4,0 V 2 H 2.33 C 1.6,2 1,2.6 1,3.33 V 18.66 C 1,19.4 1.6,20 2.33,20 H 9.66 C 10.4,20 11,19.4 11,18.67 V 3.33 C 11,2.6 10.4,2 9.67,2 H 8 V 0 Z + </string> + <string name="config_batterymeterBoltPath" translatable="false"> + M 8.58,10 C 8.77,10 8.89,10.2 8.8,10.37 L 5.94,15.74 C 5.7,16.19 5,16.02 5,15.5 V 12 H 3.42 C 3.23,12 3.11,11.8 3.2,11.63 L 6.06,6.26 C 6.3,5.81 7,5.98 7,6.5 V 10 Z + </string> + <string name="config_batterymeterPowersavePath" translatable="false"> + M 9,11 C 9,11.55 8.55,12 8,12 H 7 V 13 C 7,13.55 6.55,14 6,14 5.45,14 5,13.55 5,13 V 12 H 4 C 3.45,12 3,11.55 3,11 3,10.45 3.45,10.005 4,10 H 5 V 9 C 5,8.45 5.45,8 6,8 6.55,8 7,8.45 7,9 V 10 H 8 C 8.55,10 9,10.45 9,11 Z + </string> + <bool name="config_batterymeterDualTone">true</bool> +</resources> diff --git a/packages/overlays/IconPackFilledLauncherOverlay/Android.mk b/packages/overlays/IconPackFilledLauncherOverlay/Android.mk index d2e5b605b6b0..2460fa4675ff 100644 --- a/packages/overlays/IconPackFilledLauncherOverlay/Android.mk +++ b/packages/overlays/IconPackFilledLauncherOverlay/Android.mk @@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_RRO_THEME := IconPackFilledLauncher -LOCAL_CERTIFICATE := platform + LOCAL_PRODUCT_MODULE := true LOCAL_SRC_FILES := $(call all-subdir-java-files) diff --git a/packages/overlays/IconPackFilledSettingsOverlay/Android.mk b/packages/overlays/IconPackFilledSettingsOverlay/Android.mk index 0443560a573e..3cc071d732e1 100644 --- a/packages/overlays/IconPackFilledSettingsOverlay/Android.mk +++ b/packages/overlays/IconPackFilledSettingsOverlay/Android.mk @@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_RRO_THEME := IconPackFilledSettings -LOCAL_CERTIFICATE := platform + LOCAL_PRODUCT_MODULE := true LOCAL_SRC_FILES := $(call all-subdir-java-files) diff --git a/packages/overlays/IconPackFilledSystemUIOverlay/Android.mk b/packages/overlays/IconPackFilledSystemUIOverlay/Android.mk index 2506132e3058..f0276927b8e2 100644 --- a/packages/overlays/IconPackFilledSystemUIOverlay/Android.mk +++ b/packages/overlays/IconPackFilledSystemUIOverlay/Android.mk @@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_RRO_THEME := IconPackFilledSystemUI -LOCAL_CERTIFICATE := platform + LOCAL_PRODUCT_MODULE := true LOCAL_SRC_FILES := $(call all-subdir-java-files) diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/Android.mk b/packages/overlays/IconPackFilledThemePickerOverlay/Android.mk new file mode 100644 index 000000000000..6d15603663b5 --- /dev/null +++ b/packages/overlays/IconPackFilledThemePickerOverlay/Android.mk @@ -0,0 +1,31 @@ +# +# Copyright 2019, The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_RRO_THEME := IconPackFilledThemePicker +LOCAL_CERTIFICATE := platform +LOCAL_PRODUCT_MODULE := true + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res + +LOCAL_PACKAGE_NAME := IconPackFilledThemePickerOverlay +LOCAL_SDK_VERSION := current + +include $(BUILD_RRO_PACKAGE) diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/AndroidManifest.xml b/packages/overlays/IconPackFilledThemePickerOverlay/AndroidManifest.xml new file mode 100644 index 000000000000..35023ab9653e --- /dev/null +++ b/packages/overlays/IconPackFilledThemePickerOverlay/AndroidManifest.xml @@ -0,0 +1,24 @@ +<!-- +/** + * Copyright (c) 2019, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.theme.icon_pack.filled.themepicker" + android:versionCode="1" + android:versionName="1.0"> + <overlay android:targetPackage="com.android.wallpaper" android:category="android.theme.customization.icon_pack.themepicker" android:priority="1"/> + <application android:label="Filled" android:hasCode="false"/> +</manifest> diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_add_24px.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_add_24px.xml new file mode 100644 index 000000000000..1768723b65e9 --- /dev/null +++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_add_24px.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M5,13h6v6c0,0.55,0.45,1,1,1s1-0.45,1-1v-6h6c0.55,0,1-0.45,1-1s-0.45-1-1-1h-6V5c0-0.55-0.45-1-1-1s-1,0.45-1,1v6H5 c-0.55,0-1,0.45-1,1S4.45,13,5,13z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_close_24px.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_close_24px.xml new file mode 100644 index 000000000000..4bfff2cb3ad8 --- /dev/null +++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_close_24px.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M5.7,18.3c0.39,0.39,1.02,0.39,1.41,0L12,13.41l4.89,4.89c0.39,0.39,1.02,0.39,1.41,0s0.39-1.02,0-1.41L13.41,12l4.89-4.89 c0.38-0.38,0.38-1.02,0-1.4c-0.39-0.39-1.02-0.39-1.41,0c0,0,0,0,0,0L12,10.59L7.11,5.7c-0.39-0.39-1.02-0.39-1.41,0 s-0.39,1.02,0,1.41L10.59,12L5.7,16.89C5.31,17.28,5.31,17.91,5.7,18.3z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_colorize_24px.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_colorize_24px.xml new file mode 100644 index 000000000000..aa3a925b50c8 --- /dev/null +++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_colorize_24px.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M11.21,5.2L11.21,5.2c-0.39,0.39-0.39,1.02,0,1.41l0.71,0.71l-8.63,8.63C3.11,16.14,3,16.4,3,16.66V20c0,0.55,0.45,1,1,1 h3.34c0.27,0,0.52-0.11,0.71-0.29l8.63-8.63l0.72,0.72c0.39,0.39,1.02,0.39,1.41,0c0.39-0.39,0.39-1.02,0-1.41l-1.21-1.22L20,7.75 c0.78-0.78,0.78-2.05,0-2.83L19.08,4c-0.78-0.78-2.05-0.78-2.83,0l-2.41,2.41L12.61,5.2C12.23,4.81,11.6,4.81,11.21,5.2z M14.98,10.94L6.92,19H5v-1.92l8.06-8.06L14.98,10.94z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_delete_24px.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_delete_24px.xml new file mode 100644 index 000000000000..94c63118cf76 --- /dev/null +++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_delete_24px.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M18,4h-2.5l-0.71-0.71C14.61,3.11,14.35,3,14.09,3H9.9C9.64,3,9.38,3.11,9.2,3.29L8.49,4h-2.5c-0.55,0-1,0.45-1,1 s0.45,1,1,1h12c0.55,0,1-0.45,1-1C19,4.45,18.55,4,18,4z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M6,19c0,1.1,0.9,2,2,2h8c1.1,0,2-0.9,2-2V7H6V19z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_font.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_font.xml new file mode 100644 index 000000000000..760382385f79 --- /dev/null +++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_font.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M 12.07 8.6 L 11.94 8.6 L 10.43 13.06 L 13.56 13.06 Z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M19,2H5C3.35,2,2,3.35,2,5v14c0,1.65,1.35,3,3,3h14c1.65,0,3-1.35,3-3V5C22,3.35,20.65,2,19,2z M15.3,18l-1.01-2.87H9.7 L8.7,18H6.2l4.49-12h2.6l4.51,12H15.3z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_clock.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_clock.xml new file mode 100644 index 000000000000..11260159e3bd --- /dev/null +++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_clock.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M11.99,22C17.52,22,22,17.52,22,12c0-5.52-4.48-10-10.01-10C6.47,2,2,6.48,2,12C2,17.52,6.47,22,11.99,22z M11,6.82 c0-0.4,0.25-0.72,0.75-0.72s0.75,0.32,0.75,0.72v5.43l3.87,2.3c0.01,0,0.01,0.01,0.02,0.01c0.33,0.21,0.44,0.64,0.23,0.98 c-0.2,0.34-0.64,0.44-0.98,0.24L11,13V6.82z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_grid.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_grid.xml new file mode 100644 index 000000000000..0397b6c3744c --- /dev/null +++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_grid.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M22,7c0.55,0,1-0.45,1-1s-0.45-1-1-1h-3V2c0-0.55-0.45-1-1-1s-1,0.45-1,1v3h-4V2c0-0.55-0.45-1-1-1s-1,0.45-1,1v3H7V2 c0-0.55-0.45-1-1-1S5,1.45,5,2v3H2C1.45,5,1,5.45,1,6s0.45,1,1,1h3v4H2c-0.55,0-1,0.45-1,1s0.45,1,1,1h3v4H2c-0.55,0-1,0.45-1,1 s0.45,1,1,1h3v3c0,0.55,0.45,1,1,1s1-0.45,1-1v-3h4v3c0,0.55,0.45,1,1,1s1-0.45,1-1v-3h4v3c0,0.55,0.45,1,1,1s1-0.45,1-1v-3h3 c0.55,0,1-0.45,1-1s-0.45-1-1-1h-3v-4h3c0.55,0,1-0.45,1-1s-0.45-1-1-1h-3V7H22z M11,17H7v-4h4V17z M11,11H7V7h4V11z M17,17h-4v-4 h4V17z M17,11h-4V7h4V11z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_theme.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_theme.xml new file mode 100644 index 000000000000..6f0462cd9932 --- /dev/null +++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_theme.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M18,2H6C5.45,2,5,2.45,5,3v9c0,1.65,1.35,3,3,3h2v6c0,0.55,0.45,1,1,1h2c0.55,0,1-0.45,1-1v-6h2c1.65,0,3-1.35,3-3V3 C19,2.45,18.55,2,18,2z M17,9H7V4h2v2c0,0.55,0.45,1,1,1s1-0.45,1-1V4h2v2c0,0.55,0.45,1,1,1c0.55,0,1-0.45,1-1V4h2V9z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_wallpaper.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_wallpaper.xml new file mode 100644 index 000000000000..ea195ca209e7 --- /dev/null +++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_nav_wallpaper.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M 15.5 7 C 16.3284271247 7 17 7.67157287525 17 8.5 C 17 9.32842712475 16.3284271247 10 15.5 10 C 14.6715728753 10 14 9.32842712475 14 8.5 C 14 7.67157287525 14.6715728753 7 15.5 7 Z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M3.5,11h1C4.78,11,5,10.78,5,10.5V5h5.5C10.78,5,11,4.78,11,4.5v-1C11,3.22,10.78,3,10.5,3H5C3.9,3,3,3.9,3,5v5.5 C3,10.78,3.22,11,3.5,11z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M19,3h-5.5C13.22,3,13,3.22,13,3.5v1C13,4.78,13.22,5,13.5,5H19v5.5c0,0.28,0.22,0.5,0.5,0.5h1c0.28,0,0.5-0.22,0.5-0.5V5 C21,3.9,20.1,3,19,3z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M20.5,13h-1c-0.28,0-0.5,0.22-0.5,0.5V19h-5.5c-0.28,0-0.5,0.22-0.5,0.5v1c0,0.28,0.22,0.5,0.5,0.5H19c1.1,0,2-0.9,2-2 v-5.5C21,13.22,20.78,13,20.5,13z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M10.5,19H5v-5.5C5,13.22,4.78,13,4.5,13h-1C3.22,13,3,13.22,3,13.5V19c0,1.1,0.9,2,2,2h5.5c0.28,0,0.5-0.22,0.5-0.5v-1 C11,19.22,10.78,19,10.5,19z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M10.78,12.98c-0.4-0.5-1.16-0.5-1.56,0l-2.57,3.21C6.39,16.51,6.62,17,7.04,17H17c0.41,0,0.65-0.47,0.4-0.8l-1.6-2.13 c-0.4-0.53-1.2-0.53-1.6,0l-1.23,1.64L10.78,12.98z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_shapes_24px.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_shapes_24px.xml new file mode 100644 index 000000000000..cea09b56dd1d --- /dev/null +++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_shapes_24px.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M20,9h-3v2h3v9H10v-2H8v2c0,1.1,0.9,2,2,2h10c1.1,0,2-0.9,2-2v-9C22,9.9,21.1,9,20,9z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M15,9c0-1.07-0.25-2.09-0.68-3C13.2,3.64,10.79,2,8,2C4.13,2,1,5.13,1,9c0,2.79,1.64,5.2,4,6.32C5.91,15.75,6.93,16,8,16 C11.87,16,15,12.87,15,9z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_tune.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_tune.xml new file mode 100644 index 000000000000..ae03b51f7010 --- /dev/null +++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_tune.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="20dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="20dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M8,9c-0.55,0-1,0.45-1,1v1H4c-0.55,0-1,0.45-1,1c0,0.55,0.45,1,1,1h3v1c0,0.55,0.45,1,1,1s1-0.45,1-1v-4 C9,9.45,8.55,9,8,9z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M20,5h-3V4c0-0.55-0.45-1-1-1s-1,0.45-1,1v4c0,0.55,0.45,1,1,1s1-0.45,1-1V7h3c0.55,0,1-0.45,1-1C21,5.45,20.55,5,20,5z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M20,11h-9v2h9c0.55,0,1-0.45,1-1C21,11.45,20.55,11,20,11z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M20,17h-7v-1c0-0.55-0.45-1-1-1s-1,0.45-1,1v4c0,0.55,0.45,1,1,1s1-0.45,1-1v-1h7c0.55,0,1-0.45,1-1S20.55,17,20,17z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M4,7h9V5H4C3.45,5,3,5.45,3,6C3,6.55,3.45,7,4,7z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M3,18c0,0.55,0.45,1,1,1h5v-2H4C3.45,17,3,17.45,3,18z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_wifi_24px.xml b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_wifi_24px.xml new file mode 100644 index 000000000000..03e142e2be35 --- /dev/null +++ b/packages/overlays/IconPackFilledThemePickerOverlay/res/drawable/ic_wifi_24px.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M11.29,19.29c0.39,0.39,1.03,0.4,1.42,0L14,18c0.47-0.47,0.38-1.28-0.22-1.58C13.25,16.15,12.64,16,12,16 c-0.64,0-1.24,0.15-1.77,0.41c-0.59,0.29-0.69,1.11-0.22,1.58L11.29,19.29z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M17.6,14.39l0.71-0.71c0.42-0.42,0.39-1.12-0.08-1.5C16.52,10.82,14.35,10,12,10c-2.34,0-4.5,0.81-6.21,2.17 c-0.47,0.37-0.51,1.07-0.09,1.49l0.71,0.71c0.35,0.36,0.92,0.39,1.32,0.08C8.91,13.54,10.39,13,12,13c1.61,0,3.1,0.55,4.29,1.47 C16.69,14.78,17.25,14.75,17.6,14.39z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M21.83,10.16l0.71-0.71c0.42-0.42,0.38-1.09-0.06-1.48C19.68,5.5,16.01,4,12,4C8.01,4,4.36,5.49,1.56,7.94 C1.12,8.33,1.08,9,1.49,9.41l0.71,0.71c0.37,0.37,0.96,0.4,1.35,0.06C5.81,8.2,8.77,7,12,7c3.25,0,6.22,1.22,8.49,3.22 C20.88,10.56,21.47,10.53,21.83,10.16z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackRoundedAndroidOverlay/Android.mk b/packages/overlays/IconPackRoundedAndroidOverlay/Android.mk index 2937fb862618..c6ad4ac04092 100644 --- a/packages/overlays/IconPackRoundedAndroidOverlay/Android.mk +++ b/packages/overlays/IconPackRoundedAndroidOverlay/Android.mk @@ -17,7 +17,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_RRO_THEME := IconPackRoundedAndroid -LOCAL_CERTIFICATE := platform + LOCAL_PRODUCT_MODULE := true LOCAL_SRC_FILES := $(call all-subdir-java-files) diff --git a/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_bluetooth_transient_animation.xml b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_bluetooth_transient_animation.xml new file mode 100644 index 000000000000..09e7360069c4 --- /dev/null +++ b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_bluetooth_transient_animation.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@*android:drawable/ic_bluetooth_transient_animation_drawable"> + <target android:name="_R_G_L_1_G_D_0_P_0" + android:animation="@*android:anim/ic_bluetooth_transient_animation_0"/> + <target android:name="_R_G_L_0_G_D_0_P_0" + android:animation="@*android:anim/ic_bluetooth_transient_animation_1"/> + <target android:name="time_group" + android:animation="@*android:anim/ic_bluetooth_transient_animation_2"/> +</animated-vector> diff --git a/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_bluetooth_transient_animation_drawable.xml b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_bluetooth_transient_animation_drawable.xml new file mode 100644 index 000000000000..e0f155a013ef --- /dev/null +++ b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_bluetooth_transient_animation_drawable.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" + android:width="24dp" android:viewportHeight="24" + android:viewportWidth="24"> + <group android:name="_R_G"> + <group android:name="_R_G_L_2_G" android:translateX="3.75" + android:translateY="1.4410000000000007"> + <path android:name="_R_G_L_2_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M13.78 5.28 C13.78,5.28 9.03,0.53 9.03,0.53 C8.82,0.31 8.49,0.25 8.21,0.37 C7.93,0.48 7.75,0.75 7.75,1.06 C7.75,1.06 7.75,8.75 7.75,8.75 C7.75,8.75 3.78,4.78 3.78,4.78 C3.49,4.49 3.01,4.49 2.72,4.78 C2.43,5.07 2.43,5.55 2.72,5.84 C2.72,5.84 7.44,10.56 7.44,10.56 C7.44,10.56 2.72,15.28 2.72,15.28 C2.43,15.57 2.43,16.05 2.72,16.34 C3.01,16.63 3.49,16.63 3.78,16.34 C3.78,16.34 7.75,12.37 7.75,12.37 C7.75,12.37 7.75,20.06 7.75,20.06 C7.75,20.36 7.93,20.64 8.21,20.75 C8.31,20.79 8.4,20.81 8.5,20.81 C8.7,20.81 8.89,20.73 9.03,20.59 C9.03,20.59 13.78,15.84 13.78,15.84 C14.07,15.55 14.07,15.07 13.78,14.78 C13.78,14.78 9.56,10.56 9.56,10.56 C9.56,10.56 13.78,6.34 13.78,6.34 C14.07,6.05 14.07,5.57 13.78,5.28c M12.19 15.31 C12.19,15.31 9.25,18.25 9.25,18.25 C9.25,18.25 9.25,12.37 9.25,12.37 C9.25,12.37 12.19,15.31 12.19,15.31c M9.25 8.75 C9.25,8.75 9.25,2.87 9.25,2.87 C9.25,2.87 12.19,5.81 12.19,5.81 C12.19,5.81 9.25,8.75 9.25,8.75c "/> + </group> + <group android:name="_R_G_L_1_G" android:translateX="3.75" + android:translateY="1.4410000000000007"> + <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M15.25 9.56 C14.7,9.56 14.25,10.01 14.25,10.56 C14.25,11.11 14.7,11.56 15.25,11.56 C15.8,11.56 16.25,11.11 16.25,10.56 C16.25,10.01 15.8,9.56 15.25,9.56c "/> + </group> + <group android:name="_R_G_L_0_G" android:translateX="3.75" + android:translateY="1.4410000000000007"> + <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M1.25 9.56 C0.7,9.56 0.25,10.01 0.25,10.56 C0.25,11.11 0.7,11.56 1.25,11.56 C1.8,11.56 2.25,11.11 2.25,10.56 C2.25,10.01 1.8,9.56 1.25,9.56c "/> + </group> + </group> + <group android:name="time_group"/> +</vector> +
\ No newline at end of file diff --git a/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_hotspot_transient_animation.xml b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_hotspot_transient_animation.xml new file mode 100644 index 000000000000..1317f66b4131 --- /dev/null +++ b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_hotspot_transient_animation.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@*android:drawable/ic_hotspot_transient_animation_drawable"> + <target android:name="_R_G_L_0_G_D_0_P_0" android:animation="@*android:anim/ic_hotspot_transient_animation_0"/> + <target android:name="_R_G_L_0_G_D_1_P_0" android:animation="@*android:anim/ic_hotspot_transient_animation_1"/> + <target android:name="_R_G_L_0_G_D_2_P_0" android:animation="@*android:anim/ic_hotspot_transient_animation_2"/> + <target android:name="time_group" android:animation="@*android:anim/ic_hotspot_transient_animation_3"/> +</animated-vector> diff --git a/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_hotspot_transient_animation_drawable.xml b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_hotspot_transient_animation_drawable.xml new file mode 100644 index 000000000000..6b87f63eb193 --- /dev/null +++ b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_hotspot_transient_animation_drawable.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> + +<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" + android:width="24dp" android:viewportHeight="24" + android:viewportWidth="24"> + <group android:name="_R_G"> + <group android:name="_R_G_L_0_G" android:translateX="2" + android:translateY="1.552999999999999"> + <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M8.5 10.95 C8.5,11.77 9.17,12.45 10,12.45 C10.83,12.45 11.5,11.77 11.5,10.95 C11.5,10.12 10.83,9.45 10,9.45 C9.17,9.45 8.5,10.12 8.5,10.95c "/> + <path android:name="_R_G_L_0_G_D_1_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M13.01 15.01 C13.3,15.31 13.77,15.31 14.07,15.01 C15.15,13.93 15.75,12.48 15.75,10.95 C15.75,9.41 15.15,7.97 14.07,6.88 C11.82,4.64 8.18,4.64 5.94,6.88 C4.85,7.97 4.25,9.41 4.25,10.95 C4.25,12.48 4.85,13.93 5.94,15.01 C6.08,15.16 6.27,15.23 6.47,15.23 C6.66,15.23 6.85,15.16 6.99,15.01 C7.29,14.72 7.29,14.25 6.99,13.95 C6.19,13.15 5.75,12.08 5.75,10.95 C5.75,9.81 6.19,8.74 6.99,7.94 C8.65,6.28 11.35,6.28 13.01,7.94 C13.81,8.74 14.25,9.81 14.25,10.95 C14.25,12.08 13.81,13.15 13.01,13.95 C12.71,14.25 12.71,14.72 13.01,15.01c "/> + <path android:name="_R_G_L_0_G_D_2_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M16.36 18.06 C16.56,18.06 16.75,17.99 16.89,17.84 C18.74,16 19.75,13.55 19.75,10.95 C19.75,8.34 18.74,5.89 16.89,4.05 C13.09,0.25 6.91,0.25 3.11,4.05 C1.26,5.89 0.25,8.34 0.25,10.95 C0.25,13.55 1.26,16 3.11,17.84 C3.4,18.13 3.87,18.13 4.17,17.84 C4.46,17.55 4.46,17.07 4.17,16.78 C2.61,15.22 1.75,13.15 1.75,10.95 C1.75,8.74 2.61,6.67 4.17,5.11 C7.38,1.9 12.62,1.9 15.83,5.11 C17.39,6.67 18.25,8.74 18.25,10.95 C18.25,13.15 17.39,15.22 15.83,16.78 C15.54,17.07 15.54,17.55 15.83,17.84 C15.98,17.99 16.17,18.06 16.36,18.06c "/> + </group> + </group> + <group android:name="time_group"/> +</vector> diff --git a/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation.xml b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation.xml new file mode 100644 index 000000000000..ae64e569f399 --- /dev/null +++ b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@*android:drawable/ic_signal_wifi_transient_animation_drawable"> + <target android:name="_R_G_L_4_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_0"/> + <target android:name="_R_G_L_3_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_1"/> + <target android:name="_R_G_L_3_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_2"/> + <target android:name="_R_G_L_2_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_3"/> + <target android:name="_R_G_L_2_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_4"/> + <target android:name="_R_G_L_1_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_5"/> + <target android:name="_R_G_L_1_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_6"/> + <target android:name="_R_G_L_0_G" android:animation="@*android:anim/ic_signal_wifi_transient_animation_7"/> + <target android:name="time_group" android:animation="@*android:anim/ic_signal_wifi_transient_animation_8"/> +</animated-vector> diff --git a/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation_drawable.xml b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation_drawable.xml new file mode 100644 index 000000000000..4a2505adee83 --- /dev/null +++ b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_signal_wifi_transient_animation_drawable.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" android:width="24dp" android:viewportHeight="24" + android:viewportWidth="24"> + <group android:name="_R_G"> + <group android:name="_R_G_L_4_G" android:translateX="0.10500000000000043" + android:translateY="1.7490000000000006"> + <path android:name="_R_G_L_4_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M11.9 0.25 C7.65,0.25 3.78,1.91 0.88,4.63 C0.3,5.17 0.25,6.07 0.75,6.68 C0.75,6.68 11.12,19.31 11.12,19.31 C11.32,19.55 11.61,19.67 11.9,19.67 C12.18,19.67 12.47,19.55 12.67,19.31 C12.67,19.31 23.04,6.68 23.04,6.68 C23.54,6.07 23.49,5.17 22.91,4.63 C20.01,1.91 16.14,0.25 11.9,0.25c M11.9 17.89 C11.9,17.89 1.91,5.73 1.91,5.73 C4.64,3.16 8.18,1.75 11.9,1.75 C15.61,1.75 19.15,3.16 21.88,5.73 C21.88,5.73 11.9,17.89 11.9,17.89c "/> + </group> + <group android:name="_R_G_L_3_G" android:translateX="0.10500000000000043" + android:translateY="1.75"> + <path android:name="_R_G_L_3_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M11.9 0.25 C7.65,0.25 3.78,1.91 0.88,4.63 C0.3,5.17 0.25,6.07 0.75,6.68 C0.75,6.68 7.48,14.87 7.48,14.87 C7.48,14.87 7.48,14.87 7.48,14.87 C7.48,14.87 8,15.51 8,15.51 C8,15.51 11.12,19.31 11.12,19.31 C11.32,19.55 11.61,19.67 11.9,19.67 C12.18,19.67 12.47,19.55 12.67,19.31 C12.67,19.31 15.79,15.51 15.79,15.51 C15.79,15.51 16.31,14.87 16.31,14.87 C16.31,14.87 16.31,14.87 16.31,14.87 C16.31,14.87 23.04,6.68 23.04,6.68 C23.54,6.07 23.49,5.17 22.91,4.63 C20.01,1.91 16.14,0.25 11.9,0.25c M15.67 13.29 C15.57,13.15 15.47,13.01 15.36,12.88 C14.54,11.88 13.3,11.24 11.9,11.24 C10.5,11.24 9.25,11.88 8.43,12.88 C8.32,13.01 8.22,13.15 8.12,13.29 C8.12,13.29 1.91,5.73 1.91,5.73 C4.64,3.16 8.18,1.75 11.9,1.75 C15.61,1.75 19.15,3.16 21.88,5.73 C21.88,5.73 15.67,13.29 15.67,13.29c "/> + </group> + <group android:name="_R_G_L_2_G" android:translateX="0.10500000000000043" + android:translateY="1.7490000000000006"> + <path android:name="_R_G_L_2_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M11.89 0.25 C7.65,0.25 3.77,1.91 0.88,4.63 C0.3,5.17 0.25,6.07 0.75,6.68 C0.75,6.68 11.12,19.31 11.12,19.31 C11.32,19.55 11.61,19.67 11.89,19.67 C12.18,19.67 12.47,19.55 12.67,19.31 C12.67,19.31 23.04,6.68 23.04,6.68 C23.54,6.07 23.48,5.17 22.91,4.63 C20.01,1.91 16.14,0.25 11.89,0.25c M17.61 10.93 C17.5,10.8 17.4,10.66 17.28,10.54 C15.92,9.12 14.01,8.24 11.89,8.24 C9.77,8.24 7.86,9.12 6.51,10.54 C6.39,10.66 6.29,10.8 6.18,10.92 C6.18,10.92 1.91,5.73 1.91,5.73 C4.64,3.16 8.18,1.75 11.89,1.75 C15.6,1.75 19.15,3.16 21.88,5.73 C21.88,5.73 17.61,10.93 17.61,10.93c "/> + </group> + <group android:name="_R_G_L_1_G" android:translateX="0.10500000000000043" + android:translateY="1.7490000000000006"> + <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M11.9 0.25 C7.65,0.25 3.78,1.91 0.88,4.63 C0.3,5.17 0.25,6.07 0.75,6.68 C0.75,6.68 3.2,9.66 3.2,9.66 C3.2,9.66 3.2,9.66 3.2,9.66 C3.2,9.66 11.12,19.31 11.12,19.31 C11.52,19.8 12.27,19.8 12.67,19.31 C12.67,19.31 20.6,9.66 20.6,9.66 C20.6,9.66 20.59,9.66 20.59,9.66 C20.59,9.66 23.04,6.68 23.04,6.68 C23.54,6.07 23.49,5.17 22.91,4.63 C20.01,1.91 16.14,0.25 11.9,0.25c M19.6 8.5 C19.51,8.41 19.43,8.32 19.34,8.23 C17.41,6.37 14.8,5.24 11.9,5.24 C8.99,5.24 6.38,6.38 4.45,8.23 C4.36,8.32 4.28,8.41 4.19,8.51 C4.19,8.51 1.91,5.73 1.91,5.73 C4.64,3.16 8.18,1.75 11.9,1.75 C15.61,1.75 19.15,3.16 21.88,5.73 C21.88,5.73 19.6,8.5 19.6,8.5c "/> + </group> + <group android:name="_R_G_L_0_G" android:translateX="0.10500000000000043" + android:translateY="1.75"> + <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#000000" + android:fillAlpha="1" android:fillType="nonZero" + android:pathData=" M11.9 0.25 C7.65,0.25 3.78,1.91 0.88,4.63 C0.3,5.17 0.25,6.07 0.75,6.68 C0.75,6.68 11.12,19.31 11.12,19.31 C11.52,19.8 12.27,19.8 12.67,19.31 C12.67,19.31 23.04,6.68 23.04,6.68 C23.54,6.07 23.49,5.17 22.91,4.63 C20.01,1.91 16.14,0.25 11.9,0.25c "/> + </group> + </group> + <group android:name="time_group"/> +</vector> diff --git a/packages/overlays/IconPackRoundedAndroidOverlay/res/values/config.xml b/packages/overlays/IconPackRoundedAndroidOverlay/res/values/config.xml new file mode 100644 index 000000000000..ebcac82c695f --- /dev/null +++ b/packages/overlays/IconPackRoundedAndroidOverlay/res/values/config.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2019, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="config_batterymeterPerimeterPath" translatable="false"> + M 11,1.505 H 8 V 0.995 C 8,0.445 7.55,-0.005 7,-0.005 H 5 C 4.45,-0.005 4,0.445 4,0.995 V 1.505 H 1 C 0.45,1.505 0,1.955 0,2.505 V 19.005 C 0,19.555 0.45,20.005 1,20.005 H 11 C 11.55,20.005 12,19.555 12,19.005 V 2.505 C 12,1.955 11.543,1.505 11,1.505 Z M 10.5,18.505 H 1.5 V 3.005 H 10.5 Z + </string> + + <string name="config_batterymeterFillMask" translatable="false"> + M 10.5,18.505 H 1.5 V 3.005 H 10.5 Z + </string> + <string name="config_batterymeterBoltPath" translatable="false"> + M 3.92,11.5 H 5 V 15.01 C 5,15.17 5.13,15.26 5.25,15.26 5.33,15.26 5.42,15.22 5.47,15.13 L 8.3,9.87 C 8.39,9.7 8.27,9.5 8.08,9.5 H 7 V 5.99 C 7,5.83 6.87,5.74 6.75,5.74 6.67,5.74 6.58,5.78 6.53,5.87 L 3.7,11.13 C 3.61,11.3 3.73,11.5 3.92,11.5 Z + </string> + <string name="config_batterymeterPowersavePath" translatable="false"> + M 3.75,11.25 H 5.25 V 12.75 C 5.25,13.16 5.59,13.5 6,13.5 6.41,13.5 6.75,13.16 6.75,12.75 V 11.25 H 8.25 C 8.66,11.25 9,10.91 9,10.5 9,10.09 8.66,9.7499 8.25,9.7499 H 6.75 V 8.2499 C 6.75,7.8399 6.41,7.4999 6,7.4999 5.59,7.4999 5.2794,7.841 5.25,8.2499 V 9.7499 H 3.75 C 3.34,9.7499 3,10.09 3,10.5 3,10.91 3.3401,11.25 3.75,11.25 Z + </string> +</resources> diff --git a/packages/overlays/IconPackRoundedLauncherOverlay/Android.mk b/packages/overlays/IconPackRoundedLauncherOverlay/Android.mk index 7adfe3b88eb6..713e2819bb65 100644 --- a/packages/overlays/IconPackRoundedLauncherOverlay/Android.mk +++ b/packages/overlays/IconPackRoundedLauncherOverlay/Android.mk @@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_RRO_THEME := IconPackRoundedLauncher -LOCAL_CERTIFICATE := platform + LOCAL_PRODUCT_MODULE := true LOCAL_SRC_FILES := $(call all-subdir-java-files) diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/Android.mk b/packages/overlays/IconPackRoundedSettingsOverlay/Android.mk index 44ac6dd939dd..6c775190f548 100644 --- a/packages/overlays/IconPackRoundedSettingsOverlay/Android.mk +++ b/packages/overlays/IconPackRoundedSettingsOverlay/Android.mk @@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_RRO_THEME := IconPackRoundedSettings -LOCAL_CERTIFICATE := platform + LOCAL_PRODUCT_MODULE := true LOCAL_SRC_FILES := $(call all-subdir-java-files) diff --git a/packages/overlays/IconPackRoundedSystemUIOverlay/Android.mk b/packages/overlays/IconPackRoundedSystemUIOverlay/Android.mk index 2d34a54fb4de..4e21b41828c8 100644 --- a/packages/overlays/IconPackRoundedSystemUIOverlay/Android.mk +++ b/packages/overlays/IconPackRoundedSystemUIOverlay/Android.mk @@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_RRO_THEME := IconPackRoundedSystemUI -LOCAL_CERTIFICATE := platform + LOCAL_PRODUCT_MODULE := true LOCAL_SRC_FILES := $(call all-subdir-java-files) diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/Android.mk b/packages/overlays/IconPackRoundedThemePickerOverlay/Android.mk new file mode 100644 index 000000000000..ae48186e05ee --- /dev/null +++ b/packages/overlays/IconPackRoundedThemePickerOverlay/Android.mk @@ -0,0 +1,31 @@ +# +# Copyright 2019, The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_RRO_THEME := IconPackRoundedThemePicker +LOCAL_CERTIFICATE := platform +LOCAL_PRODUCT_MODULE := true + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res + +LOCAL_PACKAGE_NAME := IconPackRoundedThemePickerOverlay +LOCAL_SDK_VERSION := current + +include $(BUILD_RRO_PACKAGE) diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/AndroidManifest.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/AndroidManifest.xml new file mode 100644 index 000000000000..9a90a05713fe --- /dev/null +++ b/packages/overlays/IconPackRoundedThemePickerOverlay/AndroidManifest.xml @@ -0,0 +1,24 @@ +<!-- +/** + * Copyright (c) 2019, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.theme.icon_pack.rounded.themepicker" + android:versionCode="1" + android:versionName="1.0"> + <overlay android:targetPackage="com.android.wallpaper" android:category="android.theme.customization.icon_pack.themepicker" android:priority="1"/> + <application android:label="Rounded" android:hasCode="false"/> +</manifest> diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_add_24px.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_add_24px.xml new file mode 100644 index 000000000000..707369ab3446 --- /dev/null +++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_add_24px.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M4.67,12.75h6.58v6.58C11.25,19.7,11.59,20,12,20s0.75-0.3,0.75-0.67v-6.58h6.58C19.7,12.75,20,12.41,20,12 s-0.3-0.75-0.67-0.75h-6.58V4.67C12.75,4.3,12.41,4,12,4s-0.75,0.3-0.75,0.67v6.58H4.67C4.3,11.25,4,11.59,4,12 S4.3,12.75,4.67,12.75z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_close_24px.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_close_24px.xml new file mode 100644 index 000000000000..1dca14d9de77 --- /dev/null +++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_close_24px.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M18.78,5.22c-0.29-0.29-0.77-0.29-1.06,0L12,10.94L6.28,5.22c-0.29-0.29-0.77-0.29-1.06,0s-0.29,0.77,0,1.06L10.94,12 l-5.72,5.72c-0.29,0.29-0.29,0.77,0,1.06C5.37,18.93,5.56,19,5.75,19s0.38-0.07,0.53-0.22L12,13.06l5.72,5.72 c0.15,0.15,0.34,0.22,0.53,0.22s0.38-0.07,0.53-0.22c0.29-0.29,0.29-0.77,0-1.06L13.06,12l5.72-5.72 C19.07,5.99,19.07,5.51,18.78,5.22z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_colorize_24px.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_colorize_24px.xml new file mode 100644 index 000000000000..5c21b23f0bc7 --- /dev/null +++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_colorize_24px.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M11.47,4.47c-0.29,0.29-0.29,0.77,0,1.06l1.5,1.5l-9.68,9.68C3.11,16.89,3,17.15,3,17.42l0,2.58c0,0.55,0.45,1,1,1L6.58,21 c0,0,0,0,0,0c0.27,0,0.52-0.11,0.71-0.29l9.68-9.68l1.5,1.5c0.15,0.15,0.34,0.22,0.53,0.22c0.19,0,0.38-0.07,0.53-0.22 c0.29-0.29,0.29-0.77,0-1.06l-2.09-2.09l2.97-2.97c0.78-0.78,0.78-2.05,0-2.83c-0.78-0.78-2.05-0.78-2.83,0l-2.97,2.97l-2.09-2.09 C12.24,4.17,11.77,4.17,11.47,4.47z M6.38,19.5L4.5,19.49l0-1.87l9-9l1.88,1.88L6.38,19.5z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_delete_24px.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_delete_24px.xml new file mode 100644 index 000000000000..48a430fc420b --- /dev/null +++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_delete_24px.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M20,4h-1h-4c0-0.55-0.45-1-1-1h-4C9.45,3,9,3.45,9,4H5H4C3.59,4,3.25,4.34,3.25,4.75S3.59,5.5,4,5.5h1V18 c0,1.66,1.34,3,3,3h8c1.66,0,3-1.34,3-3V5.5h1c0.41,0,0.75-0.34,0.75-0.75S20.41,4,20,4z M17.5,18c0,0.83-0.67,1.5-1.5,1.5H8 c-0.83,0-1.5-0.67-1.5-1.5V5.5h11V18z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M14.25,8c-0.41,0-0.75,0.34-0.75,0.75v7.5c0,0.41,0.34,0.75,0.75,0.75S15,16.66,15,16.25v-7.5C15,8.34,14.66,8,14.25,8z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M9.75,8C9.34,8,9,8.34,9,8.75v7.5C9,16.66,9.34,17,9.75,17s0.75-0.34,0.75-0.75v-7.5C10.5,8.34,10.16,8,9.75,8z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_font.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_font.xml new file mode 100644 index 000000000000..bbae929cf76e --- /dev/null +++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_font.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M20,2H4C2.9,2,2,2.9,2,4v16c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V4C22,2.9,21.1,2,20,2z M20.5,20c0,0.27-0.23,0.5-0.5,0.5H4 c-0.27,0-0.5-0.23-0.5-0.5V4c0-0.27,0.23-0.5,0.5-0.5h16c0.27,0,0.5,0.23,0.5,0.5V20z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M10.69,6L6.2,18h2.5l1-2.87h4.59L15.3,18h2.5L13.29,6H10.69z M10.43,13.06l1.51-4.46h0.13l1.49,4.46H10.43z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_clock.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_clock.xml new file mode 100644 index 000000000000..9c9d663f850c --- /dev/null +++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_clock.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M19.07,4.93c-3.91-3.9-10.24-3.9-14.14,0.01s-3.9,10.24,0.01,14.14s10.24,3.9,14.14-0.01C20.95,17.2,22,14.65,22,12 C22,9.35,20.94,6.81,19.07,4.93z M18,18c-1.6,1.59-3.76,2.48-6.02,2.48c-4.69-0.01-8.49-3.83-8.48-8.52 c0.01-4.69,3.83-8.49,8.52-8.48c4.69,0.01,8.49,3.83,8.48,8.52C20.49,14.25,19.6,16.41,18,18z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M12.75,11.69V6.75C12.75,6.34,12.41,6,12,6s-0.75,0.34-0.75,0.75V12c0,0.2,0.08,0.39,0.22,0.53l3.25,3.25 c0.15,0.15,0.34,0.22,0.53,0.22s0.38-0.07,0.53-0.22c0.29-0.29,0.29-0.77,0-1.06L12.75,11.69z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_grid.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_grid.xml new file mode 100644 index 000000000000..c81ca1e4bde1 --- /dev/null +++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_grid.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M20.25,7C20.66,7,21,6.66,21,6.25S20.66,5.5,20.25,5.5H18.5V3.75C18.5,3.34,18.16,3,17.75,3S17,3.34,17,3.75V5.5h-4.25 V3.75C12.75,3.34,12.41,3,12,3s-0.75,0.34-0.75,0.75V5.5H7V3.75C7,3.34,6.66,3,6.25,3S5.5,3.34,5.5,3.75V5.5H3.75 C3.34,5.5,3,5.84,3,6.25S3.34,7,3.75,7H5.5v4.25H3.75C3.34,11.25,3,11.59,3,12s0.34,0.75,0.75,0.75H5.5V17H3.75 C3.34,17,3,17.34,3,17.75s0.34,0.75,0.75,0.75H5.5v1.75C5.5,20.66,5.84,21,6.25,21S7,20.66,7,20.25V18.5h4.25v1.75 c0,0.41,0.34,0.75,0.75,0.75s0.75-0.34,0.75-0.75V18.5H17v1.75c0,0.41,0.34,0.75,0.75,0.75s0.75-0.34,0.75-0.75V18.5h1.75 c0.41,0,0.75-0.34,0.75-0.75S20.66,17,20.25,17H18.5v-4.25h1.75c0.41,0,0.75-0.34,0.75-0.75s-0.34-0.75-0.75-0.75H18.5V7H20.25z M7,7h4.25v4.25H7V7z M7,17v-4.25h4.25V17H7z M17,17h-4.25v-4.25H17V17z M17,11.25h-4.25V7H17V11.25z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_theme.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_theme.xml new file mode 100644 index 000000000000..32d154b0a8ea --- /dev/null +++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_theme.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M11.5,22h1c1.1,0,2-0.9,2-2v-6H16c1.66,0,3-1.34,3-3V3c0-0.55-0.45-1-1-1H6C5.45,2,5,2.45,5,3v8c0,1.66,1.34,3,3,3h1.5v6 C9.5,21.1,10.4,22,11.5,22z M9,3.5v2.75C9,6.66,9.34,7,9.75,7s0.75-0.34,0.75-0.75V3.5h3v2.75C13.5,6.66,13.84,7,14.25,7 S15,6.66,15,6.25V3.5h2.5V9h-11V3.5H9z M8,12.5c-0.83,0-1.5-0.67-1.5-1.5v-0.5h11V11c0,0.83-0.67,1.5-1.5,1.5h-3V14v6 c0,0.28-0.22,0.5-0.5,0.5h-1c-0.28,0-0.5-0.22-0.5-0.5v-6v-1.5H8z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_wallpaper.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_wallpaper.xml new file mode 100644 index 000000000000..21daf9d00e89 --- /dev/null +++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_nav_wallpaper.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M 16 6.75 C 16.6903559373 6.75 17.25 7.30964406271 17.25 8 C 17.25 8.69035593729 16.6903559373 9.25 16 9.25 C 15.3096440627 9.25 14.75 8.69035593729 14.75 8 C 14.75 7.30964406271 15.3096440627 6.75 16 6.75 Z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M3.75,10.75c0.41,0,0.75-0.34,0.75-0.75V4.75c0-0.14,0.11-0.25,0.25-0.25H10c0.41,0,0.75-0.34,0.75-0.75S10.41,3,10,3 H4.75C3.79,3,3,3.79,3,4.75V10C3,10.41,3.34,10.75,3.75,10.75z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M10,19.5H4.75c-0.14,0-0.25-0.11-0.25-0.25V14c0-0.41-0.34-0.75-0.75-0.75S3,13.59,3,14v5.25C3,20.21,3.79,21,4.75,21H10 c0.41,0,0.75-0.34,0.75-0.75S10.41,19.5,10,19.5z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M20.25,13.25c-0.41,0-0.75,0.34-0.75,0.75v5.25c0,0.14-0.11,0.25-0.25,0.25H14c-0.41,0-0.75,0.34-0.75,0.75 S13.59,21,14,21h5.25c0.96,0,1.75-0.79,1.75-1.75V14C21,13.59,20.66,13.25,20.25,13.25z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M19.25,3H14c-0.41,0-0.75,0.34-0.75,0.75S13.59,4.5,14,4.5h5.25c0.14,0,0.25,0.11,0.25,0.25V10 c0,0.41,0.34,0.75,0.75,0.75S21,10.41,21,10V4.75C21,3.79,20.21,3,19.25,3z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M13.73,12.37l-2.6,3.35l-1.74-2.1c-0.2-0.25-0.58-0.24-0.78,0.01l-1.99,2.56C6.36,16.52,6.59,17,7.01,17h9.98 c0.41,0,0.65-0.47,0.4-0.8l-2.87-3.83C14.32,12.11,13.93,12.11,13.73,12.37z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_shapes_24px.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_shapes_24px.xml new file mode 100644 index 000000000000..19ce4e3ca7ad --- /dev/null +++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_shapes_24px.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M20,9h-2.51c0,0.1,0.01,0.2,0.01,0.3v1.2H20c0.28,0,0.5,0.22,0.5,0.5v9c0,0.28-0.22,0.5-0.5,0.5H10 c-0.28,0-0.5-0.22-0.5-0.5v-2.5H8V20c0,1.1,0.9,2,2,2h10c1.1,0,2-0.9,2-2v-9C22,9.9,21.1,9,20,9z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M16,9c0-3.87-3.13-7-7-7C5.13,2,2,5.13,2,9c0,3.87,3.13,7,7,7C12.87,16,16,12.87,16,9z M3.5,9c0-3.03,2.47-5.5,5.5-5.5 s5.5,2.47,5.5,5.5s-2.47,5.5-5.5,5.5S3.5,12.03,3.5,9z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_tune.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_tune.xml new file mode 100644 index 000000000000..2a56cc54bae8 --- /dev/null +++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_tune.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="20dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="20dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M3.75,4.75C3.34,4.75,3,5.09,3,5.5s0.34,0.75,0.75,0.75H14v-1.5H3.75z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M20.25,4.75H17v-1.5c0-0.41-0.34-0.75-0.75-0.75S15.5,2.84,15.5,3.25v4.5c0,0.41,0.34,0.75,0.75,0.75S17,8.16,17,7.75v-1.5 h3.25C20.66,6.25,21,5.91,21,5.5S20.66,4.75,20.25,4.75z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M20.25,11.25H10v1.5h10.25c0.41,0,0.75-0.34,0.75-0.75S20.66,11.25,20.25,11.25z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M3.75,17.75C3.34,17.75,3,18.09,3,18.5s0.34,0.75,0.75,0.75H10v-1.5H3.75z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M20.25,17.75H13v-1.5c0-0.41-0.34-0.75-0.75-0.75s-0.75,0.34-0.75,0.75v4.5c0,0.41,0.34,0.75,0.75,0.75S13,21.16,13,20.75 v-1.5h7.25c0.41,0,0.75-0.34,0.75-0.75S20.66,17.75,20.25,17.75z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M8.5,9.75C8.5,9.34,8.16,9,7.75,9S7,9.34,7,9.75v1.5H3.75C3.34,11.25,3,11.59,3,12s0.34,0.75,0.75,0.75H7v1.5 C7,14.66,7.34,15,7.75,15s0.75-0.34,0.75-0.75V9.75z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_wifi_24px.xml b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_wifi_24px.xml new file mode 100644 index 000000000000..0a1c3055870d --- /dev/null +++ b/packages/overlays/IconPackRoundedThemePickerOverlay/res/drawable/ic_wifi_24px.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp" > + <path + android:fillColor="@android:color/white" + android:pathData="M12,2.75C7.95,2.69,4.05,4.3,1.22,7.2C0.96,7.5,0.97,7.95,1.24,8.23C1.53,8.53,2,8.54,2.3,8.25c2.55-2.61,6.05-4.06,9.7-4 c3.65-0.06,7.17,1.4,9.72,4.02c0.28,0.27,0.73,0.28,1.03,0.01c0.31-0.28,0.33-0.75,0.05-1.06C19.96,4.32,16.06,2.69,12,2.75z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M15.78,14.82c0.05,0.06,0.1,0.11,0.17,0.15c0.34,0.23,0.81,0.14,1.04-0.21s0.14-0.81-0.21-1.04 c-2.64-2.64-6.91-2.64-9.55,0c-0.27,0.29-0.27,0.73,0,1.02c0.28,0.3,0.76,0.32,1.06,0.04h0.03c0,0,0,0,0.01-0.01 c2.05-2.05,5.37-2.04,7.42,0.01C15.75,14.8,15.76,14.81,15.78,14.82z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M 12 17 C 12.8284271247 17 13.5 17.6715728753 13.5 18.5 C 13.5 19.3284271247 12.8284271247 20 12 20 C 11.1715728753 20 10.5 19.3284271247 10.5 18.5 C 10.5 17.6715728753 11.1715728753 17 12 17 Z" /> + <path + android:fillColor="@android:color/white" + android:pathData="M20.03,11.79c0.3-0.29,0.3-0.77,0.01-1.06h-0.01c-2.12-2.18-5.01-3.44-8.04-3.5c-3.04,0.06-5.93,1.32-8.05,3.5 c-0.29,0.3-0.28,0.77,0.01,1.06c0.3,0.29,0.77,0.28,1.06-0.01c1.85-1.88,4.36-2.96,7-3c2.62,0.05,5.11,1.13,6.95,3 C19.25,12.07,19.73,12.08,20.03,11.79z" /> +</vector>
\ No newline at end of file diff --git a/packages/overlays/IconShapeRoundedRectOverlay/Android.mk b/packages/overlays/IconShapeRoundedRectOverlay/Android.mk index 08428d192fae..21cd011ef83c 100644 --- a/packages/overlays/IconShapeRoundedRectOverlay/Android.mk +++ b/packages/overlays/IconShapeRoundedRectOverlay/Android.mk @@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_RRO_THEME := IconShapeRoundedRect -LOCAL_CERTIFICATE := platform + LOCAL_PRODUCT_MODULE := true LOCAL_SRC_FILES := $(call all-subdir-java-files) diff --git a/packages/overlays/IconShapeSquareOverlay/Android.mk b/packages/overlays/IconShapeSquareOverlay/Android.mk index ceb745ae1429..c8728838303c 100644 --- a/packages/overlays/IconShapeSquareOverlay/Android.mk +++ b/packages/overlays/IconShapeSquareOverlay/Android.mk @@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_RRO_THEME := IconShapeSquare -LOCAL_CERTIFICATE := platform + LOCAL_PRODUCT_MODULE := true LOCAL_SRC_FILES := $(call all-subdir-java-files) diff --git a/packages/overlays/IconShapeSquircleOverlay/Android.mk b/packages/overlays/IconShapeSquircleOverlay/Android.mk index 34edc3b78b09..fa5fe6906dce 100644 --- a/packages/overlays/IconShapeSquircleOverlay/Android.mk +++ b/packages/overlays/IconShapeSquircleOverlay/Android.mk @@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_RRO_THEME := IconShapeSquircle -LOCAL_CERTIFICATE := platform + LOCAL_PRODUCT_MODULE := true LOCAL_SRC_FILES := $(call all-subdir-java-files) diff --git a/packages/overlays/IconShapeTeardropOverlay/Android.mk b/packages/overlays/IconShapeTeardropOverlay/Android.mk index 834a1c357c61..d5f01f39965b 100644 --- a/packages/overlays/IconShapeTeardropOverlay/Android.mk +++ b/packages/overlays/IconShapeTeardropOverlay/Android.mk @@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_RRO_THEME := IconShapeTeardrop -LOCAL_CERTIFICATE := platform + LOCAL_PRODUCT_MODULE := true LOCAL_SRC_FILES := $(call all-subdir-java-files) diff --git a/packages/overlays/NavigationBarMode2ButtonOverlay/Android.mk b/packages/overlays/NavigationBarMode2ButtonOverlay/Android.mk index 410d6d87c61d..be86ef2d3ff4 100644 --- a/packages/overlays/NavigationBarMode2ButtonOverlay/Android.mk +++ b/packages/overlays/NavigationBarMode2ButtonOverlay/Android.mk @@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_RRO_THEME := NavigationBarMode2Button -LOCAL_CERTIFICATE := platform + LOCAL_SRC_FILES := $(call all-subdir-java-files) diff --git a/packages/overlays/NavigationBarMode3ButtonOverlay/Android.mk b/packages/overlays/NavigationBarMode3ButtonOverlay/Android.mk index 2bc9a6aea9eb..f44a362b266d 100644 --- a/packages/overlays/NavigationBarMode3ButtonOverlay/Android.mk +++ b/packages/overlays/NavigationBarMode3ButtonOverlay/Android.mk @@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_RRO_THEME := NavigationBarMode3Button -LOCAL_CERTIFICATE := platform + LOCAL_SRC_FILES := $(call all-subdir-java-files) diff --git a/packages/overlays/NavigationBarModeGesturalOverlay/Android.mk b/packages/overlays/NavigationBarModeGesturalOverlay/Android.mk index 5f7e0eb62a46..02e2074accb7 100644 --- a/packages/overlays/NavigationBarModeGesturalOverlay/Android.mk +++ b/packages/overlays/NavigationBarModeGesturalOverlay/Android.mk @@ -18,7 +18,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_RRO_THEME := NavigationBarModeGestural -LOCAL_CERTIFICATE := platform + LOCAL_SRC_FILES := $(call all-subdir-java-files) diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto index 21c6035da038..fe92d45b297d 100644 --- a/proto/src/metrics_constants/metrics_constants.proto +++ b/proto/src/metrics_constants/metrics_constants.proto @@ -7206,6 +7206,13 @@ message MetricsEvent { // ACTION: Share Wi-Fi hotspot by generating a QR code ACTION_SETTINGS_SHARE_WIFI_HOTSPOT_QR_CODE = 1712; + // OPEN: Settings > Network & internet > Mobile network > Delete sim + DIALOG_DELETE_SIM_CONFIRMATION = 1713; + + // OPEN: Settings > Network & internet > Mobile network > Delete sim > (answer yes to + // confirmation) + DIALOG_DELETE_SIM_PROGRESS = 1714; + // ---- End Q Constants, all Q constants go above this line ---- // Add new aosp constants above this line. // END OF AOSP CONSTANTS diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java index a94d1dc7f243..a64f4e475b7d 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java @@ -94,6 +94,7 @@ import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; /** * Entry point service for autofill management. @@ -192,9 +193,9 @@ public final class AutofillManagerService mUi = new AutoFillUI(ActivityThread.currentActivityThread().getSystemUiContext()); mAm = LocalServices.getService(ActivityManagerInternal.class); - DeviceConfig.addOnPropertyChangedListener(DeviceConfig.NAMESPACE_AUTOFILL, + DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_AUTOFILL, ActivityThread.currentApplication().getMainExecutor(), - (namespace, key, value) -> onDeviceConfigChange(key)); + (properties) -> onDeviceConfigChange(properties.getKeyset())); setLogLevelFromSettings(); setMaxPartitionsFromSettings(); @@ -270,15 +271,17 @@ public final class AutofillManagerService } } - private void onDeviceConfigChange(@NonNull String key) { - switch (key) { - case AutofillManager.DEVICE_CONFIG_AUTOFILL_SMART_SUGGESTION_SUPPORTED_MODES: - case AutofillManager.DEVICE_CONFIG_AUGMENTED_SERVICE_IDLE_UNBIND_TIMEOUT: - case AutofillManager.DEVICE_CONFIG_AUGMENTED_SERVICE_REQUEST_TIMEOUT: - setDeviceConfigProperties(); - break; - default: - Slog.i(mTag, "Ignoring change on " + key); + private void onDeviceConfigChange(@NonNull Set<String> keys) { + for (String key : keys) { + switch (key) { + case AutofillManager.DEVICE_CONFIG_AUTOFILL_SMART_SUGGESTION_SUPPORTED_MODES: + case AutofillManager.DEVICE_CONFIG_AUGMENTED_SERVICE_IDLE_UNBIND_TIMEOUT: + case AutofillManager.DEVICE_CONFIG_AUGMENTED_SERVICE_REQUEST_TIMEOUT: + setDeviceConfigProperties(); + break; + default: + Slog.i(mTag, "Ignoring change on " + key); + } } } diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index f1963b37a6cf..386dec472019 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -57,6 +57,7 @@ import android.service.autofill.FillEventHistory; import android.service.autofill.FillEventHistory.Event; import android.service.autofill.FillResponse; import android.service.autofill.IAutoFillService; +import android.service.autofill.SaveInfo; import android.service.autofill.UserData; import android.util.ArrayMap; import android.util.ArraySet; @@ -595,8 +596,8 @@ final class AutofillManagerServiceImpl ArrayList<Session> previousSessions = null; for (int i = 0; i < size; i++) { final Session previousSession = mSessions.valueAt(i); - // TODO(b/113281366): only return sessions asked to be kept alive / add CTS test - if (previousSession.taskId == session.taskId && previousSession.id != session.id) { + if (previousSession.taskId == session.taskId && previousSession.id != session.id + && (previousSession.getSaveInfoFlagsLocked() & SaveInfo.FLAG_DELAY_SAVE) != 0) { if (previousSessions == null) { previousSessions = new ArrayList<>(size); } diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java index 3d392c70a1e7..80b0375a229d 100644 --- a/services/autofill/java/com/android/server/autofill/Helper.java +++ b/services/autofill/java/com/android/server/autofill/Helper.java @@ -211,25 +211,26 @@ public final class Helper { * Gets the {@link AutofillId} of the autofillable nodes in the {@code structure}. */ @NonNull - static ArraySet<AutofillId> getAutofillableIds(@NonNull AssistStructure structure) { - final ArraySet<AutofillId> ids = new ArraySet<>(); + static ArrayList<AutofillId> getAutofillIds(@NonNull AssistStructure structure, + boolean autofillableOnly) { + final ArrayList<AutofillId> ids = new ArrayList<>(); final int size = structure.getWindowNodeCount(); for (int i = 0; i < size; i++) { final WindowNode node = structure.getWindowNodeAt(i); - addAutofillableIds(node.getRootViewNode(), ids); + addAutofillableIds(node.getRootViewNode(), ids, autofillableOnly); } return ids; } private static void addAutofillableIds(@NonNull ViewNode node, - @NonNull ArraySet<AutofillId> ids) { - if (node.getAutofillType() != View.AUTOFILL_TYPE_NONE) { + @NonNull ArrayList<AutofillId> ids, boolean autofillableOnly) { + if (!autofillableOnly || node.getAutofillType() != View.AUTOFILL_TYPE_NONE) { ids.add(node.getAutofillId()); } final int size = node.getChildCount(); for (int i = 0; i < size; i++) { final ViewNode child = node.getChildAt(i); - addAutofillableIds(child, ids); + addAutofillableIds(child, ids, autofillableOnly); } } diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 9e73684d5f93..f35e464634e0 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -26,14 +26,12 @@ import static android.view.autofill.AutofillManager.ACTION_VIEW_ENTERED; import static android.view.autofill.AutofillManager.ACTION_VIEW_EXITED; import static android.view.autofill.AutofillManager.FLAG_SMART_SUGGESTION_SYSTEM; import static android.view.autofill.AutofillManager.getSmartSuggestionModeToString; -import static android.view.autofill.Helper.toList; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; import static com.android.server.autofill.Helper.getNumericValue; import static com.android.server.autofill.Helper.sDebug; import static com.android.server.autofill.Helper.sVerbose; import static com.android.server.autofill.Helper.toArray; -import static com.android.server.autofill.ViewState.STATE_RESTARTED_SESSION; import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS; import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE; @@ -284,7 +282,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * on autofilling the app. */ @GuardedBy("mLock") - private ArraySet<AutofillId> mAugmentedAutofillableIds; + private ArrayList<AutofillId> mAugmentedAutofillableIds; /** * When {@code true}, the session was created only to handle Augmented Autofill requests (i.e., @@ -337,6 +335,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return; } + final ArrayList<AutofillId> ids = Helper.getAutofillIds(structure, + /* autofillableOnly= */false); + for (int i = 0; i < ids.size(); i++) { + ids.get(i).setSessionId(Session.this.id); + } + // Flags used to start the session. int flags = structure.getFlags(); @@ -564,7 +568,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * Reads a new structure and then request a new fill response from the fill service. */ @GuardedBy("mLock") - private void requestNewFillResponseLocked(int flags) { + private void requestNewFillResponseLocked(@NonNull ViewState viewState, int newState, + int flags) { if (mForAugmentedAutofillOnly || (flags & FLAG_AUGMENTED_AUTOFILL_REQUEST) != 0) { // TODO(b/122858578): log metrics if (sVerbose) { @@ -575,6 +580,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState triggerAugmentedAutofillLocked(); return; } + viewState.setState(newState); int requestId; @@ -1202,9 +1208,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") @Nullable - private FillResponse getLastResponseLocked(@Nullable String logPrefix) { + private FillResponse getLastResponseLocked(@Nullable String logPrefixFmt) { + final String logPrefix = sDebug && logPrefixFmt != null + ? String.format(logPrefixFmt, this.id) + : null; if (mContexts == null) { - if (sDebug && logPrefix != null) Slog.d(TAG, logPrefix + ": no contexts"); + if (logPrefix != null) Slog.d(TAG, logPrefix + ": no contexts"); return null; } if (mResponses == null) { @@ -1240,6 +1249,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return response == null ? null : response.getSaveInfo(); } + @GuardedBy("mLock") + int getSaveInfoFlagsLocked() { + final SaveInfo saveInfo = getSaveInfoLocked(); + return saveInfo == null ? 0 : saveInfo.getFlags(); + } + /** * Generates a {@link android.service.autofill.FillEventHistory.Event#TYPE_CONTEXT_COMMITTED} * when necessary. @@ -1251,7 +1266,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState private void handleLogContextCommitted() { final FillResponse lastResponse; synchronized (mLock) { - lastResponse = getLastResponseLocked("logContextCommited()"); + lastResponse = getLastResponseLocked("logContextCommited(%s)"); } if (lastResponse == null) { @@ -1294,7 +1309,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") private void logContextCommittedLocked(@Nullable ArrayList<AutofillId> detectedFieldIds, @Nullable ArrayList<FieldClassification> detectedFieldClassifications) { - final FillResponse lastResponse = getLastResponseLocked("logContextCommited()"); + final FillResponse lastResponse = getLastResponseLocked("logContextCommited(%s)"); if (lastResponse == null) return; final int flags = lastResponse.getFlags(); @@ -1609,7 +1624,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState + id + " destroyed"); return false; } - final FillResponse response = getLastResponseLocked("showSaveLocked()"); + final FillResponse response = getLastResponseLocked("showSaveLocked(%s)"); final SaveInfo saveInfo = response == null ? null : response.getSaveInfo(); /* @@ -1623,13 +1638,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * - server didn't ask to keep session alive */ if (saveInfo == null) { - if (sVerbose) Slog.v(TAG, "showSaveLocked(): no saveInfo from service"); + if (sVerbose) Slog.v(TAG, "showSaveLocked(" + this.id + "): no saveInfo from service"); return true; } if ((saveInfo.getFlags() & SaveInfo.FLAG_DELAY_SAVE) != 0) { // TODO(b/113281366): log metrics - if (sDebug) Slog.v(TAG, "showSaveLocked(): service asked to delay save"); + if (sDebug) Slog.v(TAG, "showSaveLocked(" + this.id + "): service asked to delay save"); return false; } @@ -1961,7 +1976,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (node != null) { final AutofillValue value = node.getAutofillValue(); if (sDebug) { - Slog.d(TAG, "getValueFromContexts(" + autofillId + ") at " + i + ": " + value); + Slog.d(TAG, "getValueFromContexts(" + this.id + "/" + autofillId + ") at " + + i + ": " + value); } if (value != null && !value.isEmpty()) { return value; @@ -2065,7 +2081,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return; } - if (sVerbose) Slog.v(TAG, "callSaveLocked(): mViewStates=" + mViewStates); + if (sVerbose) Slog.v(TAG, "callSaveLocked(" + this.id + "): mViewStates=" + mViewStates); if (mContexts == null) { Slog.w(TAG, "callSaveLocked(): no contexts"); @@ -2108,15 +2124,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final ArrayList<FillContext> contexts; if (previousSessions != null) { if (sDebug) { - Slog.d(TAG, "mergeSessions(): Merging the content of " + previousSessions.size() - + " sessions for task " + taskId); + Slog.d(TAG, "mergeSessions(" + this.id + "): Merging the content of " + + previousSessions.size() + " sessions for task " + taskId); } contexts = new ArrayList<>(); for (int i = 0; i < previousSessions.size(); i++) { final Session previousSession = previousSessions.get(i); final ArrayList<FillContext> previousContexts = previousSession.mContexts; if (previousContexts == null) { - Slog.w(TAG, "mergeSessions(): Not merging null contexts from " + Slog.w(TAG, "mergeSessions(" + this.id + "): Not merging null contexts from " + previousSession.id); continue; } @@ -2124,14 +2140,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState previousSession.updateValuesForSaveLocked(); } if (sDebug) { - Slog.d(TAG, "mergeSessions(): adding " + previousContexts.size() + Slog.d(TAG, "mergeSessions(" + this.id + "): adding " + previousContexts.size() + " context from previous session #" + previousSession.id); } contexts.addAll(previousContexts); if (mClientState == null && previousSession.mClientState != null) { if (sDebug) { - Slog.d(TAG, "mergeSessions(): setting client state from previous session" - + previousSession.id); + Slog.d(TAG, "mergeSessions(" + this.id + "): setting client state from " + + "previous session" + previousSession.id); } mClientState = previousSession.mClientState; } @@ -2165,19 +2181,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @NonNull ViewState viewState, int flags) { if ((flags & FLAG_MANUAL_REQUEST) != 0) { if (sDebug) Slog.d(TAG, "Re-starting session on view " + id + " and flags " + flags); - viewState.setState(STATE_RESTARTED_SESSION); - requestNewFillResponseLocked(flags); + requestNewFillResponseLocked(viewState, ViewState.STATE_RESTARTED_SESSION, flags); return; } // If it's not, then check if it it should start a partition. if (shouldStartNewPartitionLocked(id)) { if (sDebug) { - Slog.d(TAG, "Starting partition for view id " + id + ": " + Slog.d(TAG, "Starting partition or augmented request for view id " + id + ": " + viewState.getStateAsString()); } - viewState.setState(ViewState.STATE_STARTED_PARTITION); - requestNewFillResponseLocked(flags); + requestNewFillResponseLocked(viewState, ViewState.STATE_STARTED_PARTITION, flags); } else { if (sVerbose) { Slog.v(TAG, "Not starting new partition for view " + id + ": " @@ -2250,9 +2264,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState + id + " destroyed"); return; } + id.setSessionId(this.id); if (sVerbose) { - Slog.v(TAG, "updateLocked(): id=" + id + ", action=" + actionAsString(action) - + ", flags=" + flags); + Slog.v(TAG, "updateLocked(" + this.id + "): id=" + id + ", action=" + + actionAsString(action) + ", flags=" + flags); } ViewState viewState = mViewStates.get(id); @@ -2283,8 +2298,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // View is triggering autofill. mCurrentViewId = viewState.id; viewState.update(value, virtualBounds, flags); - viewState.setState(ViewState.STATE_STARTED_SESSION); - requestNewFillResponseLocked(flags); + requestNewFillResponseLocked(viewState, ViewState.STATE_STARTED_SESSION, flags); break; case ACTION_VALUE_CHANGED: if (mCompatMode && (viewState.getState() & ViewState.STATE_URL_BAR) != 0) { @@ -2386,6 +2400,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState Slog.v(TAG, "entered on virtual child " + id + ": " + virtualBounds); } + // Update the view states first... + mCurrentViewId = viewState.id; + viewState.setCurrentValue(value); + if (mCompatMode && (viewState.getState() & ViewState.STATE_URL_BAR) != 0) { if (sDebug) Slog.d(TAG, "Ignoring VIEW_ENTERED on URL BAR (id=" + id + ")"); return; @@ -2397,10 +2415,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (sDebug) Slog.d(TAG, "updateLocked(" + id + "): augmented-autofillable"); - // Update the view states first... - mCurrentViewId = viewState.id; - viewState.setCurrentValue(value); - // ...then trigger the augmented autofill UI triggerAugmentedAutofillLocked(); return; @@ -2520,14 +2534,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } private void notifyUnavailableToClient(int sessionFinishedState, - @Nullable ArraySet<AutofillId> autofillableIds) { + @Nullable ArrayList<AutofillId> autofillableIds) { synchronized (mLock) { if (mCurrentViewId == null) return; try { if (mHasCallback) { mClient.notifyNoFillUi(id, mCurrentViewId, sessionFinishedState); } else if (sessionFinishedState != 0) { - mClient.setSessionFinished(sessionFinishedState, toList(autofillableIds)); + mClient.setSessionFinished(sessionFinishedState, autofillableIds); } } catch (RemoteException e) { Slog.e(TAG, "Error notifying client no fill UI: id=" + mCurrentViewId, e); @@ -2653,10 +2667,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final FillContext context = getFillContextByRequestIdLocked(requestId); - final ArraySet<AutofillId> autofillableIds; + final ArrayList<AutofillId> autofillableIds; if (context != null) { final AssistStructure structure = context.getStructure(); - autofillableIds = Helper.getAutofillableIds(structure); + autofillableIds = Helper.getAutofillIds(structure, /* autofillableOnly= */true); } else { Slog.w(TAG, "processNullResponseLocked(): no context for req " + requestId); autofillableIds = null; @@ -2762,7 +2776,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState remoteService.getComponentName().getPackageName()); mAugmentedRequestsLogs.add(log); - remoteService.onRequestAutofillLocked(id, mClient, taskId, mComponentName, mCurrentViewId, + final AutofillId focusedId = AutofillId.withoutSession(mCurrentViewId); + + remoteService.onRequestAutofillLocked(id, mClient, taskId, mComponentName, focusedId, currentValue); if (mAugmentedAutofillDestroyer == null) { @@ -3293,7 +3309,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState */ @GuardedBy("mLock") void removeSelfLocked() { - if (sVerbose) Slog.v(TAG, "removeSelfLocked(): " + mPendingSaveUi); + if (sVerbose) Slog.v(TAG, "removeSelfLocked(" + this.id + "): " + mPendingSaveUi); if (mDestroyed) { Slog.w(TAG, "Call to Session#removeSelfLocked() rejected - session: " + id + " destroyed"); diff --git a/services/autofill/java/com/android/server/autofill/ViewState.java b/services/autofill/java/com/android/server/autofill/ViewState.java index 33a2e5059ddf..e1b089cf28b2 100644 --- a/services/autofill/java/com/android/server/autofill/ViewState.java +++ b/services/autofill/java/com/android/server/autofill/ViewState.java @@ -214,7 +214,7 @@ final class ViewState { if (mDatasetId != null) { builder.append(", datasetId:" ).append(mDatasetId); } - builder.append("state:" ).append(getStateAsString()); + builder.append(", state:").append(getStateAsString()); if (mCurrentValue != null) { builder.append(", currentValue:" ).append(mCurrentValue); } diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java index 843aa741f2c3..73f5cb8326ea 100644 --- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java +++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java @@ -297,6 +297,9 @@ final class SaveUi { // First apply the unconditional transformations (if any) to the templates. final ArrayList<Pair<Integer, InternalTransformation>> transformations = customDescription.getTransformations(); + if (sVerbose) { + Slog.v(TAG, "applyCustomDescription(): transformations = " + transformations); + } if (transformations != null) { if (!InternalTransformation.batchApply(valueFinder, template, transformations)) { Slog.w(TAG, "could not apply main transformations on custom description"); @@ -345,6 +348,10 @@ final class SaveUi { // Apply batch updates (if any). final ArrayList<Pair<InternalValidator, BatchUpdates>> updates = customDescription.getUpdates(); + if (sVerbose) { + Slog.v(TAG, "applyCustomDescription(): view = " + customSubtitleView + + " updates=" + updates); + } if (updates != null) { final int size = updates.size(); if (sDebug) Slog.d(TAG, "custom description has " + size + " batch updates"); diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java index a2d3d4c25b1d..5c6258ffe4cb 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java @@ -50,6 +50,7 @@ import android.os.ShellCallback; import android.os.UserHandle; import android.os.UserManager; import android.provider.DeviceConfig; +import android.provider.DeviceConfig.Properties; import android.provider.Settings; import android.service.contentcapture.ActivityEvent.ActivityEventType; import android.util.ArraySet; @@ -131,9 +132,9 @@ public final class ContentCaptureManagerService extends com.android.internal.R.string.config_defaultContentCaptureService), UserManager.DISALLOW_CONTENT_CAPTURE, /*packageUpdatePolicy=*/ PACKAGE_UPDATE_POLICY_NO_REFRESH); - DeviceConfig.addOnPropertyChangedListener(DeviceConfig.NAMESPACE_CONTENT_CAPTURE, + DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_CONTENT_CAPTURE, ActivityThread.currentApplication().getMainExecutor(), - (namespace, key, value) -> onDeviceConfigChange(key, value)); + (properties) -> onDeviceConfigChange(properties)); setDeviceConfigProperties(); if (mDevCfgLogHistorySize > 0) { @@ -255,23 +256,25 @@ public final class ContentCaptureManagerService extends return enabled; } - private void onDeviceConfigChange(@NonNull String key, @Nullable String value) { - switch (key) { - case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED: - setDisabledByDeviceConfig(value); - return; - case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOGGING_LEVEL: - setLoggingLevelFromDeviceConfig(); - return; - case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_MAX_BUFFER_SIZE: - case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_FLUSH_FREQUENCY: - case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE: - case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_TEXT_CHANGE_FLUSH_FREQUENCY: - case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_UNBIND_TIMEOUT: - setFineTuneParamsFromDeviceConfig(); - return; - default: - Slog.i(mTag, "Ignoring change on " + key); + private void onDeviceConfigChange(@NonNull Properties properties) { + for (String key : properties.getKeyset()) { + switch (key) { + case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED: + setDisabledByDeviceConfig(properties.getString(key, null)); + return; + case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOGGING_LEVEL: + setLoggingLevelFromDeviceConfig(); + return; + case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_MAX_BUFFER_SIZE: + case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_FLUSH_FREQUENCY: + case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE: + case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_TEXT_CHANGE_FLUSH_FREQUENCY: + case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_UNBIND_TIMEOUT: + setFineTuneParamsFromDeviceConfig(); + return; + default: + Slog.i(mTag, "Ignoring change on " + key); + } } } diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java index a7921b5f3892..4399e4267fda 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java @@ -161,8 +161,14 @@ final class ContentCapturePerUserService @Override // from PerUserSystemService @GuardedBy("mLock") protected boolean updateLocked(boolean disabled) { - destroyLocked(); final boolean disabledStateChanged = super.updateLocked(disabled); + if (disabledStateChanged) { + // update session content capture enabled state. + for (int i = 0; i < mSessions.size(); i++) { + mSessions.valueAt(i).setContentCaptureEnabledLocked(!disabled); + } + } + destroyLocked(); updateRemoteServiceLocked(disabled); return disabledStateChanged; } @@ -542,7 +548,8 @@ final class ContentCapturePerUserService Slog.v(TAG, "setContentCaptureWhitelist(" + (packages == null ? "null_packages" : packages.size() + " packages") + ", " + (activities == null - ? "null_activities" : activities.size() + " activities") + ")"); + ? "null_activities" : activities.size() + " activities") + ")" + + " for user " + mUserId); } mMaster.mGlobalContentCaptureOptions.setWhitelist(mUserId, packages, activities); } diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java index d38dfd409439..2643db1d5851 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java @@ -16,6 +16,8 @@ package com.android.server.contentcapture; import static android.service.contentcapture.ContentCaptureService.setClientState; +import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_FALSE; +import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_TRUE; import static android.view.contentcapture.ContentCaptureSession.NO_SESSION_ID; import static android.view.contentcapture.ContentCaptureSession.STATE_ACTIVE; import static android.view.contentcapture.ContentCaptureSession.STATE_DISABLED; @@ -24,13 +26,16 @@ import static android.view.contentcapture.ContentCaptureSession.STATE_SERVICE_UP import android.annotation.NonNull; import android.content.ComponentName; +import android.os.Bundle; import android.os.IBinder; +import android.os.RemoteException; import android.service.contentcapture.ContentCaptureService; import android.service.contentcapture.SnapshotData; import android.util.LocalLog; import android.util.Slog; import android.view.contentcapture.ContentCaptureContext; import android.view.contentcapture.ContentCaptureSessionId; +import android.view.contentcapture.MainContentCaptureSession; import com.android.internal.annotations.GuardedBy; import com.android.internal.os.IResultReceiver; @@ -108,6 +113,20 @@ final class ContentCaptureServerSession { } /** + * Changes the {@link ContentCaptureService} enabled state. + */ + @GuardedBy("mLock") + public void setContentCaptureEnabledLocked(boolean enabled) { + try { + final Bundle extras = new Bundle(); + extras.putBoolean(MainContentCaptureSession.EXTRA_ENABLED_STATE, true); + mSessionStateReceiver.send(enabled ? RESULT_CODE_TRUE : RESULT_CODE_FALSE, extras); + } catch (RemoteException e) { + Slog.w(TAG, "Error async reporting result to client: " + e); + } + } + + /** * Notifies the {@link ContentCaptureService} of a snapshot of an activity. */ @GuardedBy("mLock") diff --git a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java index 9b70272ed952..770931179c5e 100644 --- a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java +++ b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java @@ -28,6 +28,7 @@ import android.app.contentsuggestions.SelectionsRequest; import android.content.ComponentName; import android.content.pm.PackageManager; import android.content.pm.ServiceInfo; +import android.graphics.ColorSpace; import android.graphics.GraphicBuffer; import android.os.Bundle; import android.os.RemoteException; @@ -99,11 +100,17 @@ public final class ContentSuggestionsPerUserService extends ActivityManager.TaskSnapshot snapshot = mActivityTaskManagerInternal.getTaskSnapshot(taskId, false); GraphicBuffer snapshotBuffer = null; + int colorSpaceId = 0; if (snapshot != null) { snapshotBuffer = snapshot.getSnapshot(); + ColorSpace colorSpace = snapshot.getColorSpace(); + if (colorSpace != null) { + colorSpaceId = colorSpace.getId(); + } } - service.provideContextImage(taskId, snapshotBuffer, imageContextRequestExtras); + service.provideContextImage(taskId, snapshotBuffer, colorSpaceId, + imageContextRequestExtras); } } diff --git a/services/contentsuggestions/java/com/android/server/contentsuggestions/RemoteContentSuggestionsService.java b/services/contentsuggestions/java/com/android/server/contentsuggestions/RemoteContentSuggestionsService.java index 4b36352bac51..a8b7b814a9ba 100644 --- a/services/contentsuggestions/java/com/android/server/contentsuggestions/RemoteContentSuggestionsService.java +++ b/services/contentsuggestions/java/com/android/server/contentsuggestions/RemoteContentSuggestionsService.java @@ -68,9 +68,9 @@ public class RemoteContentSuggestionsService extends } void provideContextImage(int taskId, @Nullable GraphicBuffer contextImage, - @NonNull Bundle imageContextRequestExtras) { + int colorSpaceId, @NonNull Bundle imageContextRequestExtras) { scheduleAsyncRequest((s) -> s.provideContextImage(taskId, contextImage, - imageContextRequestExtras)); + colorSpaceId, imageContextRequestExtras)); } void suggestContentSelections( diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index 1fa62cec6e30..1220e82485e7 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -760,7 +760,7 @@ class AlarmManagerService extends SystemService { if (predicate.test(alarm)) { alarms.remove(i); if (!reOrdering) { - decrementAlarmCount(alarm.uid); + decrementAlarmCount(alarm.uid, 1); } didRemove = true; if (alarm.alarmClock != null) { @@ -1764,7 +1764,7 @@ class AlarmManagerService extends SystemService { + ", callingPackage: " + callingPackage; // STOPSHIP (b/128866264): Just to catch breakages. Remove before final release. Slog.wtf(TAG, errorMsg); - // TODO b/129995049: Resume throwing once issue is resolved. + // TODO b/129995049: Resume throwing after some soak time without errors // throw new UnsupportedOperationException(errorMsg); } setImplLocked(type, triggerAtTime, triggerElapsed, windowLength, maxElapsed, @@ -3113,17 +3113,21 @@ class AlarmManagerService extends SystemService { } } for (int i = mPendingWhileIdleAlarms.size() - 1; i >= 0; i--) { - if (mPendingWhileIdleAlarms.get(i).matches(operation, directReceiver)) { + final Alarm alarm = mPendingWhileIdleAlarms.get(i); + if (alarm.matches(operation, directReceiver)) { // Don't set didRemove, since this doesn't impact the scheduled alarms. mPendingWhileIdleAlarms.remove(i); + decrementAlarmCount(alarm.uid, 1); } } for (int i = mPendingBackgroundAlarms.size() - 1; i >= 0; i--) { final ArrayList<Alarm> alarmsForUid = mPendingBackgroundAlarms.valueAt(i); for (int j = alarmsForUid.size() - 1; j >= 0; j--) { - if (alarmsForUid.get(j).matches(operation, directReceiver)) { + final Alarm alarm = alarmsForUid.get(j); + if (alarm.matches(operation, directReceiver)) { // Don't set didRemove, since this doesn't impact the scheduled alarms. alarmsForUid.remove(j); + decrementAlarmCount(alarm.uid, 1); } } if (alarmsForUid.size() == 0) { @@ -3169,6 +3173,7 @@ class AlarmManagerService extends SystemService { if (a.uid == uid) { // Don't set didRemove, since this doesn't impact the scheduled alarms. mPendingWhileIdleAlarms.remove(i); + decrementAlarmCount(uid, 1); } } for (int i = mPendingBackgroundAlarms.size() - 1; i >= 0; i --) { @@ -3176,6 +3181,7 @@ class AlarmManagerService extends SystemService { for (int j = alarmsForUid.size() - 1; j >= 0; j--) { if (alarmsForUid.get(j).uid == uid) { alarmsForUid.remove(j); + decrementAlarmCount(uid, 1); } } if (alarmsForUid.size() == 0) { @@ -3221,13 +3227,16 @@ class AlarmManagerService extends SystemService { if (a.matches(packageName)) { // Don't set didRemove, since this doesn't impact the scheduled alarms. mPendingWhileIdleAlarms.remove(i); + decrementAlarmCount(a.uid, 1); } } for (int i = mPendingBackgroundAlarms.size() - 1; i >= 0; i --) { final ArrayList<Alarm> alarmsForUid = mPendingBackgroundAlarms.valueAt(i); for (int j = alarmsForUid.size() - 1; j >= 0; j--) { - if (alarmsForUid.get(j).matches(packageName)) { + final Alarm alarm = alarmsForUid.get(j); + if (alarm.matches(packageName)) { alarmsForUid.remove(j); + decrementAlarmCount(alarm.uid, 1); } } if (alarmsForUid.size() == 0) { @@ -3272,10 +3281,15 @@ class AlarmManagerService extends SystemService { if (a.uid == uid) { // Don't set didRemove, since this doesn't impact the scheduled alarms. mPendingWhileIdleAlarms.remove(i); + decrementAlarmCount(uid, 1); } } for (int i = mPendingBackgroundAlarms.size() - 1; i >= 0; i--) { if (mPendingBackgroundAlarms.keyAt(i) == uid) { + final ArrayList<Alarm> toRemove = mPendingBackgroundAlarms.valueAt(i); + if (toRemove != null) { + decrementAlarmCount(uid, toRemove.size()); + } mPendingBackgroundAlarms.removeAt(i); } } @@ -3308,11 +3322,18 @@ class AlarmManagerService extends SystemService { if (UserHandle.getUserId(mPendingWhileIdleAlarms.get(i).creatorUid) == userHandle) { // Don't set didRemove, since this doesn't impact the scheduled alarms. - mPendingWhileIdleAlarms.remove(i); + final Alarm removed = mPendingWhileIdleAlarms.remove(i); + decrementAlarmCount(removed.uid, 1); } } for (int i = mPendingBackgroundAlarms.size() - 1; i >= 0; i--) { if (UserHandle.getUserId(mPendingBackgroundAlarms.keyAt(i)) == userHandle) { + final ArrayList<Alarm> toRemove = mPendingBackgroundAlarms.valueAt(i); + if (toRemove != null) { + for (int j = 0; j < toRemove.size(); j++) { + decrementAlarmCount(toRemove.get(j).uid, 1); + } + } mPendingBackgroundAlarms.removeAt(i); } } @@ -3844,7 +3865,7 @@ class AlarmManagerService extends SystemService { Slog.w(TAG, "Failure sending alarm.", e); } Trace.traceEnd(Trace.TRACE_TAG_POWER); - decrementAlarmCount(alarm.uid); + decrementAlarmCount(alarm.uid, 1); } } @@ -4198,7 +4219,7 @@ class AlarmManagerService extends SystemService { removeImpl(alarm.operation, null); } } - decrementAlarmCount(alarm.uid); + decrementAlarmCount(alarm.uid, 1); } break; } @@ -4828,16 +4849,21 @@ class AlarmManagerService extends SystemService { } } - private void decrementAlarmCount(int uid) { + private void decrementAlarmCount(int uid, int decrement) { + int oldCount = 0; final int uidIndex = mAlarmsPerUid.indexOfKey(uid); if (uidIndex >= 0) { - final int newCount = mAlarmsPerUid.valueAt(uidIndex) - 1; - if (newCount > 0) { - mAlarmsPerUid.setValueAt(uidIndex, newCount); + oldCount = mAlarmsPerUid.valueAt(uidIndex); + if (oldCount > decrement) { + mAlarmsPerUid.setValueAt(uidIndex, oldCount - decrement); } else { mAlarmsPerUid.removeAt(uidIndex); } } + if (oldCount < decrement) { + Slog.wtf(TAG, "Attempt to decrement existing alarm count " + oldCount + " by " + + decrement + " for uid " + uid); + } } private class ShellCmd extends ShellCommand { diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java index 6573c3bf797c..188d65494868 100644 --- a/services/core/java/com/android/server/BluetoothManagerService.java +++ b/services/core/java/com/android/server/BluetoothManagerService.java @@ -1136,11 +1136,21 @@ class BluetoothManagerService extends IBluetoothManager.Stub { public void unbindBluetoothProfileService(int bluetoothProfile, IBluetoothProfileServiceConnection proxy) { synchronized (mProfileServices) { - ProfileServiceConnections psc = mProfileServices.get(new Integer(bluetoothProfile)); + Integer profile = new Integer(bluetoothProfile); + ProfileServiceConnections psc = mProfileServices.get(profile); if (psc == null) { return; } psc.removeProxy(proxy); + if (psc.isEmpty()) { + // All prxoies are disconnected, unbind with the service. + try { + mContext.unbindService(psc); + } catch (IllegalArgumentException e) { + Slog.e(TAG, "Unable to unbind service with intent: " + psc.mIntent, e); + } + mProfileServices.remove(profile); + } } } @@ -1298,6 +1308,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mProxies.kill(); } + private boolean isEmpty() { + return mProxies.getRegisteredCallbackCount() == 0; + } + @Override public void onServiceConnected(ComponentName className, IBinder service) { // remove timeout message diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index 44fc45e13218..34702ecbab4d 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -120,7 +120,9 @@ import com.android.server.location.MockProvider; import com.android.server.location.PassiveProvider; import com.android.server.location.RemoteListenerHelper; +import java.io.ByteArrayOutputStream; import java.io.FileDescriptor; +import java.io.PrintStream; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; @@ -2299,6 +2301,7 @@ public class LocationManagerService extends ILocationManager.Stub { private boolean mIsForegroundUid; private Location mLastFixBroadcast; private long mLastStatusBroadcast; + private Throwable mStackTrace; // for debugging only /** * Note: must be constructed with lock held. @@ -2311,6 +2314,10 @@ public class LocationManagerService extends ILocationManager.Stub { mIsForegroundUid = isImportanceForeground( mActivityManager.getPackageImportance(mReceiver.mCallerIdentity.mPackageName)); + if (D && receiver.mCallerIdentity.mPid == Process.myPid()) { + mStackTrace = new Throwable(); + } + ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider); if (records == null) { records = new ArrayList<>(); @@ -2361,11 +2368,26 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public String toString() { - return "UpdateRecord[" + mProvider + " " + mReceiver.mCallerIdentity.mPackageName - + "(" + mReceiver.mCallerIdentity.mUid + (mIsForegroundUid ? " foreground" - : " background") - + ")" + " " + mRealRequest + " " - + mReceiver.mWorkSource + "]"; + StringBuilder b = new StringBuilder("UpdateRecord["); + b.append(mProvider).append(" "); + b.append(mReceiver.mCallerIdentity.mPackageName); + b.append("(").append(mReceiver.mCallerIdentity.mUid); + if (mIsForegroundUid) { + b.append(" foreground"); + } else { + b.append(" background"); + } + b.append(") "); + b.append(mRealRequest).append(" ").append(mReceiver.mWorkSource); + + if (mStackTrace != null) { + ByteArrayOutputStream tmp = new ByteArrayOutputStream(); + mStackTrace.printStackTrace(new PrintStream(tmp)); + b.append("\n\n").append(tmp.toString()).append("\n"); + } + + b.append("]"); + return b.toString(); } } diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java index b44009fd6880..7b5b419a7aa0 100644 --- a/services/core/java/com/android/server/PackageWatchdog.java +++ b/services/core/java/com/android/server/PackageWatchdog.java @@ -29,6 +29,7 @@ import android.os.Environment; import android.os.Handler; import android.os.Looper; import android.os.SystemClock; +import android.provider.DeviceConfig; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; @@ -61,6 +62,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.TimeUnit; /** * Monitors the health of packages on the system and notifies interested observers when packages @@ -69,10 +71,22 @@ import java.util.Set; */ public class PackageWatchdog { private static final String TAG = "PackageWatchdog"; + + static final String PROPERTY_WATCHDOG_TRIGGER_DURATION_MILLIS = + "watchdog_trigger_failure_duration_millis"; + static final String PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT = + "watchdog_trigger_failure_count"; + static final String PROPERTY_WATCHDOG_EXPLICIT_HEALTH_CHECK_ENABLED = + "watchdog_explicit_health_check_enabled"; + // Duration to count package failures before it resets to 0 - private static final int TRIGGER_DURATION_MS = 60000; + private static final int DEFAULT_TRIGGER_FAILURE_DURATION_MS = + (int) TimeUnit.MINUTES.toMillis(1); // Number of package failures within the duration above before we notify observers - static final int TRIGGER_FAILURE_COUNT = 5; + private static final int DEFAULT_TRIGGER_FAILURE_COUNT = 5; + // Whether explicit health checks are enabled or not + private static final boolean DEFAULT_EXPLICIT_HEALTH_CHECK_ENABLED = true; + private static final int DB_VERSION = 1; private static final String TAG_PACKAGE_WATCHDOG = "package-watchdog"; private static final String TAG_PACKAGE = "package"; @@ -83,6 +97,7 @@ public class PackageWatchdog { private static final String ATTR_EXPLICIT_HEALTH_CHECK_DURATION = "health-check-duration"; private static final String ATTR_PASSED_HEALTH_CHECK = "passed-health-check"; + @GuardedBy("PackageWatchdog.class") private static PackageWatchdog sPackageWatchdog; private final Object mLock = new Object(); @@ -100,11 +115,15 @@ public class PackageWatchdog { // File containing the XML data of monitored packages /data/system/package-watchdog.xml private final AtomicFile mPolicyFile; private final ExplicitHealthCheckController mHealthCheckController; + @GuardedBy("mLock") + private boolean mIsPackagesReady; // Flag to control whether explicit health checks are supported or not @GuardedBy("mLock") - private boolean mIsHealthCheckEnabled = true; + private boolean mIsHealthCheckEnabled = DEFAULT_EXPLICIT_HEALTH_CHECK_ENABLED; @GuardedBy("mLock") - private boolean mIsPackagesReady; + private int mTriggerFailureDurationMs = DEFAULT_TRIGGER_FAILURE_DURATION_MS; + @GuardedBy("mLock") + private int mTriggerFailureCount = DEFAULT_TRIGGER_FAILURE_COUNT; // SystemClock#uptimeMillis when we last executed #syncState // 0 if no prune is scheduled. @GuardedBy("mLock") @@ -153,8 +172,8 @@ public class PackageWatchdog { mHealthCheckController.setCallbacks(packageName -> onHealthCheckPassed(packageName), packages -> onSupportedPackages(packages), () -> syncRequestsAsync()); - // Controller is initially disabled until here where we may enable it and sync our state - setExplicitHealthCheckEnabled(mIsHealthCheckEnabled); + setPropertyChangedListenerLocked(); + updateConfigs(); } } @@ -332,14 +351,13 @@ public class PackageWatchdog { } } - // TODO(b/120598832): Set depending on DeviceConfig flag /** * Enables or disables explicit health checks. * <p> If explicit health checks are enabled, the health check service is started. * <p> If explicit health checks are disabled, pending explicit health check requests are * passed and the health check service is stopped. */ - public void setExplicitHealthCheckEnabled(boolean enabled) { + private void setExplicitHealthCheckEnabled(boolean enabled) { synchronized (mLock) { mIsHealthCheckEnabled = enabled; mHealthCheckController.setEnabled(enabled); @@ -390,6 +408,12 @@ public class PackageWatchdog { String getName(); } + long getTriggerFailureCount() { + synchronized (mLock) { + return mTriggerFailureCount; + } + } + /** * Serializes and syncs health check requests with the {@link ExplicitHealthCheckController}. */ @@ -646,7 +670,7 @@ public class PackageWatchdog { XmlUtils.beginDocument(parser, TAG_PACKAGE_WATCHDOG); int outerDepth = parser.getDepth(); while (XmlUtils.nextElementWithin(parser, outerDepth)) { - ObserverInternal observer = ObserverInternal.read(parser); + ObserverInternal observer = ObserverInternal.read(parser, this); if (observer != null) { mAllObservers.put(observer.mName, observer); } @@ -661,6 +685,48 @@ public class PackageWatchdog { } } + /** Adds a {@link DeviceConfig#OnPropertyChangedListener}. */ + private void setPropertyChangedListenerLocked() { + DeviceConfig.addOnPropertyChangedListener( + DeviceConfig.NAMESPACE_ROLLBACK, + mContext.getMainExecutor(), + (namespace, name, value) -> { + if (!DeviceConfig.NAMESPACE_ROLLBACK.equals(namespace)) { + return; + } + updateConfigs(); + }); + } + + /** + * Health check is enabled or disabled after reading the flags + * from DeviceConfig. + */ + private void updateConfigs() { + synchronized (mLock) { + mTriggerFailureCount = DeviceConfig.getInt( + DeviceConfig.NAMESPACE_ROLLBACK, + PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT, + DEFAULT_TRIGGER_FAILURE_COUNT); + if (mTriggerFailureCount <= 0) { + mTriggerFailureCount = DEFAULT_TRIGGER_FAILURE_COUNT; + } + + mTriggerFailureDurationMs = DeviceConfig.getInt( + DeviceConfig.NAMESPACE_ROLLBACK, + PROPERTY_WATCHDOG_TRIGGER_DURATION_MILLIS, + DEFAULT_TRIGGER_FAILURE_DURATION_MS); + if (mTriggerFailureDurationMs <= 0) { + mTriggerFailureDurationMs = DEFAULT_TRIGGER_FAILURE_COUNT; + } + + setExplicitHealthCheckEnabled(DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_ROLLBACK, + PROPERTY_WATCHDOG_EXPLICIT_HEALTH_CHECK_ENABLED, + DEFAULT_EXPLICIT_HEALTH_CHECK_ENABLED)); + } + } + /** * Persists mAllObservers to file. Threshold information is ignored. */ @@ -805,7 +871,7 @@ public class PackageWatchdog { * #loadFromFile which in turn is only called on construction of the * singleton PackageWatchdog. **/ - public static ObserverInternal read(XmlPullParser parser) { + public static ObserverInternal read(XmlPullParser parser, PackageWatchdog watchdog) { String observerName = null; if (TAG_OBSERVER.equals(parser.getName())) { observerName = parser.getAttributeValue(null, ATTR_NAME); @@ -829,7 +895,7 @@ public class PackageWatchdog { boolean hasPassedHealthCheck = Boolean.parseBoolean( parser.getAttributeValue(null, ATTR_PASSED_HEALTH_CHECK)); if (!TextUtils.isEmpty(packageName)) { - packages.add(new MonitoredPackage(packageName, duration, + packages.add(watchdog.new MonitoredPackage(packageName, duration, healthCheckDuration, hasPassedHealthCheck)); } } catch (NumberFormatException e) { @@ -856,7 +922,7 @@ public class PackageWatchdog { * <p> Note, the PackageWatchdog#mLock must always be held when reading or writing * instances of this class. */ - static class MonitoredPackage { + class MonitoredPackage { // Health check states // TODO(b/120598832): Prefix with HEALTH_CHECK // mName has not passed health check but has requested a health check @@ -931,7 +997,7 @@ public class PackageWatchdog { public boolean onFailureLocked() { final long now = SystemClock.uptimeMillis(); final long duration = now - mUptimeStartMs; - if (duration > TRIGGER_DURATION_MS) { + if (duration > mTriggerFailureDurationMs) { // TODO(b/120598832): Reseting to 1 is not correct // because there may be more than 1 failure in the last trigger window from now // This is the RescueParty impl, will leave for now @@ -940,7 +1006,7 @@ public class PackageWatchdog { } else { mFailures++; } - boolean failed = mFailures >= TRIGGER_FAILURE_COUNT; + boolean failed = mFailures >= mTriggerFailureCount; if (failed) { mFailures = 0; } @@ -1065,7 +1131,7 @@ public class PackageWatchdog { } /** Returns a {@link String} representation of the current health check state. */ - private static String toString(int state) { + private String toString(int state) { switch (state) { case STATE_ACTIVE: return "ACTIVE"; @@ -1081,7 +1147,7 @@ public class PackageWatchdog { } /** Returns {@code value} if it is greater than 0 or {@link Long#MAX_VALUE} otherwise. */ - private static long toPositive(long value) { + private long toPositive(long value) { return value > 0 ? value : Long.MAX_VALUE; } } diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java index 1fe0271ac1ec..fb1a962c0ef3 100644 --- a/services/core/java/com/android/server/RescueParty.java +++ b/services/core/java/com/android/server/RescueParty.java @@ -72,8 +72,12 @@ public class RescueParty { static final int LEVEL_FACTORY_RESET = 4; @VisibleForTesting static final String PROP_RESCUE_BOOT_COUNT = "sys.rescue_boot_count"; + /** + * The boot trigger window size must always be greater than Watchdog's deadlock timeout + * {@link Watchdog#DEFAULT_TIMEOUT}. + */ @VisibleForTesting - static final long BOOT_TRIGGER_WINDOW_MILLIS = 300 * DateUtils.SECOND_IN_MILLIS; + static final long BOOT_TRIGGER_WINDOW_MILLIS = 600 * DateUtils.SECOND_IN_MILLIS; @VisibleForTesting static final long PERSISTENT_APP_CRASH_TRIGGER_WINDOW_MILLIS = 30 * DateUtils.SECOND_IN_MILLIS; @VisibleForTesting diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 6c1ffa7225f0..baec3ccd953f 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -802,8 +802,8 @@ class StorageManagerService extends IStorageManager.Stub } }); // For now, simply clone property when it changes - DeviceConfig.addOnPropertyChangedListener(DeviceConfig.NAMESPACE_STORAGE, - mContext.getMainExecutor(), (namespace, name, value) -> { + DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_STORAGE, + mContext.getMainExecutor(), (properties) -> { refreshIsolatedStorageSettings(); }); refreshIsolatedStorageSettings(); @@ -1063,11 +1063,7 @@ class StorageManagerService extends IStorageManager.Stub } private boolean supportsBlockCheckpoint() throws RemoteException { - // Only the system process is permitted to start checkpoints - if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) { - throw new SecurityException("no permission to check block based checkpoint support"); - } - + enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS); return mVold.supportsBlockCheckpoint(); } @@ -2726,11 +2722,7 @@ class StorageManagerService extends IStorageManager.Stub */ @Override public boolean needsCheckpoint() throws RemoteException { - // Only the system process is permitted to commit checkpoints - if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) { - throw new SecurityException("no permission to commit checkpoint changes"); - } - + enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS); return mVold.needsCheckpoint(); } diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index da91187c053f..28bc34859e6c 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -66,6 +66,7 @@ import com.android.internal.telephony.PhoneConstantConversions; import com.android.internal.telephony.PhoneConstants; import com.android.internal.telephony.TelephonyIntents; import com.android.internal.telephony.TelephonyPermissions; +import com.android.internal.util.ArrayUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.server.am.BatteryStatsService; @@ -1164,36 +1165,33 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { @Override public void notifyCarrierNetworkChange(boolean active) { - // only CarrierService with carrier privilege rule should have the permission. - int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; - try { - subId = Arrays.stream(SubscriptionManager.from(mContext) + // only CarrierService with carrier privilege rule should have the permission + int[] subIds = Arrays.stream(SubscriptionManager.from(mContext) .getActiveSubscriptionIdList()) - .filter(i -> TelephonyPermissions.checkCarrierPrivilegeForSubId(i)) - .findFirst().getAsInt(); - } catch (NoSuchElementException ex) { + .filter(i -> TelephonyPermissions.checkCarrierPrivilegeForSubId(i)).toArray(); + if (ArrayUtils.isEmpty(subIds)) { loge("notifyCarrierNetworkChange without carrier privilege"); - } - // the active subId does not have carrier privilege. - if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { + // the active subId does not have carrier privilege. throw new SecurityException("notifyCarrierNetworkChange without carrier privilege"); } - int phoneId = SubscriptionManager.getPhoneId(subId); - - if (VDBG) { - log("notifyCarrierNetworkChange: active=" + active + "subId: " + subId); - } synchronized (mRecords) { mCarrierNetworkChangeState = active; - for (Record r : mRecords) { - if (r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE) && - idMatch(r.subId, subId, phoneId)) { - try { - r.callback.onCarrierNetworkChange(active); - } catch (RemoteException ex) { - mRemoveList.add(r.binder); + for (int subId : subIds) { + int phoneId = SubscriptionManager.getPhoneId(subId); + + if (VDBG) { + log("notifyCarrierNetworkChange: active=" + active + "subId: " + subId); + } + for (Record r : mRecords) { + if (r.matchPhoneStateListenerEvent( + PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE) && + idMatch(r.subId, subId, phoneId)) { + try { + r.callback.onCarrierNetworkChange(active); + } catch (RemoteException ex) { + mRemoveList.add(r.binder); + } } } } diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 76136dfc81a0..df92106ae0d0 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -634,7 +634,7 @@ public final class ActiveServices { } if (allowBackgroundActivityStarts) { - r.hasStartedWhitelistingBgActivityStarts = true; + r.setHasStartedWhitelistingBgActivityStarts(true); scheduleCleanUpHasStartedWhitelistingBgActivityStartsLocked(r); } @@ -761,11 +761,6 @@ public final class ActiveServices { } service.callStart = false; - // the service will not necessarily be brought down, so only clear the whitelisting state - // for start-based bg activity starts now, and drop any existing future cleanup callback - service.setHasStartedWhitelistingBgActivityStarts(false); - mAm.mHandler.removeCallbacks(service.startedWhitelistingBgActivityStartsCleanUp); - bringDownServiceIfNeededLocked(service, false, false); } @@ -1768,12 +1763,7 @@ public final class ActiveServices { callerApp.uid, callerApp.processName, callingPackage); IBinder binder = connection.asBinder(); - ArrayList<ConnectionRecord> clist = s.getConnections().get(binder); - if (clist == null) { - clist = new ArrayList<ConnectionRecord>(); - s.putConnection(binder, clist); - } - clist.add(c); + s.addConnection(binder, c); b.connections.add(c); if (activity != null) { activity.addConnection(c); @@ -1792,9 +1782,9 @@ public final class ActiveServices { if (s.app != null) { updateServiceClientActivitiesLocked(s.app, c, true); } - clist = mServiceConnections.get(binder); + ArrayList<ConnectionRecord> clist = mServiceConnections.get(binder); if (clist == null) { - clist = new ArrayList<ConnectionRecord>(); + clist = new ArrayList<>(); mServiceConnections.put(binder, clist); } clist.add(c); @@ -3828,8 +3818,8 @@ public final class ActiveServices { public PendingIntent getRunningServiceControlPanelLocked(ComponentName name) { int userId = UserHandle.getUserId(Binder.getCallingUid()); ServiceRecord r = getServiceByNameLocked(name, userId); - ArrayMap<IBinder, ArrayList<ConnectionRecord>> connections = r.getConnections(); if (r != null) { + ArrayMap<IBinder, ArrayList<ConnectionRecord>> connections = r.getConnections(); for (int conni = connections.size() - 1; conni >= 0; conni--) { ArrayList<ConnectionRecord> conn = connections.valueAt(conni); for (int i=0; i<conn.size(); i++) { diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index d7decb4126fc..0da39e7aee76 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -26,7 +26,8 @@ import android.net.Uri; import android.os.Build; import android.os.Handler; import android.provider.DeviceConfig; -import android.provider.DeviceConfig.OnPropertyChangedListener; +import android.provider.DeviceConfig.OnPropertiesChangedListener; +import android.provider.DeviceConfig.Properties; import android.provider.Settings; import android.text.TextUtils; import android.text.TextUtils.SimpleStringSplitter; @@ -315,23 +316,25 @@ final class ActivityManagerConstants extends ContentObserver { private static final Uri ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS_URI = Settings.Global.getUriFor(Settings.Global.ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS); - private final OnPropertyChangedListener mOnDeviceConfigChangedListener = - new OnPropertyChangedListener() { + private final OnPropertiesChangedListener mOnDeviceConfigChangedListener = + new OnPropertiesChangedListener() { @Override - public void onPropertyChanged(String namespace, String name, String value) { - if (name == null) { - return; - } - switch (name) { - case KEY_MAX_CACHED_PROCESSES: - updateMaxCachedProcesses(); - break; - case KEY_DEFAULT_BACKGROUND_ACTIVITY_STARTS_ENABLED: - case KEY_BACKGROUND_ACTIVITY_STARTS_PACKAGE_NAMES_WHITELIST: - updateBackgroundActivityStarts(); - break; - default: - break; + public void onPropertiesChanged(Properties properties) { + for (String name : properties.getKeyset()) { + if (name == null) { + return; + } + switch (name) { + case KEY_MAX_CACHED_PROCESSES: + updateMaxCachedProcesses(); + break; + case KEY_DEFAULT_BACKGROUND_ACTIVITY_STARTS_ENABLED: + case KEY_BACKGROUND_ACTIVITY_STARTS_PACKAGE_NAMES_WHITELIST: + updateBackgroundActivityStarts(); + break; + default: + break; + } } } }; @@ -362,7 +365,7 @@ final class ActivityManagerConstants extends ContentObserver { if (mSystemServerAutomaticHeapDumpEnabled) { updateEnableAutomaticSystemServerHeapDumps(); } - DeviceConfig.addOnPropertyChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, ActivityThread.currentApplication().getMainExecutor(), mOnDeviceConfigChangedListener); updateMaxCachedProcesses(); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 11ddceb9b8f2..4d0d3d2dc578 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2185,24 +2185,38 @@ public class ActivityManagerService extends IActivityManager.Stub "hidden_api_access_statslog_sampling_rate"; public void onPropertiesChanged(DeviceConfig.Properties properties) { - int logSampleRate = properties.getInt(HIDDEN_API_ACCESS_LOG_SAMPLING_RATE, 0x0); - if (logSampleRate < 0 || logSampleRate > 0x10000) { - logSampleRate = -1; - } - if (logSampleRate != -1 && logSampleRate != mLogSampleRate) { + int logSampleRate = properties.getInt(HIDDEN_API_ACCESS_LOG_SAMPLING_RATE, + mLogSampleRate); + int statslogSampleRate = properties.getInt(HIDDEN_API_ACCESS_STATSLOG_SAMPLING_RATE, + mStatslogSampleRate); + setSampleRates(logSampleRate, statslogSampleRate); + } + + private void setSampleRates(int logSampleRate, int statslogSampleRate) { + if (logSampleRate >= 0 && logSampleRate <= 0x10000 + && logSampleRate != mLogSampleRate) { mLogSampleRate = logSampleRate; ZYGOTE_PROCESS.setHiddenApiAccessLogSampleRate(mLogSampleRate); } - int statslogSampleRate = - properties.getInt(HIDDEN_API_ACCESS_STATSLOG_SAMPLING_RATE, 0); - if (statslogSampleRate < 0 || statslogSampleRate > 0x10000) { - statslogSampleRate = -1; - } - if (statslogSampleRate != -1 && statslogSampleRate != mStatslogSampleRate) { + if (statslogSampleRate >= 0 && statslogSampleRate <= 0x10000 + && statslogSampleRate != mStatslogSampleRate) { mStatslogSampleRate = statslogSampleRate; ZYGOTE_PROCESS.setHiddenApiAccessStatslogSampleRate(mStatslogSampleRate); } + + } + + /** + * Set initial sampling rates from DeviceConfig. This is required after each restart, + * if they never get updated. + */ + private void initializeSampleRates() { + int logSampleRate = DeviceConfig.getInt(DeviceConfig.NAMESPACE_APP_COMPAT, + HIDDEN_API_ACCESS_LOG_SAMPLING_RATE, 0); + int statslogSampleRate = DeviceConfig.getInt(DeviceConfig.NAMESPACE_APP_COMPAT, + HIDDEN_API_ACCESS_STATSLOG_SAMPLING_RATE, 0); + setSampleRates(logSampleRate, statslogSampleRate); } public HiddenApiSettings(Handler handler, Context context) { @@ -2219,6 +2233,7 @@ public class ActivityManagerService extends IActivityManager.Stub Settings.Global.getUriFor(Settings.Global.HIDDEN_API_POLICY), false, this); + initializeSampleRates(); DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_APP_COMPAT, mContext.getMainExecutor(), this); update(); diff --git a/services/core/java/com/android/server/am/AppCompactor.java b/services/core/java/com/android/server/am/AppCompactor.java index 13b55dbcad48..b0f8f86ada6b 100644 --- a/services/core/java/com/android/server/am/AppCompactor.java +++ b/services/core/java/com/android/server/am/AppCompactor.java @@ -30,7 +30,8 @@ import android.os.Process; import android.os.SystemClock; import android.os.Trace; import android.provider.DeviceConfig; -import android.provider.DeviceConfig.OnPropertyChangedListener; +import android.provider.DeviceConfig.OnPropertiesChangedListener; +import android.provider.DeviceConfig.Properties; import android.text.TextUtils; import android.util.EventLog; import android.util.Slog; @@ -126,29 +127,31 @@ public final class AppCompactor { private final ArrayList<ProcessRecord> mPendingCompactionProcesses = new ArrayList<ProcessRecord>(); private final ActivityManagerService mAm; - private final OnPropertyChangedListener mOnFlagsChangedListener = - new OnPropertyChangedListener() { + private final OnPropertiesChangedListener mOnFlagsChangedListener = + new OnPropertiesChangedListener() { @Override - public void onPropertyChanged(String namespace, String name, String value) { + public void onPropertiesChanged(Properties properties) { synchronized (mPhenotypeFlagLock) { - if (KEY_USE_COMPACTION.equals(name)) { - updateUseCompaction(); - } else if (KEY_COMPACT_ACTION_1.equals(name) - || KEY_COMPACT_ACTION_2.equals(name)) { - updateCompactionActions(); - } else if (KEY_COMPACT_THROTTLE_1.equals(name) - || KEY_COMPACT_THROTTLE_2.equals(name) - || KEY_COMPACT_THROTTLE_3.equals(name) - || KEY_COMPACT_THROTTLE_4.equals(name)) { - updateCompactionThrottles(); - } else if (KEY_COMPACT_STATSD_SAMPLE_RATE.equals(name)) { - updateStatsdSampleRate(); - } else if (KEY_COMPACT_FULL_RSS_THROTTLE_KB.equals(name)) { - updateFullRssThrottle(); - } else if (KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB.equals(name)) { - updateFullDeltaRssThrottle(); - } else if (KEY_COMPACT_PROC_STATE_THROTTLE.equals(name)) { - updateProcStateThrottle(); + for (String name : properties.getKeyset()) { + if (KEY_USE_COMPACTION.equals(name)) { + updateUseCompaction(); + } else if (KEY_COMPACT_ACTION_1.equals(name) + || KEY_COMPACT_ACTION_2.equals(name)) { + updateCompactionActions(); + } else if (KEY_COMPACT_THROTTLE_1.equals(name) + || KEY_COMPACT_THROTTLE_2.equals(name) + || KEY_COMPACT_THROTTLE_3.equals(name) + || KEY_COMPACT_THROTTLE_4.equals(name)) { + updateCompactionThrottles(); + } else if (KEY_COMPACT_STATSD_SAMPLE_RATE.equals(name)) { + updateStatsdSampleRate(); + } else if (KEY_COMPACT_FULL_RSS_THROTTLE_KB.equals(name)) { + updateFullRssThrottle(); + } else if (KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB.equals(name)) { + updateFullDeltaRssThrottle(); + } else if (KEY_COMPACT_PROC_STATE_THROTTLE.equals(name)) { + updateProcStateThrottle(); + } } } if (mTestCallback != null) { @@ -229,7 +232,7 @@ public final class AppCompactor { * starts the background thread if necessary. */ public void init() { - DeviceConfig.addOnPropertyChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, ActivityThread.currentApplication().getMainExecutor(), mOnFlagsChangedListener); synchronized (mPhenotypeFlagLock) { updateUseCompaction(); diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags index 7c2ea3fc57f2..30a297e221ae 100644 --- a/services/core/java/com/android/server/am/EventLogTags.logtags +++ b/services/core/java/com/android/server/am/EventLogTags.logtags @@ -144,3 +144,6 @@ option java_package com.android.server.am 30064 am_on_top_resumed_gained_called (User|1|5),(Component Name|3),(Reason|3) # The activity's onTopResumedActivityChanged(false) has been called. 30065 am_on_top_resumed_lost_called (User|1|5),(Component Name|3),(Reason|3) + +# An activity been add into stopping list +30066 am_add_to_stopping (User|1|5),(Token|1|5),(Component Name|3),(Reason|3)
\ No newline at end of file diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index 043daefe0fdb..7abfcea306cc 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -38,6 +38,7 @@ import static android.os.Process.setProcessGroup; import static android.os.Process.setThreadPriority; import static android.os.Process.setThreadScheduler; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKUP; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ; @@ -60,8 +61,8 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH; import android.app.ActivityManager; import android.app.usage.UsageEvents; import android.content.Context; -import android.os.Binder; import android.os.Debug; +import android.os.Handler; import android.os.IBinder; import android.os.PowerManagerInternal; import android.os.Process; @@ -78,6 +79,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.procstats.ProcessStats; import com.android.server.LocalServices; +import com.android.server.ServiceThread; import com.android.server.wm.ActivityServiceConnectionsHolder; import com.android.server.wm.WindowProcessController; @@ -148,6 +150,12 @@ public final class OomAdjuster { /** Track all uids that have actively running processes. */ ActiveUids mActiveUids; + /** + * The handler to execute {@link #setProcessGroup} (it may be heavy if the process has many + * threads) for reducing the time spent in {@link #applyOomAdjLocked}. + */ + private final Handler mProcessGroupHandler; + private final ArraySet<BroadcastQueue> mTmpBroadcastQueue = new ArraySet(); private final ActivityManagerService mService; @@ -161,6 +169,28 @@ public final class OomAdjuster { mLocalPowerManager = LocalServices.getService(PowerManagerInternal.class); mConstants = mService.mConstants; mAppCompact = new AppCompactor(mService); + + // The process group is usually critical to the response time of foreground app, so the + // setter should apply it as soon as possible. + final ServiceThread adjusterThread = new ServiceThread(TAG, TOP_APP_PRIORITY_BOOST, + false /* allowIo */); + adjusterThread.start(); + Process.setThreadGroupAndCpuset(adjusterThread.getThreadId(), THREAD_GROUP_TOP_APP); + mProcessGroupHandler = new Handler(adjusterThread.getLooper(), msg -> { + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "setProcessGroup"); + final int pid = msg.arg1; + final int group = msg.arg2; + try { + setProcessGroup(pid, group); + } catch (Exception e) { + if (DEBUG_ALL) { + Slog.w(TAG, "Failed setting process group of " + pid + " to " + group, e); + } + } finally { + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + } + return true; + }); } void initSettings() { @@ -1726,9 +1756,9 @@ public final class OomAdjuster { processGroup = THREAD_GROUP_DEFAULT; break; } - long oldId = Binder.clearCallingIdentity(); + mProcessGroupHandler.sendMessage(mProcessGroupHandler.obtainMessage( + 0 /* unused */, app.pid, processGroup)); try { - setProcessGroup(app.pid, processGroup); if (curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) { // do nothing if we already switched to RT if (oldSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) { @@ -1791,13 +1821,9 @@ public final class OomAdjuster { } } } catch (Exception e) { - if (false) { - Slog.w(TAG, "Failed setting process group of " + app.pid - + " to " + app.getCurrentSchedulingGroup()); - Slog.w(TAG, "at location", e); + if (DEBUG_ALL) { + Slog.w(TAG, "Failed setting thread priority of " + app.pid, e); } - } finally { - Binder.restoreCallingIdentity(oldId); } } } diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index e891e6e450ba..563b2f3bfb32 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -1186,8 +1186,8 @@ class ProcessRecord implements WindowProcessListener { !mAllowBackgroundActivityStartsTokens.isEmpty()); } - void addBoundClientUids(ArraySet<Integer> clientUids) { - mBoundClientUids.addAll(clientUids); + void addBoundClientUid(int clientUid) { + mBoundClientUids.add(clientUid); mWindowProcessController.setBoundClientUids(mBoundClientUids); } diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index 27c62d03f960..0426ec1d9771 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -38,7 +38,6 @@ import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; import android.util.ArrayMap; -import android.util.ArraySet; import android.util.Slog; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; @@ -131,10 +130,10 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN int pendingConnectionImportance; // To be filled in to ProcessRecord once it connects // any current binding to this service has BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS flag? - private boolean hasBindingWhitelistingBgActivityStarts; + private boolean mHasBindingWhitelistingBgActivityStarts; // is this service currently whitelisted to start activities from background by providing // allowBackgroundActivityStarts=true to startServiceLocked()? - boolean hasStartedWhitelistingBgActivityStarts; + private boolean mHasStartedWhitelistingBgActivityStarts; // used to clean up the state of hasStartedWhitelistingBgActivityStarts after a timeout Runnable startedWhitelistingBgActivityStartsCleanUp; @@ -384,13 +383,13 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN if (whitelistManager) { pw.print(prefix); pw.print("whitelistManager="); pw.println(whitelistManager); } - if (hasBindingWhitelistingBgActivityStarts) { + if (mHasBindingWhitelistingBgActivityStarts) { pw.print(prefix); pw.print("hasBindingWhitelistingBgActivityStarts="); - pw.println(hasBindingWhitelistingBgActivityStarts); + pw.println(mHasBindingWhitelistingBgActivityStarts); } - if (hasStartedWhitelistingBgActivityStarts) { + if (mHasStartedWhitelistingBgActivityStarts) { pw.print(prefix); pw.print("hasStartedWhitelistingBgActivityStarts="); - pw.println(hasStartedWhitelistingBgActivityStarts); + pw.println(mHasStartedWhitelistingBgActivityStarts); } if (delayed) { pw.print(prefix); pw.print("delayed="); pw.println(delayed); @@ -542,7 +541,8 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN public void setProcess(ProcessRecord _proc) { if (_proc != null) { - if (hasStartedWhitelistingBgActivityStarts || hasBindingWhitelistingBgActivityStarts) { + if (mHasStartedWhitelistingBgActivityStarts + || mHasBindingWhitelistingBgActivityStarts) { _proc.addAllowBackgroundActivityStartsToken(this); } else { _proc.removeAllowBackgroundActivityStartsToken(this); @@ -580,15 +580,17 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN return connections; } - void putConnection(IBinder binder, ArrayList<ConnectionRecord> clist) { - connections.put(binder, clist); - // if we have a process attached, add bound client uids of this connection to it + void addConnection(IBinder binder, ConnectionRecord c) { + ArrayList<ConnectionRecord> clist = connections.get(binder); + if (clist == null) { + clist = new ArrayList<>(); + connections.put(binder, clist); + } + clist.add(c); + + // if we have a process attached, add bound client uid of this connection to it if (app != null) { - ArraySet<Integer> boundClientUids = new ArraySet<>(); - for (int i = 0; i < clist.size(); i++) { - boundClientUids.add(clist.get(i).clientUid); - } - app.addBoundClientUids(boundClientUids); + app.addBoundClientUid(c.clientUid); } } @@ -614,22 +616,22 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN break; } } - if (hasBindingWhitelistingBgActivityStarts != hasWhitelistingBinding) { - hasBindingWhitelistingBgActivityStarts = hasWhitelistingBinding; + if (mHasBindingWhitelistingBgActivityStarts != hasWhitelistingBinding) { + mHasBindingWhitelistingBgActivityStarts = hasWhitelistingBinding; updateParentProcessBgActivityStartsWhitelistingToken(); } } void setHasBindingWhitelistingBgActivityStarts(boolean newValue) { - if (hasBindingWhitelistingBgActivityStarts != newValue) { - hasBindingWhitelistingBgActivityStarts = newValue; + if (mHasBindingWhitelistingBgActivityStarts != newValue) { + mHasBindingWhitelistingBgActivityStarts = newValue; updateParentProcessBgActivityStartsWhitelistingToken(); } } void setHasStartedWhitelistingBgActivityStarts(boolean newValue) { - if (hasStartedWhitelistingBgActivityStarts != newValue) { - hasStartedWhitelistingBgActivityStarts = newValue; + if (mHasStartedWhitelistingBgActivityStarts != newValue) { + mHasStartedWhitelistingBgActivityStarts = newValue; updateParentProcessBgActivityStartsWhitelistingToken(); } } @@ -647,7 +649,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN if (app == null) { return; } - if (hasStartedWhitelistingBgActivityStarts || hasBindingWhitelistingBgActivityStarts) { + if (mHasStartedWhitelistingBgActivityStarts || mHasBindingWhitelistingBgActivityStarts) { // if the token is already there it's safe to "re-add it" - we're deadling with // a set of Binder objects app.addAllowBackgroundActivityStartsToken(this); diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java index 19c3a71810fb..64f4a35032c0 100644 --- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java +++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java @@ -81,6 +81,7 @@ public class SettingsToPropertiesMapper { static final String[] sDeviceConfigScopes = new String[] { DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT, DeviceConfig.NAMESPACE_INPUT_NATIVE_BOOT, + DeviceConfig.NAMESPACE_INTELLIGENCE_CONTENT_SUGGESTIONS, DeviceConfig.NAMESPACE_MEDIA_NATIVE, DeviceConfig.NAMESPACE_NETD_NATIVE, DeviceConfig.NAMESPACE_RUNTIME_NATIVE, diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index c56d8eabf0bb..d04aa8931dca 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -3292,7 +3292,7 @@ public class AppOpsService extends IAppOpsService.Stub { pw.println(" Starts a given operation for a particular application."); pw.println(" stop [--user <USER_ID>] <PACKAGE | UID> <OP> "); pw.println(" Stops a given operation for a particular application."); - pw.println(" set [--user <USER_ID>] <--uid PACKAGE | PACKAGE | UID> <OP> <MODE>"); + pw.println(" set [--user <USER_ID>] <[--uid] PACKAGE | UID> <OP> <MODE>"); pw.println(" Set the mode for a particular application and operation."); pw.println(" get [--user <USER_ID>] <PACKAGE | UID> [<OP>]"); pw.println(" Return the mode for a particular application and optional operation."); @@ -3305,12 +3305,11 @@ public class AppOpsService extends IAppOpsService.Stub { pw.println(" read-settings"); pw.println(" Read the last written settings, replacing current state in RAM."); pw.println(" options:"); - pw.println(" <PACKAGE> an Android package name."); + pw.println(" <PACKAGE> an Android package name or its UID if prefixed by --uid"); pw.println(" <OP> an AppOps operation."); pw.println(" <MODE> one of allow, ignore, deny, or default"); pw.println(" <USER_ID> the user id under which the package is installed. If --user is not"); pw.println(" specified, the current user is assumed."); - pw.println(" --uid PACKAGE refer to the UID of the package"); } static int onShellCommand(Shell shell, String cmd) { diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 507f398d2422..d5109123ab12 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -1825,14 +1825,28 @@ public class AudioService extends IAudioService.Stub && streamTypeAlias == AudioSystem.STREAM_MUSIC // vol change on a full volume device && ((device & mFullVolumeDevices) != 0)) { - int keyCode = (direction == -1) ? KeyEvent.KEYCODE_VOLUME_DOWN : - KeyEvent.KEYCODE_VOLUME_UP; - final long ident = Binder.clearCallingIdentity(); - try { - mHdmiPlaybackClient.sendKeyEvent(keyCode, true); - mHdmiPlaybackClient.sendKeyEvent(keyCode, false); - } finally { - Binder.restoreCallingIdentity(ident); + int keyCode = KeyEvent.KEYCODE_UNKNOWN; + switch (direction) { + case AudioManager.ADJUST_RAISE: + keyCode = KeyEvent.KEYCODE_VOLUME_UP; + break; + case AudioManager.ADJUST_LOWER: + keyCode = KeyEvent.KEYCODE_VOLUME_DOWN; + break; + case AudioManager.ADJUST_TOGGLE_MUTE: + keyCode = KeyEvent.KEYCODE_VOLUME_MUTE; + break; + default: + break; + } + if (keyCode != KeyEvent.KEYCODE_UNKNOWN) { + final long ident = Binder.clearCallingIdentity(); + try { + mHdmiPlaybackClient.sendKeyEvent(keyCode, true); + mHdmiPlaybackClient.sendKeyEvent(keyCode, false); + } finally { + Binder.restoreCallingIdentity(ident); + } } } diff --git a/services/core/java/com/android/server/biometrics/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/AuthenticationClient.java index 74b72210859c..1e0f2051933c 100644 --- a/services/core/java/com/android/server/biometrics/AuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/AuthenticationClient.java @@ -63,11 +63,11 @@ public abstract class AuthenticationClient extends ClientMonitor { */ public abstract boolean shouldFrameworkHandleLockout(); - public AuthenticationClient(Context context, Metrics metrics, + public AuthenticationClient(Context context, Constants constants, BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token, BiometricServiceBase.ServiceListener listener, int targetUserId, int groupId, long opId, boolean restricted, String owner, int cookie, boolean requireConfirmation) { - super(context, metrics, daemon, halDeviceId, token, listener, targetUserId, groupId, + super(context, constants, daemon, halDeviceId, token, listener, targetUserId, groupId, restricted, owner, cookie); mOpId = opId; mRequireConfirmation = requireConfirmation; @@ -126,7 +126,7 @@ public abstract class AuthenticationClient extends ClientMonitor { final BiometricServiceBase.ServiceListener listener = getListener(); - mMetricsLogger.action(mMetrics.actionBiometricAuth(), authenticated); + mMetricsLogger.action(mConstants.actionBiometricAuth(), authenticated); boolean result = false; try { @@ -225,7 +225,7 @@ public abstract class AuthenticationClient extends ClientMonitor { final int result = getDaemonWrapper().authenticate(mOpId, getGroupId()); if (result != 0) { Slog.w(getLogTag(), "startAuthentication failed, result=" + result); - mMetricsLogger.histogram(mMetrics.tagAuthStartError(), result); + mMetricsLogger.histogram(mConstants.tagAuthStartError(), result); onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */); return result; diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java index 3f856d3e8eb2..d3c62bed7b5f 100644 --- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java +++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java @@ -138,7 +138,7 @@ public abstract class BiometricServiceBase extends SystemService /** * @return the metrics constants for a biometric implementation. */ - protected abstract Metrics getMetrics(); + protected abstract Constants getConstants(); /** * @param userId @@ -220,7 +220,7 @@ public abstract class BiometricServiceBase extends SystemService public AuthenticationClientImpl(Context context, DaemonWrapper daemon, long halDeviceId, IBinder token, ServiceListener listener, int targetUserId, int groupId, long opId, boolean restricted, String owner, int cookie, boolean requireConfirmation) { - super(context, getMetrics(), daemon, halDeviceId, token, listener, targetUserId, + super(context, getConstants(), daemon, halDeviceId, token, listener, targetUserId, groupId, opId, restricted, owner, cookie, requireConfirmation); } @@ -283,7 +283,7 @@ public abstract class BiometricServiceBase extends SystemService IBinder token, ServiceListener listener, int userId, int groupId, byte[] cryptoToken, boolean restricted, String owner, final int[] disabledFeatures) { - super(context, getMetrics(), daemon, halDeviceId, token, listener, + super(context, getConstants(), daemon, halDeviceId, token, listener, userId, groupId, cryptoToken, restricted, owner, getBiometricUtils(), disabledFeatures); } @@ -302,7 +302,7 @@ public abstract class BiometricServiceBase extends SystemService DaemonWrapper daemon, long halDeviceId, IBinder token, ServiceListener listener, int templateId, int groupId, int userId, boolean restricted, String owner) { - super(context, getMetrics(), daemon, halDeviceId, token, listener, templateId, groupId, + super(context, getConstants(), daemon, halDeviceId, token, listener, templateId, groupId, userId, restricted, owner, getBiometricUtils()); } @@ -329,7 +329,7 @@ public abstract class BiometricServiceBase extends SystemService ServiceListener listener, int groupId, int userId, boolean restricted, String owner, List<? extends BiometricAuthenticator.Identifier> enrolledList, BiometricUtils utils) { - super(context, getMetrics(), daemon, halDeviceId, token, listener, groupId, userId, + super(context, getConstants(), daemon, halDeviceId, token, listener, groupId, userId, restricted, owner); mEnrolledList = enrolledList; mUtils = utils; @@ -655,7 +655,7 @@ public abstract class BiometricServiceBase extends SystemService @Override public void serviceDied(long cookie) { Slog.e(getTag(), "HAL died"); - mMetricsLogger.count(getMetrics().tagHalDied(), 1); + mMetricsLogger.count(getConstants().tagHalDied(), 1); mHALDeathCount++; mCurrentUserId = UserHandle.USER_NULL; handleError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, @@ -845,7 +845,7 @@ public abstract class BiometricServiceBase extends SystemService } mHandler.post(() -> { - mMetricsLogger.histogram(getMetrics().tagAuthToken(), opId != 0L ? 1 : 0); + mMetricsLogger.histogram(getConstants().tagAuthToken(), opId != 0L ? 1 : 0); // Get performance stats object for this user. HashMap<Integer, PerformanceStats> pmap diff --git a/services/core/java/com/android/server/biometrics/ClientMonitor.java b/services/core/java/com/android/server/biometrics/ClientMonitor.java index 006558080ef0..942e0501d88d 100644 --- a/services/core/java/com/android/server/biometrics/ClientMonitor.java +++ b/services/core/java/com/android/server/biometrics/ClientMonitor.java @@ -63,7 +63,7 @@ public abstract class ClientMonitor extends LoggableMonitor implements IBinder.D private final int mCookie; protected final MetricsLogger mMetricsLogger; - protected final Metrics mMetrics; + protected final Constants mConstants; protected boolean mAlreadyCancelled; protected boolean mAlreadyDone; @@ -80,12 +80,12 @@ public abstract class ClientMonitor extends LoggableMonitor implements IBinder.D * permission * @param owner name of the client that owns this */ - public ClientMonitor(Context context, Metrics metrics, + public ClientMonitor(Context context, Constants constants, BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token, BiometricServiceBase.ServiceListener listener, int userId, int groupId, boolean restricted, String owner, int cookie) { mContext = context; - mMetrics = metrics; + mConstants = constants; mDaemon = daemon; mHalDeviceId = halDeviceId; mToken = token; @@ -108,7 +108,7 @@ public abstract class ClientMonitor extends LoggableMonitor implements IBinder.D } protected String getLogTag() { - return mMetrics.logTag(); + return mConstants.logTag(); } public int getCookie() { @@ -145,6 +145,31 @@ public abstract class ClientMonitor extends LoggableMonitor implements IBinder.D public abstract boolean onEnumerationResult( BiometricAuthenticator.Identifier identifier, int remaining); + public int[] getAcquireIgnorelist() { + return new int[0]; + } + public int[] getAcquireVendorIgnorelist() { + return new int[0]; + } + + private boolean blacklistContains(int acquiredInfo, int vendorCode) { + if (acquiredInfo == mConstants.acquireVendorCode()) { + for (int i = 0; i < getAcquireVendorIgnorelist().length; i++) { + if (getAcquireVendorIgnorelist()[i] == vendorCode) { + if (DEBUG) Slog.v(getLogTag(), "Ignoring vendor message: " + vendorCode); + return true; + } + } + } else { + for (int i = 0; i < getAcquireIgnorelist().length; i++) { + if (getAcquireIgnorelist()[i] == acquiredInfo) { + if (DEBUG) Slog.v(getLogTag(), "Ignoring message: " + acquiredInfo); + return true; + } + } + } + return false; + } public boolean isAlreadyDone() { return mAlreadyDone; @@ -160,7 +185,7 @@ public abstract class ClientMonitor extends LoggableMonitor implements IBinder.D super.logOnAcquired(mContext, acquiredInfo, vendorCode, getTargetUserId()); if (DEBUG) Slog.v(getLogTag(), "Acquired: " + acquiredInfo + " " + vendorCode); try { - if (mListener != null) { + if (mListener != null && !blacklistContains(acquiredInfo, vendorCode)) { mListener.onAcquired(getHalDeviceId(), acquiredInfo, vendorCode); } return false; // acquisition continues... diff --git a/services/core/java/com/android/server/biometrics/Metrics.java b/services/core/java/com/android/server/biometrics/Constants.java index 02e44e94e395..874fd428ea8a 100644 --- a/services/core/java/com/android/server/biometrics/Metrics.java +++ b/services/core/java/com/android/server/biometrics/Constants.java @@ -16,7 +16,7 @@ package com.android.server.biometrics; -public interface Metrics { +public interface Constants { /** The log tag */ String logTag(); @@ -31,4 +31,6 @@ public interface Metrics { /** Integers for MetricsLogger.action() */ int actionBiometricAuth(); int actionBiometricEnroll(); + + int acquireVendorCode(); }
\ No newline at end of file diff --git a/services/core/java/com/android/server/biometrics/EnrollClient.java b/services/core/java/com/android/server/biometrics/EnrollClient.java index e656d9809582..854528f0654d 100644 --- a/services/core/java/com/android/server/biometrics/EnrollClient.java +++ b/services/core/java/com/android/server/biometrics/EnrollClient.java @@ -40,12 +40,12 @@ public abstract class EnrollClient extends ClientMonitor { public abstract boolean shouldVibrate(); - public EnrollClient(Context context, Metrics metrics, + public EnrollClient(Context context, Constants constants, BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token, BiometricServiceBase.ServiceListener listener, int userId, int groupId, byte[] cryptoToken, boolean restricted, String owner, BiometricUtils utils, final int[] disabledFeatures) { - super(context, metrics, daemon, halDeviceId, token, listener, userId, groupId, restricted, + super(context, constants, daemon, halDeviceId, token, listener, userId, groupId, restricted, owner, 0 /* cookie */); mBiometricUtils = utils; mCryptoToken = Arrays.copyOf(cryptoToken, cryptoToken.length); @@ -78,7 +78,7 @@ public abstract class EnrollClient extends ClientMonitor { if (shouldVibrate()) { vibrateSuccess(); } - mMetricsLogger.action(mMetrics.actionBiometricEnroll()); + mMetricsLogger.action(mConstants.actionBiometricEnroll()); try { final BiometricServiceBase.ServiceListener listener = getListener(); if (listener != null) { @@ -105,7 +105,7 @@ public abstract class EnrollClient extends ClientMonitor { disabledFeatures); if (result != 0) { Slog.w(getLogTag(), "startEnroll failed, result=" + result); - mMetricsLogger.histogram(mMetrics.tagEnrollStartError(), result); + mMetricsLogger.histogram(mConstants.tagEnrollStartError(), result); onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */); return result; diff --git a/services/core/java/com/android/server/biometrics/EnumerateClient.java b/services/core/java/com/android/server/biometrics/EnumerateClient.java index 44ac0373507a..f889d2b41c16 100644 --- a/services/core/java/com/android/server/biometrics/EnumerateClient.java +++ b/services/core/java/com/android/server/biometrics/EnumerateClient.java @@ -30,11 +30,11 @@ import java.util.ArrayList; * A class to keep track of the enumeration state for a given client. */ public abstract class EnumerateClient extends ClientMonitor { - public EnumerateClient(Context context, Metrics metrics, + public EnumerateClient(Context context, Constants constants, BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token, BiometricServiceBase.ServiceListener listener, int groupId, int userId, boolean restricted, String owner) { - super(context, metrics, daemon, halDeviceId, token, listener, userId, groupId, restricted, + super(context, constants, daemon, halDeviceId, token, listener, userId, groupId, restricted, owner, 0 /* cookie */); } @@ -55,7 +55,7 @@ public abstract class EnumerateClient extends ClientMonitor { if (result != 0) { Slog.w(getLogTag(), "start enumerate for user " + getTargetUserId() + " failed, result=" + result); - mMetricsLogger.histogram(mMetrics.tagEnumerateStartError(), result); + mMetricsLogger.histogram(mConstants.tagEnumerateStartError(), result); onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */); return result; diff --git a/services/core/java/com/android/server/biometrics/RemovalClient.java b/services/core/java/com/android/server/biometrics/RemovalClient.java index a18f336f3ef4..bccab7b7f67c 100644 --- a/services/core/java/com/android/server/biometrics/RemovalClient.java +++ b/services/core/java/com/android/server/biometrics/RemovalClient.java @@ -33,11 +33,11 @@ public abstract class RemovalClient extends ClientMonitor { private final int mBiometricId; private final BiometricUtils mBiometricUtils; - public RemovalClient(Context context, Metrics metrics, + public RemovalClient(Context context, Constants constants, BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token, BiometricServiceBase.ServiceListener listener, int biometricId, int groupId, int userId, boolean restricted, String owner, BiometricUtils utils) { - super(context, metrics, daemon, halDeviceId, token, listener, userId, groupId, restricted, + super(context, constants, daemon, halDeviceId, token, listener, userId, groupId, restricted, owner, 0 /* cookie */); mBiometricId = biometricId; mBiometricUtils = utils; @@ -60,7 +60,7 @@ public abstract class RemovalClient extends ClientMonitor { if (result != 0) { Slog.w(getLogTag(), "startRemove with id = " + mBiometricId + " failed, result=" + result); - mMetricsLogger.histogram(mMetrics.tagRemoveStartError(), result); + mMetricsLogger.histogram(mConstants.tagRemoveStartError(), result); onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */); return result; diff --git a/services/core/java/com/android/server/biometrics/face/FaceMetrics.java b/services/core/java/com/android/server/biometrics/face/FaceConstants.java index 1c5cd5a84402..143eed5db523 100644 --- a/services/core/java/com/android/server/biometrics/face/FaceMetrics.java +++ b/services/core/java/com/android/server/biometrics/face/FaceConstants.java @@ -16,10 +16,12 @@ package com.android.server.biometrics.face; +import android.hardware.face.FaceManager; + import com.android.internal.logging.nano.MetricsProto; -import com.android.server.biometrics.Metrics; +import com.android.server.biometrics.Constants; -public class FaceMetrics implements Metrics { +public class FaceConstants implements Constants { @Override public String logTag() { return FaceService.TAG; @@ -64,4 +66,9 @@ public class FaceMetrics implements Metrics { public int actionBiometricEnroll() { return MetricsProto.MetricsEvent.ACTION_FACE_ENROLL; } + + @Override + public int acquireVendorCode() { + return FaceManager.FACE_ACQUIRED_VENDOR; + } } diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java index f5a96c786c88..feb58a3ad5ce 100644 --- a/services/core/java/com/android/server/biometrics/face/FaceService.java +++ b/services/core/java/com/android/server/biometrics/face/FaceService.java @@ -65,7 +65,7 @@ import com.android.server.biometrics.AuthenticationClient; import com.android.server.biometrics.BiometricServiceBase; import com.android.server.biometrics.BiometricUtils; import com.android.server.biometrics.EnumerateClient; -import com.android.server.biometrics.Metrics; +import com.android.server.biometrics.Constants; import com.android.server.biometrics.RemovalClient; import org.json.JSONArray; @@ -131,6 +131,26 @@ public class FaceService extends BiometricServiceBase { } @Override + public int[] getAcquireIgnorelist() { + if (isBiometricPrompt()) { + return mBiometricPromptIgnoreList; + } else { + // Keyguard + return mKeyguardIgnoreList; + } + } + + @Override + public int[] getAcquireVendorIgnorelist() { + if (isBiometricPrompt()) { + return mBiometricPromptIgnoreListVendor; + } else { + // Keyguard + return mKeyguardIgnoreListVendor; + } + } + + @Override public boolean onAcquired(int acquireInfo, int vendorCode) { if (acquireInfo == FaceManager.FACE_ACQUIRED_RECALIBRATE) { @@ -205,6 +225,17 @@ public class FaceService extends BiometricServiceBase { final EnrollClientImpl client = new EnrollClientImpl(getContext(), mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), mCurrentUserId, 0 /* groupId */, cryptoToken, restricted, opPackageName, disabledFeatures) { + + @Override + public int[] getAcquireIgnorelist() { + return mEnrollIgnoreList; + } + + @Override + public int[] getAcquireVendorIgnorelist() { + return mEnrollIgnoreListVendor; + } + @Override public boolean shouldVibrate() { return false; @@ -293,7 +324,7 @@ public class FaceService extends BiometricServiceBase { } final boolean restricted = isRestricted(); - final RemovalClient client = new RemovalClient(getContext(), getMetrics(), + final RemovalClient client = new RemovalClient(getContext(), getConstants(), mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), faceId, 0 /* groupId */, userId, restricted, token.toString(), getBiometricUtils()) { @Override @@ -310,7 +341,7 @@ public class FaceService extends BiometricServiceBase { checkPermission(MANAGE_BIOMETRIC); final boolean restricted = isRestricted(); - final EnumerateClient client = new EnumerateClient(getContext(), getMetrics(), + final EnumerateClient client = new EnumerateClient(getContext(), getConstants(), mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), userId, userId, restricted, getContext().getOpPackageName()) { @Override @@ -638,13 +669,20 @@ public class FaceService extends BiometricServiceBase { } } - private final FaceMetrics mFaceMetrics = new FaceMetrics(); + private final FaceConstants mFaceConstants = new FaceConstants(); @GuardedBy("this") private IBiometricsFace mDaemon; // One of the AuthenticationClient constants private int mCurrentUserLockoutMode; + private int[] mBiometricPromptIgnoreList; + private int[] mBiometricPromptIgnoreListVendor; + private int[] mKeyguardIgnoreList; + private int[] mKeyguardIgnoreListVendor; + private int[] mEnrollIgnoreList; + private int[] mEnrollIgnoreListVendor; + /** * Receives callbacks from the HAL. */ @@ -830,6 +868,19 @@ public class FaceService extends BiometricServiceBase { public FaceService(Context context) { super(context); + + mBiometricPromptIgnoreList = getContext().getResources() + .getIntArray(R.array.config_face_acquire_biometricprompt_ignorelist); + mBiometricPromptIgnoreListVendor = getContext().getResources() + .getIntArray(R.array.config_face_acquire_vendor_biometricprompt_ignorelist); + mKeyguardIgnoreList = getContext().getResources() + .getIntArray(R.array.config_face_acquire_keyguard_ignorelist); + mKeyguardIgnoreListVendor = getContext().getResources() + .getIntArray(R.array.config_face_acquire_vendor_keyguard_ignorelist); + mEnrollIgnoreList = getContext().getResources() + .getIntArray(R.array.config_face_acquire_enroll_ignorelist); + mEnrollIgnoreListVendor = getContext().getResources() + .getIntArray(R.array.config_face_acquire_vendor_enroll_ignorelist); } @Override @@ -855,8 +906,8 @@ public class FaceService extends BiometricServiceBase { } @Override - protected Metrics getMetrics() { - return mFaceMetrics; + protected Constants getConstants() { + return mFaceConstants; } @Override @@ -887,7 +938,7 @@ public class FaceService extends BiometricServiceBase { try { userId = getUserOrWorkProfileId(clientPackage, userId); if (userId != mCurrentUserId) { - final File baseDir = Environment.getDataVendorCeDirectory(userId); + final File baseDir = Environment.getDataVendorDeDirectory(userId); final File faceDir = new File(baseDir, FACE_DATA_DIR); if (!faceDir.exists()) { if (!faceDir.mkdir()) { diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintMetrics.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintConstants.java index a1115c8bcfa7..bdaff71a17b9 100644 --- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintMetrics.java +++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintConstants.java @@ -16,10 +16,12 @@ package com.android.server.biometrics.fingerprint; +import android.hardware.fingerprint.FingerprintManager; + import com.android.internal.logging.nano.MetricsProto; -import com.android.server.biometrics.Metrics; +import com.android.server.biometrics.Constants; -public class FingerprintMetrics implements Metrics { +public class FingerprintConstants implements Constants { @Override public String logTag() { @@ -65,4 +67,9 @@ public class FingerprintMetrics implements Metrics { public int actionBiometricEnroll() { return MetricsProto.MetricsEvent.ACTION_FINGERPRINT_ENROLL; } + + @Override + public int acquireVendorCode() { + return FingerprintManager.FINGERPRINT_ACQUIRED_VENDOR; + } } diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java index 6ebeaf9ebe59..d91670d20a54 100644 --- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java @@ -68,7 +68,7 @@ import com.android.server.biometrics.BiometricServiceBase; import com.android.server.biometrics.BiometricUtils; import com.android.server.biometrics.ClientMonitor; import com.android.server.biometrics.EnumerateClient; -import com.android.server.biometrics.Metrics; +import com.android.server.biometrics.Constants; import com.android.server.biometrics.RemovalClient; import org.json.JSONArray; @@ -284,7 +284,7 @@ public class FingerprintService extends BiometricServiceBase { } final boolean restricted = isRestricted(); - final RemovalClient client = new RemovalClient(getContext(), getMetrics(), + final RemovalClient client = new RemovalClient(getContext(), getConstants(), mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), fingerId, groupId, userId, restricted, token.toString(), getBiometricUtils()) { @Override @@ -301,7 +301,7 @@ public class FingerprintService extends BiometricServiceBase { checkPermission(MANAGE_FINGERPRINT); final boolean restricted = isRestricted(); - final EnumerateClient client = new EnumerateClient(getContext(), getMetrics(), + final EnumerateClient client = new EnumerateClient(getContext(), getConstants(), mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), userId, userId, restricted, getContext().getOpPackageName()) { @Override @@ -557,7 +557,7 @@ public class FingerprintService extends BiometricServiceBase { } } - private final FingerprintMetrics mFingerprintMetrics = new FingerprintMetrics(); + private final FingerprintConstants mFingerprintConstants = new FingerprintConstants(); private final CopyOnWriteArrayList<IFingerprintClientActiveCallback> mClientActiveCallbacks = new CopyOnWriteArrayList<>(); @@ -736,8 +736,8 @@ public class FingerprintService extends BiometricServiceBase { } @Override - protected Metrics getMetrics() { - return mFingerprintMetrics; + protected Constants getConstants() { + return mFingerprintConstants; } @Override diff --git a/services/core/java/com/android/server/biometrics/iris/IrisService.java b/services/core/java/com/android/server/biometrics/iris/IrisService.java index cb8a772356cc..2817315ac175 100644 --- a/services/core/java/com/android/server/biometrics/iris/IrisService.java +++ b/services/core/java/com/android/server/biometrics/iris/IrisService.java @@ -23,7 +23,7 @@ import android.hardware.biometrics.BiometricsProtoEnums; import com.android.server.biometrics.AuthenticationClient; import com.android.server.biometrics.BiometricServiceBase; import com.android.server.biometrics.BiometricUtils; -import com.android.server.biometrics.Metrics; +import com.android.server.biometrics.Constants; import java.util.List; @@ -75,7 +75,7 @@ public class IrisService extends BiometricServiceBase { } @Override - protected Metrics getMetrics() { + protected Constants getConstants() { return null; } diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java index ba4dcdbb7ee3..998ee1ed9ebe 100644 --- a/services/core/java/com/android/server/content/ContentService.java +++ b/services/core/java/com/android/server/content/ContentService.java @@ -1104,11 +1104,13 @@ public final class ContentService extends IContentService.Stub { @Override public void addStatusChangeListener(int mask, ISyncStatusObserver callback) { + final int callingUid = Binder.getCallingUid(); long identityToken = clearCallingIdentity(); try { SyncManager syncManager = getSyncManager(); if (syncManager != null && callback != null) { - syncManager.getSyncStorageEngine().addStatusChangeListener(mask, callback); + syncManager.getSyncStorageEngine().addStatusChangeListener( + mask, UserHandle.getUserId(callingUid), callback); } } finally { restoreCallingIdentity(identityToken); diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index 9f80a83038af..7e79a12d12eb 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -3375,7 +3375,8 @@ public class SyncManager { } scheduleSyncOperationH(op); - mSyncStorageEngine.reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); + mSyncStorageEngine.reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, + target.userId); } /** @@ -3877,7 +3878,8 @@ public class SyncManager { EventLog.writeEvent(2720, syncOperation.toEventLog(SyncStorageEngine.EVENT_STOP)); mSyncStorageEngine.stopSyncEvent(rowId, elapsedTime, - resultMessage, downstreamActivity, upstreamActivity); + resultMessage, downstreamActivity, upstreamActivity, + syncOperation.target.userId); } } diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java index 6b441a0d0f30..c7a3f4b173c3 100644 --- a/services/core/java/com/android/server/content/SyncStorageEngine.java +++ b/services/core/java/com/android/server/content/SyncStorageEngine.java @@ -43,6 +43,7 @@ import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.UserHandle; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.AtomicFile; import android.util.EventLog; import android.util.Log; @@ -54,6 +55,7 @@ import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastXmlSerializer; +import com.android.internal.util.IntPair; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -588,9 +590,10 @@ public class SyncStorageEngine { return mSyncRandomOffset; } - public void addStatusChangeListener(int mask, ISyncStatusObserver callback) { + public void addStatusChangeListener(int mask, int userId, ISyncStatusObserver callback) { synchronized (mAuthorities) { - mChangeListeners.register(callback, mask); + final long cookie = IntPair.of(userId, mask); + mChangeListeners.register(callback, cookie); } } @@ -622,14 +625,16 @@ public class SyncStorageEngine { } } - void reportChange(int which) { + void reportChange(int which, int callingUserId) { ArrayList<ISyncStatusObserver> reports = null; synchronized (mAuthorities) { int i = mChangeListeners.beginBroadcast(); while (i > 0) { i--; - Integer mask = (Integer)mChangeListeners.getBroadcastCookie(i); - if ((which & mask.intValue()) == 0) { + final long cookie = (long) mChangeListeners.getBroadcastCookie(i); + final int userId = IntPair.first(cookie); + final int mask = IntPair.second(cookie); + if ((which & mask) == 0 || callingUserId != userId) { continue; } if (reports == null) { @@ -719,7 +724,7 @@ public class SyncStorageEngine { new Bundle(), syncExemptionFlag, callingUid, callingPid); } - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); + reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, userId); queueBackup(); } @@ -787,7 +792,7 @@ public class SyncStorageEngine { requestSync(aInfo, SyncOperation.REASON_IS_SYNCABLE, new Bundle(), ContentResolver.SYNC_EXEMPTION_NONE, callingUid, callingPid); } - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); + reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, target.userId); } public Pair<Long, Long> getBackoff(EndPoint info) { @@ -833,7 +838,7 @@ public class SyncStorageEngine { } } if (changed) { - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); + reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, info.userId); } } @@ -871,7 +876,7 @@ public class SyncStorageEngine { } public void clearAllBackoffsLocked() { - boolean changed = false; + final ArraySet<Integer> changedUserIds = new ArraySet<>(); synchronized (mAuthorities) { // Clear backoff for all sync adapters. for (AccountInfo accountInfo : mAccounts.values()) { @@ -888,14 +893,14 @@ public class SyncStorageEngine { } authorityInfo.backoffTime = NOT_IN_BACKOFF_MODE; authorityInfo.backoffDelay = NOT_IN_BACKOFF_MODE; - changed = true; + changedUserIds.add(accountInfo.accountAndUser.userId); } } } } - if (changed) { - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); + for (int i = changedUserIds.size() - 1; i > 0; i--) { + reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, changedUserIds.valueAt(i)); } } @@ -921,7 +926,7 @@ public class SyncStorageEngine { } authority.delayUntil = delayUntil; } - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); + reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, info.userId); } /** @@ -964,7 +969,7 @@ public class SyncStorageEngine { new Bundle(), syncExemptionFlag, callingUid, callingPid); } - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); + reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, userId); mContext.sendBroadcast(ContentResolver.ACTION_SYNC_CONN_STATUS_CHANGED); queueBackup(); } @@ -1015,7 +1020,7 @@ public class SyncStorageEngine { SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident); status.pending = pendingValue; } - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING); + reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING, info.userId); } /** @@ -1103,7 +1108,7 @@ public class SyncStorageEngine { activeSyncContext.mStartTime); getCurrentSyncs(authorityInfo.target.userId).add(syncInfo); } - reportActiveChange(); + reportActiveChange(activeSyncContext.mSyncOperation.target.userId); return syncInfo; } @@ -1120,14 +1125,14 @@ public class SyncStorageEngine { getCurrentSyncs(userId).remove(syncInfo); } - reportActiveChange(); + reportActiveChange(userId); } /** * To allow others to send active change reports, to poke clients. */ - public void reportActiveChange() { - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE); + public void reportActiveChange(int userId) { + reportChange(ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE, userId); } /** @@ -1162,12 +1167,12 @@ public class SyncStorageEngine { if (Log.isLoggable(TAG, Log.VERBOSE)) Slog.v(TAG, "returning historyId " + id); } - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS); + reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS, op.target.userId); return id; } public void stopSyncEvent(long historyId, long elapsedTime, String resultMessage, - long downstreamActivity, long upstreamActivity) { + long downstreamActivity, long upstreamActivity, int userId) { synchronized (mAuthorities) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Slog.v(TAG, "stopSyncEvent: historyId=" + historyId); @@ -1307,7 +1312,7 @@ public class SyncStorageEngine { } } - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS); + reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS, userId); } /** diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 3abd0ba29871..6d0137551729 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -16,12 +16,15 @@ package com.android.server.display; +import static android.Manifest.permission.CAPTURE_SECURE_VIDEO_OUTPUT; +import static android.Manifest.permission.CAPTURE_VIDEO_OUTPUT; +import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR; -import static android.hardware.display.DisplayManager - .VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD; +import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE; +import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS; import static android.hardware.display.DisplayViewport.VIEWPORT_EXTERNAL; import static android.hardware.display.DisplayViewport.VIEWPORT_INTERNAL; import static android.hardware.display.DisplayViewport.VIEWPORT_VIRTUAL; @@ -1979,6 +1982,18 @@ public final class DisplayManagerService extends SystemService { } } + // Sometimes users can have sensitive information in system decoration windows. An app + // could create a virtual display with system decorations support and read the user info + // from the surface. + // We should only allow adding flag VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS + // to virtual displays that are owned by the system. + if (callingUid != Process.SYSTEM_UID + && (flags & VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0) { + if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "createVirtualDisplay()")) { + throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission"); + } + } + final long token = Binder.clearCallingIdentity(); try { return createVirtualDisplayInternal(callback, projection, callingUid, packageName, @@ -2279,9 +2294,7 @@ public final class DisplayManagerService extends SystemService { Slog.e(TAG, "Unable to query projection service for permissions", e); } } - if (mContext.checkCallingPermission( - android.Manifest.permission.CAPTURE_VIDEO_OUTPUT) - == PackageManager.PERMISSION_GRANTED) { + if (checkCallingPermission(CAPTURE_VIDEO_OUTPUT, "canProjectVideo()")) { return true; } return canProjectSecureVideo(projection); @@ -2297,9 +2310,17 @@ public final class DisplayManagerService extends SystemService { Slog.e(TAG, "Unable to query projection service for permissions", e); } } - return mContext.checkCallingPermission( - android.Manifest.permission.CAPTURE_SECURE_VIDEO_OUTPUT) - == PackageManager.PERMISSION_GRANTED; + return checkCallingPermission(CAPTURE_SECURE_VIDEO_OUTPUT, "canProjectSecureVideo()"); + } + + private boolean checkCallingPermission(String permission, String func) { + if (mContext.checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED) { + return true; + } + final String msg = "Permission Denial: " + func + " from pid=" + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid() + " requires " + permission; + Slog.w(TAG, msg); + return false; } } diff --git a/services/core/java/com/android/server/gpu/GpuService.java b/services/core/java/com/android/server/gpu/GpuService.java index 0f73f379900b..d4396534d067 100644 --- a/services/core/java/com/android/server/gpu/GpuService.java +++ b/services/core/java/com/android/server/gpu/GpuService.java @@ -37,6 +37,7 @@ import android.os.Handler; import android.os.SystemProperties; import android.os.UserHandle; import android.provider.DeviceConfig; +import android.provider.DeviceConfig.Properties; import android.provider.Settings; import android.util.Base64; import android.util.Slog; @@ -138,18 +139,19 @@ public class GpuService extends SystemService { } } - private final class DeviceConfigListener implements DeviceConfig.OnPropertyChangedListener { + private final class DeviceConfigListener implements DeviceConfig.OnPropertiesChangedListener { DeviceConfigListener() { super(); - DeviceConfig.addOnPropertyChangedListener(DeviceConfig.NAMESPACE_GAME_DRIVER, + DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_GAME_DRIVER, mContext.getMainExecutor(), this); } @Override - public void onPropertyChanged(String namespace, String name, String value) { + public void onPropertiesChanged(Properties properties) { synchronized (mDeviceConfigLock) { - if (Settings.Global.GAME_DRIVER_BLACKLISTS.equals(name)) { - parseBlacklists(value != null ? value : ""); + if (properties.getKeyset().contains(Settings.Global.GAME_DRIVER_BLACKLISTS)) { + parseBlacklists( + properties.getString(Settings.Global.GAME_DRIVER_BLACKLISTS, "")); setBlacklist(); } } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 651ce7d431fe..30d244f1bcce 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -1409,7 +1409,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mIWindowManager = IWindowManager.Stub.asInterface( ServiceManager.getService(Context.WINDOW_SERVICE)); mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class); - mImeDisplayValidator = mWindowManagerInternal::shouldShowSystemDecorOnDisplay; + mImeDisplayValidator = displayId -> mWindowManagerInternal.shouldShowIme(displayId); mCaller = new HandlerCaller(context, null, new HandlerCaller.Callback() { @Override public void executeMessage(Message msg) { @@ -2139,7 +2139,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (displayId == DEFAULT_DISPLAY || displayId == INVALID_DISPLAY) { return FALLBACK_DISPLAY_ID; } - // Show IME window on fallback display when the display is not allowed. + + // Show IME window on fallback display when the display doesn't support system decorations + // or the display is virtual and isn't owned by system for security concern. return checker.displayCanShowIme(displayId) ? displayId : FALLBACK_DISPLAY_ID; } diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java index 580150ed887e..e0b8e71ef713 100644 --- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java @@ -79,6 +79,9 @@ import com.android.internal.inputmethod.StartInputReason; import com.android.internal.inputmethod.UnbindReason; import com.android.internal.messages.nano.SystemMessageProto; import com.android.internal.notification.SystemNotificationChannels; +import com.android.internal.os.TransferPipe; +import com.android.internal.util.DumpUtils; +import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.function.pooled.PooledLambda; import com.android.internal.view.IInputContext; import com.android.internal.view.IInputMethodClient; @@ -90,6 +93,8 @@ import com.android.server.SystemService; import com.android.server.wm.WindowManagerInternal; import java.io.FileDescriptor; +import java.io.IOException; +import java.io.PrintWriter; import java.lang.annotation.Retention; import java.util.Collections; import java.util.List; @@ -645,6 +650,14 @@ public final class MultiClientInputMethodManagerService { mSelfReportedDisplayId = selfReportedDisplayId; mClientId = InputMethodClientIdSource.getNext(); } + + @GuardedBy("PerUserData.mLock") + void dumpLocked(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) { + ipw.println("mState=" + mState + ",mBindingSequence=" + mBindingSequence + + ",mWriteChannel=" + mWriteChannel + + ",mInputMethodSession=" + mInputMethodSession + + ",mMSInputMethodSession=" + mMSInputMethodSession); + } } private static final class UserDataMap { @@ -673,6 +686,22 @@ public final class MultiClientInputMethodManagerService { return mMap.removeReturnOld(userId); } } + + @AnyThread + void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) { + synchronized (mMap) { + for (int i = 0; i < mMap.size(); i++) { + int userId = mMap.keyAt(i); + PerUserData data = mMap.valueAt(i); + ipw.println("userId=" + userId + ", data="); + if (data != null) { + ipw.increaseIndent(); + data.dump(fd, ipw, args); + ipw.decreaseIndent(); + } + } + } + } } private static final class TokenInfo { @@ -967,6 +996,71 @@ public final class MultiClientInputMethodManagerService { } } + @AnyThread + void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) { + synchronized (mLock) { + ipw.println("mState=" + mState + + ",mCurrentInputMethod=" + mCurrentInputMethod + + ",mCurrentInputMethodInfo=" + mCurrentInputMethodInfo); + + if (mCurrentInputMethod != null) { + // indentation will not be kept. So add visual separator here. + ipw.println(">>Dump CurrentInputMethod>>"); + ipw.flush(); + try { + TransferPipe.dumpAsync(mCurrentInputMethod.asBinder(), fd, args); + } catch (IOException | RemoteException e) { + ipw.println("Failed to dump input method service: " + e); + } + ipw.println("<<Dump CurrentInputMethod<<"); + } + + ipw.println("mDisplayIdToImeWindowTokenMap="); + for (TokenInfo info : mDisplayIdToImeWindowTokenMap) { + ipw.println(" display=" + info.mDisplayId + ",token=" + + info.mToken); + } + ipw.println("mClientMap="); + ipw.increaseIndent(); + for (int i = 0; i < mClientMap.size(); i++) { + + ipw.println("binder=" + mClientMap.keyAt(i)); + ipw.println(" InputMethodClientInfo="); + InputMethodClientInfo info = mClientMap.valueAt(i); + if (info != null) { + ipw.increaseIndent(); + info.dumpLocked(fd, ipw, args); + ipw.decreaseIndent(); + } + } + ipw.decreaseIndent(); + ipw.println("mClientIdToClientMap="); + ipw.increaseIndent(); + for (int i = 0; i < mClientIdToClientMap.size(); i++) { + ipw.println("clientId=" + mClientIdToClientMap.keyAt(i)); + ipw.println(" InputMethodClientInfo="); + InputMethodClientInfo info = mClientIdToClientMap.valueAt(i); + if (info != null) { + ipw.increaseIndent(); + info.dumpLocked(fd, ipw, args); + ipw.decreaseIndent(); + } + if (info.mClient != null) { + // indentation will not be kept. So add visual separator here. + ipw.println(">>DumpClientStart>>"); + ipw.flush(); // all writes should be flushed to guarantee order. + try { + TransferPipe.dumpAsync(info.mClient.asBinder(), fd, args); + } catch (IOException | RemoteException e) { + ipw.println(" Failed to dump client:" + e); + } + ipw.println("<<DumpClientEnd<<"); + } + } + ipw.decreaseIndent(); + } + } + private static final class ClientDeathRecipient implements IBinder.DeathRecipient { private final PerUserData mPerUserData; private final IInputMethodClient mClient; @@ -1106,6 +1200,16 @@ public final class MultiClientInputMethodManagerService { } return Collections.singletonList(info); } + + @AnyThread + void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) { + synchronized (mArray) { + for (int i = 0; i < mArray.size(); i++) { + ipw.println("userId=" + mArray.keyAt(i)); + ipw.println(" InputMethodInfo=" + mArray.valueAt(i)); + } + } + } } /** @@ -1601,5 +1705,19 @@ public final class MultiClientInputMethodManagerService { @Nullable FileDescriptor err, String[] args, @Nullable ShellCallback callback, ResultReceiver resultReceiver) { } + + @BinderThread + @Override + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; + final String prefixChild = " "; + pw.println("Current Multi Client Input Method Manager state:"); + IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); + ipw.println("mUserDataMap="); + if (mUserDataMap != null) { + ipw.increaseIndent(); + mUserDataMap.dump(fd, ipw, args); + } + } } } diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java index d5883bb3befb..b676618f4cd3 100644 --- a/services/core/java/com/android/server/job/JobSchedulerService.java +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -18,6 +18,7 @@ package com.android.server.job; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER; +import static android.text.format.DateUtils.MINUTE_IN_MILLIS; import android.annotation.NonNull; import android.annotation.UserIdInt; @@ -463,32 +464,6 @@ public class JobSchedulerService extends com.android.server.SystemService private static final String KEY_CONN_CONGESTION_DELAY_FRAC = "conn_congestion_delay_frac"; private static final String KEY_CONN_PREFETCH_RELAX_FRAC = "conn_prefetch_relax_frac"; private static final String KEY_USE_HEARTBEATS = "use_heartbeats"; - private static final String KEY_TIME_CONTROLLER_SKIP_NOT_READY_JOBS = - "tc_skip_not_ready_jobs"; - private static final String KEY_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS = - "qc_allowed_time_per_period_ms"; - private static final String KEY_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS = - "qc_in_quota_buffer_ms"; - private static final String KEY_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS = - "qc_window_size_active_ms"; - private static final String KEY_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS = - "qc_window_size_working_ms"; - private static final String KEY_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS = - "qc_window_size_frequent_ms"; - private static final String KEY_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS = - "qc_window_size_rare_ms"; - private static final String KEY_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS = - "qc_max_execution_time_ms"; - private static final String KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE = - "qc_max_job_count_active"; - private static final String KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING = - "qc_max_job_count_working"; - private static final String KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT = - "qc_max_job_count_frequent"; - private static final String KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE = - "qc_max_job_count_rare"; - private static final String KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME = - "qc_max_count_per_allowed_time"; private static final int DEFAULT_MIN_IDLE_COUNT = 1; private static final int DEFAULT_MIN_CHARGING_COUNT = 1; @@ -510,30 +485,6 @@ public class JobSchedulerService extends com.android.server.SystemService private static final float DEFAULT_CONN_CONGESTION_DELAY_FRAC = 0.5f; private static final float DEFAULT_CONN_PREFETCH_RELAX_FRAC = 0.5f; private static final boolean DEFAULT_USE_HEARTBEATS = false; - private static final boolean DEFAULT_TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true; - private static final long DEFAULT_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS = - 10 * 60 * 1000L; // 10 minutes - private static final long DEFAULT_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS = - 30 * 1000L; // 30 seconds - private static final long DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS = - 10 * 60 * 1000L; // 10 minutes for ACTIVE -- ACTIVE apps can run jobs at any time - private static final long DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS = - 2 * 60 * 60 * 1000L; // 2 hours - private static final long DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS = - 8 * 60 * 60 * 1000L; // 8 hours - private static final long DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS = - 24 * 60 * 60 * 1000L; // 24 hours - private static final long DEFAULT_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS = - 4 * 60 * 60 * 1000L; // 4 hours - private static final int DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE = - 200; // 1200/hr - private static final int DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING = - 1200; // 600/hr - private static final int DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT = - 1800; // 225/hr - private static final int DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE = - 2400; // 100/hr - private static final int DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME = 20; /** * Minimum # of idle jobs that must be ready in order to force the JMS to schedule things @@ -680,97 +631,6 @@ public class JobSchedulerService extends com.android.server.SystemService */ public boolean USE_HEARTBEATS = DEFAULT_USE_HEARTBEATS; - /** - * Whether or not TimeController should skip setting wakeup alarms for jobs that aren't - * ready now. - */ - public boolean TIME_CONTROLLER_SKIP_NOT_READY_JOBS = - DEFAULT_TIME_CONTROLLER_SKIP_NOT_READY_JOBS; - - /** How much time each app will have to run jobs within their standby bucket window. */ - public long QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS = - DEFAULT_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS; - - /** - * How much time the package should have before transitioning from out-of-quota to in-quota. - * This should not affect processing if the package is already in-quota. - */ - public long QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS = - DEFAULT_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS; - - /** - * The quota window size of the particular standby bucket. Apps in this standby bucket are - * expected to run only {@link #QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past - * WINDOW_SIZE_MS. - */ - public long QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS = - DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS; - - /** - * The quota window size of the particular standby bucket. Apps in this standby bucket are - * expected to run only {@link #QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past - * WINDOW_SIZE_MS. - */ - public long QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS = - DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS; - - /** - * The quota window size of the particular standby bucket. Apps in this standby bucket are - * expected to run only {@link #QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past - * WINDOW_SIZE_MS. - */ - public long QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS = - DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS; - - /** - * The quota window size of the particular standby bucket. Apps in this standby bucket are - * expected to run only {@link #QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past - * WINDOW_SIZE_MS. - */ - public long QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS = - DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS; - - /** - * The maximum amount of time an app can have its jobs running within a 24 hour window. - */ - public long QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS = - DEFAULT_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS; - - /** - * The maximum number of jobs an app can run within this particular standby bucket's - * window size. - */ - public int QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE = - DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE; - - /** - * The maximum number of jobs an app can run within this particular standby bucket's - * window size. - */ - public int QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING = - DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING; - - /** - * The maximum number of jobs an app can run within this particular standby bucket's - * window size. - */ - public int QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT = - DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT; - - /** - * The maximum number of jobs an app can run within this particular standby bucket's - * window size. - */ - public int QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE = - DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE; - - /** - * The maximum number of jobs that can run within the past - * {@link #QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS}. - */ - public int QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME = - DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME; - private final KeyValueListParser mParser = new KeyValueListParser(','); void updateConstantsLocked(String value) { @@ -834,45 +694,6 @@ public class JobSchedulerService extends com.android.server.SystemService CONN_PREFETCH_RELAX_FRAC = mParser.getFloat(KEY_CONN_PREFETCH_RELAX_FRAC, DEFAULT_CONN_PREFETCH_RELAX_FRAC); USE_HEARTBEATS = mParser.getBoolean(KEY_USE_HEARTBEATS, DEFAULT_USE_HEARTBEATS); - TIME_CONTROLLER_SKIP_NOT_READY_JOBS = mParser.getBoolean( - KEY_TIME_CONTROLLER_SKIP_NOT_READY_JOBS, - DEFAULT_TIME_CONTROLLER_SKIP_NOT_READY_JOBS); - QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS = mParser.getDurationMillis( - KEY_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS, - DEFAULT_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS); - QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS = mParser.getDurationMillis( - KEY_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS, - DEFAULT_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS); - QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS = mParser.getDurationMillis( - KEY_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS, - DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS); - QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS = mParser.getDurationMillis( - KEY_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS, - DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS); - QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS = mParser.getDurationMillis( - KEY_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS, - DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS); - QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS = mParser.getDurationMillis( - KEY_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS, - DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS); - QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS = mParser.getDurationMillis( - KEY_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS, - DEFAULT_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS); - QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE = mParser.getInt( - KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE, - DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE); - QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING = mParser.getInt( - KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING, - DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING); - QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT = mParser.getInt( - KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT, - DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT); - QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE = mParser.getInt( - KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE, - DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE); - QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME = mParser.getInt( - KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME, - DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME); } void dump(IndentingPrintWriter pw) { @@ -915,37 +736,11 @@ public class JobSchedulerService extends com.android.server.SystemService pw.printPair(KEY_CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC).println(); pw.printPair(KEY_CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC).println(); pw.printPair(KEY_USE_HEARTBEATS, USE_HEARTBEATS).println(); - pw.printPair(KEY_TIME_CONTROLLER_SKIP_NOT_READY_JOBS, - TIME_CONTROLLER_SKIP_NOT_READY_JOBS).println(); - pw.printPair(KEY_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS, - QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS).println(); - pw.printPair(KEY_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS, - QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS).println(); - pw.printPair(KEY_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS, - QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS).println(); - pw.printPair(KEY_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS, - QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS).println(); - pw.printPair(KEY_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS, - QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS).println(); - pw.printPair(KEY_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS, - QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS).println(); - pw.printPair(KEY_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS, - QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS).println(); - pw.printPair(KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE, - QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE).println(); - pw.printPair(KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING, - QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING).println(); - pw.printPair(KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT, - QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT).println(); - pw.printPair(KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE, - QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE).println(); - pw.printPair(KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME, - QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME).println(); + pw.decreaseIndent(); } - void dump(ProtoOutputStream proto, long fieldId) { - final long token = proto.start(fieldId); + void dump(ProtoOutputStream proto) { proto.write(ConstantsProto.MIN_IDLE_COUNT, MIN_IDLE_COUNT); proto.write(ConstantsProto.MIN_CHARGING_COUNT, MIN_CHARGING_COUNT); proto.write(ConstantsProto.MIN_BATTERY_NOT_LOW_COUNT, MIN_BATTERY_NOT_LOW_COUNT); @@ -973,40 +768,6 @@ public class JobSchedulerService extends com.android.server.SystemService proto.write(ConstantsProto.CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC); proto.write(ConstantsProto.CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC); proto.write(ConstantsProto.USE_HEARTBEATS, USE_HEARTBEATS); - - final long tcToken = proto.start(ConstantsProto.TIME_CONTROLLER); - proto.write(ConstantsProto.TimeController.SKIP_NOT_READY_JOBS, - TIME_CONTROLLER_SKIP_NOT_READY_JOBS); - proto.end(tcToken); - - final long qcToken = proto.start(ConstantsProto.QUOTA_CONTROLLER); - proto.write(ConstantsProto.QuotaController.ALLOWED_TIME_PER_PERIOD_MS, - QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS); - proto.write(ConstantsProto.QuotaController.IN_QUOTA_BUFFER_MS, - QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS); - proto.write(ConstantsProto.QuotaController.ACTIVE_WINDOW_SIZE_MS, - QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS); - proto.write(ConstantsProto.QuotaController.WORKING_WINDOW_SIZE_MS, - QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS); - proto.write(ConstantsProto.QuotaController.FREQUENT_WINDOW_SIZE_MS, - QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS); - proto.write(ConstantsProto.QuotaController.RARE_WINDOW_SIZE_MS, - QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS); - proto.write(ConstantsProto.QuotaController.MAX_EXECUTION_TIME_MS, - QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS); - proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_ACTIVE, - QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE); - proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_WORKING, - QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING); - proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_FREQUENT, - QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT); - proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_RARE, - QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE); - proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_PER_ALLOWED_TIME, - QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME); - proto.end(qcToken); - - proto.end(token); } } @@ -1636,6 +1397,9 @@ public class JobSchedulerService extends com.android.server.SystemService public void onBootPhase(int phase) { if (PHASE_SYSTEM_SERVICES_READY == phase) { mConstantsObserver.start(getContext().getContentResolver()); + for (StateController controller : mControllers) { + controller.onSystemServicesReady(); + } mAppStateTracker = Preconditions.checkNotNull( LocalServices.getService(AppStateTracker.class)); @@ -1801,7 +1565,8 @@ public class JobSchedulerService extends com.android.server.SystemService * * @see #maybeQueueReadyJobsForExecutionLocked */ - private JobStatus getRescheduleJobForFailureLocked(JobStatus failureToReschedule) { + @VisibleForTesting + JobStatus getRescheduleJobForFailureLocked(JobStatus failureToReschedule) { final long elapsedNowMillis = sElapsedRealtimeClock.millis(); final JobInfo job = failureToReschedule.getJob(); @@ -1848,6 +1613,10 @@ public class JobSchedulerService extends com.android.server.SystemService elapsedNowMillis + delayMillis, JobStatus.NO_LATEST_RUNTIME, backoffAttempts, failureToReschedule.getLastSuccessfulRunTime(), sSystemClock.millis()); + if (job.isPeriodic()) { + newJob.setOriginalLatestRunTimeElapsed( + failureToReschedule.getOriginalLatestRunTimeElapsed()); + } for (int ic=0; ic<mControllers.size(); ic++) { StateController controller = mControllers.get(ic); controller.rescheduleForFailureLocked(newJob, failureToReschedule); @@ -1868,23 +1637,41 @@ public class JobSchedulerService extends com.android.server.SystemService * @return A new job representing the execution criteria for this instantiation of the * recurring job. */ - private JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) { + @VisibleForTesting + JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) { final long elapsedNow = sElapsedRealtimeClock.millis(); - // Compute how much of the period is remaining. - long runEarly = 0L; - - // If this periodic was rescheduled it won't have a deadline. - if (periodicToReschedule.hasDeadlineConstraint()) { - runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0L); + final long newLatestRuntimeElapsed; + final long period = periodicToReschedule.getJob().getIntervalMillis(); + final long latestRunTimeElapsed = periodicToReschedule.getOriginalLatestRunTimeElapsed(); + final long flex = periodicToReschedule.getJob().getFlexMillis(); + + if (elapsedNow > latestRunTimeElapsed) { + // The job ran past its expected run window. Have it count towards the current window + // and schedule a new job for the next window. + if (DEBUG) { + Slog.i(TAG, "Periodic job ran after its intended window."); + } + final long diffMs = (elapsedNow - latestRunTimeElapsed); + int numSkippedWindows = (int) (diffMs / period) + 1; // +1 to include original window + if (period != flex && diffMs > Math.min(30 * MINUTE_IN_MILLIS, (period - flex) / 2)) { + if (DEBUG) { + Slog.d(TAG, "Custom flex job ran too close to next window."); + } + // For custom flex periods, if the job was run too close to the next window, + // skip the next window and schedule for the following one. + numSkippedWindows += 1; + } + newLatestRuntimeElapsed = latestRunTimeElapsed + (period * numSkippedWindows); + } else { + newLatestRuntimeElapsed = latestRunTimeElapsed + period; } - long flex = periodicToReschedule.getJob().getFlexMillis(); - long period = periodicToReschedule.getJob().getIntervalMillis(); - long newLatestRuntimeElapsed = elapsedNow + runEarly + period; - long newEarliestRunTimeElapsed = newLatestRuntimeElapsed - flex; + + final long newEarliestRunTimeElapsed = newLatestRuntimeElapsed - flex; if (DEBUG) { Slog.v(TAG, "Rescheduling executed periodic. New execution window [" + - newEarliestRunTimeElapsed/1000 + ", " + newLatestRuntimeElapsed/1000 + "]s"); + newEarliestRunTimeElapsed / 1000 + ", " + newLatestRuntimeElapsed / 1000 + + "]s"); } return new JobStatus(periodicToReschedule, getCurrentHeartbeat(), newEarliestRunTimeElapsed, newLatestRuntimeElapsed, @@ -3424,6 +3211,11 @@ public class JobSchedulerService extends com.android.server.SystemService }; synchronized (mLock) { mConstants.dump(pw); + for (StateController controller : mControllers) { + pw.increaseIndent(); + controller.dumpConstants(pw); + pw.decreaseIndent(); + } pw.println(); pw.println(" Heartbeat:"); @@ -3614,7 +3406,13 @@ public class JobSchedulerService extends com.android.server.SystemService }; synchronized (mLock) { - mConstants.dump(proto, JobSchedulerServiceDumpProto.SETTINGS); + final long settingsToken = proto.start(JobSchedulerServiceDumpProto.SETTINGS); + mConstants.dump(proto); + for (StateController controller : mControllers) { + controller.dumpConstants(proto); + } + proto.end(settingsToken); + proto.write(JobSchedulerServiceDumpProto.CURRENT_HEARTBEAT, mHeartbeat); proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT, mNextBucketHeartbeat[0]); proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT, mNextBucketHeartbeat[1]); diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java index 48f21e446edc..fd20e116d82a 100644 --- a/services/core/java/com/android/server/job/controllers/JobStatus.java +++ b/services/core/java/com/android/server/job/controllers/JobStatus.java @@ -161,6 +161,12 @@ public final class JobStatus { */ private final long latestRunTimeElapsedMillis; + /** + * Valid only for periodic jobs. The original latest point in the future at which this + * job was expected to run. + */ + private long mOriginalLatestRunTimeElapsedMillis; + /** How many times this job has failed, used to compute back-off. */ private final int numFailures; @@ -394,6 +400,7 @@ public final class JobStatus { this.earliestRunTimeElapsedMillis = earliestRunTimeElapsedMillis; this.latestRunTimeElapsedMillis = latestRunTimeElapsedMillis; + this.mOriginalLatestRunTimeElapsedMillis = latestRunTimeElapsedMillis; this.numFailures = numFailures; int requiredConstraints = job.getConstraintFlags(); @@ -871,6 +878,14 @@ public final class JobStatus { return latestRunTimeElapsedMillis; } + public long getOriginalLatestRunTimeElapsed() { + return mOriginalLatestRunTimeElapsedMillis; + } + + public void setOriginalLatestRunTimeElapsed(long latestRunTimeElapsed) { + mOriginalLatestRunTimeElapsedMillis = latestRunTimeElapsed; + } + /** * Return the fractional position of "now" within the "run time" window of * this job. diff --git a/services/core/java/com/android/server/job/controllers/QuotaController.java b/services/core/java/com/android/server/job/controllers/QuotaController.java index 11f0939a27d6..2a9d3f30c782 100644 --- a/services/core/java/com/android/server/job/controllers/QuotaController.java +++ b/services/core/java/com/android/server/job/controllers/QuotaController.java @@ -34,9 +34,12 @@ import android.app.IUidObserver; import android.app.usage.UsageStatsManagerInternal; import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener; import android.content.BroadcastReceiver; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.database.ContentObserver; +import android.net.Uri; import android.os.BatteryManager; import android.os.BatteryManagerInternal; import android.os.Handler; @@ -44,8 +47,10 @@ import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.os.UserHandle; +import android.provider.Settings; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.KeyValueListParser; import android.util.Log; import android.util.Slog; import android.util.SparseArray; @@ -57,6 +62,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.BackgroundThread; import com.android.internal.util.IndentingPrintWriter; import com.android.server.LocalServices; +import com.android.server.job.ConstantsProto; import com.android.server.job.JobSchedulerService; import com.android.server.job.StateControllerProto; @@ -354,6 +360,7 @@ public final class QuotaController extends StateController { private final AlarmManager mAlarmManager; private final ChargingTracker mChargeTracker; private final Handler mHandler; + private final QcConstants mQcConstants; private volatile boolean mInParole; @@ -489,6 +496,7 @@ public final class QuotaController extends StateController { mChargeTracker.startTracking(); mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); + mQcConstants = new QcConstants(mHandler); final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); mContext.registerReceiverAsUser(mPackageAddedReceiver, UserHandle.ALL, filter, null, null); @@ -506,7 +514,12 @@ public final class QuotaController extends StateController { // ignored; both services live in system_server } - onConstantsUpdatedLocked(); + mShouldThrottle = !mConstants.USE_HEARTBEATS; + } + + @Override + public void onSystemServicesReady() { + mQcConstants.start(mContext.getContentResolver()); } @Override @@ -581,89 +594,9 @@ public final class QuotaController extends StateController { @Override public void onConstantsUpdatedLocked() { - boolean changed = false; if (mShouldThrottle == mConstants.USE_HEARTBEATS) { mShouldThrottle = !mConstants.USE_HEARTBEATS; - changed = true; - } - long newAllowedTimeMs = Math.min(MAX_PERIOD_MS, - Math.max(MINUTE_IN_MILLIS, mConstants.QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS)); - if (mAllowedTimePerPeriodMs != newAllowedTimeMs) { - mAllowedTimePerPeriodMs = newAllowedTimeMs; - mAllowedTimeIntoQuotaMs = mAllowedTimePerPeriodMs - mQuotaBufferMs; - changed = true; - } - long newQuotaBufferMs = Math.max(0, - Math.min(5 * MINUTE_IN_MILLIS, mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS)); - if (mQuotaBufferMs != newQuotaBufferMs) { - mQuotaBufferMs = newQuotaBufferMs; - mAllowedTimeIntoQuotaMs = mAllowedTimePerPeriodMs - mQuotaBufferMs; - mMaxExecutionTimeIntoQuotaMs = mMaxExecutionTimeMs - mQuotaBufferMs; - changed = true; - } - long newActivePeriodMs = Math.max(mAllowedTimePerPeriodMs, - Math.min(MAX_PERIOD_MS, mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS)); - if (mBucketPeriodsMs[ACTIVE_INDEX] != newActivePeriodMs) { - mBucketPeriodsMs[ACTIVE_INDEX] = newActivePeriodMs; - changed = true; - } - long newWorkingPeriodMs = Math.max(mAllowedTimePerPeriodMs, - Math.min(MAX_PERIOD_MS, mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS)); - if (mBucketPeriodsMs[WORKING_INDEX] != newWorkingPeriodMs) { - mBucketPeriodsMs[WORKING_INDEX] = newWorkingPeriodMs; - changed = true; - } - long newFrequentPeriodMs = Math.max(mAllowedTimePerPeriodMs, - Math.min(MAX_PERIOD_MS, mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS)); - if (mBucketPeriodsMs[FREQUENT_INDEX] != newFrequentPeriodMs) { - mBucketPeriodsMs[FREQUENT_INDEX] = newFrequentPeriodMs; - changed = true; - } - long newRarePeriodMs = Math.max(mAllowedTimePerPeriodMs, - Math.min(MAX_PERIOD_MS, mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS)); - if (mBucketPeriodsMs[RARE_INDEX] != newRarePeriodMs) { - mBucketPeriodsMs[RARE_INDEX] = newRarePeriodMs; - changed = true; - } - long newMaxExecutionTimeMs = Math.max(60 * MINUTE_IN_MILLIS, - Math.min(MAX_PERIOD_MS, mConstants.QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS)); - if (mMaxExecutionTimeMs != newMaxExecutionTimeMs) { - mMaxExecutionTimeMs = newMaxExecutionTimeMs; - mMaxExecutionTimeIntoQuotaMs = mMaxExecutionTimeMs - mQuotaBufferMs; - changed = true; - } - int newMaxCountPerAllowedPeriod = Math.max(10, - mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME); - if (mMaxJobCountPerAllowedTime != newMaxCountPerAllowedPeriod) { - mMaxJobCountPerAllowedTime = newMaxCountPerAllowedPeriod; - changed = true; - } - int newActiveMaxJobCount = Math.max(mMaxJobCountPerAllowedTime, - Math.max(MIN_BUCKET_JOB_COUNT, mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE)); - if (mMaxBucketJobCounts[ACTIVE_INDEX] != newActiveMaxJobCount) { - mMaxBucketJobCounts[ACTIVE_INDEX] = newActiveMaxJobCount; - changed = true; - } - int newWorkingMaxJobCount = Math.max(mMaxJobCountPerAllowedTime, - Math.max(MIN_BUCKET_JOB_COUNT, mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING)); - if (mMaxBucketJobCounts[WORKING_INDEX] != newWorkingMaxJobCount) { - mMaxBucketJobCounts[WORKING_INDEX] = newWorkingMaxJobCount; - changed = true; - } - int newFrequentMaxJobCount = Math.max(mMaxJobCountPerAllowedTime, - Math.max(MIN_BUCKET_JOB_COUNT, mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT)); - if (mMaxBucketJobCounts[FREQUENT_INDEX] != newFrequentMaxJobCount) { - mMaxBucketJobCounts[FREQUENT_INDEX] = newFrequentMaxJobCount; - changed = true; - } - int newRareMaxJobCount = Math.max(mMaxJobCountPerAllowedTime, - Math.max(MIN_BUCKET_JOB_COUNT, mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE)); - if (mMaxBucketJobCounts[RARE_INDEX] != newRareMaxJobCount) { - mMaxBucketJobCounts[RARE_INDEX] = newRareMaxJobCount; - changed = true; - } - if (changed) { // Update job bookkeeping out of band. BackgroundThread.getHandler().post(() -> { synchronized (mLock) { @@ -1891,6 +1824,311 @@ public final class QuotaController extends StateController { } } + @VisibleForTesting + class QcConstants extends ContentObserver { + private ContentResolver mResolver; + private final KeyValueListParser mParser = new KeyValueListParser(','); + + private static final String KEY_ALLOWED_TIME_PER_PERIOD_MS = "allowed_time_per_period_ms"; + private static final String KEY_IN_QUOTA_BUFFER_MS = "in_quota_buffer_ms"; + private static final String KEY_WINDOW_SIZE_ACTIVE_MS = "window_size_active_ms"; + private static final String KEY_WINDOW_SIZE_WORKING_MS = "window_size_working_ms"; + private static final String KEY_WINDOW_SIZE_FREQUENT_MS = "window_size_frequent_ms"; + private static final String KEY_WINDOW_SIZE_RARE_MS = "window_size_rare_ms"; + private static final String KEY_MAX_EXECUTION_TIME_MS = "max_execution_time_ms"; + private static final String KEY_MAX_JOB_COUNT_ACTIVE = "max_job_count_active"; + private static final String KEY_MAX_JOB_COUNT_WORKING = "max_job_count_working"; + private static final String KEY_MAX_JOB_COUNT_FREQUENT = "max_job_count_frequent"; + private static final String KEY_MAX_JOB_COUNT_RARE = "max_job_count_rare"; + private static final String KEY_MAX_JOB_COUNT_PER_ALLOWED_TIME = + "max_count_per_allowed_time"; + + private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_MS = + 10 * 60 * 1000L; // 10 minutes + private static final long DEFAULT_IN_QUOTA_BUFFER_MS = + 30 * 1000L; // 30 seconds + private static final long DEFAULT_WINDOW_SIZE_ACTIVE_MS = + 10 * 60 * 1000L; // 10 minutes for ACTIVE -- ACTIVE apps can run jobs at any time + private static final long DEFAULT_WINDOW_SIZE_WORKING_MS = + 2 * 60 * 60 * 1000L; // 2 hours + private static final long DEFAULT_WINDOW_SIZE_FREQUENT_MS = + 8 * 60 * 60 * 1000L; // 8 hours + private static final long DEFAULT_WINDOW_SIZE_RARE_MS = + 24 * 60 * 60 * 1000L; // 24 hours + private static final long DEFAULT_MAX_EXECUTION_TIME_MS = + 4 * 60 * 60 * 1000L; // 4 hours + private static final int DEFAULT_MAX_JOB_COUNT_ACTIVE = + 200; // 1200/hr + private static final int DEFAULT_MAX_JOB_COUNT_WORKING = + 1200; // 600/hr + private static final int DEFAULT_MAX_JOB_COUNT_FREQUENT = + 1800; // 225/hr + private static final int DEFAULT_MAX_JOB_COUNT_RARE = + 2400; // 100/hr + private static final int DEFAULT_MAX_JOB_COUNT_PER_ALLOWED_TIME = 20; + + /** How much time each app will have to run jobs within their standby bucket window. */ + public long ALLOWED_TIME_PER_PERIOD_MS = DEFAULT_ALLOWED_TIME_PER_PERIOD_MS; + + /** + * How much time the package should have before transitioning from out-of-quota to in-quota. + * This should not affect processing if the package is already in-quota. + */ + public long IN_QUOTA_BUFFER_MS = DEFAULT_IN_QUOTA_BUFFER_MS; + + /** + * The quota window size of the particular standby bucket. Apps in this standby bucket are + * expected to run only {@link #ALLOWED_TIME_PER_PERIOD_MS} within the past + * WINDOW_SIZE_MS. + */ + public long WINDOW_SIZE_ACTIVE_MS = DEFAULT_WINDOW_SIZE_ACTIVE_MS; + + /** + * The quota window size of the particular standby bucket. Apps in this standby bucket are + * expected to run only {@link #ALLOWED_TIME_PER_PERIOD_MS} within the past + * WINDOW_SIZE_MS. + */ + public long WINDOW_SIZE_WORKING_MS = DEFAULT_WINDOW_SIZE_WORKING_MS; + + /** + * The quota window size of the particular standby bucket. Apps in this standby bucket are + * expected to run only {@link #ALLOWED_TIME_PER_PERIOD_MS} within the past + * WINDOW_SIZE_MS. + */ + public long WINDOW_SIZE_FREQUENT_MS = DEFAULT_WINDOW_SIZE_FREQUENT_MS; + + /** + * The quota window size of the particular standby bucket. Apps in this standby bucket are + * expected to run only {@link #ALLOWED_TIME_PER_PERIOD_MS} within the past + * WINDOW_SIZE_MS. + */ + public long WINDOW_SIZE_RARE_MS = DEFAULT_WINDOW_SIZE_RARE_MS; + + /** + * The maximum amount of time an app can have its jobs running within a 24 hour window. + */ + public long MAX_EXECUTION_TIME_MS = DEFAULT_MAX_EXECUTION_TIME_MS; + + /** + * The maximum number of jobs an app can run within this particular standby bucket's + * window size. + */ + public int MAX_JOB_COUNT_ACTIVE = DEFAULT_MAX_JOB_COUNT_ACTIVE; + + /** + * The maximum number of jobs an app can run within this particular standby bucket's + * window size. + */ + public int MAX_JOB_COUNT_WORKING = DEFAULT_MAX_JOB_COUNT_WORKING; + + /** + * The maximum number of jobs an app can run within this particular standby bucket's + * window size. + */ + public int MAX_JOB_COUNT_FREQUENT = DEFAULT_MAX_JOB_COUNT_FREQUENT; + + /** + * The maximum number of jobs an app can run within this particular standby bucket's + * window size. + */ + public int MAX_JOB_COUNT_RARE = DEFAULT_MAX_JOB_COUNT_RARE; + + /** + * The maximum number of jobs that can run within the past + * {@link #ALLOWED_TIME_PER_PERIOD_MS}. + */ + public int MAX_JOB_COUNT_PER_ALLOWED_TIME = DEFAULT_MAX_JOB_COUNT_PER_ALLOWED_TIME; + + QcConstants(Handler handler) { + super(handler); + } + + private void start(ContentResolver resolver) { + mResolver = resolver; + mResolver.registerContentObserver(Settings.Global.getUriFor( + Settings.Global.JOB_SCHEDULER_QUOTA_CONTROLLER_CONSTANTS), false, this); + updateConstants(); + } + + @Override + public void onChange(boolean selfChange, Uri uri) { + final String constants = Settings.Global.getString( + mResolver, Settings.Global.JOB_SCHEDULER_QUOTA_CONTROLLER_CONSTANTS); + + try { + mParser.setString(constants); + } catch (Exception e) { + // Failed to parse the settings string, log this and move on with defaults. + Slog.e(TAG, "Bad jobscheduler quota controller settings", e); + } + + ALLOWED_TIME_PER_PERIOD_MS = mParser.getDurationMillis( + KEY_ALLOWED_TIME_PER_PERIOD_MS, DEFAULT_ALLOWED_TIME_PER_PERIOD_MS); + IN_QUOTA_BUFFER_MS = mParser.getDurationMillis( + KEY_IN_QUOTA_BUFFER_MS, DEFAULT_IN_QUOTA_BUFFER_MS); + WINDOW_SIZE_ACTIVE_MS = mParser.getDurationMillis( + KEY_WINDOW_SIZE_ACTIVE_MS, DEFAULT_WINDOW_SIZE_ACTIVE_MS); + WINDOW_SIZE_WORKING_MS = mParser.getDurationMillis( + KEY_WINDOW_SIZE_WORKING_MS, DEFAULT_WINDOW_SIZE_WORKING_MS); + WINDOW_SIZE_FREQUENT_MS = mParser.getDurationMillis( + KEY_WINDOW_SIZE_FREQUENT_MS, DEFAULT_WINDOW_SIZE_FREQUENT_MS); + WINDOW_SIZE_RARE_MS = mParser.getDurationMillis( + KEY_WINDOW_SIZE_RARE_MS, DEFAULT_WINDOW_SIZE_RARE_MS); + MAX_EXECUTION_TIME_MS = mParser.getDurationMillis( + KEY_MAX_EXECUTION_TIME_MS, DEFAULT_MAX_EXECUTION_TIME_MS); + MAX_JOB_COUNT_ACTIVE = mParser.getInt( + KEY_MAX_JOB_COUNT_ACTIVE, DEFAULT_MAX_JOB_COUNT_ACTIVE); + MAX_JOB_COUNT_WORKING = mParser.getInt( + KEY_MAX_JOB_COUNT_WORKING, DEFAULT_MAX_JOB_COUNT_WORKING); + MAX_JOB_COUNT_FREQUENT = mParser.getInt( + KEY_MAX_JOB_COUNT_FREQUENT, DEFAULT_MAX_JOB_COUNT_FREQUENT); + MAX_JOB_COUNT_RARE = mParser.getInt( + KEY_MAX_JOB_COUNT_RARE, DEFAULT_MAX_JOB_COUNT_RARE); + MAX_JOB_COUNT_PER_ALLOWED_TIME = mParser.getInt( + KEY_MAX_JOB_COUNT_PER_ALLOWED_TIME, DEFAULT_MAX_JOB_COUNT_PER_ALLOWED_TIME); + + updateConstants(); + } + + @VisibleForTesting + void updateConstants() { + synchronized (mLock) { + boolean changed = false; + + long newAllowedTimeMs = Math.min(MAX_PERIOD_MS, + Math.max(MINUTE_IN_MILLIS, ALLOWED_TIME_PER_PERIOD_MS)); + if (mAllowedTimePerPeriodMs != newAllowedTimeMs) { + mAllowedTimePerPeriodMs = newAllowedTimeMs; + mAllowedTimeIntoQuotaMs = mAllowedTimePerPeriodMs - mQuotaBufferMs; + changed = true; + } + long newQuotaBufferMs = Math.max(0, + Math.min(5 * MINUTE_IN_MILLIS, IN_QUOTA_BUFFER_MS)); + if (mQuotaBufferMs != newQuotaBufferMs) { + mQuotaBufferMs = newQuotaBufferMs; + mAllowedTimeIntoQuotaMs = mAllowedTimePerPeriodMs - mQuotaBufferMs; + mMaxExecutionTimeIntoQuotaMs = mMaxExecutionTimeMs - mQuotaBufferMs; + changed = true; + } + long newActivePeriodMs = Math.max(mAllowedTimePerPeriodMs, + Math.min(MAX_PERIOD_MS, WINDOW_SIZE_ACTIVE_MS)); + if (mBucketPeriodsMs[ACTIVE_INDEX] != newActivePeriodMs) { + mBucketPeriodsMs[ACTIVE_INDEX] = newActivePeriodMs; + changed = true; + } + long newWorkingPeriodMs = Math.max(mAllowedTimePerPeriodMs, + Math.min(MAX_PERIOD_MS, WINDOW_SIZE_WORKING_MS)); + if (mBucketPeriodsMs[WORKING_INDEX] != newWorkingPeriodMs) { + mBucketPeriodsMs[WORKING_INDEX] = newWorkingPeriodMs; + changed = true; + } + long newFrequentPeriodMs = Math.max(mAllowedTimePerPeriodMs, + Math.min(MAX_PERIOD_MS, WINDOW_SIZE_FREQUENT_MS)); + if (mBucketPeriodsMs[FREQUENT_INDEX] != newFrequentPeriodMs) { + mBucketPeriodsMs[FREQUENT_INDEX] = newFrequentPeriodMs; + changed = true; + } + long newRarePeriodMs = Math.max(mAllowedTimePerPeriodMs, + Math.min(MAX_PERIOD_MS, WINDOW_SIZE_RARE_MS)); + if (mBucketPeriodsMs[RARE_INDEX] != newRarePeriodMs) { + mBucketPeriodsMs[RARE_INDEX] = newRarePeriodMs; + changed = true; + } + long newMaxExecutionTimeMs = Math.max(60 * MINUTE_IN_MILLIS, + Math.min(MAX_PERIOD_MS, MAX_EXECUTION_TIME_MS)); + if (mMaxExecutionTimeMs != newMaxExecutionTimeMs) { + mMaxExecutionTimeMs = newMaxExecutionTimeMs; + mMaxExecutionTimeIntoQuotaMs = mMaxExecutionTimeMs - mQuotaBufferMs; + changed = true; + } + int newMaxCountPerAllowedPeriod = Math.max(10, + MAX_JOB_COUNT_PER_ALLOWED_TIME); + if (mMaxJobCountPerAllowedTime != newMaxCountPerAllowedPeriod) { + mMaxJobCountPerAllowedTime = newMaxCountPerAllowedPeriod; + changed = true; + } + int newActiveMaxJobCount = Math.max(mMaxJobCountPerAllowedTime, + Math.max(MIN_BUCKET_JOB_COUNT, MAX_JOB_COUNT_ACTIVE)); + if (mMaxBucketJobCounts[ACTIVE_INDEX] != newActiveMaxJobCount) { + mMaxBucketJobCounts[ACTIVE_INDEX] = newActiveMaxJobCount; + changed = true; + } + int newWorkingMaxJobCount = Math.max(mMaxJobCountPerAllowedTime, + Math.max(MIN_BUCKET_JOB_COUNT, MAX_JOB_COUNT_WORKING)); + if (mMaxBucketJobCounts[WORKING_INDEX] != newWorkingMaxJobCount) { + mMaxBucketJobCounts[WORKING_INDEX] = newWorkingMaxJobCount; + changed = true; + } + int newFrequentMaxJobCount = Math.max(mMaxJobCountPerAllowedTime, + Math.max(MIN_BUCKET_JOB_COUNT, MAX_JOB_COUNT_FREQUENT)); + if (mMaxBucketJobCounts[FREQUENT_INDEX] != newFrequentMaxJobCount) { + mMaxBucketJobCounts[FREQUENT_INDEX] = newFrequentMaxJobCount; + changed = true; + } + int newRareMaxJobCount = Math.max(mMaxJobCountPerAllowedTime, + Math.max(MIN_BUCKET_JOB_COUNT, MAX_JOB_COUNT_RARE)); + if (mMaxBucketJobCounts[RARE_INDEX] != newRareMaxJobCount) { + mMaxBucketJobCounts[RARE_INDEX] = newRareMaxJobCount; + changed = true; + } + + if (changed && mShouldThrottle) { + // Update job bookkeeping out of band. + BackgroundThread.getHandler().post(() -> { + synchronized (mLock) { + maybeUpdateAllConstraintsLocked(); + } + }); + } + } + } + + private void dump(IndentingPrintWriter pw) { + pw.println(); + pw.println("QuotaController:"); + pw.increaseIndent(); + pw.printPair(KEY_ALLOWED_TIME_PER_PERIOD_MS, ALLOWED_TIME_PER_PERIOD_MS).println(); + pw.printPair(KEY_IN_QUOTA_BUFFER_MS, IN_QUOTA_BUFFER_MS).println(); + pw.printPair(KEY_WINDOW_SIZE_ACTIVE_MS, WINDOW_SIZE_ACTIVE_MS).println(); + pw.printPair(KEY_WINDOW_SIZE_WORKING_MS, WINDOW_SIZE_WORKING_MS).println(); + pw.printPair(KEY_WINDOW_SIZE_FREQUENT_MS, WINDOW_SIZE_FREQUENT_MS).println(); + pw.printPair(KEY_WINDOW_SIZE_RARE_MS, WINDOW_SIZE_RARE_MS).println(); + pw.printPair(KEY_MAX_EXECUTION_TIME_MS, MAX_EXECUTION_TIME_MS).println(); + pw.printPair(KEY_MAX_JOB_COUNT_ACTIVE, MAX_JOB_COUNT_ACTIVE).println(); + pw.printPair(KEY_MAX_JOB_COUNT_WORKING, MAX_JOB_COUNT_WORKING).println(); + pw.printPair(KEY_MAX_JOB_COUNT_FREQUENT, MAX_JOB_COUNT_FREQUENT).println(); + pw.printPair(KEY_MAX_JOB_COUNT_RARE, MAX_JOB_COUNT_RARE).println(); + pw.printPair(KEY_MAX_JOB_COUNT_PER_ALLOWED_TIME, MAX_JOB_COUNT_PER_ALLOWED_TIME) + .println(); + pw.decreaseIndent(); + } + + private void dump(ProtoOutputStream proto) { + final long qcToken = proto.start(ConstantsProto.QUOTA_CONTROLLER); + proto.write(ConstantsProto.QuotaController.ALLOWED_TIME_PER_PERIOD_MS, + ALLOWED_TIME_PER_PERIOD_MS); + proto.write(ConstantsProto.QuotaController.IN_QUOTA_BUFFER_MS, IN_QUOTA_BUFFER_MS); + proto.write(ConstantsProto.QuotaController.ACTIVE_WINDOW_SIZE_MS, + WINDOW_SIZE_ACTIVE_MS); + proto.write(ConstantsProto.QuotaController.WORKING_WINDOW_SIZE_MS, + WINDOW_SIZE_WORKING_MS); + proto.write(ConstantsProto.QuotaController.FREQUENT_WINDOW_SIZE_MS, + WINDOW_SIZE_FREQUENT_MS); + proto.write(ConstantsProto.QuotaController.RARE_WINDOW_SIZE_MS, WINDOW_SIZE_RARE_MS); + proto.write(ConstantsProto.QuotaController.MAX_EXECUTION_TIME_MS, + MAX_EXECUTION_TIME_MS); + proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_ACTIVE, MAX_JOB_COUNT_ACTIVE); + proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_WORKING, + MAX_JOB_COUNT_WORKING); + proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_FREQUENT, + MAX_JOB_COUNT_FREQUENT); + proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_RARE, MAX_JOB_COUNT_RARE); + proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_PER_ALLOWED_TIME, + MAX_JOB_COUNT_PER_ALLOWED_TIME); + proto.end(qcToken); + } + } + //////////////////////// TESTING HELPERS ///////////////////////////// @VisibleForTesting @@ -1943,6 +2181,12 @@ public final class QuotaController extends StateController { return mTimingSessions.get(userId, packageName); } + @VisibleForTesting + @NonNull + QcConstants getQcConstants() { + return mQcConstants; + } + //////////////////////////// DATA DUMP ////////////////////////////// @Override @@ -2188,4 +2432,14 @@ public final class QuotaController extends StateController { proto.end(mToken); proto.end(token); } + + @Override + public void dumpConstants(IndentingPrintWriter pw) { + mQcConstants.dump(pw); + } + + @Override + public void dumpConstants(ProtoOutputStream proto) { + mQcConstants.dump(proto); + } } diff --git a/services/core/java/com/android/server/job/controllers/StateController.java b/services/core/java/com/android/server/job/controllers/StateController.java index 74628fb9b502..51be38be990d 100644 --- a/services/core/java/com/android/server/job/controllers/StateController.java +++ b/services/core/java/com/android/server/job/controllers/StateController.java @@ -52,6 +52,13 @@ public abstract class StateController { } /** + * Called when the system boot phase has reached + * {@link com.android.server.SystemService#PHASE_SYSTEM_SERVICES_READY}. + */ + public void onSystemServicesReady() { + } + + /** * Implement the logic here to decide whether a job should be tracked by this controller. * This logic is put here so the JobManager can be completely agnostic of Controller logic. * Also called when updating a task, so implementing controllers have to be aware of @@ -127,4 +134,12 @@ public abstract class StateController { Predicate<JobStatus> predicate); public abstract void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, Predicate<JobStatus> predicate); + + /** Dump any internal constants the Controller may have. */ + public void dumpConstants(IndentingPrintWriter pw) { + } + + /** Dump any internal constants the Controller may have. */ + public void dumpConstants(ProtoOutputStream proto) { + } } diff --git a/services/core/java/com/android/server/job/controllers/TimeController.java b/services/core/java/com/android/server/job/controllers/TimeController.java index 70deb38080b4..ababad961c2d 100644 --- a/services/core/java/com/android/server/job/controllers/TimeController.java +++ b/services/core/java/com/android/server/job/controllers/TimeController.java @@ -18,13 +18,20 @@ package com.android.server.job.controllers; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AlarmManager; import android.app.AlarmManager.OnAlarmListener; +import android.content.ContentResolver; import android.content.Context; +import android.database.ContentObserver; +import android.net.Uri; +import android.os.Handler; import android.os.Process; import android.os.UserHandle; import android.os.WorkSource; +import android.provider.Settings; +import android.util.KeyValueListParser; import android.util.Log; import android.util.Slog; import android.util.TimeUtils; @@ -32,6 +39,7 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IndentingPrintWriter; +import com.android.server.job.ConstantsProto; import com.android.server.job.JobSchedulerService; import com.android.server.job.StateControllerProto; @@ -55,6 +63,9 @@ public final class TimeController extends StateController { /** Delay alarm tag for logging purposes */ private final String DELAY_TAG = "*job.delay*"; + private final Handler mHandler; + private final TcConstants mTcConstants; + private long mNextJobExpiredElapsedMillis; private long mNextDelayExpiredElapsedMillis; @@ -70,6 +81,14 @@ public final class TimeController extends StateController { mNextJobExpiredElapsedMillis = Long.MAX_VALUE; mNextDelayExpiredElapsedMillis = Long.MAX_VALUE; mChainedAttributionEnabled = mService.isChainedAttributionEnabled(); + + mHandler = new Handler(mContext.getMainLooper()); + mTcConstants = new TcConstants(mHandler); + } + + @Override + public void onSystemServicesReady() { + mTcConstants.start(mContext.getContentResolver()); } /** @@ -118,7 +137,7 @@ public final class TimeController extends StateController { job.hasDeadlineConstraint() ? job.getLatestRunTimeElapsed() : Long.MAX_VALUE; final long delayExpiredElapsed = job.hasTimingDelayConstraint() ? job.getEarliestRunTime() : Long.MAX_VALUE; - if (mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS) { + if (mTcConstants.SKIP_NOT_READY_JOBS) { if (wouldBeReadyWithConstraintLocked(job, JobStatus.CONSTRAINT_TIMING_DELAY)) { maybeUpdateDelayAlarmLocked(delayExpiredElapsed, ws); } @@ -148,14 +167,8 @@ public final class TimeController extends StateController { } @Override - public void onConstantsUpdatedLocked() { - checkExpiredDeadlinesAndResetAlarm(); - checkExpiredDelaysAndResetAlarm(); - } - - @Override public void evaluateStateLocked(JobStatus job) { - if (!mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS) { + if (!mTcConstants.SKIP_NOT_READY_JOBS) { return; } @@ -248,7 +261,7 @@ public final class TimeController extends StateController { } it.remove(); } else { // Sorted by expiry time, so take the next one and stop. - if (mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS + if (mTcConstants.SKIP_NOT_READY_JOBS && !wouldBeReadyWithConstraintLocked( job, JobStatus.CONSTRAINT_DEADLINE)) { if (DEBUG) { @@ -308,7 +321,7 @@ public final class TimeController extends StateController { ready = true; } } else { - if (mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS + if (mTcConstants.SKIP_NOT_READY_JOBS && !wouldBeReadyWithConstraintLocked( job, JobStatus.CONSTRAINT_TIMING_DELAY)) { if (DEBUG) { @@ -439,6 +452,87 @@ public final class TimeController extends StateController { } }; + @VisibleForTesting + void recheckAlarmsLocked() { + checkExpiredDeadlinesAndResetAlarm(); + checkExpiredDelaysAndResetAlarm(); + } + + @VisibleForTesting + class TcConstants extends ContentObserver { + private ContentResolver mResolver; + private final KeyValueListParser mParser = new KeyValueListParser(','); + + private static final String KEY_SKIP_NOT_READY_JOBS = "skip_not_ready_jobs"; + + private static final boolean DEFAULT_SKIP_NOT_READY_JOBS = true; + + /** + * Whether or not TimeController should skip setting wakeup alarms for jobs that aren't + * ready now. + */ + public boolean SKIP_NOT_READY_JOBS = DEFAULT_SKIP_NOT_READY_JOBS; + + /** + * Creates a content observer. + * + * @param handler The handler to run {@link #onChange} on, or null if none. + */ + TcConstants(Handler handler) { + super(handler); + } + + private void start(ContentResolver resolver) { + mResolver = resolver; + mResolver.registerContentObserver(Settings.Global.getUriFor( + Settings.Global.JOB_SCHEDULER_TIME_CONTROLLER_CONSTANTS), false, this); + onChange(true, null); + } + + @Override + public void onChange(boolean selfChange, Uri uri) { + final String constants = Settings.Global.getString( + mResolver, Settings.Global.JOB_SCHEDULER_TIME_CONTROLLER_CONSTANTS); + + try { + mParser.setString(constants); + } catch (Exception e) { + // Failed to parse the settings string, log this and move on with defaults. + Slog.e(TAG, "Bad jobscheduler time controller settings", e); + } + + final boolean oldVal = SKIP_NOT_READY_JOBS; + SKIP_NOT_READY_JOBS = mParser.getBoolean( + KEY_SKIP_NOT_READY_JOBS, DEFAULT_SKIP_NOT_READY_JOBS); + + if (oldVal != SKIP_NOT_READY_JOBS) { + synchronized (mLock) { + recheckAlarmsLocked(); + } + } + } + + private void dump(IndentingPrintWriter pw) { + pw.println(); + pw.println("TimeController:"); + pw.increaseIndent(); + pw.printPair(KEY_SKIP_NOT_READY_JOBS, SKIP_NOT_READY_JOBS).println(); + pw.decreaseIndent(); + } + + private void dump(ProtoOutputStream proto) { + final long tcToken = proto.start(ConstantsProto.TIME_CONTROLLER); + proto.write(ConstantsProto.TimeController.SKIP_NOT_READY_JOBS, SKIP_NOT_READY_JOBS); + proto.end(tcToken); + } + } + + @VisibleForTesting + @NonNull + TcConstants getTcConstants() { + return mTcConstants; + } + @Override public void dumpControllerStateLocked(IndentingPrintWriter pw, Predicate<JobStatus> predicate) { @@ -513,4 +607,14 @@ public final class TimeController extends StateController { proto.end(mToken); proto.end(token); } + + @Override + public void dumpConstants(IndentingPrintWriter pw) { + mTcConstants.dump(pw); + } + + @Override + public void dumpConstants(ProtoOutputStream proto) { + mTcConstants.dump(proto); + } } diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java index c73965024c55..1dffcf9c89a6 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java @@ -333,6 +333,7 @@ public class RecoverableKeyStoreDb { String[] selectionArguments = new String[] {String.valueOf(userId)}; ensureUserMetadataEntryExists(userId); + invalidateKeysForUser(userId); return db.update(UserMetadataEntry.TABLE_NAME, values, selection, selectionArguments); } @@ -394,16 +395,13 @@ public class RecoverableKeyStoreDb { /** * Updates status of old keys to {@code RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE}. */ - public void invalidateKeysWithOldGenerationId(int userId, int newGenerationId) { + public void invalidateKeysForUser(int userId) { SQLiteDatabase db = mKeyStoreDbHelper.getWritableDatabase(); ContentValues values = new ContentValues(); values.put(KeysEntry.COLUMN_NAME_RECOVERY_STATUS, RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE); - String selection = - KeysEntry.COLUMN_NAME_USER_ID + " = ? AND " - + KeysEntry.COLUMN_NAME_GENERATION_ID + " < ?"; - db.update(KeysEntry.TABLE_NAME, values, selection, - new String[] {String.valueOf(userId), String.valueOf(newGenerationId)}); + String selection = KeysEntry.COLUMN_NAME_USER_ID + " = ?"; + db.update(KeysEntry.TABLE_NAME, values, selection, new String[] {String.valueOf(userId)}); } /** diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index c6f6c50a308d..19ff2c11d14c 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -1164,6 +1164,15 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // Carrier might want to manage notifications themselves final PersistableBundle config = mCarrierConfigManager.getConfigForSubId(subId); + if (!CarrierConfigManager.isConfigForIdentifiedCarrier(config)) { + if (LOGV) Slog.v(TAG, "isConfigForIdentifiedCarrier returned false"); + // Don't show notifications until we confirm that the loaded config is from an + // identified carrier, which may want to manage their own notifications. This method + // should be called every time the carrier config changes anyways, and there's no + // reason to alert if there isn't a carrier. + return; + } + final boolean notifyWarning = getBooleanDefeatingNullable(config, KEY_DATA_WARNING_NOTIFICATION_BOOL, true); final boolean notifyLimit = getBooleanDefeatingNullable(config, diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 21a862a5ec6c..cdcb64143ed6 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -26,6 +26,7 @@ import static android.app.Notification.FLAG_ONGOING_EVENT; import static android.app.NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED; import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED; import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED; +import static android.app.NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED; import static android.app.NotificationManager.IMPORTANCE_LOW; import static android.app.NotificationManager.IMPORTANCE_MIN; import static android.app.NotificationManager.IMPORTANCE_NONE; @@ -111,6 +112,7 @@ import android.app.NotificationManager; import android.app.NotificationManager.Policy; import android.app.PendingIntent; import android.app.Person; +import android.app.RemoteInput; import android.app.StatusBarManager; import android.app.UriGrantsManager; import android.app.admin.DeviceAdminInfo; @@ -1811,14 +1813,15 @@ public class NotificationManagerService extends SystemService { } private void registerDeviceConfigChange() { - DeviceConfig.addOnPropertyChangedListener( + DeviceConfig.addOnPropertiesChangedListener( DeviceConfig.NAMESPACE_SYSTEMUI, getContext().getMainExecutor(), - (namespace, name, value) -> { - if (!DeviceConfig.NAMESPACE_SYSTEMUI.equals(namespace)) { + (properties) -> { + if (!DeviceConfig.NAMESPACE_SYSTEMUI.equals(properties.getNamespace())) { return; } - if (SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE.equals(name)) { + if (properties.getKeyset() + .contains(SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE)) { mAssistants.resetDefaultAssistantsIfNecessary(); } }); @@ -3753,7 +3756,7 @@ public class NotificationManagerService extends SystemService { pkg, userId, true, granted); getContext().sendBroadcastAsUser(new Intent( - NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED) + ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED) .setPackage(pkg) .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT), UserHandle.of(userId), null); @@ -3913,7 +3916,7 @@ public class NotificationManagerService extends SystemService { userId, true, granted); getContext().sendBroadcastAsUser(new Intent( - NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED) + ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED) .setPackage(listener.getPackageName()) .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.of(userId), null); @@ -3929,7 +3932,9 @@ public class NotificationManagerService extends SystemService { public void setNotificationAssistantAccessGrantedForUser(ComponentName assistant, int userId, boolean granted) { checkCallerIsSystemOrSystemUiOrShell(); - mAssistants.setUserSet(userId, true); + for (UserInfo ui : mUm.getEnabledProfiles(userId)) { + mAssistants.setUserSet(ui.id, true); + } final long identity = Binder.clearCallingIdentity(); try { setNotificationAssistantAccessGrantedForUserInternal(assistant, userId, granted); @@ -4143,30 +4148,36 @@ public class NotificationManagerService extends SystemService { @VisibleForTesting protected void setNotificationAssistantAccessGrantedForUserInternal( - ComponentName assistant, int userId, boolean granted) { - if (assistant == null) { - ComponentName allowedAssistant = CollectionUtils.firstOrNull( - mAssistants.getAllowedComponents(userId)); - if (allowedAssistant != null) { - setNotificationAssistantAccessGrantedForUserInternal( - allowedAssistant, userId, false); - } - return; - } - if (!granted || mAllowedManagedServicePackages.test(assistant.getPackageName(), userId, - mAssistants.getRequiredPermission())) { - mConditionProviders.setPackageOrComponentEnabled(assistant.flattenToString(), - userId, false, granted); - mAssistants.setPackageOrComponentEnabled(assistant.flattenToString(), - userId, true, granted); + ComponentName assistant, int baseUserId, boolean granted) { + List<UserInfo> users = mUm.getEnabledProfiles(baseUserId); + if (users != null) { + for (UserInfo user : users) { + int userId = user.id; + if (assistant == null) { + ComponentName allowedAssistant = CollectionUtils.firstOrNull( + mAssistants.getAllowedComponents(userId)); + if (allowedAssistant != null) { + setNotificationAssistantAccessGrantedForUserInternal( + allowedAssistant, userId, false); + } + continue; + } + if (!granted || mAllowedManagedServicePackages.test(assistant.getPackageName(), + userId, mAssistants.getRequiredPermission())) { + mConditionProviders.setPackageOrComponentEnabled(assistant.flattenToString(), + userId, false, granted); + mAssistants.setPackageOrComponentEnabled(assistant.flattenToString(), + userId, true, granted); - getContext().sendBroadcastAsUser(new Intent( - NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED) - .setPackage(assistant.getPackageName()) - .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), - UserHandle.of(userId), null); + getContext().sendBroadcastAsUser( + new Intent(ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED) + .setPackage(assistant.getPackageName()) + .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), + UserHandle.of(userId), null); - handleSavePolicyFile(); + handleSavePolicyFile(); + } + } } } @@ -4833,17 +4844,35 @@ public class NotificationManagerService extends SystemService { : null; boolean isForegroundCall = CATEGORY_CALL.equals(notification.category) && (notification.flags & FLAG_FOREGROUND_SERVICE) != 0; - // OR message style (which always has a person) + // OR message style (which always has a person) with any remote input Class<? extends Notification.Style> style = notification.getNotificationStyle(); boolean isMessageStyle = Notification.MessagingStyle.class.equals(style); - boolean notificationAppropriateToBubble = isMessageStyle + boolean notificationAppropriateToBubble = + (isMessageStyle && hasValidRemoteInput(notification)) || (peopleList != null && !peopleList.isEmpty() && isForegroundCall); + // OR something that was previously a bubble & still exists boolean bubbleUpdate = oldRecord != null && (oldRecord.getNotification().flags & FLAG_BUBBLE) != 0; return canBubble && (notificationAppropriateToBubble || appIsForeground || bubbleUpdate); } + private boolean hasValidRemoteInput(Notification n) { + // Also check for inline reply + Notification.Action[] actions = n.actions; + if (actions != null) { + // Get the remote inputs + for (int i = 0; i < actions.length; i++) { + Notification.Action action = actions[i]; + RemoteInput[] inputs = action.getRemoteInputs(); + if (inputs != null && inputs.length > 0) { + return true; + } + } + } + return false; + } + private void doChannelWarningToast(CharSequence toastText) { Binder.withCleanCallingIdentity(() -> { final int defaultWarningEnabled = Build.IS_DEBUGGABLE ? 1 : 0; diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java index ee07c7de9dbc..209ccdae75d0 100644 --- a/services/core/java/com/android/server/om/OverlayManagerService.java +++ b/services/core/java/com/android/server/om/OverlayManagerService.java @@ -720,6 +720,26 @@ public final class OverlayManagerService extends SystemService { } @Override + public String[] getDefaultOverlayPackages() throws RemoteException { + try { + traceBegin(TRACE_TAG_RRO, "OMS#getDefaultOverlayPackages"); + getContext().enforceCallingOrSelfPermission( + android.Manifest.permission.MODIFY_THEME_OVERLAY, null); + + final long ident = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + return mImpl.getDefaultOverlayPackages(); + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } finally { + traceEnd(TRACE_TAG_RRO); + } + } + + @Override public void onShellCommand(@NonNull final FileDescriptor in, @NonNull final FileDescriptor out, @NonNull final FileDescriptor err, @NonNull final String[] args, @NonNull final ShellCallback callback, diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java index 3a84b1e18e36..092dbc817e32 100644 --- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java +++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java @@ -641,6 +641,10 @@ final class OverlayManagerServiceImpl { pw.println("Default overlays: " + TextUtils.join(";", mDefaultOverlays)); } + @NonNull String[] getDefaultOverlayPackages() { + return mDefaultOverlays; + } + List<String> getEnabledOverlayPackageNames(@NonNull final String targetPackageName, final int userId) { final List<OverlayInfo> overlays = mSettings.getOverlaysForTarget(targetPackageName, diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java index 21b6f12b6f8b..497385fef39c 100644 --- a/services/core/java/com/android/server/pm/ApexManager.java +++ b/services/core/java/com/android/server/pm/ApexManager.java @@ -22,21 +22,21 @@ import android.apex.ApexInfo; import android.apex.ApexInfoList; import android.apex.ApexSessionInfo; import android.apex.IApexService; -import android.content.BroadcastReceiver; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageParser; import android.content.pm.PackageParser.PackageParserException; +import android.os.HandlerThread; import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceManager.ServiceNotFoundException; +import android.os.SystemClock; +import android.sysprop.ApexProperties; import android.util.Slog; -import com.android.internal.annotations.GuardedBy; import com.android.internal.util.IndentingPrintWriter; +import com.android.server.SystemService; import java.io.File; import java.io.PrintWriter; @@ -45,75 +45,108 @@ import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.concurrent.CountDownLatch; import java.util.function.Function; import java.util.stream.Collectors; /** * ApexManager class handles communications with the apex service to perform operation and queries, * as well as providing caching to avoid unnecessary calls to the service. + * + * @hide */ -class ApexManager { - static final String TAG = "ApexManager"; - private final IApexService mApexService; - private final Context mContext; - private final Object mLock = new Object(); - @GuardedBy("mLock") +public final class ApexManager extends SystemService { + private static final String TAG = "ApexManager"; + private IApexService mApexService; + + private final CountDownLatch mActivePackagesCacheLatch = new CountDownLatch(1); private Map<String, PackageInfo> mActivePackagesCache; - ApexManager(Context context) { + private final CountDownLatch mApexFilesCacheLatch = new CountDownLatch(1); + private ApexInfo[] mApexFiles; + + public ApexManager(Context context) { + super(context); + } + + @Override + public void onStart() { try { mApexService = IApexService.Stub.asInterface( - ServiceManager.getServiceOrThrow("apexservice")); + ServiceManager.getServiceOrThrow("apexservice")); } catch (ServiceNotFoundException e) { throw new IllegalStateException("Required service apexservice not available"); } - mContext = context; + publishLocalService(ApexManager.class, this); + HandlerThread oneShotThread = new HandlerThread("ApexManagerOneShotHandler"); + oneShotThread.start(); + oneShotThread.getThreadHandler().post(this::initSequence); + oneShotThread.quitSafely(); } - void systemReady() { - mContext.registerReceiver(new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - onBootCompleted(); - mContext.unregisterReceiver(this); - } - }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED)); + private void initSequence() { + populateApexFilesCache(); + parseApexFiles(); } - private void populateActivePackagesCacheIfNeeded() { - synchronized (mLock) { - if (mActivePackagesCache != null) { - return; - } + private void populateApexFilesCache() { + if (mApexFiles != null) { + return; + } + long startTimeMicros = SystemClock.currentTimeMicro(); + Slog.i(TAG, "Starting to populate apex files cache"); + try { + mApexFiles = mApexService.getActivePackages(); + Slog.i(TAG, "IPC to apexd finished in " + (SystemClock.currentTimeMicro() + - startTimeMicros) + " μs"); + } catch (RemoteException re) { + // TODO: make sure this error is propagated to system server. + Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString()); + re.rethrowAsRuntimeException(); + } + mApexFilesCacheLatch.countDown(); + Slog.i(TAG, "Finished populating apex files cache in " + (SystemClock.currentTimeMicro() + - startTimeMicros) + " μs"); + } + + private void parseApexFiles() { + waitForLatch(mApexFilesCacheLatch); + if (mApexFiles == null) { + throw new IllegalStateException("mApexFiles must be populated"); + } + long startTimeMicros = SystemClock.currentTimeMicro(); + Slog.i(TAG, "Starting to parse apex files"); + List<PackageInfo> list = new ArrayList<>(); + // TODO: this can be parallelized. + for (ApexInfo ai : mApexFiles) { try { - List<PackageInfo> list = new ArrayList<>(); - final ApexInfo[] activePkgs = mApexService.getActivePackages(); - for (ApexInfo ai : activePkgs) { - // If the device is using flattened APEX, don't report any APEX - // packages since they won't be managed or updated by PackageManager. - if ((new File(ai.packagePath)).isDirectory()) { - break; - } - try { - list.add(PackageParser.generatePackageInfoFromApex( - new File(ai.packagePath), PackageManager.GET_META_DATA - | PackageManager.GET_SIGNING_CERTIFICATES)); - } catch (PackageParserException pe) { - throw new IllegalStateException("Unable to parse: " + ai, pe); - } + // If the device is using flattened APEX, don't report any APEX + // packages since they won't be managed or updated by PackageManager. + if ((new File(ai.packagePath)).isDirectory()) { + break; } - mActivePackagesCache = list.stream().collect( - Collectors.toMap(p -> p.packageName, Function.identity())); - } catch (RemoteException re) { - Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString()); - throw new RuntimeException(re); + list.add(PackageParser.generatePackageInfoFromApex( + new File(ai.packagePath), PackageManager.GET_META_DATA + | PackageManager.GET_SIGNING_CERTIFICATES)); + } catch (PackageParserException pe) { + // TODO: make sure this error is propagated to system server. + throw new IllegalStateException("Unable to parse: " + ai, pe); } } + mActivePackagesCache = list.stream().collect( + Collectors.toMap(p -> p.packageName, Function.identity())); + mActivePackagesCacheLatch.countDown(); + Slog.i(TAG, "Finished parsing apex files in " + (SystemClock.currentTimeMicro() + - startTimeMicros) + " μs"); } /** * Retrieves information about an active APEX package. * + * <p>This method blocks caller thread until {@link #parseApexFiles()} succeeds. Note that in + * case {@link #parseApexFiles()}} throws an exception this method will never finish + * essentially putting device into a boot loop. + * * @param packageName the package name to look for. Note that this is the package name reported * in the APK container manifest (i.e. AndroidManifest.xml), which might * differ from the one reported in the APEX manifest (i.e. @@ -122,30 +155,43 @@ class ApexManager { * is not found. */ @Nullable PackageInfo getActivePackage(String packageName) { - populateActivePackagesCacheIfNeeded(); + waitForLatch(mActivePackagesCacheLatch); return mActivePackagesCache.get(packageName); } /** * Retrieves information about all active APEX packages. * + * <p>This method blocks caller thread until {@link #parseApexFiles()} succeeds. Note that in + * case {@link #parseApexFiles()}} throws an exception this method will never finish + * essentially putting device into a boot loop. + * * @return a Collection of PackageInfo object, each one containing information about a different * active package. */ Collection<PackageInfo> getActivePackages() { - populateActivePackagesCacheIfNeeded(); + waitForLatch(mActivePackagesCacheLatch); return mActivePackagesCache.values(); } /** * Checks if {@code packageName} is an apex package. * + * <p>This method blocks caller thread until {@link #populateApexFilesCache()} succeeds. Note + * that in case {@link #populateApexFilesCache()} throws an exception this method will never + * finish essentially putting device into a boot loop. + * * @param packageName package to check. * @return {@code true} if {@code packageName} is an apex package. */ boolean isApexPackage(String packageName) { - populateActivePackagesCacheIfNeeded(); - return mActivePackagesCache.containsKey(packageName); + waitForLatch(mApexFilesCacheLatch); + for (ApexInfo ai : mApexFiles) { + if (ai.packageName.equals(packageName)) { + return true; + } + } + return false; } /** @@ -237,11 +283,7 @@ class ApexManager { * @return true if APEX packages can be managed on this device, false otherwise. */ boolean isApexSupported() { - populateActivePackagesCacheIfNeeded(); - // There is no system-wide property available to check if APEX are flattened and hence can't - // be updated. In absence of such property, we assume that if we didn't index APEX packages - // since they were flattened, no APEX management should be possible. - return !mActivePackagesCache.isEmpty(); + return ApexProperties.updatable().orElse(false); } /** @@ -277,6 +319,19 @@ class ApexManager { } /** + * Blocks current thread until {@code latch} has counted down to zero. + * + * @throws RuntimeException if thread was interrupted while waiting. + */ + private void waitForLatch(CountDownLatch latch) { + try { + latch.await(); + } catch (InterruptedException e) { + throw new RuntimeException("Interrupted waiting for cache to be populated", e); + } + } + + /** * Dumps various state information to the provided {@link PrintWriter} object. * * @param pw the {@link PrintWriter} object to send information to. @@ -289,7 +344,7 @@ class ApexManager { ipw.println("Active APEX packages:"); ipw.increaseIndent(); try { - populateActivePackagesCacheIfNeeded(); + waitForLatch(mActivePackagesCacheLatch); for (PackageInfo pi : mActivePackagesCache.values()) { if (packageName != null && !packageName.equals(pi.packageName)) { continue; @@ -334,8 +389,4 @@ class ApexManager { ipw.println("Couldn't communicate with apexd."); } } - - public void onBootCompleted() { - populateActivePackagesCacheIfNeeded(); - } } diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java index 3a7919a0d9ba..d6108b7cc390 100644 --- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java +++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java @@ -24,6 +24,7 @@ import android.app.ActivityManagerInternal; import android.app.ActivityOptions; import android.app.AppOpsManager; import android.app.IApplicationThread; +import android.app.admin.DevicePolicyEventLogger; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -37,6 +38,7 @@ import android.os.Binder; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; +import android.stats.devicepolicy.DevicePolicyEnums; import android.text.TextUtils; import com.android.internal.annotations.VisibleForTesting; @@ -69,6 +71,11 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { verifyCallingPackage(callingPackage); + DevicePolicyEventLogger + .createEvent(DevicePolicyEnums.CROSS_PROFILE_APPS_GET_TARGET_USER_PROFILES) + .setStrings(new String[] {callingPackage}) + .write(); + return getTargetUserProfilesUnchecked( callingPackage, mInjector.getCallingUserId()); } @@ -85,6 +92,11 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { verifyCallingPackage(callingPackage); + DevicePolicyEventLogger + .createEvent(DevicePolicyEnums.CROSS_PROFILE_APPS_START_ACTIVITY_AS_USER) + .setStrings(new String[] {callingPackage}) + .write(); + final int callerUserId = mInjector.getCallingUserId(); final int callingUid = mInjector.getCallingUid(); diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 74fb4b27100e..5f6e7399f3a7 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -2050,6 +2050,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { synchronized (mLock) { assertCallerIsOwnerOrRootLocked(); + if (isStagedAndInTerminalState()) { + // We keep the session in the database if it's in a finalized state. It will be + // removed by PackageInstallerService when the last update time is old enough. + // Also, in such cases cleanStageDir() has already been executed so no need to + // do it now. + return; + } if (mCommitted && params.isStaged) { synchronized (mLock) { mDestroyed = true; diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index e08af6f3521f..a64ae9c5fe85 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -939,6 +939,7 @@ public class PackageManagerService extends IPackageManager.Stub ComponentName mCustomResolverComponentName; boolean mResolverReplaced = false; + boolean mOkToReplacePersistentPackages = false; private final @Nullable ComponentName mIntentFilterVerifierComponent; private final @Nullable IntentFilterVerifier<ActivityIntentInfo> mIntentFilterVerifier; @@ -2374,6 +2375,8 @@ public class PackageManagerService extends IPackageManager.Stub public PackageManagerService(Context context, Installer installer, boolean factoryTest, boolean onlyCore) { + mApexManager = LocalServices.getService(ApexManager.class); + LockGuard.installLock(mPackages, LockGuard.INDEX_PACKAGES); Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "create package manager"); EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START, @@ -2470,7 +2473,6 @@ public class PackageManagerService extends IPackageManager.Stub mProtectedPackages = new ProtectedPackages(mContext); - mApexManager = new ApexManager(context); synchronized (mInstallLock) { // writer synchronized (mPackages) { @@ -2921,28 +2923,50 @@ public class PackageManagerService extends IPackageManager.Stub // Remove disable package settings for updated system apps that were // removed via an OTA. If the update is no longer present, remove the // app completely. Otherwise, revoke their system privileges. - for (String deletedAppName : possiblyDeletedUpdatedSystemApps) { - PackageParser.Package deletedPkg = mPackages.get(deletedAppName); - mSettings.removeDisabledSystemPackageLPw(deletedAppName); + for (int i = possiblyDeletedUpdatedSystemApps.size() - 1; i >= 0; --i) { + final String packageName = possiblyDeletedUpdatedSystemApps.get(i); + final PackageParser.Package pkg = mPackages.get(packageName); final String msg; - if (deletedPkg == null) { + + // remove from the disabled system list; do this first so any future + // scans of this package are performed without this state + mSettings.removeDisabledSystemPackageLPw(packageName); + + if (pkg == null) { // should have found an update, but, we didn't; remove everything - msg = "Updated system package " + deletedAppName + msg = "Updated system package " + packageName + " no longer exists; removing its data"; // Actual deletion of code and data will be handled by later // reconciliation step } else { // found an update; revoke system privileges - msg = "Updated system package + " + deletedAppName - + " no longer exists; revoking system privileges"; + msg = "Updated system package " + packageName + + " no longer exists; rescanning package on data"; - // Don't do anything if a stub is removed from the system image. If - // we were to remove the uncompressed version from the /data partition, - // this is where it'd be done. + // NOTE: We don't do anything special if a stub is removed from the + // system image. But, if we were [like removing the uncompressed + // version from the /data partition], this is where it'd be done. + + // remove the package from the system and re-scan it without any + // special privileges + removePackageLI(pkg, true); + try { + final File codePath = new File(pkg.applicationInfo.getCodePath()); + scanPackageTracedLI(codePath, 0, scanFlags, 0, null); + } catch (PackageManagerException e) { + Slog.e(TAG, "Failed to parse updated, ex-system package: " + + e.getMessage()); + } + } + + // one final check. if we still have a package setting [ie. it was + // previously scanned and known to the system], but, we don't have + // a package [ie. there was an error scanning it from the /data + // partition], completely remove the package data. + final PackageSetting ps = mSettings.mPackages.get(packageName); + if (ps != null && mPackages.get(packageName) == null) { + removePackageDataLIF(ps, null, null, 0, false); - final PackageSetting deletedPs = mSettings.mPackages.get(deletedAppName); - deletedPkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM; - deletedPs.pkgFlags &= ~ApplicationInfo.FLAG_SYSTEM; } logCriticalInfo(Log.WARN, msg); } @@ -14972,12 +14996,14 @@ public class PackageManagerService extends IPackageManager.Stub final int installReason; @Nullable MultiPackageInstallParams mParentInstallParams; + final long requiredInstalledVersionCode; InstallParams(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer, int installFlags, String installerPackageName, String volumeUuid, VerificationInfo verificationInfo, UserHandle user, String packageAbiOverride, String[] grantedPermissions, List<String> whitelistedRestrictedPermissions, - PackageParser.SigningDetails signingDetails, int installReason) { + PackageParser.SigningDetails signingDetails, int installReason, + long requiredInstalledVersionCode) { super(user); this.origin = origin; this.move = move; @@ -14991,6 +15017,7 @@ public class PackageManagerService extends IPackageManager.Stub this.whitelistedRestrictedPermissions = whitelistedRestrictedPermissions; this.signingDetails = signingDetails; this.installReason = installReason; + this.requiredInstalledVersionCode = requiredInstalledVersionCode; } InstallParams(ActiveInstallSession activeInstallSession) { @@ -15021,6 +15048,8 @@ public class PackageManagerService extends IPackageManager.Stub whitelistedRestrictedPermissions = activeInstallSession.getSessionParams() .whitelistedRestrictedPermissions; signingDetails = activeInstallSession.getSigningDetails(); + requiredInstalledVersionCode = activeInstallSession.getSessionParams() + .requiredInstalledVersionCode; } @Override @@ -15049,6 +15078,23 @@ public class PackageManagerService extends IPackageManager.Stub } } + if (requiredInstalledVersionCode != PackageManager.VERSION_CODE_HIGHEST) { + if (dataOwnerPkg == null) { + Slog.w(TAG, "Required installed version code was " + + requiredInstalledVersionCode + + " but package is not installed"); + return PackageHelper.RECOMMEND_FAILED_WRONG_INSTALLED_VERSION; + } + + if (dataOwnerPkg.getLongVersionCode() != requiredInstalledVersionCode) { + Slog.w(TAG, "Required installed version code was " + + requiredInstalledVersionCode + + " but actual installed version is " + + dataOwnerPkg.getLongVersionCode()); + return PackageHelper.RECOMMEND_FAILED_WRONG_INSTALLED_VERSION; + } + } + if (dataOwnerPkg != null) { if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags, dataOwnerPkg.applicationInfo.flags)) { @@ -15175,6 +15221,8 @@ public class PackageManagerService extends IPackageManager.Stub loc = installLocationPolicy(pkgLite); if (loc == PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE) { ret = PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE; + } else if (loc == PackageHelper.RECOMMEND_FAILED_WRONG_INSTALLED_VERSION) { + ret = PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION; } else if (!onInt) { // Override install location with flags if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) { @@ -17324,7 +17372,8 @@ public class PackageManagerService extends IPackageManager.Stub + " target SDK " + oldTargetSdk + " does."); } // Prevent persistent apps from being updated - if ((oldPackage.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0) { + if (((oldPackage.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0) + && !mOkToReplacePersistentPackages) { throw new PrepareFailure(PackageManager.INSTALL_FAILED_INVALID_APK, "Package " + oldPackage.packageName + " is a persistent app. " + "Persistent apps are not updateable."); @@ -18759,6 +18808,7 @@ public class PackageManagerService extends IPackageManager.Stub boolean installedStateChanged = false; if (deletedPs != null) { if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) { + final SparseBooleanArray changedUsers = new SparseBooleanArray(); synchronized (mPackages) { clearIntentFilterVerificationsLPw(deletedPs.name, UserHandle.USER_ALL); clearDefaultBrowserIfNeeded(packageName); @@ -18790,10 +18840,9 @@ public class PackageManagerService extends IPackageManager.Stub } } } + clearPackagePreferredActivitiesLPw( + deletedPs.name, changedUsers, UserHandle.USER_ALL); } - final SparseBooleanArray changedUsers = new SparseBooleanArray(); - clearPackagePreferredActivitiesLPw( - deletedPs.name, changedUsers, UserHandle.USER_ALL); if (changedUsers.size() > 0) { updateDefaultHomeNotLocked(changedUsers); postPreferredActivityChangedBroadcast(UserHandle.USER_ALL); @@ -21462,7 +21511,6 @@ public class PackageManagerService extends IPackageManager.Stub storage.registerListener(mStorageListener); mInstallerService.systemReady(); - mApexManager.systemReady(); mPackageDexOptimizer.systemReady(); getStorageManagerInternal().addExternalStoragePolicy( @@ -21505,10 +21553,12 @@ public class PackageManagerService extends IPackageManager.Stub mModuleInfoProvider.systemReady(); + mOkToReplacePersistentPackages = true; // Installer service might attempt to install some packages that have been staged for // installation on reboot. Make sure this is the last component to be call since the // installation might require other components to be ready. mInstallerService.restoreAndApplyStagedSessionIfNeeded(); + mOkToReplacePersistentPackages = false; } public void waitForAppDataPrepared() { @@ -23285,7 +23335,7 @@ public class PackageManagerService extends IPackageManager.Stub installerPackageName, volumeUuid, null /*verificationInfo*/, user, packageAbiOverride, null /*grantedPermissions*/, null /*whitelistedRestrictedPermissions*/, PackageParser.SigningDetails.UNKNOWN, - PackageManager.INSTALL_REASON_UNKNOWN); + PackageManager.INSTALL_REASON_UNKNOWN, PackageManager.VERSION_CODE_HIGHEST); params.setTraceMethod("movePackage").setTraceCookie(System.identityHashCode(params)); msg.obj = params; @@ -23911,6 +23961,18 @@ public class PackageManagerService extends IPackageManager.Stub } @Override + public int getTargetSdkVersionForPackage(String packageName) + throws RemoteException { + int callingUser = UserHandle.getUserId(Binder.getCallingUid()); + ApplicationInfo info = getApplicationInfo(packageName, 0, callingUser); + if (info == null) { + throw new RemoteException( + "Couldn't get ApplicationInfo for package " + packageName); + } + return info.targetSdkVersion; + } + + @Override public boolean[] isAudioPlaybackCaptureAllowed(String[] packageNames) throws RemoteException { int callingUser = UserHandle.getUserId(Binder.getCallingUid()); diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java index ead09b424d61..e85989315417 100644 --- a/services/core/java/com/android/server/pm/PackageSetting.java +++ b/services/core/java/com/android/server/pm/PackageSetting.java @@ -185,16 +185,18 @@ public final class PackageSetting extends PackageSettingBase { proto.write(PackageProto.NAME, (realName != null ? realName : name)); proto.write(PackageProto.UID, appId); proto.write(PackageProto.VERSION_CODE, versionCode); - proto.write(PackageProto.VERSION_STRING, pkg.mVersionName); proto.write(PackageProto.INSTALL_TIME_MS, firstInstallTime); proto.write(PackageProto.UPDATE_TIME_MS, lastUpdateTime); proto.write(PackageProto.INSTALLER_NAME, installerPackageName); if (pkg != null) { + proto.write(PackageProto.VERSION_STRING, pkg.mVersionName); + long splitToken = proto.start(PackageProto.SPLITS); proto.write(PackageProto.SplitProto.NAME, "base"); proto.write(PackageProto.SplitProto.REVISION_CODE, pkg.baseRevisionCode); proto.end(splitToken); + if (pkg.splitNames != null) { for (int i = 0; i < pkg.splitNames.length; i++) { splitToken = proto.start(PackageProto.SPLITS); diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index 803ab2d299e2..170d0850bf0f 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -44,13 +44,11 @@ import android.os.ParcelFileDescriptor; import android.os.PowerManager; import android.os.RemoteException; import android.os.ServiceManager; -import android.os.storage.IStorageManager; import android.util.Slog; import android.util.SparseArray; import android.util.apk.ApkSignatureVerifier; import com.android.internal.annotations.GuardedBy; -import com.android.internal.content.PackageHelper; import com.android.internal.os.BackgroundThread; import java.io.File; @@ -165,6 +163,22 @@ public class StagingManager { continue; } long activeVersion = activePackage.applicationInfo.longVersionCode; + if (session.params.requiredInstalledVersionCode + != PackageManager.VERSION_CODE_HIGHEST) { + if (activeVersion != session.params.requiredInstalledVersionCode) { + session.setStagedSessionFailed( + SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, + "Installed version of APEX package " + newPackage.packageName + + " does not match required. Active version: " + activeVersion + + " required: " + session.params.requiredInstalledVersionCode); + + if (!mApexManager.abortActiveSession()) { + Slog.e(TAG, "Failed to abort apex session " + session.sessionId); + } + return false; + } + } + boolean allowsDowngrade = PackageManagerServiceUtils.isDowngradePermitted( session.params.installFlags, activePackage.applicationInfo.flags); if (activeVersion > newPackage.versionCode && !allowsDowngrade) { @@ -255,21 +269,6 @@ public class StagingManager { } } - // Make sure we start a filesystem checkpoint on the next boot. - try { - IStorageManager storageManager = PackageHelper.getStorageManager(); - if (storageManager.supportsCheckpoint()) { - storageManager.startCheckpoint(1 /* numRetries */); - } - } catch (Exception e) { // TODO(b/130190815) make a RemoteException again - // While StorageManager lives in the same process, the native implementation - // it calls through lives in 'vold'; so, this call can fail if 'vold' isn't - // reachable. - // Since we can live without filesystem checkpointing, just warn in this case - // and continue. - Slog.w(TAG, "Could not start filesystem checkpoint:", e); - } - session.setStagedSessionReady(); if (sessionContainsApex(session) && !mApexManager.markStagedSessionReady(session.sessionId)) { diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java index 615472661f9e..d4c7ec355c19 100644 --- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java +++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java @@ -50,6 +50,7 @@ import android.os.UserManager; // out of order to avoid merge conflict import android.os.SystemClock; import android.os.UserHandle; import android.provider.DeviceConfig; +import android.util.ArraySet; import android.util.IntArray; import android.util.Log; import android.util.SparseBooleanArray; @@ -69,10 +70,9 @@ import java.security.SecureRandom; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.ArrayList; -import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; -import java.util.Map; import java.util.Random; import java.util.Set; import java.util.concurrent.LinkedBlockingQueue; @@ -106,15 +106,9 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { @GuardedBy("mLock") private final SparseBooleanArray mAllocatedRollbackIds = new SparseBooleanArray(); - // Package rollback data for rollback-enabled installs that have not yet - // been committed. Maps from sessionId to rollback data. + // Package rollback data for rollbacks we are in the process of enabling. @GuardedBy("mLock") - private final Map<Integer, RollbackData> mPendingRollbacks = new HashMap<>(); - - // Map from child session id's for enabled rollbacks to their - // corresponding parent session ids. - @GuardedBy("mLock") - private final Map<Integer, Integer> mChildSessions = new HashMap<>(); + private final Set<NewRollback> mNewRollbacks = new ArraySet<>(); // The list of all rollbacks, including available and committed rollbacks. // This list is null until the rollback data has been loaded. @@ -135,7 +129,6 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { // No need for guarding with lock because value is only accessed in handler thread. private long mRelativeBootTime = calculateRelativeBootTime(); - RollbackManagerServiceImpl(Context context) { mContext = context; // Note that we're calling onStart here because this object is only constructed on @@ -356,31 +349,6 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { return; } - // Verify the RollbackData is up to date with what's installed on - // device. - // TODO: We assume that between now and the time we commit the - // downgrade install, the currently installed package version does not - // change. This is not safe to assume, particularly in the case of a - // rollback racing with a roll-forward fix of a buggy package. - // Figure out how to ensure we don't commit the rollback if - // roll forward happens at the same time. - for (PackageRollbackInfo info : data.info.getPackages()) { - VersionedPackage installedVersion = getInstalledPackageVersion(info.getPackageName()); - if (installedVersion == null) { - // TODO: Test this case - sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE_ROLLBACK_UNAVAILABLE, - "Package to roll back is not installed"); - return; - } - - if (!packageVersionsEqual(info.getVersionRolledBackFrom(), installedVersion)) { - // TODO: Test this case - sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE_ROLLBACK_UNAVAILABLE, - "Package version to roll back not installed."); - return; - } - } - // Get a context for the caller to use to install the downgraded // version of the package. Context context = null; @@ -419,6 +387,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { } } params.setRequestDowngrade(true); + params.setRequiredInstalledVersionCode( + info.getVersionRolledBackFrom().getLongVersionCode()); if (data.isStaged()) { params.setStaged(); } @@ -588,6 +558,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { // rollback sessions been applied. List<RollbackData> enabling = new ArrayList<>(); List<RollbackData> restoreInProgress = new ArrayList<>(); + Set<String> apexPackageNames = new HashSet<>(); synchronized (mLock) { ensureRollbackDataLoadedLocked(); for (RollbackData data : mRollbacks) { @@ -597,6 +568,12 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { } else if (data.restoreUserDataInProgress) { restoreInProgress.add(data); } + + for (PackageRollbackInfo info : data.info.getPackages()) { + if (info.isApex()) { + apexPackageNames.add(info.getPackageName()); + } + } } } } @@ -634,7 +611,14 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { } } - mPackageHealthObserver.onBootCompleted(); + for (String apexPackageName : apexPackageNames) { + // We will not recieve notifications when an apex is updated, + // so check now in case any rollbacks ought to be expired. The + // onPackagedReplace function is safe to call if the package + // hasn't actually been updated. + onPackageReplaced(apexPackageName); + } + mPackageHealthObserver.onBootCompletedAsync(); }); } @@ -832,7 +816,6 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { // TODO: It would be nice if package manager or package installer told // us the session directly, rather than have to search for it // ourselves. - PackageInstaller.SessionInfo session = null; // getAllSessions only returns sessions for the associated user. // Create a context with the right user so we can find the matching @@ -843,7 +826,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { return false; } - int parentSessionId = -1; + PackageInstaller.SessionInfo parentSession = null; + PackageInstaller.SessionInfo packageSession = null; PackageInstaller installer = context.getPackageManager().getPackageInstaller(); for (PackageInstaller.SessionInfo info : installer.getAllSessions()) { if (info.isMultiPackage()) { @@ -851,21 +835,21 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { PackageInstaller.SessionInfo child = installer.getSessionInfo(childId); if (sessionMatchesForEnableRollback(child, installFlags, newPackageCodePath)) { // TODO: Check we only have one matching session? - parentSessionId = info.getSessionId(); - session = child; + parentSession = info; + packageSession = child; break; } } } else if (sessionMatchesForEnableRollback(info, installFlags, newPackageCodePath)) { // TODO: Check we only have one matching session? - parentSessionId = info.getSessionId(); - session = info; + parentSession = info; + packageSession = info; break; } } - if (session == null) { - Log.e(TAG, "Unable to find session id for enabled rollback."); + if (parentSession == null || packageSession == null) { + Log.e(TAG, "Unable to find session for enabled rollback."); return false; } @@ -877,7 +861,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { ensureRollbackDataLoadedLocked(); for (int i = 0; i < mRollbacks.size(); ++i) { RollbackData data = mRollbacks.get(i); - if (data.apkSessionId == parentSessionId) { + if (data.apkSessionId == parentSession.getSessionId()) { rd = data; break; } @@ -890,7 +874,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { PackageParser.PackageLite newPackage = null; try { newPackage = PackageParser.parsePackageLite( - new File(session.resolvedBaseCodePath), 0); + new File(packageSession.resolvedBaseCodePath), 0); } catch (PackageParser.PackageParserException e) { Log.e(TAG, "Unable to parse new package", e); return false; @@ -908,16 +892,32 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { return false; } - return enableRollbackForSession(session, installedUsers, true); + NewRollback newRollback; + synchronized (mLock) { + // See if we already have a NewRollback that contains this package + // session. If not, create a NewRollback for the parent session + // that we will use for all the packages in the session. + newRollback = getNewRollbackForPackageSessionLocked(packageSession.getSessionId()); + if (newRollback == null) { + newRollback = createNewRollbackLocked(parentSession); + mNewRollbacks.add(newRollback); + } + } + + return enableRollbackForPackageSession(newRollback.data, packageSession, + installedUsers, /* snapshotUserData*/ true); } /** * Do code and userdata backups to enable rollback of the given session. * In case of multiPackage sessions, <code>session</code> should be one of * the child sessions, not the parent session. + * + * @return true on success, false on failure. */ - private boolean enableRollbackForSession(PackageInstaller.SessionInfo session, - @NonNull int[] installedUsers, boolean snapshotUserData) { + private boolean enableRollbackForPackageSession(RollbackData data, + PackageInstaller.SessionInfo session, @NonNull int[] installedUsers, + boolean snapshotUserData) { // TODO: Don't attempt to enable rollback for split installs. final int installFlags = session.installFlags; if ((installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) == 0) { @@ -972,41 +972,14 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { VersionedPackage installedVersion = new VersionedPackage(packageName, pkgInfo.getLongVersionCode()); - PackageRollbackInfo info = new PackageRollbackInfo(newVersion, installedVersion, + PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo( + newVersion, installedVersion, new IntArray() /* pendingBackups */, new ArrayList<>() /* pendingRestores */, isApex, IntArray.wrap(installedUsers), new SparseLongArray() /* ceSnapshotInodes */); - RollbackData data; - try { - int childSessionId = session.getSessionId(); - int parentSessionId = session.getParentSessionId(); - if (parentSessionId == PackageInstaller.SessionInfo.INVALID_ID) { - parentSessionId = childSessionId; - } - - synchronized (mLock) { - // TODO: no need to add to mChildSessions if childSessionId is - // the same as parentSessionId. - mChildSessions.put(childSessionId, parentSessionId); - data = mPendingRollbacks.get(parentSessionId); - if (data == null) { - int rollbackId = allocateRollbackIdLocked(); - if (session.isStaged()) { - data = mRollbackStore.createStagedRollback(rollbackId, parentSessionId); - } else { - data = mRollbackStore.createNonStagedRollback(rollbackId); - } - mPendingRollbacks.put(parentSessionId, data); - } - data.info.getPackages().add(info); - } - } catch (IOException e) { - Log.e(TAG, "Unable to create rollback for " + packageName, e); - return false; - } if (snapshotUserData && !isApex) { - mAppDataRollbackHelper.snapshotAppData(data.info.getRollbackId(), info); + mAppDataRollbackHelper.snapshotAppData(data.info.getRollbackId(), packageRollbackInfo); } try { @@ -1021,6 +994,10 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { Log.e(TAG, "Unable to copy package for rollback for " + packageName, e); return false; } + + synchronized (mLock) { + data.info.getPackages().add(packageRollbackInfo); + } return true; } @@ -1091,8 +1068,14 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { return; } + NewRollback newRollback; + synchronized (mLock) { + newRollback = createNewRollbackLocked(session); + } + if (!session.isMultiPackage()) { - if (!enableRollbackForSession(session, new int[0], false)) { + if (!enableRollbackForPackageSession(newRollback.data, session, + new int[0], /* snapshotUserData */ false)) { Log.e(TAG, "Unable to enable rollback for session: " + sessionId); result.offer(false); return; @@ -1106,7 +1089,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { result.offer(false); return; } - if (!enableRollbackForSession(childSession, new int[0], false)) { + if (!enableRollbackForPackageSession(newRollback.data, childSession, + new int[0], /* snapshotUserData */ false)) { Log.e(TAG, "Unable to enable rollback for session: " + sessionId); result.offer(false); return; @@ -1114,8 +1098,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { } } - completeEnableRollback(sessionId, true); - result.offer(true); + result.offer(completeEnableRollback(newRollback, true) != null); }); try { @@ -1246,9 +1229,19 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { @Override public void onFinished(int sessionId, boolean success) { - RollbackData rollback = completeEnableRollback(sessionId, success); - if (rollback != null && !rollback.isStaged()) { - makeRollbackAvailable(rollback); + NewRollback newRollback; + synchronized (mLock) { + newRollback = getNewRollbackForPackageSessionLocked(sessionId); + if (newRollback != null) { + mNewRollbacks.remove(newRollback); + } + } + + if (newRollback != null) { + RollbackData rollback = completeEnableRollback(newRollback, success); + if (rollback != null && !rollback.isStaged()) { + makeRollbackAvailable(rollback); + } } } } @@ -1258,25 +1251,22 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { * This should be called after rollback has been enabled for all packages * in the rollback. It does not make the rollback available yet. * - * @return the rollback data for a successfully enable-completed rollback. + * @return the rollback data for a successfully enable-completed rollback, + * or null on error. */ - private RollbackData completeEnableRollback(int sessionId, boolean success) { - RollbackData data = null; - synchronized (mLock) { - Integer parentSessionId = mChildSessions.remove(sessionId); - if (parentSessionId != null) { - sessionId = parentSessionId; - } - - data = mPendingRollbacks.remove(sessionId); - } - - if (data == null) { + private RollbackData completeEnableRollback(NewRollback newRollback, boolean success) { + RollbackData data = newRollback.data; + if (!success) { + // The install session was aborted, clean up the pending install. + deleteRollback(data); return null; } - if (!success) { - // The install session was aborted, clean up the pending install. + // It's safe to access data.info outside a synchronized block because + // this is running on the handler thread and all changes to the + // data.info occur on the handler thread. + if (data.info.getPackages().size() != newRollback.packageSessionIds.length) { + Log.e(TAG, "Failed to enable rollback for all packages in session."); deleteRollback(data); return null; } @@ -1360,7 +1350,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { } @GuardedBy("mLock") - private int allocateRollbackIdLocked() throws IOException { + private int allocateRollbackIdLocked() { int n = 0; int rollbackId; do { @@ -1371,7 +1361,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { } } while (n++ < 32); - throw new IOException("Failed to allocate rollback ID"); + throw new IllegalStateException("Failed to allocate rollback ID"); } private void deleteRollback(RollbackData rollbackData) { @@ -1446,4 +1436,60 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { + Manifest.permission.TEST_MANAGE_ROLLBACKS); } } + + private static class NewRollback { + public final RollbackData data; + + /** + * Session ids for all packages in the install. + * For multi-package sessions, this is the list of child session ids. + * For normal sessions, this list is a single element with the normal + * session id. + */ + public final int[] packageSessionIds; + + NewRollback(RollbackData data, int[] packageSessionIds) { + this.data = data; + this.packageSessionIds = packageSessionIds; + } + } + + NewRollback createNewRollbackLocked(PackageInstaller.SessionInfo parentSession) { + int rollbackId = allocateRollbackIdLocked(); + final RollbackData data; + int parentSessionId = parentSession.getSessionId(); + + if (parentSession.isStaged()) { + data = mRollbackStore.createStagedRollback(rollbackId, parentSessionId); + } else { + data = mRollbackStore.createNonStagedRollback(rollbackId); + } + + int[] packageSessionIds; + if (parentSession.isMultiPackage()) { + packageSessionIds = parentSession.getChildSessionIds(); + } else { + packageSessionIds = new int[]{parentSessionId}; + } + + return new NewRollback(data, packageSessionIds); + } + + /** + * Returns the NewRollback associated with the given package session. + * Returns null if no NewRollback is found for the given package + * session. + */ + NewRollback getNewRollbackForPackageSessionLocked(int packageSessionId) { + // We expect mNewRollbacks to be a very small list; linear search + // should be plenty fast. + for (NewRollback newRollbackData : mNewRollbacks) { + for (int id : newRollbackData.packageSessionIds) { + if (id == packageSessionId) { + return newRollbackData; + } + } + } + return null; + } } diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java index d8f07feb1ddb..bcef66ce2a53 100644 --- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java +++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java @@ -16,6 +16,7 @@ package com.android.server.rollback; +import android.annotation.Nullable; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -31,8 +32,8 @@ import android.os.FileUtils; import android.os.Handler; import android.os.HandlerThread; import android.os.PowerManager; +import android.os.SystemProperties; import android.text.TextUtils; -import android.util.Pair; import android.util.Slog; import android.util.StatsLog; @@ -49,9 +50,12 @@ import java.io.IOException; import java.io.PrintWriter; import java.util.Collections; import java.util.List; +import java.util.concurrent.TimeUnit; /** - * {@code PackageHealthObserver} for {@code RollbackManagerService}. + * {@link PackageHealthObserver} for {@link RollbackManagerService}. + * This class monitors crashes and triggers RollbackManager rollback accordingly. + * It also monitors native crashes for some short while after boot. * * @hide */ @@ -59,12 +63,21 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve private static final String TAG = "RollbackPackageHealthObserver"; private static final String NAME = "rollback-observer"; private static final int INVALID_ROLLBACK_ID = -1; + // TODO: make the following values configurable via DeviceConfig + private static final long NATIVE_CRASH_POLLING_INTERVAL_MILLIS = + TimeUnit.SECONDS.toMillis(30); + private static final long NUMBER_OF_NATIVE_CRASH_POLLS = 10; + private final Context mContext; private final Handler mHandler; private final File mLastStagedRollbackIdFile; + // this field is initialized in the c'tor and then only accessed from mHandler thread, so + // no need to guard with a lock + private long mNumberOfNativeCrashPollsRemaining; RollbackPackageHealthObserver(Context context) { mContext = context; + mNumberOfNativeCrashPollsRemaining = NUMBER_OF_NATIVE_CRASH_POLLS; HandlerThread handlerThread = new HandlerThread("RollbackPackageHealthObserver"); handlerThread.start(); mHandler = handlerThread.getThreadHandler(); @@ -76,75 +89,49 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve @Override public int onHealthCheckFailed(VersionedPackage failedPackage) { - VersionedPackage moduleMetadataPackage = getModuleMetadataPackage(); - if (moduleMetadataPackage == null) { - // Ignore failure, no mainline update available - return PackageHealthObserverImpact.USER_IMPACT_NONE; - } - - if (getAvailableRollback(mContext.getSystemService(RollbackManager.class), - failedPackage, moduleMetadataPackage) == null) { + if (getAvailableRollback(mContext.getSystemService(RollbackManager.class), failedPackage) + == null) { // Don't handle the notification, no rollbacks available for the package return PackageHealthObserverImpact.USER_IMPACT_NONE; + } else { + // Rollback is available, we may get a callback into #execute + return PackageHealthObserverImpact.USER_IMPACT_MEDIUM; } - // Rollback is available, we may get a callback into #execute - return PackageHealthObserverImpact.USER_IMPACT_MEDIUM; } @Override public boolean execute(VersionedPackage failedPackage) { + RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class); VersionedPackage moduleMetadataPackage = getModuleMetadataPackage(); - if (moduleMetadataPackage == null) { - // Ignore failure, no mainline update available - return false; - } + RollbackInfo rollback = getAvailableRollback(rollbackManager, failedPackage); - RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class); - Pair<RollbackInfo, Boolean> rollbackPair = getAvailableRollback(rollbackManager, - failedPackage, moduleMetadataPackage); - if (rollbackPair == null) { + if (rollback == null) { Slog.w(TAG, "Expected rollback but no valid rollback found for package: [ " + failedPackage.getPackageName() + "] with versionCode: [" + failedPackage.getVersionCode() + "]"); return false; } - RollbackInfo rollback = rollbackPair.first; - // We only log mainline package rollbacks, so check if rollback contains the - // module metadata provider, if it does, the rollback is a mainline rollback - boolean hasModuleMetadataPackage = rollbackPair.second; - - if (hasModuleMetadataPackage) { - StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED, - StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE, - moduleMetadataPackage.getPackageName(), - moduleMetadataPackage.getVersionCode()); - } + logEvent(moduleMetadataPackage, + StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE); LocalIntentReceiver rollbackReceiver = new LocalIntentReceiver((Intent result) -> { - if (hasModuleMetadataPackage) { - int status = result.getIntExtra(RollbackManager.EXTRA_STATUS, - RollbackManager.STATUS_FAILURE); - if (status == RollbackManager.STATUS_SUCCESS) { - if (rollback.isStaged()) { - int rollbackId = rollback.getRollbackId(); - BroadcastReceiver listener = - listenForStagedSessionReady(rollbackManager, rollbackId, - moduleMetadataPackage); - handleStagedSessionChange(rollbackManager, rollbackId, listener, - moduleMetadataPackage); - } else { - StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED, - StatsLog - .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS, - moduleMetadataPackage.getPackageName(), - moduleMetadataPackage.getVersionCode()); - } + int status = result.getIntExtra(RollbackManager.EXTRA_STATUS, + RollbackManager.STATUS_FAILURE); + if (status == RollbackManager.STATUS_SUCCESS) { + if (rollback.isStaged()) { + int rollbackId = rollback.getRollbackId(); + BroadcastReceiver listener = + listenForStagedSessionReady(rollbackManager, rollbackId, + moduleMetadataPackage); + handleStagedSessionChange(rollbackManager, rollbackId, listener, + moduleMetadataPackage); } else { - StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED, - StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE, - moduleMetadataPackage.getPackageName(), - moduleMetadataPackage.getVersionCode()); + logEvent(moduleMetadataPackage, + StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS); } + } else { + logEvent(moduleMetadataPackage, + StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE); } }); @@ -169,16 +156,29 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve PackageWatchdog.getInstance(mContext).startObservingHealth(this, packages, durationMs); } - /** Verifies the rollback state after a reboot. */ - public void onBootCompleted() { + /** Verifies the rollback state after a reboot and schedules polling for sometime after reboot + * to check for native crashes and mitigate them if needed. + */ + public void onBootCompletedAsync() { + mHandler.post(()->onBootCompleted()); + } + + private void onBootCompleted() { + RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class); + PackageInstaller packageInstaller = mContext.getPackageManager().getPackageInstaller(); + String moduleMetadataPackageName = getModuleMetadataPackageName(); + VersionedPackage newModuleMetadataPackage = getModuleMetadataPackage(); + + if (getAvailableRollback(rollbackManager, newModuleMetadataPackage) != null) { + scheduleCheckAndMitigateNativeCrashes(); + } + int rollbackId = popLastStagedRollbackId(); if (rollbackId == INVALID_ROLLBACK_ID) { // No staged rollback before reboot return; } - RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class); - PackageInstaller packageInstaller = mContext.getPackageManager().getPackageInstaller(); RollbackInfo rollback = null; for (RollbackInfo info : rollbackManager.getRecentlyCommittedRollbacks()) { if (rollbackId == info.getRollbackId()) { @@ -192,27 +192,16 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve return; } - String moduleMetadataPackageName = getModuleMetadataPackageName(); - if (moduleMetadataPackageName == null) { - // Only log mainline staged rollbacks - return; - } - // Use the version of the metadata package that was installed before // we rolled back for logging purposes. - VersionedPackage moduleMetadataPackage = null; + VersionedPackage oldModuleMetadataPackage = null; for (PackageRollbackInfo packageRollback : rollback.getPackages()) { - if (moduleMetadataPackageName.equals(packageRollback.getPackageName())) { - moduleMetadataPackage = packageRollback.getVersionRolledBackFrom(); + if (packageRollback.getPackageName().equals(moduleMetadataPackageName)) { + oldModuleMetadataPackage = packageRollback.getVersionRolledBackFrom(); break; } } - if (moduleMetadataPackage == null) { - // Only log mainline staged rollbacks - return; - } - int sessionId = rollback.getCommittedSessionId(); PackageInstaller.SessionInfo sessionInfo = packageInstaller.getSessionInfo(sessionId); if (sessionInfo == null) { @@ -220,42 +209,33 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve return; } if (sessionInfo.isStagedSessionApplied()) { - StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED, - StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS, - moduleMetadataPackage.getPackageName(), - moduleMetadataPackage.getVersionCode()); + logEvent(oldModuleMetadataPackage, + StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS); } else if (sessionInfo.isStagedSessionReady()) { // TODO: What do for staged session ready but not applied } else { - StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED, - StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE, - moduleMetadataPackage.getPackageName(), - moduleMetadataPackage.getVersionCode()); + logEvent(oldModuleMetadataPackage, + StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE); } } - private Pair<RollbackInfo, Boolean> getAvailableRollback(RollbackManager rollbackManager, - VersionedPackage failedPackage, VersionedPackage moduleMetadataPackage) { + private RollbackInfo getAvailableRollback(RollbackManager rollbackManager, + VersionedPackage failedPackage) { for (RollbackInfo rollback : rollbackManager.getAvailableRollbacks()) { - // We only rollback mainline packages, so check if rollback contains the - // module metadata provider, if it does, the rollback is a mainline rollback - boolean hasModuleMetadataPackage = false; - boolean hasFailedPackage = false; for (PackageRollbackInfo packageRollback : rollback.getPackages()) { - hasModuleMetadataPackage |= packageRollback.getPackageName().equals( - moduleMetadataPackage.getPackageName()); - hasFailedPackage |= packageRollback.getPackageName().equals( + boolean hasFailedPackage = packageRollback.getPackageName().equals( failedPackage.getPackageName()) && packageRollback.getVersionRolledBackFrom().getVersionCode() == failedPackage.getVersionCode(); - } - if (hasFailedPackage) { - return new Pair<RollbackInfo, Boolean>(rollback, hasModuleMetadataPackage); + if (hasFailedPackage) { + return rollback; + } } } return null; } + @Nullable private String getModuleMetadataPackageName() { String packageName = mContext.getResources().getString( R.string.config_defaultModuleMetadataProvider); @@ -265,6 +245,7 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve return packageName; } + @Nullable private VersionedPackage getModuleMetadataPackage() { String packageName = getModuleMetadataPackageName(); if (packageName == null) { @@ -311,18 +292,13 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve if (sessionInfo.isStagedSessionReady()) { mContext.unregisterReceiver(listener); saveLastStagedRollbackId(rollbackId); - StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED, + logEvent(moduleMetadataPackage, StatsLog - .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED, - moduleMetadataPackage.getPackageName(), - moduleMetadataPackage.getVersionCode()); + .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED); mContext.getSystemService(PowerManager.class).reboot("Rollback staged install"); } else if (sessionInfo.isStagedSessionFailed()) { - StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED, - StatsLog - .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE, - moduleMetadataPackage.getPackageName(), - moduleMetadataPackage.getVersionCode()); + logEvent(moduleMetadataPackage, + StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE); mContext.unregisterReceiver(listener); } } @@ -358,4 +334,42 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve mLastStagedRollbackIdFile.delete(); return rollbackId; } + + private static void logEvent(@Nullable VersionedPackage moduleMetadataPackage, int type) { + Slog.i(TAG, "Watchdog event occurred of type: " + type); + if (moduleMetadataPackage != null) { + StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED, type, + moduleMetadataPackage.getPackageName(), moduleMetadataPackage.getVersionCode()); + } + } + + /** + * This method should be only called on mHandler thread, since it modifies + * {@link #mNumberOfNativeCrashPollsRemaining} and we want to keep this class lock free. + */ + private void checkAndMitigateNativeCrashes() { + mNumberOfNativeCrashPollsRemaining--; + // Check if native watchdog reported a crash + if ("1".equals(SystemProperties.get("ro.init.updatable_crashing"))) { + execute(getModuleMetadataPackage()); + // we stop polling after an attempt to execute rollback, regardless of whether the + // attempt succeeds or not + } else { + if (mNumberOfNativeCrashPollsRemaining > 0) { + mHandler.postDelayed(() -> checkAndMitigateNativeCrashes(), + NATIVE_CRASH_POLLING_INTERVAL_MILLIS); + } + } + } + + /** + * Since this method can eventually trigger a RollbackManager rollback, it should be called + * only once boot has completed {@code onBootCompleted} and not earlier, because the install + * session must be entirely completed before we try to rollback. + */ + private void scheduleCheckAndMitigateNativeCrashes() { + Slog.i(TAG, "Scheduling " + mNumberOfNativeCrashPollsRemaining + " polls to check " + + "and mitigate native crashes"); + mHandler.post(()->checkAndMitigateNativeCrashes()); + } } diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java index 2cfa46532834..8a26368c3e13 100644 --- a/services/core/java/com/android/server/rollback/RollbackStore.java +++ b/services/core/java/com/android/server/rollback/RollbackStore.java @@ -194,7 +194,7 @@ class RollbackStore { * Creates a new RollbackData instance for a non-staged rollback with * backupDir assigned. */ - RollbackData createNonStagedRollback(int rollbackId) throws IOException { + RollbackData createNonStagedRollback(int rollbackId) { File backupDir = new File(mRollbackDataDir, Integer.toString(rollbackId)); return new RollbackData(rollbackId, backupDir, -1); } @@ -203,8 +203,7 @@ class RollbackStore { * Creates a new RollbackData instance for a staged rollback with * backupDir assigned. */ - RollbackData createStagedRollback(int rollbackId, int stagedSessionId) - throws IOException { + RollbackData createStagedRollback(int rollbackId, int stagedSessionId) { File backupDir = new File(mRollbackDataDir, Integer.toString(rollbackId)); return new RollbackData(rollbackId, backupDir, stagedSessionId); } diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java index b9b5aae03587..7734d6bd30a7 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java @@ -77,7 +77,8 @@ public interface StatusBarManagerInternal { void onCameraLaunchGestureDetected(int source); void topAppWindowChanged(int displayId, boolean menuVisible); void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis, int dockedStackVis, - int mask, Rect fullscreenBounds, Rect dockedBounds, String cause); + int mask, Rect fullscreenBounds, Rect dockedBounds, boolean isNavbarColorManagedByIme, + String cause); void toggleSplitScreen(); void appTransitionFinished(int displayId); diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index a5656c3e40f0..b2d7084fcd05 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -262,9 +262,10 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D @Override public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis, int dockedStackVis, int mask, Rect fullscreenBounds, Rect dockedBounds, - String cause) { + boolean isNavbarColorManagedByIme, String cause) { StatusBarManagerService.this.setSystemUiVisibility(displayId, vis, fullscreenStackVis, - dockedStackVis, mask, fullscreenBounds, dockedBounds, cause); + dockedStackVis, mask, fullscreenBounds, dockedBounds, isNavbarColorManagedByIme, + cause); } @Override @@ -872,11 +873,13 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D public void setSystemUiVisibility(int displayId, int vis, int mask, String cause) { final UiState state = getUiState(displayId); setSystemUiVisibility(displayId, vis, 0, 0, mask, - state.mFullscreenStackBounds, state.mDockedStackBounds, cause); + state.mFullscreenStackBounds, state.mDockedStackBounds, + state.mNavbarColorManagedByIme, cause); } private void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis, - int dockedStackVis, int mask, Rect fullscreenBounds, Rect dockedBounds, String cause) { + int dockedStackVis, int mask, Rect fullscreenBounds, Rect dockedBounds, + boolean isNavbarColorManagedByIme, String cause) { // also allows calls from window manager which is in this process. enforceStatusBarService(); @@ -884,7 +887,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D synchronized (mLock) { updateUiVisibilityLocked(displayId, vis, fullscreenStackVis, dockedStackVis, mask, - fullscreenBounds, dockedBounds); + fullscreenBounds, dockedBounds, isNavbarColorManagedByIme); disableLocked( displayId, mCurrentUserId, @@ -896,17 +899,19 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D private void updateUiVisibilityLocked(final int displayId, final int vis, final int fullscreenStackVis, final int dockedStackVis, final int mask, - final Rect fullscreenBounds, final Rect dockedBounds) { + final Rect fullscreenBounds, final Rect dockedBounds, + final boolean isNavbarColorManagedByIme) { final UiState state = getUiState(displayId); if (!state.systemUiStateEquals(vis, fullscreenStackVis, dockedStackVis, - fullscreenBounds, dockedBounds)) { + fullscreenBounds, dockedBounds, isNavbarColorManagedByIme)) { state.setSystemUiState(vis, fullscreenStackVis, dockedStackVis, fullscreenBounds, - dockedBounds); + dockedBounds, isNavbarColorManagedByIme); mHandler.post(() -> { if (mBar != null) { try { mBar.setSystemUiVisibility(displayId, vis, fullscreenStackVis, - dockedStackVis, mask, fullscreenBounds, dockedBounds); + dockedStackVis, mask, fullscreenBounds, dockedBounds, + isNavbarColorManagedByIme); } catch (RemoteException ex) { Log.w(TAG, "Can not get StatusBar!"); } @@ -945,6 +950,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D private int mImeBackDisposition = 0; private boolean mShowImeSwitcher = false; private IBinder mImeToken = null; + private boolean mNavbarColorManagedByIme = false; private int getDisabled1() { return mDisabled1; @@ -972,21 +978,25 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D } private void setSystemUiState(final int vis, final int fullscreenStackVis, - final int dockedStackVis, final Rect fullscreenBounds, final Rect dockedBounds) { + final int dockedStackVis, final Rect fullscreenBounds, final Rect dockedBounds, + final boolean navbarColorManagedByIme) { mSystemUiVisibility = vis; mFullscreenStackSysUiVisibility = fullscreenStackVis; mDockedStackSysUiVisibility = dockedStackVis; mFullscreenStackBounds.set(fullscreenBounds); mDockedStackBounds.set(dockedBounds); + mNavbarColorManagedByIme = navbarColorManagedByIme; } private boolean systemUiStateEquals(final int vis, final int fullscreenStackVis, - final int dockedStackVis, final Rect fullscreenBounds, final Rect dockedBounds) { + final int dockedStackVis, final Rect fullscreenBounds, final Rect dockedBounds, + final boolean navbarColorManagedByIme) { return mSystemUiVisibility == vis && mFullscreenStackSysUiVisibility == fullscreenStackVis && mDockedStackSysUiVisibility == dockedStackVis && mFullscreenStackBounds.equals(fullscreenBounds) - && mDockedStackBounds.equals(dockedBounds); + && mDockedStackBounds.equals(dockedBounds) + && mNavbarColorManagedByIme == navbarColorManagedByIme; } private void setImeWindowState(final int vis, final int backDisposition, @@ -1051,7 +1061,8 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D state.mImeBackDisposition, state.mShowImeSwitcher, gatherDisableActionsLocked(mCurrentUserId, 2), state.mFullscreenStackSysUiVisibility, state.mDockedStackSysUiVisibility, - state.mImeToken, state.mFullscreenStackBounds, state.mDockedStackBounds); + state.mImeToken, state.mFullscreenStackBounds, state.mDockedStackBounds, + state.mNavbarColorManagedByIme); } } diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index 6ce42ec4cb44..b6a5be807fb6 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -1108,6 +1108,7 @@ final class AccessibilityController { // the window manager is still looking for where to put it. // We will do the work when we get a focus change callback. // TODO(b/112273690): Support multiple displays + // TODO(b/129098348): Support embedded displays if (mService.getDefaultDisplayContentLocked().mCurrentFocus == null) { return; } @@ -1400,7 +1401,28 @@ final class AccessibilityController { if (w.isVisibleLw()) { outWindows.put(mTempLayer++, w); } - }, false /* traverseTopToBottom */ ); + }, false /* traverseTopToBottom */); + mService.mRoot.forAllWindows(w -> { + final WindowState win = findRootDisplayParentWindow(w); + if (win != null && win.getDisplayContent().isDefaultDisplay && w.isVisibleLw()) { + // TODO(b/129098348): insert windows on child displays into outWindows based on + // root-display-parent window. + outWindows.put(mTempLayer++, w); + } + }, false /* traverseTopToBottom */); + } + + private WindowState findRootDisplayParentWindow(WindowState win) { + WindowState displayParentWindow = win.getDisplayContent().getParentWindow(); + if (displayParentWindow == null) { + return null; + } + WindowState candidate = displayParentWindow; + while (candidate != null) { + displayParentWindow = candidate; + candidate = displayParentWindow.getDisplayContent().getParentWindow(); + } + return displayParentWindow; } private class MyHandler extends Handler { diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 4278860a0d77..802683a67f80 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -916,8 +916,12 @@ final class ActivityRecord extends ConfigurationContainer { } } + static boolean isResolverActivity(String className) { + return ResolverActivity.class.getName().equals(className); + } + boolean isResolverActivity() { - return ResolverActivity.class.getName().equals(mActivityComponent.getClassName()); + return isResolverActivity(mActivityComponent.getClassName()); } boolean isResolverOrChildActivity() { diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index d7a518539deb..fe99fd20b855 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -1822,7 +1822,8 @@ class ActivityStack extends ConfigurationContainer { prev.setDeferHidingClient(false); // If we were visible then resumeTopActivities will release resources before // stopping. - addToStopping(prev, true /* scheduleIdle */, false /* idleDelayed */); + addToStopping(prev, true /* scheduleIdle */, false /* idleDelayed */, + "completePauseLocked"); } } else { if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "App died during pause, not stopping: " + prev); @@ -1883,8 +1884,11 @@ class ActivityStack extends ConfigurationContainer { mRootActivityContainer.ensureActivitiesVisible(resuming, 0, !PRESERVE_WINDOWS); } - private void addToStopping(ActivityRecord r, boolean scheduleIdle, boolean idleDelayed) { + private void addToStopping(ActivityRecord r, boolean scheduleIdle, boolean idleDelayed, + String reason) { if (!mStackSupervisor.mStoppingActivities.contains(r)) { + EventLog.writeEvent(EventLogTags.AM_ADD_TO_STOPPING, r.mUserId, + System.identityHashCode(r), r.shortComponentName, reason); mStackSupervisor.mStoppingActivities.add(r); } @@ -2433,7 +2437,7 @@ class ActivityStack extends ConfigurationContainer { case PAUSING: case PAUSED: addToStopping(r, true /* scheduleIdle */, - canEnterPictureInPicture /* idleDelayed */); + canEnterPictureInPicture /* idleDelayed */, "makeInvisible"); break; default: @@ -4084,7 +4088,8 @@ class ActivityStack extends ConfigurationContainer { if (mode == FINISH_AFTER_VISIBLE && (r.visible || r.nowVisible) && next != null && !next.nowVisible && !isFloating) { if (!mStackSupervisor.mStoppingActivities.contains(r)) { - addToStopping(r, false /* scheduleIdle */, false /* idleDelayed */); + addToStopping(r, false /* scheduleIdle */, false /* idleDelayed */, + "finishCurrentActivityLocked"); } if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to STOPPING: "+ r + " (finish requested)"); @@ -5424,7 +5429,7 @@ class ActivityStack extends ConfigurationContainer { if (isAttached()) { getDisplay().positionChildAtBottom(this); } - if (!isActivityTypeHome() || getDisplay().isRemoved()) { + if (!isActivityTypeHome() || !isAttached()) { remove(); } } diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java index 7eac07c47741..919141c13622 100644 --- a/services/core/java/com/android/server/wm/ActivityStartController.java +++ b/services/core/java/com/android/server/wm/ActivityStartController.java @@ -171,7 +171,12 @@ public class ActivityStartController { void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason, int displayId) { final ActivityOptions options = ActivityOptions.makeBasic(); options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN); - options.setLaunchActivityType(ACTIVITY_TYPE_HOME); + if (!ActivityRecord.isResolverActivity(aInfo.name)) { + // The resolver activity shouldn't be put in home stack because when the foreground is + // standard type activity, the resolver activity should be put on the top of current + // foreground instead of bring home stack to front. + options.setLaunchActivityType(ACTIVITY_TYPE_HOME); + } options.setLaunchDisplayId(displayId); mLastHomeActivityStartResult = obtainStarter(intent, "startHomeActivity: " + reason) .setOutActivity(tmpOutRecord) diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 4ef8753bd131..e7e34bb881e4 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -160,6 +160,10 @@ class ActivityStarter { private int mCallingUid; private ActivityOptions mOptions; + // If it is true, background activity can only be started in an existing task that contains + // an activity with same uid. + private boolean mRestrictedBgActivity; + private int mLaunchMode; private boolean mLaunchTaskBehind; private int mLaunchFlags; @@ -455,6 +459,7 @@ class ActivityStarter { mIntent = starter.mIntent; mCallingUid = starter.mCallingUid; mOptions = starter.mOptions; + mRestrictedBgActivity = starter.mRestrictedBgActivity; mLaunchTaskBehind = starter.mLaunchTaskBehind; mLaunchFlags = starter.mLaunchFlags; @@ -551,7 +556,8 @@ class ActivityStarter { mLastStartActivityTimeMs = System.currentTimeMillis(); mLastStartActivityRecord[0] = r; mLastStartActivityResult = startActivity(r, sourceRecord, voiceSession, voiceInteractor, - startFlags, doResume, options, inTask, mLastStartActivityRecord); + startFlags, doResume, options, inTask, mLastStartActivityRecord, + false /* restrictedBgActivity */); mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(mLastStartActivityResult, mLastStartActivityRecord[0]); return mLastStartActivityResult; @@ -760,22 +766,17 @@ class ActivityStarter { abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid, callingPid, resolvedType, aInfo.applicationInfo); - boolean abortBackgroundStart = false; + boolean restrictedBgActivity = false; if (!abort) { try { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "shouldAbortBackgroundActivityStart"); - abortBackgroundStart = shouldAbortBackgroundActivityStart(callingUid, + restrictedBgActivity = shouldAbortBackgroundActivityStart(callingUid, callingPid, callingPackage, realCallingUid, realCallingPid, callerApp, originatingPendingIntent, allowBackgroundActivityStart, intent); } finally { Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } - abort |= (abortBackgroundStart && !mService.isBackgroundActivityStartsEnabled()); - // TODO: remove this toast after feature development is done - if (abortBackgroundStart) { - showBackgroundActivityBlockedToast(abort, callingPackage); - } } // Merge the two options bundles, while realCallerOptions takes precedence. @@ -918,8 +919,10 @@ class ActivityStarter { || stack.getResumedActivity().info.applicationInfo.uid != realCallingUid)) { if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid, realCallingPid, realCallingUid, "Activity start")) { - mController.addPendingActivityLaunch(new PendingActivityLaunch(r, - sourceRecord, startFlags, stack, callerApp)); + if (!restrictedBgActivity) { + mController.addPendingActivityLaunch(new PendingActivityLaunch(r, + sourceRecord, startFlags, stack, callerApp)); + } ActivityOptions.abort(checkedOptions); return ActivityManager.START_SWITCHES_CANCELED; } @@ -929,7 +932,7 @@ class ActivityStarter { mController.doPendingActivityLaunches(false); final int res = startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags, - true /* doResume */, checkedOptions, inTask, outActivity); + true /* doResume */, checkedOptions, inTask, outActivity, restrictedBgActivity); mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(res, outActivity[0]); return res; } @@ -1038,6 +1041,12 @@ class ActivityStarter { } } } + // don't abort if the callingUid has SYSTEM_ALERT_WINDOW permission + if (mService.hasSystemAlertWindowPermission(callingUid, callingPid, callingPackage)) { + Slog.w(TAG, "Background activity start for " + callingPackage + + " allowed because SYSTEM_ALERT_WINDOW permission is granted."); + return false; + } // anything that has fallen through would currently be aborted Slog.w(TAG, "Background activity start [callingPackage: " + callingPackage + "; callingUid: " + callingUid @@ -1395,13 +1404,13 @@ class ActivityStarter { private int startActivity(final ActivityRecord r, ActivityRecord sourceRecord, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask, - ActivityRecord[] outActivity) { + ActivityRecord[] outActivity, boolean restrictedBgActivity) { int result = START_CANCELED; final ActivityStack startedActivityStack; try { mService.mWindowManager.deferSurfaceLayout(); result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor, - startFlags, doResume, options, inTask, outActivity); + startFlags, doResume, options, inTask, outActivity, restrictedBgActivity); } finally { final ActivityStack currentStack = r.getActivityStack(); startedActivityStack = currentStack != null ? currentStack : mTargetStack; @@ -1437,14 +1446,40 @@ class ActivityStarter { return result; } + /** + * Return true if background activity is really aborted. + * + * TODO(b/131748165): Refactor the logic so we don't need to call this method everywhere. + */ + private boolean handleBackgroundActivityAbort(ActivityRecord r) { + // TODO(b/131747138): Remove toast and refactor related code in Q release. + boolean abort = !mService.isBackgroundActivityStartsEnabled(); + showBackgroundActivityBlockedToast(abort, r.launchedFromPackage); + if (!abort) { + return false; + } + ActivityRecord resultRecord = r.resultTo; + String resultWho = r.resultWho; + int requestCode = r.requestCode; + if (resultRecord != null) { + ActivityStack resultStack = resultRecord.getActivityStack(); + resultStack.sendActivityResultLocked(-1, resultRecord, resultWho, requestCode, + RESULT_CANCELED, null); + } + // We pretend to the caller that it was really started to make it backward compatible, but + // they will just get a cancel result. + ActivityOptions.abort(r.pendingOptions); + return true; + } + // Note: This method should only be called from {@link startActivity}. private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask, - ActivityRecord[] outActivity) { - + ActivityRecord[] outActivity, boolean restrictedBgActivity) { setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession, - voiceInteractor); + voiceInteractor, restrictedBgActivity); + final int preferredWindowingMode = mLaunchParams.mWindowingMode; computeLaunchingTaskFlags(); @@ -1652,7 +1687,7 @@ class ActivityStarter { } else { // This not being started from an existing activity, and not part of a new task... // just put it in the top task, though these days this case should never happen. - setTaskToCurrentTopOrCreateNewTask(); + result = setTaskToCurrentTopOrCreateNewTask(); } if (result != START_SUCCESS) { return result; @@ -1725,6 +1760,7 @@ class ActivityStarter { mIntent = null; mCallingUid = -1; mOptions = null; + mRestrictedBgActivity = false; mLaunchTaskBehind = false; mLaunchFlags = 0; @@ -1764,7 +1800,8 @@ class ActivityStarter { private void setInitialState(ActivityRecord r, ActivityOptions options, TaskRecord inTask, boolean doResume, int startFlags, ActivityRecord sourceRecord, - IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) { + IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, + boolean restrictedBgActivity) { reset(false /* clearRequest */); mStartActivity = r; @@ -1774,6 +1811,7 @@ class ActivityStarter { mSourceRecord = sourceRecord; mVoiceSession = voiceSession; mVoiceInteractor = voiceInteractor; + mRestrictedBgActivity = restrictedBgActivity; mLaunchParams.reset(); @@ -1874,6 +1912,11 @@ class ActivityStarter { } mNoAnimation = (mLaunchFlags & FLAG_ACTIVITY_NO_ANIMATION) != 0; + + if (restrictedBgActivity) { + mAvoidMoveToFront = true; + mDoResume = false; + } } private void sendNewTaskResultRequestIfNeeded() { @@ -2075,8 +2118,8 @@ class ActivityStarter { final ActivityRecord curTop = (focusStack == null) ? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop); final TaskRecord topTask = curTop != null ? curTop.getTaskRecord() : null; - differentTopTask = topTask != null - && (topTask != intentActivity.getTaskRecord() || topTask != focusStack.topTask()); + differentTopTask = topTask != intentActivity.getTaskRecord() + || (focusStack != null && topTask != focusStack.topTask()); } else { // The existing task should always be different from those in other displays. differentTopTask = true; @@ -2271,6 +2314,9 @@ class ActivityStarter { // isLockTaskModeViolation fails below. if (mReuseTask == null) { + if (mRestrictedBgActivity && handleBackgroundActivityAbort(mStartActivity)) { + return START_ABORTED; + } final TaskRecord task = mTargetStack.createTaskRecord( mSupervisor.getNextTaskIdForUserLocked(mStartActivity.mUserId), mNewTaskInfo != null ? mNewTaskInfo : mStartActivity.info, @@ -2283,6 +2329,11 @@ class ActivityStarter { if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity + " in new task " + mStartActivity.getTaskRecord()); } else { + if (mRestrictedBgActivity && !mReuseTask.containsAppUid(mCallingUid)) { + if (handleBackgroundActivityAbort(mStartActivity)) { + return START_ABORTED; + } + } addOrReparentStartingActivity(mReuseTask, "setTaskFromReuseOrCreateNewTask"); } @@ -2322,6 +2373,12 @@ class ActivityStarter { final TaskRecord sourceTask = mSourceRecord.getTaskRecord(); final ActivityStack sourceStack = mSourceRecord.getActivityStack(); + if (mRestrictedBgActivity && !sourceTask.containsAppUid(mCallingUid)) { + if (handleBackgroundActivityAbort(mStartActivity)) { + return START_ABORTED; + } + return START_ABORTED; + } // We only want to allow changing stack in two cases: // 1. If the target task is not the top one. Otherwise we would move the launching task to // the other side, rather than show two side by side. @@ -2483,20 +2540,33 @@ class ActivityStarter { } } - private void setTaskToCurrentTopOrCreateNewTask() { + private int setTaskToCurrentTopOrCreateNewTask() { mTargetStack = computeStackFocus(mStartActivity, false, mLaunchFlags, mOptions); if (mDoResume) { mTargetStack.moveToFront("addingToTopTask"); } final ActivityRecord prev = mTargetStack.getTopActivity(); + if (mRestrictedBgActivity && prev == null) { + if (handleBackgroundActivityAbort(mStartActivity)) { + return START_ABORTED; + } + return START_ABORTED; + } final TaskRecord task = (prev != null) ? prev.getTaskRecord() : mTargetStack.createTaskRecord( mSupervisor.getNextTaskIdForUserLocked(mStartActivity.mUserId), mStartActivity.info, mIntent, null, null, true, mStartActivity, mSourceRecord, mOptions); + if (mRestrictedBgActivity && !task.containsAppUid(mCallingUid)) { + if (handleBackgroundActivityAbort(mStartActivity)) { + return START_ABORTED; + } + return START_ABORTED; + } addOrReparentStartingActivity(task, "setTaskToCurrentTopOrCreateNewTask"); mTargetStack.positionChildWindowContainerAtTop(task); if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity + " in new guessed " + mStartActivity.getTaskRecord()); + return START_SUCCESS; } private void addOrReparentStartingActivity(TaskRecord parent, String reason) { diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index a55ee5ff7f1f..4a6aa336e36f 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -108,12 +108,10 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SWITC import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_VISIBILITY; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; -import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_ACTIVITY_ID; import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT; import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_DATA; import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS; import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE; -import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_TASK_ID; import static com.android.server.wm.ActivityTaskManagerService.H.REPORT_TIME_TRACKER_MSG; import static com.android.server.wm.ActivityTaskManagerService.UiHandler.DISMISS_DIALOG_UI_MSG; import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE; @@ -136,6 +134,7 @@ import android.app.ActivityTaskManager; import android.app.ActivityThread; import android.app.AlertDialog; import android.app.AppGlobals; +import android.app.AppOpsManager; import android.app.Dialog; import android.app.IActivityController; import android.app.IActivityTaskManager; @@ -216,7 +215,6 @@ import android.text.format.Time; import android.util.ArrayMap; import android.util.EventLog; import android.util.Log; -import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; @@ -875,6 +873,16 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return getUserManager().hasUserRestriction(restriction, userId); } + boolean hasSystemAlertWindowPermission(int callingUid, int callingPid, String callingPackage) { + final int mode = getAppOpsService().noteOperation(AppOpsManager.OP_SYSTEM_ALERT_WINDOW, + callingUid, callingPackage); + if (mode == AppOpsManager.MODE_DEFAULT) { + return checkPermission(Manifest.permission.SYSTEM_ALERT_WINDOW, callingPid, callingUid) + == PERMISSION_GRANTED; + } + return mode == AppOpsManager.MODE_ALLOWED; + } + protected RecentTasks createRecentTasks() { return new RecentTasks(this, mStackSupervisor); } @@ -5810,10 +5818,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { */ Intent getSecondaryHomeIntent(String preferredPackage) { final Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null); - final boolean useSystemProvidedLauncher = mContext.getResources().getBoolean( - com.android.internal.R.bool.config_useSystemProvidedLauncherForSecondary); - if (preferredPackage == null || useSystemProvidedLauncher) { - // Using the component stored in config if no package name or forced. + if (preferredPackage == null) { + // Using the component stored in config if no package name. final String secondaryHomeComponent = mContext.getResources().getString( com.android.internal.R.string.config_secondaryHomeComponent); intent.setComponent(ComponentName.unflattenFromString(secondaryHomeComponent)); diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java index 75e34fb0d453..d4c4e6a93fa6 100644 --- a/services/core/java/com/android/server/wm/AppTransitionController.java +++ b/services/core/java/com/android/server/wm/AppTransitionController.java @@ -478,7 +478,7 @@ public class AppTransitionController { outReasons.put(windowingMode, APP_TRANSITION_WINDOWS_DRAWN); } else { outReasons.put(windowingMode, - wtoken.startingData instanceof SplashScreenStartingData + wtoken.mStartingData instanceof SplashScreenStartingData ? APP_TRANSITION_SPLASH_SCREEN : APP_TRANSITION_SNAPSHOT); } diff --git a/services/core/java/com/android/server/wm/AppWindowThumbnail.java b/services/core/java/com/android/server/wm/AppWindowThumbnail.java index 6318486683ae..4d972dca11ed 100644 --- a/services/core/java/com/android/server/wm/AppWindowThumbnail.java +++ b/services/core/java/com/android/server/wm/AppWindowThumbnail.java @@ -129,7 +129,7 @@ class AppWindowThumbnail implements Animatable { mSurfaceAnimator.startAnimation(t, new LocalAnimationAdapter( new WindowAnimationSpec(anim, position, mAppToken.getDisplayContent().mAppTransition.canSkipFirstFrame(), - mAppToken.getWindowCornerRadiusForAnimation()), + mAppToken.getDisplayContent().getWindowCornerRadius()), mAppToken.mWmService.mSurfaceAnimationRunner), false /* hidden */); } diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index 2321898b91cc..5be8e1408578 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -78,6 +78,7 @@ import static com.android.server.wm.WindowManagerService.H.NOTIFY_ACTIVITY_DRAWN import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL; import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES; import static com.android.server.wm.WindowManagerService.logWithStack; +import static com.android.server.wm.WindowState.LEGACY_POLICY_VISIBILITY; import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM; import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM; @@ -203,7 +204,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree boolean removed; // Information about an application starting window if displayed. - StartingData startingData; + StartingData mStartingData; WindowState startingWindow; StartingSurface startingSurface; boolean startingDisplayed; @@ -385,8 +386,8 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree // it from behind the starting window, so there is no need for it to also be doing its // own stuff. win.cancelAnimation(); - removeStartingWindow(); } + removeStartingWindow(); updateReportedVisibilityLocked(); } @@ -638,8 +639,8 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree // If we are being set visible, and the starting window is not yet displayed, // then make sure it doesn't get displayed. if (startingWindow != null && !startingWindow.isDrawnLw()) { - startingWindow.mPolicyVisibility = false; - startingWindow.mPolicyVisibilityAfterAnim = false; + startingWindow.clearPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY); + startingWindow.mLegacyPolicyVisibilityAfterAnim = false; } // We are becoming visible, so better freeze the screen with the windows that are @@ -874,7 +875,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG_WM, "removeAppToken: " + this + " delayed=" + delayed + " Callers=" + Debug.getCallers(4)); - if (startingData != null) { + if (mStartingData != null) { removeStartingWindow(); } @@ -1053,7 +1054,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree // If this is the last window and we had requested a starting transition window, // well there is no point now. if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Nulling last startingData"); - startingData = null; + mStartingData = null; if (mHiddenSetFromTransferredStartingWindow) { // We set the hidden state to false for the token from a transferred starting window. // We now reset it back to true since the starting window was the last window in the @@ -1486,13 +1487,13 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree final long origId = Binder.clearCallingIdentity(); try { // Transfer the starting window over to the new token. - startingData = fromToken.startingData; + mStartingData = fromToken.mStartingData; startingSurface = fromToken.startingSurface; startingDisplayed = fromToken.startingDisplayed; fromToken.startingDisplayed = false; startingWindow = tStartingWindow; reportedVisible = fromToken.reportedVisible; - fromToken.startingData = null; + fromToken.mStartingData = null; fromToken.startingSurface = null; fromToken.startingWindow = null; fromToken.startingMoved = true; @@ -1538,13 +1539,13 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree Binder.restoreCallingIdentity(origId); } return true; - } else if (fromToken.startingData != null) { + } else if (fromToken.mStartingData != null) { // The previous app was getting ready to show a // starting window, but hasn't yet done so. Steal it! if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Moving pending starting from " + fromToken + " to " + this); - startingData = fromToken.startingData; - fromToken.startingData = null; + mStartingData = fromToken.mStartingData; + fromToken.mStartingData = null; fromToken.startingMoved = true; scheduleAddStartingWindow(); return true; @@ -1932,7 +1933,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree + ", isAnimationSet=" + isSelfAnimating()); if (!w.isDrawnLw()) { Slog.v(TAG, "Not displayed: s=" + winAnimator.mSurfaceController - + " pv=" + w.mPolicyVisibility + + " pv=" + w.isVisibleByPolicy() + " mDrawState=" + winAnimator.drawStateToString() + " ph=" + w.isParentWindowHidden() + " th=" + hiddenRequested + " a=" + isSelfAnimating()); @@ -2042,7 +2043,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree return false; } - if (startingData != null) { + if (mStartingData != null) { return false; } @@ -2123,7 +2124,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SplashScreenStartingData"); - startingData = new SplashScreenStartingData(mWmService, pkg, + mStartingData = new SplashScreenStartingData(mWmService, pkg, theme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags, getMergedOverrideConfiguration()); scheduleAddStartingWindow(); @@ -2137,7 +2138,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SnapshotStartingData"); - startingData = new SnapshotStartingData(mWmService, snapshot); + mStartingData = new SnapshotStartingData(mWmService, snapshot); scheduleAddStartingWindow(); return true; } @@ -2156,18 +2157,21 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree @Override public void run() { + // Can be accessed without holding the global lock + final StartingData startingData; synchronized (mWmService.mGlobalLock) { // There can only be one adding request, silly caller! mWmService.mAnimationHandler.removeCallbacks(this); - } - if (startingData == null) { - // Animation has been canceled... do nothing. - if (DEBUG_STARTING_WINDOW) { - Slog.v(TAG, "startingData was nulled out before handling" - + " mAddStartingWindow: " + AppWindowToken.this); + if (mStartingData == null) { + // Animation has been canceled... do nothing. + if (DEBUG_STARTING_WINDOW) { + Slog.v(TAG, "startingData was nulled out before handling" + + " mAddStartingWindow: " + AppWindowToken.this); + } + return; } - return; + startingData = mStartingData; } if (DEBUG_STARTING_WINDOW) { @@ -2185,20 +2189,21 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree synchronized (mWmService.mGlobalLock) { // If the window was successfully added, then // we need to remove it. - if (removed || startingData == null) { + if (removed || mStartingData == null) { if (DEBUG_STARTING_WINDOW) { Slog.v(TAG, "Aborted starting " + AppWindowToken.this - + ": removed=" + removed + " startingData=" + startingData); + + ": removed=" + removed + " startingData=" + mStartingData); } startingWindow = null; - startingData = null; + mStartingData = null; abort = true; } else { startingSurface = surface; } if (DEBUG_STARTING_WINDOW && !abort) { - Slog.v(TAG, "Added starting " + AppWindowToken.this + ": startingWindow=" - + startingWindow + " startingView=" + startingSurface); + Slog.v(TAG, + "Added starting " + AppWindowToken.this + ": startingWindow=" + + startingWindow + " startingView=" + startingSurface); } } if (abort) { @@ -2245,21 +2250,21 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree void removeStartingWindow() { if (startingWindow == null) { - if (startingData != null) { + if (mStartingData != null) { // Starting window has not been added yet, but it is scheduled to be added. // Go ahead and cancel the request. if (DEBUG_STARTING_WINDOW) { Slog.v(TAG_WM, "Clearing startingData for token=" + this); } - startingData = null; + mStartingData = null; } return; } final WindowManagerPolicy.StartingSurface surface; - if (startingData != null) { + if (mStartingData != null) { surface = startingSurface; - startingData = null; + mStartingData = null; startingSurface = null; startingWindow = null; startingDisplayed = false; @@ -2432,14 +2437,19 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } } - private boolean shouldAnimate(int transit) { + + @VisibleForTesting + boolean shouldAnimate(int transit) { final boolean isSplitScreenPrimary = getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; final boolean allowSplitScreenPrimaryAnimation = transit != TRANSIT_WALLPAPER_OPEN; - // Don't animate when the task runs recents animation. + // Don't animate while the task runs recents animation but only if we are in the mode + // where we cancel with deferred screenshot, which means that the controller has + // transformed the task. final RecentsAnimationController controller = mWmService.getRecentsAnimationController(); - if (controller != null && controller.isAnimatingTask(getTask())) { + if (controller != null && controller.isAnimatingTask(getTask()) + && controller.shouldCancelWithDeferredScreenshot()) { return false; } @@ -2547,12 +2557,17 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree final Animation a = loadAnimation(lp, transit, enter, isVoiceInteraction); if (a != null) { + // Only apply corner radius to animation if we're not in multi window mode. + // We don't want rounded corners when in pip or split screen. + final float windowCornerRadius = !inMultiWindowMode() + ? getDisplayContent().getWindowCornerRadius() + : 0; adapter = new LocalAnimationAdapter( new WindowAnimationSpec(a, mTmpPoint, mTmpRect, getDisplayContent().mAppTransition.canSkipFirstFrame(), appStackClipMode, true /* isAppAnimation */, - getWindowCornerRadiusForAnimation()), + windowCornerRadius), mWmService.mSurfaceAnimationRunner); if (a.getZAdjustment() == Animation.ZORDER_TOP) { mNeedsZBoost = true; @@ -2989,8 +3004,8 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree pw.print(prefix); pw.print("inPendingTransaction="); pw.println(inPendingTransaction); } - if (startingData != null || removed || firstWindowDrawn || mIsExiting) { - pw.print(prefix); pw.print("startingData="); pw.print(startingData); + if (mStartingData != null || removed || firstWindowDrawn || mIsExiting) { + pw.print(prefix); pw.print("startingData="); pw.print(mStartingData); pw.print(" removed="); pw.print(removed); pw.print(" firstWindowDrawn="); pw.print(firstWindowDrawn); pw.print(" mIsExiting="); pw.println(mIsExiting); diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 41292d2b232b..652f7ee48b9f 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -90,6 +90,9 @@ import static com.android.server.wm.DisplayContentProto.ROTATION; import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION; import static com.android.server.wm.DisplayContentProto.STACKS; import static com.android.server.wm.DisplayContentProto.WINDOW_CONTAINER; +import static com.android.server.wm.DisplayContentProto.OPENING_APPS; +import static com.android.server.wm.DisplayContentProto.CHANGING_APPS; +import static com.android.server.wm.DisplayContentProto.CLOSING_APPS; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_BOOT; @@ -144,6 +147,7 @@ import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.Insets; import android.graphics.Matrix; +import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; @@ -541,6 +545,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo private final InsetsStateController mInsetsStateController; + /** @see #getParentWindow() */ + private WindowState mParentWindow; + + private Point mLocationInParentWindow = new Point(); private SurfaceControl mParentSurfaceControl; private InputWindowHandle mPortalWindowHandle; @@ -549,6 +557,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // Last systemUiVisibility we dispatched to windows. private int mLastDispatchedSystemUiVisibility = 0; + /** Corner radius that windows should have in order to match the display. */ + private final float mWindowCornerRadius; + private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> { WindowStateAnimator winAnimator = w.mWinAnimator; final AppWindowToken atoken = w.mAppToken; @@ -909,6 +920,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (mWmService.mSystemReady) { mDisplayPolicy.systemReady(); } + mWindowCornerRadius = mDisplayPolicy.getWindowCornerRadius(); mDividerControllerLocked = new DockedStackDividerController(service, this); mPinnedStackControllerLocked = new PinnedStackController(service, this); @@ -953,6 +965,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return mDisplayId; } + float getWindowCornerRadius() { + return mWindowCornerRadius; + } + WindowToken getWindowToken(IBinder binder) { return mTokenMap.get(binder); } @@ -2723,6 +2739,15 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (mFocusedApp != null) { mFocusedApp.writeNameToProto(proto, FOCUSED_APP); } + for (int i = mOpeningApps.size() - 1; i >= 0; i--) { + mOpeningApps.valueAt(i).mActivityRecord.writeIdentifierToProto(proto, OPENING_APPS); + } + for (int i = mClosingApps.size() - 1; i >= 0; i--) { + mClosingApps.valueAt(i).mActivityRecord.writeIdentifierToProto(proto, CLOSING_APPS); + } + for (int i = mChangingApps.size() - 1; i >= 0; i--) { + mChangingApps.valueAt(i).mActivityRecord.writeIdentifierToProto(proto, CHANGING_APPS); + } proto.end(token); } @@ -4562,7 +4587,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo token2.mOwnerCanManageAppTokens) ? -1 : 1; private final Predicate<WindowState> mGetOrientingWindow = w -> { - if (!w.isVisibleLw() || !w.mPolicyVisibilityAfterAnim) { + if (!w.isVisibleLw() || !w.mLegacyPolicyVisibilityAfterAnim) { return false; } final int req = w.mAttrs.screenOrientation; @@ -4923,11 +4948,14 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo /** * Re-parent the DisplayContent's top surfaces, {@link #mWindowingLayer} and - * {@link #mOverlayLayer} to the specified surfaceControl. + * {@link #mOverlayLayer} to the specified SurfaceControl. * + * @param win The window which owns the SurfaceControl. This indicates the z-order of the + * windows of this display against the windows on the parent display. * @param sc The new SurfaceControl, where the DisplayContent's surfaces will be re-parented to. */ - void reparentDisplayContent(SurfaceControl sc) { + void reparentDisplayContent(WindowState win, SurfaceControl sc) { + mParentWindow = win; mParentSurfaceControl = sc; if (mPortalWindowHandle == null) { mPortalWindowHandle = createPortalWindowHandle(sc.toString()); @@ -4936,6 +4964,41 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo .reparent(mWindowingLayer, sc).reparent(mOverlayLayer, sc); } + /** + * Get the window which owns the surface that this DisplayContent is re-parented to. + * + * @return the parent window. + */ + WindowState getParentWindow() { + return mParentWindow; + } + + /** + * Update the location of this display in the parent window. This enables windows in this + * display to compute the global transformation matrix. + * + * @param win The parent window of this display. + * @param x The x coordinate in the parent window. + * @param y The y coordinate in the parent window. + */ + void updateLocation(WindowState win, int x, int y) { + if (mParentWindow != win) { + throw new IllegalArgumentException( + "The given window is not the parent window of this display."); + } + if (mLocationInParentWindow.x != x || mLocationInParentWindow.y != y) { + mLocationInParentWindow.x = x; + mLocationInParentWindow.y = y; + if (mWmService.mAccessibilityController != null) { + mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(); + } + } + } + + Point getLocationInParentWindow() { + return mLocationInParentWindow; + } + @VisibleForTesting SurfaceControl getWindowingLayer() { return mWindowingLayer; diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 3bb365338749..26430fb09cd6 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -25,6 +25,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECOND import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.res.Configuration.UI_MODE_TYPE_CAR; import static android.content.res.Configuration.UI_MODE_TYPE_MASK; +import static android.view.Display.TYPE_BUILT_IN; import static android.view.InsetsState.TYPE_TOP_BAR; import static android.view.InsetsState.TYPE_TOP_GESTURES; import static android.view.InsetsState.TYPE_TOP_TAPPABLE_ELEMENT; @@ -112,7 +113,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Px; import android.app.ActivityManager; -import android.app.ActivityManagerInternal; import android.app.ActivityThread; import android.app.LoadedApk; import android.app.ResourcesManager; @@ -133,6 +133,7 @@ import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; import android.util.ArraySet; +import android.util.Pair; import android.util.PrintWriterPrinter; import android.util.Slog; import android.view.DisplayCutout; @@ -157,6 +158,7 @@ import android.view.accessibility.AccessibilityManager; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.policy.ScreenDecorationsUtils; import com.android.internal.util.ScreenShapeHelper; import com.android.internal.util.ScreenshotHelper; import com.android.internal.util.function.TriConsumer; @@ -2870,6 +2872,16 @@ public class DisplayPolicy { - statusBarHeight; } + /** + * Return corner radius in pixels that should be used on windows in order to cover the display. + * The radius is only valid for built-in displays since the one who configures window corner + * radius cannot know the corner radius of non-built-in display. + */ + float getWindowCornerRadius() { + return mDisplayContent.getDisplay().getType() == TYPE_BUILT_IN + ? ScreenDecorationsUtils.getWindowCornerRadius(mContext.getResources()) : 0f; + } + boolean isShowingDreamLw() { return mShowingDream; } @@ -3113,7 +3125,9 @@ public class DisplayPolicy { WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, mNonDockedStackBounds); mService.getStackBounds( WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, mDockedStackBounds); - final int visibility = updateSystemBarsLw(win, mLastSystemUiFlags, tmpVisibility); + final Pair<Integer, Boolean> result = + updateSystemBarsLw(win, mLastSystemUiFlags, tmpVisibility); + final int visibility = result.first; final int diff = visibility ^ mLastSystemUiFlags; final int fullscreenDiff = fullscreenVisibility ^ mLastFullscreenStackSysUiFlags; final int dockedDiff = dockedVisibility ^ mLastDockedStackSysUiFlags; @@ -3133,13 +3147,14 @@ public class DisplayPolicy { mLastDockedStackBounds.set(mDockedStackBounds); final Rect fullscreenStackBounds = new Rect(mNonDockedStackBounds); final Rect dockedStackBounds = new Rect(mDockedStackBounds); + final boolean isNavbarColorManagedByIme = result.second; mHandler.post(() -> { StatusBarManagerInternal statusBar = getStatusBarManagerInternal(); if (statusBar != null) { final int displayId = getDisplayId(); statusBar.setSystemUiVisibility(displayId, visibility, fullscreenVisibility, dockedVisibility, 0xffffffff, fullscreenStackBounds, - dockedStackBounds, win.toString()); + dockedStackBounds, isNavbarColorManagedByIme, win.toString()); statusBar.topAppWindowChanged(displayId, needsMenu); } }); @@ -3222,7 +3237,7 @@ public class DisplayPolicy { return vis; } - private int updateSystemBarsLw(WindowState win, int oldVis, int vis) { + private Pair<Integer, Boolean> updateSystemBarsLw(WindowState win, int oldVis, int vis) { final boolean dockedStackVisible = mDisplayContent.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); final boolean freeformStackVisible = @@ -3355,8 +3370,11 @@ public class DisplayPolicy { vis = updateLightNavigationBarLw(vis, mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState, mDisplayContent.mInputMethodWindow, navColorWin); + // Navbar color is controlled by the IME. + final boolean isManagedByIme = + navColorWin != null && navColorWin == mDisplayContent.mInputMethodWindow; - return vis; + return Pair.create(vis, isManagedByIme); } private boolean drawsBarBackground(int vis, WindowState win, BarController controller, diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java index f67b11b26b12..402ec5976840 100644 --- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java @@ -29,12 +29,11 @@ import android.annotation.Nullable; import android.graphics.Point; import android.graphics.Rect; import android.util.proto.ProtoOutputStream; +import android.view.InsetsSource; +import android.view.InsetsSourceControl; import android.view.InsetsState; import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; -import android.view.InsetsSource; -import android.view.InsetsSourceControl; -import android.view.ViewRootImpl; import com.android.internal.util.function.TriConsumer; import com.android.internal.util.function.pooled.PooledLambda; @@ -142,7 +141,7 @@ class InsetsSourceProvider { mStateController.notifyControlChanged(mControllingWin); } } - setServerVisible(mWin.wouldBeVisibleIfPolicyIgnored() && mWin.mPolicyVisibility + setServerVisible(mWin.wouldBeVisibleIfPolicyIgnored() && mWin.isVisibleByPolicy() && !mWin.mGivenInsetsPending); } diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index b33f8c7ad658..34273f3f23a5 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -426,6 +426,16 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { } @Override + public void reparentDisplayContent(IWindow window, SurfaceControl sc, int displayId) { + mService.reparentDisplayContent(window, sc, displayId); + } + + @Override + public void updateDisplayContentLocation(IWindow window, int x, int y, int displayId) { + mService.updateDisplayContentLocation(window, x, y, displayId); + } + + @Override public void updateTapExcludeRegion(IWindow window, int regionId, Region region) { final long identity = Binder.clearCallingIdentity(); try { diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java index d4d157fbe75c..8505ec2b8a82 100644 --- a/services/core/java/com/android/server/wm/TaskRecord.java +++ b/services/core/java/com/android/server/wm/TaskRecord.java @@ -1161,6 +1161,19 @@ class TaskRecord extends ConfigurationContainer { return false; } + /** + * Return true if any activities in this task belongs to input uid. + */ + boolean containsAppUid(int uid) { + for (int i = mActivities.size() - 1; i >= 0; --i) { + final ActivityRecord r = mActivities.get(i); + if (r.getUid() == uid) { + return true; + } + } + return false; + } + void getAllRunningVisibleActivitiesLocked(ArrayList<ActivityRecord> outActivities) { if (mStack != null) { for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) { diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index 78c5dbddee41..75a8dd53d2e6 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -481,7 +481,15 @@ public abstract class WindowManagerInternal { public abstract int getTopFocusedDisplayId(); /** - * Checks whether this display should support showing system decorations. + * Checks if this display is configured and allowed to show system decorations. */ public abstract boolean shouldShowSystemDecorOnDisplay(int displayId); + + /** + * Indicates that the display should show IME. + * + * @param displayId The id of the display. + * @return {@code true} if the display should show IME when an input field become focused on it. + */ + public abstract boolean shouldShowIme(int displayId); } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index dae29b2cf180..7ac887ed295d 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -35,6 +35,7 @@ import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; +import static android.view.Display.TYPE_VIRTUAL; import static android.view.WindowManager.DOCKED_INVALID; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; @@ -163,6 +164,7 @@ import android.os.PowerManager; import android.os.PowerManager.ServiceType; import android.os.PowerManagerInternal; import android.os.PowerSaveState; +import android.os.Process; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ServiceManager; @@ -238,7 +240,6 @@ import com.android.internal.os.BackgroundThread; import com.android.internal.os.IResultReceiver; import com.android.internal.policy.IKeyguardDismissCallback; import com.android.internal.policy.IShortcutService; -import com.android.internal.policy.ScreenDecorationsUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.FastPrintWriter; import com.android.internal.util.LatencyTracker; @@ -791,9 +792,6 @@ public class WindowManagerService extends IWindowManager.Stub final DisplayManager mDisplayManager; final ActivityTaskManagerService mAtmService; - /** Corner radius that windows should have in order to match the display. */ - final float mWindowCornerRadius; - /** Indicates whether this device supports wide color gamut / HDR rendering */ private boolean mHasWideColorGamutSupport; private boolean mHasHdrSupport; @@ -1018,7 +1016,6 @@ public class WindowManagerService extends IWindowManager.Stub mInputManager = inputManager; // Must be before createDisplayContentLocked. mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class); mDisplayWindowSettings = new DisplayWindowSettings(this); - mWindowCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context.getResources()); mTransactionFactory = transactionFactory; mTransaction = mTransactionFactory.make(); @@ -1878,7 +1875,8 @@ public class WindowManagerService extends IWindowManager.Stub // We need to report touchable region changes to accessibility. if (mAccessibilityController != null - && w.getDisplayContent().getDisplayId() == DEFAULT_DISPLAY) { + && (w.getDisplayContent().getDisplayId() == DEFAULT_DISPLAY + || w.getDisplayContent().getParentWindow() != null)) { mAccessibilityController.onSomeWindowResizedOrMovedLocked(); } } @@ -2010,7 +2008,8 @@ public class WindowManagerService extends IWindowManager.Stub } if (((attrChanges & LayoutParams.ACCESSIBILITY_TITLE_CHANGED) != 0) && (mAccessibilityController != null) - && (win.getDisplayId() == DEFAULT_DISPLAY)) { + && (win.getDisplayId() == DEFAULT_DISPLAY + || win.getDisplayContent().getParentWindow() != null)) { // No move or resize, but the controller checks for title changes as well mAccessibilityController.onSomeWindowResizedOrMovedLocked(); } @@ -5298,7 +5297,7 @@ public class WindowManagerService extends IWindowManager.Stub ": removed=" + win.mRemoved + " visible=" + win.isVisibleLw() + " mHasSurface=" + win.mHasSurface + " drawState=" + win.mWinAnimator.mDrawState); - if (win.mRemoved || !win.mHasSurface || !win.mPolicyVisibility) { + if (win.mRemoved || !win.mHasSurface || !win.isVisibleByPolicy()) { // Window has been removed or hidden; no draw will now happen, so stop waiting. if (DEBUG_SCREEN_ON) Slog.w(TAG_WM, "Aborted waiting for drawn: " + win); mWaitingForDrawn.remove(win); @@ -6701,6 +6700,61 @@ public class WindowManagerService extends IWindowManager.Stub } } + private void checkCallerOwnsDisplay(int displayId) { + final Display display = mDisplayManager.getDisplay(displayId); + if (display == null) { + throw new IllegalArgumentException( + "Cannot find display for non-existent displayId: " + displayId); + } + + final int callingUid = Binder.getCallingUid(); + final int displayOwnerUid = display.getOwnerUid(); + if (callingUid != displayOwnerUid) { + throw new SecurityException("The caller doesn't own the display."); + } + } + + /** @see Session#reparentDisplayContent(IWindow, SurfaceControl, int) */ + void reparentDisplayContent(IWindow client, SurfaceControl sc, int displayId) { + checkCallerOwnsDisplay(displayId); + + synchronized (mGlobalLock) { + final long token = Binder.clearCallingIdentity(); + try { + final WindowState win = windowForClientLocked(null, client, false); + if (win == null) { + Slog.w(TAG_WM, "Bad requesting window " + client); + return; + } + getDisplayContentOrCreate(displayId, null).reparentDisplayContent(win, sc); + } finally { + Binder.restoreCallingIdentity(token); + } + } + } + + /** @see Session#updateDisplayContentLocation(IWindow, int, int, int) */ + void updateDisplayContentLocation(IWindow client, int x, int y, int displayId) { + checkCallerOwnsDisplay(displayId); + + synchronized (mGlobalLock) { + final long token = Binder.clearCallingIdentity(); + try { + final WindowState win = windowForClientLocked(null, client, false); + if (win == null) { + Slog.w(TAG_WM, "Bad requesting window " + client); + return; + } + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent != null) { + displayContent.updateLocation(win, x, y); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + } + /** * Update a tap exclude region in the window identified by the provided id. Touches down on this * region will not: @@ -6871,10 +6925,21 @@ public class WindowManagerService extends IWindowManager.Stub + "not exist: " + displayId); return false; } + final Display display = displayContent.getDisplay(); + if (isUntrustedVirtualDisplay(display)) { + return false; + } return displayContent.supportsSystemDecorations(); } } + /** + * @return {@code true} if the display is non-system created virtual display. + */ + private static boolean isUntrustedVirtualDisplay(Display display) { + return display.getType() == TYPE_VIRTUAL && display.getOwnerUid() != Process.SYSTEM_UID; + } + @Override public void setShouldShowSystemDecors(int displayId, boolean shouldShow) { if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "setShouldShowSystemDecors()")) { @@ -6908,7 +6973,12 @@ public class WindowManagerService extends IWindowManager.Stub + displayId); return false; } - return mDisplayWindowSettings.shouldShowImeLocked(displayContent); + final Display display = displayContent.getDisplay(); + if (isUntrustedVirtualDisplay(display)) { + return false; + } + return mDisplayWindowSettings.shouldShowImeLocked(displayContent) + || mForceDesktopModeOnExternalDisplays; } } @@ -7352,6 +7422,14 @@ public class WindowManagerService extends IWindowManager.Stub return WindowManagerService.this.shouldShowSystemDecors(displayId); } } + + @Override + public boolean shouldShowIme(int displayId) { + synchronized (mGlobalLock) { + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + return mDisplayWindowSettings.shouldShowImeLocked(displayContent); + } + } } void registerAppFreezeListener(AppFreezeListener listener) { @@ -7512,31 +7590,6 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public void reparentDisplayContent(int displayId, SurfaceControl sc) { - final Display display = mDisplayManager.getDisplay(displayId); - if (display == null) { - throw new IllegalArgumentException( - "Can't reparent display for non-existent displayId: " + displayId); - } - - final int callingUid = Binder.getCallingUid(); - final int displayOwnerUid = display.getOwnerUid(); - if (callingUid != displayOwnerUid) { - throw new SecurityException("Only owner of the display can reparent surfaces to it."); - } - - synchronized (mGlobalLock) { - long token = Binder.clearCallingIdentity(); - try { - DisplayContent displayContent = getDisplayContentOrCreate(displayId, null); - displayContent.reparentDisplayContent(sc); - } finally { - Binder.restoreCallingIdentity(token); - } - } - } - - @Override public boolean injectInputAfterTransactionsApplied(InputEvent ev, int mode) { boolean shouldWaitForAnimToComplete = false; if (ev instanceof KeyEvent) { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index e39cd56a938c..497c91ab329d 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -251,18 +251,33 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP int mSeq; int mViewVisibility; int mSystemUiVisibility; + /** - * The visibility of the window based on policy like {@link WindowManagerPolicy}. + * The visibility flag of the window based on policy like {@link WindowManagerPolicy}. * Normally set by calling {@link #showLw} and {@link #hideLw}. + * + * TODO: b/131253938 This will eventually be split into individual visibility policy flags. + */ + static final int LEGACY_POLICY_VISIBILITY = 1; + /** + * The visibility flag that determines whether this window is visible for the current user. + */ + private static final int VISIBLE_FOR_USER = 1 << 1; + private static final int POLICY_VISIBILITY_ALL = VISIBLE_FOR_USER | LEGACY_POLICY_VISIBILITY; + /** + * The Bitwise-or of flags that contribute to visibility of the WindowState */ - boolean mPolicyVisibility = true; + private int mPolicyVisibility = POLICY_VISIBILITY_ALL; + /** - * What {@link #mPolicyVisibility} should be set to after a transition animation. - * For example, {@link #mPolicyVisibility} might true during an exit animation to hide it and - * then set to the value of {@link #mPolicyVisibilityAfterAnim} which is false after the exit - * animation is done. + * Whether {@link #LEGACY_POLICY_VISIBILITY} flag should be set after a transition animation. + * For example, {@link #LEGACY_POLICY_VISIBILITY} might be set during an exit animation to hide + * it and then unset when the value of {@link #mLegacyPolicyVisibilityAfterAnim} is false + * after the exit animation is done. + * + * TODO: b/131253938 Determine whether this can be changed to use a visibility flag instead. */ - boolean mPolicyVisibilityAfterAnim = true; + boolean mLegacyPolicyVisibilityAfterAnim = true; // overlay window is hidden because the owning app is suspended private boolean mHiddenWhileSuspended; private boolean mAppOpVisibility = true; @@ -1414,13 +1429,33 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP @Override boolean isVisible() { - return wouldBeVisibleIfPolicyIgnored() && mPolicyVisibility + return wouldBeVisibleIfPolicyIgnored() && isVisibleByPolicy() // If we don't have a provider, this window isn't used as a window generating // insets, so nobody can hide it over the inset APIs. && (mInsetProvider == null || mInsetProvider.isClientVisible()); } /** + * Ensures that all the policy visibility bits are set. + * @return {@code true} if all flags about visiblity are set + */ + boolean isVisibleByPolicy() { + return (mPolicyVisibility & POLICY_VISIBILITY_ALL) == POLICY_VISIBILITY_ALL; + } + + void clearPolicyVisibilityFlag(int policyVisibilityFlag) { + mPolicyVisibility &= ~policyVisibilityFlag; + } + + void setPolicyVisibilityFlag(int policyVisibilityFlag) { + mPolicyVisibility |= policyVisibilityFlag; + } + + private boolean isLegacyPolicyVisibility() { + return (mPolicyVisibility & LEGACY_POLICY_VISIBILITY) != 0; + } + + /** * @return {@code true} if the window would be visible if we'd ignore policy visibility, * {@code false} otherwise. */ @@ -1470,7 +1505,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP boolean isVisibleOrAdding() { final AppWindowToken atoken = mAppToken; return (mHasSurface || (!mRelayoutCalled && mViewVisibility == View.VISIBLE)) - && mPolicyVisibility && !isParentWindowHidden() + && isVisibleByPolicy() && !isParentWindowHidden() && (atoken == null || !atoken.hiddenRequested) && !mAnimatingExit && !mDestroying; } @@ -1481,7 +1516,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP * being visible. */ boolean isOnScreen() { - if (!mHasSurface || mDestroying || !mPolicyVisibility) { + if (!mHasSurface || mDestroying || !isVisibleByPolicy()) { return false; } final AppWindowToken atoken = mAppToken; @@ -1522,7 +1557,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } final boolean parentAndClientVisible = !isParentWindowHidden() && mViewVisibility == View.VISIBLE && !mToken.isHidden(); - return mHasSurface && mPolicyVisibility && !mDestroying + return mHasSurface && isVisibleByPolicy() && !mDestroying && (parentAndClientVisible || isAnimating()); } @@ -1551,7 +1586,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP @Override public boolean isDisplayedLw() { final AppWindowToken atoken = mAppToken; - return isDrawnLw() && mPolicyVisibility + return isDrawnLw() && isVisibleByPolicy() && ((!isParentWindowHidden() && (atoken == null || !atoken.hiddenRequested)) || isAnimating()); } @@ -2057,8 +2092,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP Slog.i(TAG_WM, " mSurfaceController=" + mWinAnimator.mSurfaceController + " relayoutCalled=" + mRelayoutCalled + " viewVis=" + mViewVisibility - + " policyVis=" + mPolicyVisibility - + " policyVisAfterAnim=" + mPolicyVisibilityAfterAnim + + " policyVis=" + isVisibleByPolicy() + + " policyVisAfterAnim=" + mLegacyPolicyVisibilityAfterAnim + " parentHidden=" + isParentWindowHidden() + " exiting=" + mAnimatingExit + " destroying=" + mDestroying); if (mAppToken != null) { @@ -2192,7 +2227,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (isHiddenFromUserLocked()) { if (DEBUG_VISIBILITY) Slog.w(TAG_WM, "user changing, hiding " + this + ", attrs=" + mAttrs.type + ", belonging to " + mOwnerUid); - hideLw(false); + clearPolicyVisibilityFlag(VISIBLE_FOR_USER); + } else { + setPolicyVisibilityFlag(VISIBLE_FOR_USER); } } @@ -2284,13 +2321,17 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } void checkPolicyVisibilityChange() { - if (mPolicyVisibility != mPolicyVisibilityAfterAnim) { + if (isLegacyPolicyVisibility() != mLegacyPolicyVisibilityAfterAnim) { if (DEBUG_VISIBILITY) { Slog.v(TAG, "Policy visibility changing after anim in " + - mWinAnimator + ": " + mPolicyVisibilityAfterAnim); + mWinAnimator + ": " + mLegacyPolicyVisibilityAfterAnim); + } + if (mLegacyPolicyVisibilityAfterAnim) { + setPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY); + } else { + clearPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY); } - mPolicyVisibility = mPolicyVisibilityAfterAnim; - if (!mPolicyVisibility) { + if (!isVisibleByPolicy()) { mWinAnimator.hide("checkPolicyVisibilityChange"); if (isFocused()) { if (DEBUG_FOCUS_LIGHT) Slog.i(TAG, @@ -2531,7 +2572,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } boolean showLw(boolean doAnimation, boolean requestAnim) { - if (mPolicyVisibility && mPolicyVisibilityAfterAnim) { + if (isLegacyPolicyVisibility() && mLegacyPolicyVisibilityAfterAnim) { // Already showing. return false; } @@ -2558,18 +2599,18 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (DEBUG_VISIBILITY) Slog.v(TAG, "Policy visibility true: " + this); if (doAnimation) { if (DEBUG_VISIBILITY) Slog.v(TAG, "doAnimation: mPolicyVisibility=" - + mPolicyVisibility + " animating=" + isAnimating()); + + isLegacyPolicyVisibility() + " animating=" + isAnimating()); if (!mToken.okToAnimate()) { doAnimation = false; - } else if (mPolicyVisibility && !isAnimating()) { + } else if (isLegacyPolicyVisibility() && !isAnimating()) { // Check for the case where we are currently visible and // not animating; we do not want to do animation at such a // point to become visible when we already are. doAnimation = false; } } - mPolicyVisibility = true; - mPolicyVisibilityAfterAnim = true; + setPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY); + mLegacyPolicyVisibilityAfterAnim = true; if (doAnimation) { mWinAnimator.applyAnimationLocked(TRANSIT_ENTER, true); } @@ -2593,7 +2634,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP doAnimation = false; } } - boolean current = doAnimation ? mPolicyVisibilityAfterAnim : mPolicyVisibility; + boolean current = + doAnimation ? mLegacyPolicyVisibilityAfterAnim : isLegacyPolicyVisibility(); if (!current) { // Already hiding. return false; @@ -2604,11 +2646,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP doAnimation = false; } } - mPolicyVisibilityAfterAnim = false; + mLegacyPolicyVisibilityAfterAnim = false; final boolean isFocused = isFocused(); if (!doAnimation) { if (DEBUG_VISIBILITY) Slog.v(TAG, "Policy visibility false: " + this); - mPolicyVisibility = false; + clearPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY); // Window is no longer visible -- make sure if we were waiting // for it to be displayed before enabling the display, that // we allow the display to be enabled now. @@ -3100,7 +3142,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } //TODO (multidisplay): Accessibility supported only for the default display. - if (mWmService.mAccessibilityController != null && getDisplayId() == DEFAULT_DISPLAY) { + if (mWmService.mAccessibilityController != null && (getDisplayId() == DEFAULT_DISPLAY + || getDisplayContent().getParentWindow() != null)) { mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(); } @@ -3443,11 +3486,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP pw.println(prefix + "mSeq=" + mSeq + " mSystemUiVisibility=0x" + Integer.toHexString(mSystemUiVisibility)); } - if (!mPolicyVisibility || !mPolicyVisibilityAfterAnim || !mAppOpVisibility + if (!isVisibleByPolicy() || !mLegacyPolicyVisibilityAfterAnim || !mAppOpVisibility || isParentWindowHidden() || mPermanentlyHidden || mForceHideNonSystemOverlayWindow || mHiddenWhileSuspended) { - pw.println(prefix + "mPolicyVisibility=" + mPolicyVisibility - + " mPolicyVisibilityAfterAnim=" + mPolicyVisibilityAfterAnim + pw.println(prefix + "mPolicyVisibility=" + isVisibleByPolicy() + + " mLegacyPolicyVisibilityAfterAnim=" + mLegacyPolicyVisibilityAfterAnim + " mAppOpVisibility=" + mAppOpVisibility + " parentHidden=" + isParentWindowHidden() + " mPermanentlyHidden=" + mPermanentlyHidden @@ -3904,7 +3947,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP + ": mDrawState=" + mWinAnimator.drawStateToString() + " readyForDisplay=" + isReadyForDisplay() + " starting=" + (mAttrs.type == TYPE_APPLICATION_STARTING) - + " during animation: policyVis=" + mPolicyVisibility + + " during animation: policyVis=" + isVisibleByPolicy() + " parentHidden=" + isParentWindowHidden() + " tok.hiddenRequested=" + (mAppToken != null && mAppToken.hiddenRequested) @@ -4165,7 +4208,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } //TODO (multidisplay): Accessibility is supported only for the default display. - if (mWmService.mAccessibilityController != null && getDisplayId() == DEFAULT_DISPLAY) { + if (mWmService.mAccessibilityController != null && (getDisplayId() == DEFAULT_DISPLAY + || getDisplayContent().getParentWindow() != null)) { mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(); } @@ -4313,7 +4357,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP + ", animating=" + isAnimating()); if (!isDrawnLw()) { Slog.v(TAG, "Not displayed: s=" + mWinAnimator.mSurfaceController - + " pv=" + mPolicyVisibility + + " pv=" + isVisibleByPolicy() + " mDrawState=" + mWinAnimator.mDrawState + " ph=" + isParentWindowHidden() + " th=" + (mAppToken != null ? mAppToken.hiddenRequested : false) @@ -4555,7 +4599,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP anim.scaleCurrentDuration(mWmService.getWindowAnimationScaleLocked()); final AnimationAdapter adapter = new LocalAnimationAdapter( new WindowAnimationSpec(anim, mSurfacePosition, false /* canSkipFirstFrame */, - mToken.getWindowCornerRadiusForAnimation()), + 0 /* windowCornerRadius */), mWmService.mSurfaceAnimationRunner); startAnimation(mPendingTransaction, adapter); commitPendingTransaction(); @@ -4604,6 +4648,18 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP int x = mSurfacePosition.x; int y = mSurfacePosition.y; + // We might be on a display which has been re-parented to a view in another window, so here + // computes the global location of our display. + DisplayContent dc = getDisplayContent(); + while (dc != null && dc.getParentWindow() != null) { + final WindowState displayParent = dc.getParentWindow(); + x += displayParent.mWindowFrames.mFrame.left - displayParent.mAttrs.surfaceInsets.left + + (dc.getLocationInParentWindow().x * displayParent.mGlobalScale + 0.5f); + y += displayParent.mWindowFrames.mFrame.top - displayParent.mAttrs.surfaceInsets.top + + (dc.getLocationInParentWindow().y * displayParent.mGlobalScale + 0.5f); + dc = displayParent.getDisplayContent(); + } + // If changed, also adjust transformFrameToSurfacePosition final WindowContainer parent = getParent(); if (isChildWindow()) { diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 780d4714275b..20e1ac6c913d 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -256,7 +256,7 @@ class WindowStateAnimator { mWin.checkPolicyVisibilityChange(); final DisplayContent displayContent = mWin.getDisplayContent(); - if (mAttrType == LayoutParams.TYPE_STATUS_BAR && mWin.mPolicyVisibility) { + if (mAttrType == LayoutParams.TYPE_STATUS_BAR && mWin.isVisibleByPolicy()) { // Upon completion of a not-visible to visible status bar animation a relayout is // required. if (displayContent != null) { diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index f65f0ab62f69..f0b9c62f2843 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -345,8 +345,4 @@ class WindowToken extends WindowContainer<WindowState> { mOwnerCanManageAppTokens); return mOwnerCanManageAppTokens && (layer > navLayer); } - - float getWindowCornerRadiusForAnimation() { - return mDisplayContent.isDefaultDisplay ? mWmService.mWindowCornerRadius : 0; - } } diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp index 7bc6776803a3..98c620c50f54 100644 --- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp +++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp @@ -45,6 +45,13 @@ #include <string.h> #include <utils/SystemClock.h> +static jclass class_gnssMeasurementsEvent; +static jclass class_gnssMeasurement; +static jclass class_location; +static jclass class_gnssNavigationMessage; +static jclass class_gnssClock; +static jclass class_gnssConfiguration_halInterfaceVersion; + static jobject mCallbacksObj = nullptr; static jmethodID method_reportLocation; @@ -95,6 +102,12 @@ static jmethodID method_correctionPlaneAltDeg; static jmethodID method_correctionPlaneAzimDeg; static jmethodID method_reportNfwNotification; static jmethodID method_isInEmergencySession; +static jmethodID method_gnssMeasurementsEventCtor; +static jmethodID method_locationCtor; +static jmethodID method_gnssNavigationMessageCtor; +static jmethodID method_gnssClockCtor; +static jmethodID method_gnssMeasurementCtor; +static jmethodID method_halInterfaceVersionCtor; /* * Save a pointer to JavaVm to attach/detach threads executing @@ -255,11 +268,11 @@ void JavaMethodHelper<T>::callJavaMethod( class JavaObject { public: - JavaObject(JNIEnv* env, const char* class_name); - JavaObject(JNIEnv* env, const char* class_name, const char * sz_arg_1); - JavaObject(JNIEnv* env, const char* class_name, jobject object); + JavaObject(JNIEnv* env, jclass clazz, jmethodID defaultCtor); + JavaObject(JNIEnv* env, jclass clazz, jmethodID stringCtor, const char * sz_arg_1); + JavaObject(JNIEnv* env, jclass clazz, jobject object); - virtual ~JavaObject(); + virtual ~JavaObject() = default; template<class T> void callSetter(const char* method_name, T value); @@ -273,25 +286,20 @@ class JavaObject { jobject object_; }; -JavaObject::JavaObject(JNIEnv* env, const char* class_name) : env_(env) { - clazz_ = env_->FindClass(class_name); - jmethodID ctor = env->GetMethodID(clazz_, "<init>", "()V"); - object_ = env_->NewObject(clazz_, ctor); +JavaObject::JavaObject(JNIEnv* env, jclass clazz, jmethodID defaultCtor) : env_(env), + clazz_(clazz) { + object_ = env_->NewObject(clazz_, defaultCtor); } -JavaObject::JavaObject(JNIEnv* env, const char* class_name, const char * sz_arg_1) : env_(env) { - clazz_ = env_->FindClass(class_name); - jmethodID ctor = env->GetMethodID(clazz_, "<init>", "(Ljava/lang/String;)V"); - object_ = env_->NewObject(clazz_, ctor, env->NewStringUTF(sz_arg_1)); -} -JavaObject::JavaObject(JNIEnv* env, const char* class_name, jobject object) - : env_(env), object_(object) { - clazz_ = env_->FindClass(class_name); +JavaObject::JavaObject(JNIEnv* env, jclass clazz, jmethodID stringCtor, const char * sz_arg_1) + : env_(env), clazz_(clazz) { + object_ = env_->NewObject(clazz_, stringCtor, env->NewStringUTF(sz_arg_1)); } -JavaObject::~JavaObject() { - env_->DeleteLocalRef(clazz_); + +JavaObject::JavaObject(JNIEnv* env, jclass clazz, jobject object) + : env_(env), clazz_(clazz), object_(object) { } template<class T> @@ -358,11 +366,8 @@ static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodNa } static jobject createHalInterfaceVersionJavaObject(JNIEnv* env, jint major, jint minor) { - jclass versionClass = - env->FindClass("com/android/server/location/GnssConfiguration$HalInterfaceVersion"); - jmethodID versionCtor = env->GetMethodID(versionClass, "<init>", "(II)V"); - jobject version = env->NewObject(versionClass, versionCtor, major, minor); - env->DeleteLocalRef(versionClass); + jobject version = env->NewObject(class_gnssConfiguration_halInterfaceVersion, + method_halInterfaceVersionCtor, major, minor); return version; } @@ -452,7 +457,7 @@ static JNIEnv* getJniEnv() { static jobject translateGnssLocation(JNIEnv* env, const GnssLocation_V1_0& location) { - JavaObject object(env, "android/location/Location", "gps"); + JavaObject object(env, class_location, method_locationCtor, "gps"); uint16_t flags = static_cast<uint32_t>(location.gnssLocationFlags); if (flags & GnssLocationFlags::HAS_LAT_LONG) { @@ -488,8 +493,7 @@ static jobject translateGnssLocation(JNIEnv* env, static jobject translateGnssLocation(JNIEnv* env, const GnssLocation_V2_0& location) { - JavaObject object(env, "android/location/Location", - translateGnssLocation(env, location.v1_0)); + JavaObject object(env, class_location, translateGnssLocation(env, location.v1_0)); const uint16_t flags = static_cast<uint16_t>(location.elapsedRealtime.flags); @@ -946,7 +950,7 @@ Return<void> GnssNavigationMessageCallback::gnssNavigationMessageCb( return Void(); } - JavaObject object(env, "android/location/GnssNavigationMessage"); + JavaObject object(env, class_gnssNavigationMessage, method_gnssNavigationMessageCtor); SET(Type, static_cast<int32_t>(message.type)); SET(Svid, static_cast<int32_t>(message.svid)); SET(MessageId, static_cast<int32_t>(message.messageId)); @@ -1013,7 +1017,7 @@ template<class T> void GnssMeasurementCallback::translateAndSetGnssData(const T& data) { JNIEnv* env = getJniEnv(); - JavaObject gnssClockJavaObject(env, "android/location/GnssClock"); + JavaObject gnssClockJavaObject(env, class_gnssClock, method_gnssClockCtor); translateGnssClock(gnssClockJavaObject, data); jobject clock = gnssClockJavaObject.get(); @@ -1175,41 +1179,30 @@ jobjectArray GnssMeasurementCallback::translateAllGnssMeasurements(JNIEnv* env, return nullptr; } - jclass gnssMeasurementClass = env->FindClass("android/location/GnssMeasurement"); jobjectArray gnssMeasurementArray = env->NewObjectArray( count, - gnssMeasurementClass, + class_gnssMeasurement, nullptr /* initialElement */); for (uint16_t i = 0; i < count; ++i) { - JavaObject object(env, "android/location/GnssMeasurement"); + JavaObject object(env, class_gnssMeasurement, method_gnssMeasurementCtor); translateSingleGnssMeasurement(&(measurements[i]), object); env->SetObjectArrayElement(gnssMeasurementArray, i, object.get()); } - env->DeleteLocalRef(gnssMeasurementClass); return gnssMeasurementArray; } void GnssMeasurementCallback::setMeasurementData(JNIEnv* env, jobject clock, jobjectArray measurementArray) { - jclass gnssMeasurementsEventClass = - env->FindClass("android/location/GnssMeasurementsEvent"); - jmethodID gnssMeasurementsEventCtor = - env->GetMethodID( - gnssMeasurementsEventClass, - "<init>", - "(Landroid/location/GnssClock;[Landroid/location/GnssMeasurement;)V"); - - jobject gnssMeasurementsEvent = env->NewObject(gnssMeasurementsEventClass, - gnssMeasurementsEventCtor, + jobject gnssMeasurementsEvent = env->NewObject(class_gnssMeasurementsEvent, + method_gnssMeasurementsEventCtor, clock, measurementArray); env->CallVoidMethod(mCallbacksObj, method_reportMeasurementData, gnssMeasurementsEvent); checkAndClearExceptionFromCallback(env, __FUNCTION__); - env->DeleteLocalRef(gnssMeasurementsEventClass); env->DeleteLocalRef(gnssMeasurementsEvent); } @@ -1464,8 +1457,7 @@ template<class T> Return<void> GnssBatchingCallbackUtil::gnssLocationBatchCbImpl(const hidl_vec<T>& locations) { JNIEnv* env = getJniEnv(); - jobjectArray jLocations = env->NewObjectArray(locations.size(), - env->FindClass("android/location/Location"), nullptr); + jobjectArray jLocations = env->NewObjectArray(locations.size(), class_location, nullptr); for (uint16_t i = 0; i < locations.size(); ++i) { jobject jLocation = translateGnssLocation(env, locations[i]); @@ -1617,6 +1609,36 @@ static void android_location_GnssLocationProvider_init_once(JNIEnv* env, jclass method_correctionPlaneAltDeg = env->GetMethodID(refPlaneClass, "getAltitudeMeters", "()D"); method_correctionPlaneAzimDeg = env->GetMethodID(refPlaneClass, "getAzimuthDegrees", "()D"); + jclass gnssMeasurementsEventClass = env->FindClass("android/location/GnssMeasurementsEvent"); + class_gnssMeasurementsEvent= (jclass) env->NewGlobalRef(gnssMeasurementsEventClass); + method_gnssMeasurementsEventCtor = env->GetMethodID( + class_gnssMeasurementsEvent, + "<init>", + "(Landroid/location/GnssClock;[Landroid/location/GnssMeasurement;)V"); + + jclass gnssMeasurementClass = env->FindClass("android/location/GnssMeasurement"); + class_gnssMeasurement = (jclass) env->NewGlobalRef(gnssMeasurementClass); + method_gnssMeasurementCtor = env->GetMethodID(class_gnssMeasurement, "<init>", "()V"); + + jclass locationClass = env->FindClass("android/location/Location"); + class_location = (jclass) env->NewGlobalRef(locationClass); + method_locationCtor = env->GetMethodID(class_location, "<init>", "(Ljava/lang/String;)V"); + + jclass gnssNavigationMessageClass = env->FindClass("android/location/GnssNavigationMessage"); + class_gnssNavigationMessage = (jclass) env->NewGlobalRef(gnssNavigationMessageClass); + method_gnssNavigationMessageCtor = env->GetMethodID(class_gnssNavigationMessage, "<init>", "()V"); + + jclass gnssClockClass = env->FindClass("android/location/GnssClock"); + class_gnssClock = (jclass) env->NewGlobalRef(gnssClockClass); + method_gnssClockCtor = env->GetMethodID(class_gnssClock, "<init>", "()V"); + + jclass gnssConfiguration_halInterfaceVersionClass = + env->FindClass("com/android/server/location/GnssConfiguration$HalInterfaceVersion"); + class_gnssConfiguration_halInterfaceVersion = + (jclass) env->NewGlobalRef(gnssConfiguration_halInterfaceVersionClass); + method_halInterfaceVersionCtor = + env->GetMethodID(class_gnssConfiguration_halInterfaceVersion, "<init>", "(II)V"); + /* * Save a pointer to JVM. */ @@ -1860,13 +1882,14 @@ static jboolean android_location_GnssLocationProvider_init(JNIEnv* env, jobject * Fail if the main interface fails to initialize */ if (gnssHal == nullptr) { - ALOGE("Unable to Initialize GNSS HAL\n"); + ALOGE("Unable to initialize GNSS HAL."); return JNI_FALSE; } - sp<IGnssCallback> gnssCbIface = new GnssCallback(); - Return<bool> result = false; + + // Set top level IGnss.hal callback. + sp<IGnssCallback> gnssCbIface = new GnssCallback(); if (gnssHal_V2_0 != nullptr) { result = gnssHal_V2_0->setCallback_2_0(gnssCbIface); } else if (gnssHal_V1_1 != nullptr) { @@ -1876,62 +1899,89 @@ static jboolean android_location_GnssLocationProvider_init(JNIEnv* env, jobject } if (!result.isOk() || !result) { - ALOGE("SetCallback for Gnss Interface fails\n"); + ALOGE("SetCallback for IGnss interface failed."); return JNI_FALSE; } - sp<IGnssXtraCallback> gnssXtraCbIface = new GnssXtraCallback(); + // Set IGnssXtra.hal callback. if (gnssXtraIface == nullptr) { - ALOGI("Unable to initialize GNSS Xtra interface\n"); + ALOGI("Unable to initialize IGnssXtra interface."); } else { + sp<IGnssXtraCallback> gnssXtraCbIface = new GnssXtraCallback(); result = gnssXtraIface->setCallback(gnssXtraCbIface); if (!result.isOk() || !result) { gnssXtraIface = nullptr; - ALOGI("SetCallback for Gnss Xtra Interface fails\n"); + ALOGI("SetCallback for IGnssXtra interface failed."); } } + // Set IAGnss.hal callback. + Return<void> agnssStatus; if (agnssIface_V2_0 != nullptr) { sp<IAGnssCallback_V2_0> aGnssCbIface = new AGnssCallback_V2_0(); - agnssIface_V2_0->setCallback(aGnssCbIface); + agnssStatus = agnssIface_V2_0->setCallback(aGnssCbIface); } else if (agnssIface != nullptr) { sp<IAGnssCallback_V1_0> aGnssCbIface = new AGnssCallback_V1_0(); - agnssIface->setCallback(aGnssCbIface); + agnssStatus = agnssIface->setCallback(aGnssCbIface); } else { - ALOGI("Unable to initialize AGnss interface\n"); + ALOGI("Unable to initialize IAGnss interface."); + } + + if (!agnssStatus.isOk()) { + ALOGI("SetCallback for IAGnss interface failed."); } + // Set IGnssGeofencing.hal callback. sp<IGnssGeofenceCallback> gnssGeofencingCbIface = new GnssGeofenceCallback(); if (gnssGeofencingIface != nullptr) { - gnssGeofencingIface->setCallback(gnssGeofencingCbIface); + auto status = gnssGeofencingIface->setCallback(gnssGeofencingCbIface); + if (!status.isOk()) { + ALOGI("SetCallback for IGnssGeofencing interface failed."); + } } else { - ALOGI("Unable to initialize GNSS Geofencing interface\n"); + ALOGI("Unable to initialize IGnssGeofencing interface."); } + // Set IGnssNi.hal callback. sp<IGnssNiCallback> gnssNiCbIface = new GnssNiCallback(); if (gnssNiIface != nullptr) { - gnssNiIface->setCallback(gnssNiCbIface); + auto status = gnssNiIface->setCallback(gnssNiCbIface); + if (!status.isOk()) { + ALOGI("SetCallback for IGnssNi interface failed."); + } } else { - ALOGI("Unable to initialize GNSS NI interface\n"); + ALOGI("Unable to initialize IGnssNi interface."); } + // Set IAGnssRil.hal callback. sp<IAGnssRilCallback> aGnssRilCbIface = new AGnssRilCallback(); if (agnssRilIface != nullptr) { - agnssRilIface->setCallback(aGnssRilCbIface); + auto status = agnssRilIface->setCallback(aGnssRilCbIface); + if (!status.isOk()) { + ALOGI("SetCallback for IAGnssRil interface failed."); + } } else { - ALOGI("Unable to initialize AGnss Ril interface\n"); + ALOGI("Unable to initialize IAGnssRil interface."); } + // Set IGnssVisibilityControl.hal callback. if (gnssVisibilityControlIface != nullptr) { sp<IGnssVisibilityControlCallback> gnssVisibilityControlCbIface = new GnssVisibilityControlCallback(); - gnssVisibilityControlIface->setCallback(gnssVisibilityControlCbIface); + result = gnssVisibilityControlIface->setCallback(gnssVisibilityControlCbIface); + if (!result.isOk() || !result) { + ALOGI("SetCallback for IGnssVisibilityControl interface failed."); + } } + // Set IMeasurementCorrections.hal callback. if (gnssCorrectionsIface != nullptr) { sp<IMeasurementCorrectionsCallback> gnssCorrectionsIfaceCbIface = new MeasurementCorrectionsCallback(); - gnssCorrectionsIface->setCallback(gnssCorrectionsIfaceCbIface); + result = gnssCorrectionsIface->setCallback(gnssCorrectionsIfaceCbIface); + if (!result.isOk() || !result) { + ALOGI("SetCallback for IMeasurementCorrections interface failed."); + } } return JNI_TRUE; diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index c5a206882695..22231c0ab09b 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -8034,6 +8034,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { throw new IllegalArgumentException("Component " + who + " not installed for userId:" + userHandle); } + final boolean hasIncompatibleAccountsOrNonAdb = hasIncompatibleAccountsOrNonAdbNoLock(userHandle, who); synchronized (getLockObject()) { @@ -8539,9 +8540,30 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return; } enforceCanManageProfileAndDeviceOwners(); - if ((mIsWatch || hasUserSetupCompleted(userHandle)) && !isCallerWithSystemUid()) { - throw new IllegalStateException("Cannot set the profile owner on a user which is " - + "already set-up"); + + if ((mIsWatch || hasUserSetupCompleted(userHandle))) { + if (!isCallerWithSystemUid()) { + throw new IllegalStateException("Cannot set the profile owner on a user which is " + + "already set-up"); + } + + if (!mIsWatch) { + // Only the default supervision profile owner can be set as profile owner after SUW + final String supervisor = mContext.getResources().getString( + com.android.internal.R.string + .config_defaultSupervisionProfileOwnerComponent); + if (supervisor == null) { + throw new IllegalStateException("Unable to set profile owner post-setup, no" + + "default supervisor profile owner defined"); + } + + final ComponentName supervisorComponent = ComponentName.unflattenFromString( + supervisor); + if (!owner.equals(supervisorComponent)) { + throw new IllegalStateException("Unable to set non-default profile owner" + + " post-setup " + owner); + } + } } } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index be7dd31380ba..4ac8342e6e60 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -115,6 +115,7 @@ import com.android.server.om.OverlayManagerService; import com.android.server.os.BugreportManagerService; import com.android.server.os.DeviceIdentifiersPolicyService; import com.android.server.os.SchedulingPolicyService; +import com.android.server.pm.ApexManager; import com.android.server.pm.BackgroundDexOptService; import com.android.server.pm.CrossProfileAppsService; import com.android.server.pm.DynamicCodeLoggingService; @@ -627,6 +628,12 @@ public final class SystemServer { watchdog.start(); traceEnd(); + // Start ApexManager as early as we can to give it enough time to call apexd and populate + // cache of known apex packages. Note that calling apexd will happen asynchronously. + traceBeginAndSlog("StartApexManager"); + mSystemServiceManager.startService(ApexManager.class); + traceEnd(); + Slog.i(TAG, "Reading configuration..."); final String TAG_SYSTEM_CONFIG = "ReadingSystemConfig"; traceBeginAndSlog(TAG_SYSTEM_CONFIG); diff --git a/services/net/Android.bp b/services/net/Android.bp index a44d8351939f..ab11fe4df283 100644 --- a/services/net/Android.bp +++ b/services/net/Android.bp @@ -56,6 +56,7 @@ aidl_interface { versions: [ "1", "2", + "3", ], } diff --git a/services/net/aidl/networkstack/3/android/net/DhcpResultsParcelable.aidl b/services/net/aidl/networkstack/3/android/net/DhcpResultsParcelable.aidl new file mode 100644 index 000000000000..31891de7230a --- /dev/null +++ b/services/net/aidl/networkstack/3/android/net/DhcpResultsParcelable.aidl @@ -0,0 +1,9 @@ +package android.net; +parcelable DhcpResultsParcelable { + android.net.StaticIpConfiguration baseConfiguration; + int leaseDuration; + int mtu; + String serverAddress; + String vendorInfo; + String serverHostName; +} diff --git a/services/net/aidl/networkstack/3/android/net/INetworkMonitor.aidl b/services/net/aidl/networkstack/3/android/net/INetworkMonitor.aidl new file mode 100644 index 000000000000..029968b6f324 --- /dev/null +++ b/services/net/aidl/networkstack/3/android/net/INetworkMonitor.aidl @@ -0,0 +1,24 @@ +package android.net; +interface INetworkMonitor { + oneway void start(); + oneway void launchCaptivePortalApp(); + oneway void notifyCaptivePortalAppFinished(int response); + oneway void setAcceptPartialConnectivity(); + oneway void forceReevaluation(int uid); + oneway void notifyPrivateDnsChanged(in android.net.PrivateDnsConfigParcel config); + oneway void notifyDnsResponse(int returnCode); + oneway void notifyNetworkConnected(in android.net.LinkProperties lp, in android.net.NetworkCapabilities nc); + oneway void notifyNetworkDisconnected(); + oneway void notifyLinkPropertiesChanged(in android.net.LinkProperties lp); + oneway void notifyNetworkCapabilitiesChanged(in android.net.NetworkCapabilities nc); + const int NETWORK_TEST_RESULT_VALID = 0; + const int NETWORK_TEST_RESULT_INVALID = 1; + const int NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY = 2; + const int NETWORK_VALIDATION_RESULT_VALID = 1; + const int NETWORK_VALIDATION_RESULT_PARTIAL = 2; + const int NETWORK_VALIDATION_PROBE_DNS = 4; + const int NETWORK_VALIDATION_PROBE_HTTP = 8; + const int NETWORK_VALIDATION_PROBE_HTTPS = 16; + const int NETWORK_VALIDATION_PROBE_FALLBACK = 32; + const int NETWORK_VALIDATION_PROBE_PRIVDNS = 64; +} diff --git a/services/net/aidl/networkstack/3/android/net/INetworkMonitorCallbacks.aidl b/services/net/aidl/networkstack/3/android/net/INetworkMonitorCallbacks.aidl new file mode 100644 index 000000000000..ee9871ddcd15 --- /dev/null +++ b/services/net/aidl/networkstack/3/android/net/INetworkMonitorCallbacks.aidl @@ -0,0 +1,8 @@ +package android.net; +interface INetworkMonitorCallbacks { + oneway void onNetworkMonitorCreated(in android.net.INetworkMonitor networkMonitor); + oneway void notifyNetworkTested(int testResult, @nullable String redirectUrl); + oneway void notifyPrivateDnsConfigResolved(in android.net.PrivateDnsConfigParcel config); + oneway void showProvisioningNotification(String action, String packageName); + oneway void hideProvisioningNotification(); +} diff --git a/services/net/aidl/networkstack/3/android/net/INetworkStackConnector.aidl b/services/net/aidl/networkstack/3/android/net/INetworkStackConnector.aidl new file mode 100644 index 000000000000..7da11e476c0e --- /dev/null +++ b/services/net/aidl/networkstack/3/android/net/INetworkStackConnector.aidl @@ -0,0 +1,7 @@ +package android.net; +interface INetworkStackConnector { + oneway void makeDhcpServer(in String ifName, in android.net.dhcp.DhcpServingParamsParcel params, in android.net.dhcp.IDhcpServerCallbacks cb); + oneway void makeNetworkMonitor(in android.net.Network network, String name, in android.net.INetworkMonitorCallbacks cb); + oneway void makeIpClient(in String ifName, in android.net.ip.IIpClientCallbacks callbacks); + oneway void fetchIpMemoryStore(in android.net.IIpMemoryStoreCallbacks cb); +} diff --git a/services/net/aidl/networkstack/3/android/net/INetworkStackStatusCallback.aidl b/services/net/aidl/networkstack/3/android/net/INetworkStackStatusCallback.aidl new file mode 100644 index 000000000000..f6ca6f7a78e2 --- /dev/null +++ b/services/net/aidl/networkstack/3/android/net/INetworkStackStatusCallback.aidl @@ -0,0 +1,4 @@ +package android.net; +interface INetworkStackStatusCallback { + oneway void onStatusAvailable(int statusCode); +} diff --git a/services/net/aidl/networkstack/3/android/net/InitialConfigurationParcelable.aidl b/services/net/aidl/networkstack/3/android/net/InitialConfigurationParcelable.aidl new file mode 100644 index 000000000000..c80a78785b3b --- /dev/null +++ b/services/net/aidl/networkstack/3/android/net/InitialConfigurationParcelable.aidl @@ -0,0 +1,7 @@ +package android.net; +parcelable InitialConfigurationParcelable { + android.net.LinkAddress[] ipAddresses; + android.net.IpPrefix[] directlyConnectedRoutes; + String[] dnsServers; + String gateway; +} diff --git a/services/net/aidl/networkstack/3/android/net/NattKeepalivePacketDataParcelable.aidl b/services/net/aidl/networkstack/3/android/net/NattKeepalivePacketDataParcelable.aidl new file mode 100644 index 000000000000..65de8833e6c5 --- /dev/null +++ b/services/net/aidl/networkstack/3/android/net/NattKeepalivePacketDataParcelable.aidl @@ -0,0 +1,7 @@ +package android.net; +parcelable NattKeepalivePacketDataParcelable { + byte[] srcAddress; + int srcPort; + byte[] dstAddress; + int dstPort; +} diff --git a/services/net/aidl/networkstack/3/android/net/PrivateDnsConfigParcel.aidl b/services/net/aidl/networkstack/3/android/net/PrivateDnsConfigParcel.aidl new file mode 100644 index 000000000000..2de790bb7754 --- /dev/null +++ b/services/net/aidl/networkstack/3/android/net/PrivateDnsConfigParcel.aidl @@ -0,0 +1,5 @@ +package android.net; +parcelable PrivateDnsConfigParcel { + String hostname; + String[] ips; +} diff --git a/services/net/aidl/networkstack/3/android/net/ProvisioningConfigurationParcelable.aidl b/services/net/aidl/networkstack/3/android/net/ProvisioningConfigurationParcelable.aidl new file mode 100644 index 000000000000..3a6c30496fd8 --- /dev/null +++ b/services/net/aidl/networkstack/3/android/net/ProvisioningConfigurationParcelable.aidl @@ -0,0 +1,15 @@ +package android.net; +parcelable ProvisioningConfigurationParcelable { + boolean enableIPv4; + boolean enableIPv6; + boolean usingMultinetworkPolicyTracker; + boolean usingIpReachabilityMonitor; + int requestedPreDhcpActionMs; + android.net.InitialConfigurationParcelable initialConfig; + android.net.StaticIpConfiguration staticIpConfig; + android.net.apf.ApfCapabilities apfCapabilities; + int provisioningTimeoutMs; + int ipv6AddrGenMode; + android.net.Network network; + String displayName; +} diff --git a/services/net/aidl/networkstack/3/android/net/TcpKeepalivePacketDataParcelable.aidl b/services/net/aidl/networkstack/3/android/net/TcpKeepalivePacketDataParcelable.aidl new file mode 100644 index 000000000000..e121c064f7ac --- /dev/null +++ b/services/net/aidl/networkstack/3/android/net/TcpKeepalivePacketDataParcelable.aidl @@ -0,0 +1,13 @@ +package android.net; +parcelable TcpKeepalivePacketDataParcelable { + byte[] srcAddress; + int srcPort; + byte[] dstAddress; + int dstPort; + int seq; + int ack; + int rcvWnd; + int rcvWndScale; + int tos; + int ttl; +} diff --git a/services/net/aidl/networkstack/3/android/net/dhcp/DhcpServingParamsParcel.aidl b/services/net/aidl/networkstack/3/android/net/dhcp/DhcpServingParamsParcel.aidl new file mode 100644 index 000000000000..67193ae904bc --- /dev/null +++ b/services/net/aidl/networkstack/3/android/net/dhcp/DhcpServingParamsParcel.aidl @@ -0,0 +1,11 @@ +package android.net.dhcp; +parcelable DhcpServingParamsParcel { + int serverAddr; + int serverAddrPrefixLength; + int[] defaultRouters; + int[] dnsServers; + int[] excludedAddrs; + long dhcpLeaseTimeSecs; + int linkMtu; + boolean metered; +} diff --git a/services/net/aidl/networkstack/3/android/net/dhcp/IDhcpServer.aidl b/services/net/aidl/networkstack/3/android/net/dhcp/IDhcpServer.aidl new file mode 100644 index 000000000000..914315855496 --- /dev/null +++ b/services/net/aidl/networkstack/3/android/net/dhcp/IDhcpServer.aidl @@ -0,0 +1,10 @@ +package android.net.dhcp; +interface IDhcpServer { + oneway void start(in android.net.INetworkStackStatusCallback cb); + oneway void updateParams(in android.net.dhcp.DhcpServingParamsParcel params, in android.net.INetworkStackStatusCallback cb); + oneway void stop(in android.net.INetworkStackStatusCallback cb); + const int STATUS_UNKNOWN = 0; + const int STATUS_SUCCESS = 1; + const int STATUS_INVALID_ARGUMENT = 2; + const int STATUS_UNKNOWN_ERROR = 3; +} diff --git a/services/net/aidl/networkstack/3/android/net/dhcp/IDhcpServerCallbacks.aidl b/services/net/aidl/networkstack/3/android/net/dhcp/IDhcpServerCallbacks.aidl new file mode 100644 index 000000000000..dcc4489d52a6 --- /dev/null +++ b/services/net/aidl/networkstack/3/android/net/dhcp/IDhcpServerCallbacks.aidl @@ -0,0 +1,4 @@ +package android.net.dhcp; +interface IDhcpServerCallbacks { + oneway void onDhcpServerCreated(int statusCode, in android.net.dhcp.IDhcpServer server); +} diff --git a/services/net/aidl/networkstack/3/android/net/ip/IIpClient.aidl b/services/net/aidl/networkstack/3/android/net/ip/IIpClient.aidl new file mode 100644 index 000000000000..176a5ce85373 --- /dev/null +++ b/services/net/aidl/networkstack/3/android/net/ip/IIpClient.aidl @@ -0,0 +1,16 @@ +package android.net.ip; +interface IIpClient { + oneway void completedPreDhcpAction(); + oneway void confirmConfiguration(); + oneway void readPacketFilterComplete(in byte[] data); + oneway void shutdown(); + oneway void startProvisioning(in android.net.ProvisioningConfigurationParcelable req); + oneway void stop(); + oneway void setTcpBufferSizes(in String tcpBufferSizes); + oneway void setHttpProxy(in android.net.ProxyInfo proxyInfo); + oneway void setMulticastFilter(boolean enabled); + oneway void addKeepalivePacketFilter(int slot, in android.net.TcpKeepalivePacketDataParcelable pkt); + oneway void removeKeepalivePacketFilter(int slot); + oneway void setL2KeyAndGroupHint(in String l2Key, in String groupHint); + oneway void addNattKeepalivePacketFilter(int slot, in android.net.NattKeepalivePacketDataParcelable pkt); +} diff --git a/services/net/aidl/networkstack/3/android/net/ip/IIpClientCallbacks.aidl b/services/net/aidl/networkstack/3/android/net/ip/IIpClientCallbacks.aidl new file mode 100644 index 000000000000..d6bc8089a0be --- /dev/null +++ b/services/net/aidl/networkstack/3/android/net/ip/IIpClientCallbacks.aidl @@ -0,0 +1,16 @@ +package android.net.ip; +interface IIpClientCallbacks { + oneway void onIpClientCreated(in android.net.ip.IIpClient ipClient); + oneway void onPreDhcpAction(); + oneway void onPostDhcpAction(); + oneway void onNewDhcpResults(in android.net.DhcpResultsParcelable dhcpResults); + oneway void onProvisioningSuccess(in android.net.LinkProperties newLp); + oneway void onProvisioningFailure(in android.net.LinkProperties newLp); + oneway void onLinkPropertiesChange(in android.net.LinkProperties newLp); + oneway void onReachabilityLost(in String logMsg); + oneway void onQuit(); + oneway void installPacketFilter(in byte[] filter); + oneway void startReadPacketFilter(); + oneway void setFallbackMulticastFilter(boolean enabled); + oneway void setNeighborDiscoveryOffload(boolean enable); +} diff --git a/services/net/java/android/net/ip/IIpClient.aidl b/services/net/java/android/net/ip/IIpClient.aidl index 1e7726472ff6..9989c52fc403 100644 --- a/services/net/java/android/net/ip/IIpClient.aidl +++ b/services/net/java/android/net/ip/IIpClient.aidl @@ -17,6 +17,7 @@ package android.net.ip; import android.net.ProxyInfo; import android.net.ProvisioningConfigurationParcelable; +import android.net.NattKeepalivePacketDataParcelable; import android.net.TcpKeepalivePacketDataParcelable; /** @hide */ @@ -33,4 +34,5 @@ oneway interface IIpClient { void addKeepalivePacketFilter(int slot, in TcpKeepalivePacketDataParcelable pkt); void removeKeepalivePacketFilter(int slot); void setL2KeyAndGroupHint(in String l2Key, in String groupHint); + void addNattKeepalivePacketFilter(int slot, in NattKeepalivePacketDataParcelable pkt); } diff --git a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java index c0a11b27c65c..6517303842ed 100644 --- a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java @@ -263,6 +263,9 @@ public class AlarmManagerServiceTest { mService.onStart(); spyOn(mService.mHandler); + // Stubbing the handler. Test should simulate any handling of messages synchronously. + doReturn(true).when(mService.mHandler).sendMessageAtTime(any(Message.class), anyLong()); + assertEquals(mService.mSystemUiUid, SYSTEM_UI_UID); assertEquals(mService.mClockReceiver, mClockReceiver); assertEquals(mService.mWakeLock, mWakeLock); @@ -617,11 +620,9 @@ public class AlarmManagerServiceTest { testQuotasNoDeferral(STANDBY_BUCKET_RARE); } - private void sendAndHandleBucketChanged(int bucket) { + private void assertAndHandleBucketChanged(int bucket) { when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(), anyLong())).thenReturn(bucket); - // Stubbing the handler call to simulate it synchronously here. - doReturn(true).when(mService.mHandler).sendMessage(any(Message.class)); mAppStandbyListener.onAppIdleStateChanged(TEST_CALLING_PACKAGE, UserHandle.getUserId(TEST_CALLING_UID), false, bucket, 0); final ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class); @@ -652,7 +653,7 @@ public class AlarmManagerServiceTest { // The next upcoming alarm in queue should also be set as expected. assertEquals(firstTrigger + workingQuota - 1, mTestTimer.getElapsed()); // Downgrading the bucket now - sendAndHandleBucketChanged(STANDBY_BUCKET_RARE); + assertAndHandleBucketChanged(STANDBY_BUCKET_RARE); final int rareQuota = mService.getQuotaForBucketLocked(STANDBY_BUCKET_RARE); // The last alarm should now be deferred. final long expectedNextTrigger = (firstTrigger + workingQuota - 1 - rareQuota) @@ -680,15 +681,13 @@ public class AlarmManagerServiceTest { assertEquals(deferredTrigger, mTestTimer.getElapsed()); // Upgrading the bucket now - sendAndHandleBucketChanged(STANDBY_BUCKET_ACTIVE); + assertAndHandleBucketChanged(STANDBY_BUCKET_ACTIVE); // The last alarm should now be rescheduled to go as per original expectations final long originalTrigger = firstTrigger + frequentQuota; assertEquals("Incorrect next alarm trigger", originalTrigger, mTestTimer.getElapsed()); } - private void sendAndHandleParoleChanged(boolean parole) { - // Stubbing the handler call to simulate it synchronously here. - doReturn(true).when(mService.mHandler).sendMessage(any(Message.class)); + private void assertAndHandleParoleChanged(boolean parole) { mAppStandbyListener.onParoleStateChanged(parole); final ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class); verify(mService.mHandler, atLeastOnce()).sendMessage(messageCaptor.capture()); @@ -719,7 +718,7 @@ public class AlarmManagerServiceTest { // Any subsequent alarms in queue should all be deferred assertEquals(firstTrigger + mAppStandbyWindow + 1, mTestTimer.getElapsed()); // Paroling now - sendAndHandleParoleChanged(true); + assertAndHandleParoleChanged(true); // Subsequent alarms should now go off as per original expectations. for (int i = 0; i < 5; i++) { @@ -728,7 +727,7 @@ public class AlarmManagerServiceTest { mTestTimer.expire(); } // Come out of parole - sendAndHandleParoleChanged(false); + assertAndHandleParoleChanged(false); // Subsequent alarms should again get deferred final long expectedNextTrigger = (firstTrigger + 5) + 1 + mAppStandbyWindow; @@ -938,6 +937,26 @@ public class AlarmManagerServiceTest { } @Test + public void alarmCountOnRemoveFromPendingWhileIdle() { + mService.mPendingIdleUntil = mock(AlarmManagerService.Alarm.class); + final int numAlarms = 15; + final PendingIntent[] pis = new PendingIntent[numAlarms]; + for (int i = 0; i < numAlarms; i++) { + pis[i] = getNewMockPendingIntent(); + setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + i + 5, pis[i]); + } + assertEquals(numAlarms, mService.mAlarmsPerUid.get(TEST_CALLING_UID)); + assertEquals(numAlarms, mService.mPendingWhileIdleAlarms.size()); + final int toRemove = 8; + for (int i = 0; i < toRemove; i++) { + mService.removeLocked(pis[i], null); + assertEquals(numAlarms - i - 1, mService.mAlarmsPerUid.get(TEST_CALLING_UID, 0)); + } + mService.removeLocked(TEST_CALLING_UID); + assertEquals(0, mService.mAlarmsPerUid.get(TEST_CALLING_UID, 0)); + } + + @Test public void alarmCountOnAlarmRemoved() { final int numAlarms = 10; final PendingIntent[] pis = new PendingIntent[numAlarms]; diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java new file mode 100644 index 000000000000..f7edf65a499f --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java @@ -0,0 +1,437 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.job; + +import static android.text.format.DateUtils.HOUR_IN_MILLIS; +import static android.text.format.DateUtils.MINUTE_IN_MILLIS; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; +import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.when; + +import android.app.ActivityManager; +import android.app.ActivityManagerInternal; +import android.app.IActivityManager; +import android.app.job.JobInfo; +import android.app.usage.UsageStatsManagerInternal; +import android.content.ComponentName; +import android.content.Context; +import android.content.pm.PackageManager; +import android.content.pm.PackageManagerInternal; +import android.content.res.Resources; +import android.net.ConnectivityManager; +import android.net.NetworkPolicyManager; +import android.os.BatteryManagerInternal; +import android.os.Looper; +import android.os.RemoteException; +import android.os.SystemClock; + +import com.android.server.AppStateTracker; +import com.android.server.DeviceIdleController; +import com.android.server.LocalServices; +import com.android.server.job.controllers.JobStatus; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoSession; +import org.mockito.quality.Strictness; + +import java.time.Clock; +import java.time.Duration; +import java.time.ZoneOffset; + +public class JobSchedulerServiceTest { + private JobSchedulerService mService; + + private MockitoSession mMockingSession; + @Mock + private ActivityManagerInternal mActivityMangerInternal; + @Mock + private Context mContext; + + private class TestJobSchedulerService extends JobSchedulerService { + TestJobSchedulerService(Context context) { + super(context); + } + + @Override + public boolean isChainedAttributionEnabled() { + return false; + } + } + + @Before + public void setUp() { + mMockingSession = mockitoSession() + .initMocks(this) + .strictness(Strictness.LENIENT) + .mockStatic(LocalServices.class) + .startMocking(); + + // Called in JobSchedulerService constructor. + when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper()); + doReturn(mActivityMangerInternal) + .when(() -> LocalServices.getService(ActivityManagerInternal.class)); + doReturn(mock(UsageStatsManagerInternal.class)) + .when(() -> LocalServices.getService(UsageStatsManagerInternal.class)); + // Called in BackgroundJobsController constructor. + doReturn(mock(AppStateTracker.class)) + .when(() -> LocalServices.getService(AppStateTracker.class)); + // Called in BatteryController constructor. + doReturn(mock(BatteryManagerInternal.class)) + .when(() -> LocalServices.getService(BatteryManagerInternal.class)); + // Called in ConnectivityController constructor. + when(mContext.getSystemService(ConnectivityManager.class)) + .thenReturn(mock(ConnectivityManager.class)); + when(mContext.getSystemService(NetworkPolicyManager.class)) + .thenReturn(mock(NetworkPolicyManager.class)); + // Called in DeviceIdleJobsController constructor. + doReturn(mock(DeviceIdleController.LocalService.class)) + .when(() -> LocalServices.getService(DeviceIdleController.LocalService.class)); + // Used in JobStatus. + doReturn(mock(PackageManagerInternal.class)) + .when(() -> LocalServices.getService(PackageManagerInternal.class)); + // Called via IdleController constructor. + when(mContext.getPackageManager()).thenReturn(mock(PackageManager.class)); + when(mContext.getResources()).thenReturn(mock(Resources.class)); + // Called in QuotaController constructor. + IActivityManager activityManager = ActivityManager.getService(); + spyOn(activityManager); + try { + doNothing().when(activityManager).registerUidObserver(any(), anyInt(), anyInt(), any()); + } catch (RemoteException e) { + fail("registerUidObserver threw exception: " + e.getMessage()); + } + + JobSchedulerService.sSystemClock = Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC); + JobSchedulerService.sElapsedRealtimeClock = + Clock.fixed(SystemClock.elapsedRealtimeClock().instant(), ZoneOffset.UTC); + + mService = new TestJobSchedulerService(mContext); + } + + @After + public void tearDown() { + if (mMockingSession != null) { + mMockingSession.finishMocking(); + } + } + + private Clock getAdvancedClock(Clock clock, long incrementMs) { + return Clock.offset(clock, Duration.ofMillis(incrementMs)); + } + + private void advanceElapsedClock(long incrementMs) { + JobSchedulerService.sElapsedRealtimeClock = getAdvancedClock( + JobSchedulerService.sElapsedRealtimeClock, incrementMs); + } + + private static JobInfo.Builder createJobInfo() { + return new JobInfo.Builder(351, new ComponentName("foo", "bar")); + } + + private JobStatus createJobStatus(String testTag, JobInfo.Builder jobInfoBuilder) { + return JobStatus.createFromJobInfo( + jobInfoBuilder.build(), 1234, "com.android.test", 0, testTag); + } + + /** + * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job + * with the correct delay and deadline constraints if the periodic job is completed and + * rescheduled while run in its expected running window. + */ + @Test + public void testGetRescheduleJobForPeriodic_insideWindow() { + final long now = sElapsedRealtimeClock.millis(); + JobStatus job = createJobStatus("testGetRescheduleJobForPeriodic_insideWindow", + createJobInfo().setPeriodic(HOUR_IN_MILLIS)); + final long nextWindowStartTime = now + HOUR_IN_MILLIS; + final long nextWindowEndTime = now + 2 * HOUR_IN_MILLIS; + + JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(job); + assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); + assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); + + advanceElapsedClock(10 * MINUTE_IN_MILLIS); // now + 10 minutes + + rescheduledJob = mService.getRescheduleJobForPeriodic(job); + assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); + assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); + + advanceElapsedClock(45 * MINUTE_IN_MILLIS); // now + 55 minutes + + rescheduledJob = mService.getRescheduleJobForPeriodic(job); + assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); + assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); + + advanceElapsedClock(4 * MINUTE_IN_MILLIS); // now + 59 minutes + + rescheduledJob = mService.getRescheduleJobForPeriodic(job); + assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); + assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); + } + + /** + * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job + * with the correct delay and deadline constraints if the periodic job with a custom flex + * setting is completed and rescheduled while run in its expected running window. + */ + @Test + public void testGetRescheduleJobForPeriodic_insideWindow_flex() { + JobStatus job = createJobStatus("testGetRescheduleJobForPeriodic_insideWindow_flex", + createJobInfo().setPeriodic(HOUR_IN_MILLIS, 30 * MINUTE_IN_MILLIS)); + // First window starts 30 minutes from now. + advanceElapsedClock(30 * MINUTE_IN_MILLIS); + final long now = sElapsedRealtimeClock.millis(); + final long nextWindowStartTime = now + HOUR_IN_MILLIS; + final long nextWindowEndTime = now + 90 * MINUTE_IN_MILLIS; + + JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(job); + assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); + assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); + + advanceElapsedClock(10 * MINUTE_IN_MILLIS); // now + 10 minutes + + rescheduledJob = mService.getRescheduleJobForPeriodic(job); + assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); + assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); + + advanceElapsedClock(15 * MINUTE_IN_MILLIS); // now + 25 minutes + + rescheduledJob = mService.getRescheduleJobForPeriodic(job); + assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); + assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); + + advanceElapsedClock(4 * MINUTE_IN_MILLIS); // now + 29 minutes + + rescheduledJob = mService.getRescheduleJobForPeriodic(job); + assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); + assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); + } + + /** + * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job + * with the correct delay and deadline constraints if the periodic job failed but then ran + * successfully and was rescheduled while run in its expected running window. + */ + @Test + public void testGetRescheduleJobForPeriodic_insideWindow_failedJob() { + final long now = sElapsedRealtimeClock.millis(); + final long nextWindowStartTime = now + HOUR_IN_MILLIS; + final long nextWindowEndTime = now + 2 * HOUR_IN_MILLIS; + JobStatus job = createJobStatus("testGetRescheduleJobForPeriodic_insideWindow_failedJob", + createJobInfo().setPeriodic(HOUR_IN_MILLIS)); + JobStatus failedJob = mService.getRescheduleJobForFailureLocked(job); + + JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob); + assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); + assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); + + advanceElapsedClock(5 * MINUTE_IN_MILLIS); // now + 5 minutes + failedJob = mService.getRescheduleJobForFailureLocked(job); + advanceElapsedClock(5 * MINUTE_IN_MILLIS); // now + 10 minutes + + rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob); + assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); + assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); + + advanceElapsedClock(35 * MINUTE_IN_MILLIS); // now + 45 minutes + failedJob = mService.getRescheduleJobForFailureLocked(job); + advanceElapsedClock(10 * MINUTE_IN_MILLIS); // now + 55 minutes + + rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob); + assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); + assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); + + advanceElapsedClock(2 * MINUTE_IN_MILLIS); // now + 57 minutes + failedJob = mService.getRescheduleJobForFailureLocked(job); + advanceElapsedClock(2 * MINUTE_IN_MILLIS); // now + 59 minutes + + rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob); + assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); + assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); + } + + /** + * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job + * with the correct delay and deadline constraints if the periodic job is completed and + * rescheduled when run after its expected running window. + */ + @Test + public void testGetRescheduleJobForPeriodic_outsideWindow() { + JobStatus job = createJobStatus("testGetRescheduleJobForPeriodic_outsideWindow", + createJobInfo().setPeriodic(HOUR_IN_MILLIS)); + long now = sElapsedRealtimeClock.millis(); + long nextWindowStartTime = now + HOUR_IN_MILLIS; + long nextWindowEndTime = now + 2 * HOUR_IN_MILLIS; + + advanceElapsedClock(HOUR_IN_MILLIS + MINUTE_IN_MILLIS); + // Say the job ran at the very end of its previous window. The intended JSS behavior is to + // have consistent windows, so the new window should start as soon as the previous window + // ended and end PERIOD time after the previous window ended. + JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(job); + assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); + assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); + + advanceElapsedClock(2 * HOUR_IN_MILLIS); + // Say that the job ran at this point, possibly due to device idle. + // The next window should be consistent (start and end at the time it would have had the job + // run normally in previous windows). + nextWindowStartTime += 2 * HOUR_IN_MILLIS; + nextWindowEndTime += 2 * HOUR_IN_MILLIS; + + rescheduledJob = mService.getRescheduleJobForPeriodic(job); + assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); + assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); + } + + /** + * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job + * with the correct delay and deadline constraints if the periodic job with a custom flex + * setting is completed and rescheduled when run after its expected running window. + */ + @Test + public void testGetRescheduleJobForPeriodic_outsideWindow_flex() { + JobStatus job = createJobStatus("testGetRescheduleJobForPeriodic_outsideWindow_flex", + createJobInfo().setPeriodic(HOUR_IN_MILLIS, 30 * MINUTE_IN_MILLIS)); + // First window starts 30 minutes from now. + advanceElapsedClock(30 * MINUTE_IN_MILLIS); + long now = sElapsedRealtimeClock.millis(); + long nextWindowStartTime = now + HOUR_IN_MILLIS; + long nextWindowEndTime = now + 90 * MINUTE_IN_MILLIS; + + advanceElapsedClock(31 * MINUTE_IN_MILLIS); + // Say the job ran at the very end of its previous window. The intended JSS behavior is to + // have consistent windows, so the new window should start as soon as the previous window + // ended and end PERIOD time after the previous window ended. + JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(job); + assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); + assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); + + // 5 minutes before the start of the next window. It's too close to the next window, so the + // returned job should be for the window after. + advanceElapsedClock(24 * MINUTE_IN_MILLIS); + nextWindowStartTime += HOUR_IN_MILLIS; + nextWindowEndTime += HOUR_IN_MILLIS; + rescheduledJob = mService.getRescheduleJobForPeriodic(job); + assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); + assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); + + advanceElapsedClock(2 * HOUR_IN_MILLIS + 10 * MINUTE_IN_MILLIS); + // Say that the job ran at this point, possibly due to device idle. + // The next window should be consistent (start and end at the time it would have had the job + // run normally in previous windows). + nextWindowStartTime += 2 * HOUR_IN_MILLIS; + nextWindowEndTime += 2 * HOUR_IN_MILLIS; + + rescheduledJob = mService.getRescheduleJobForPeriodic(job); + assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); + assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); + } + + /** + * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job + * with the correct delay and deadline constraints if the periodic job failed but then ran + * successfully and was rescheduled when run after its expected running window. + */ + @Test + public void testGetRescheduleJobForPeriodic_outsideWindow_failedJob() { + JobStatus job = createJobStatus("testGetRescheduleJobForPeriodic_outsideWindow_failedJob", + createJobInfo().setPeriodic(HOUR_IN_MILLIS)); + JobStatus failedJob = mService.getRescheduleJobForFailureLocked(job); + long now = sElapsedRealtimeClock.millis(); + long nextWindowStartTime = now + HOUR_IN_MILLIS; + long nextWindowEndTime = now + 2 * HOUR_IN_MILLIS; + + advanceElapsedClock(HOUR_IN_MILLIS + MINUTE_IN_MILLIS); + // Say the job ran at the very end of its previous window. The intended JSS behavior is to + // have consistent windows, so the new window should start as soon as the previous window + // ended and end PERIOD time after the previous window ended. + JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob); + assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); + assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); + + advanceElapsedClock(2 * HOUR_IN_MILLIS); + // Say that the job ran at this point, possibly due to device idle. + // The next window should be consistent (start and end at the time it would have had the job + // run normally in previous windows). + nextWindowStartTime += 2 * HOUR_IN_MILLIS; + nextWindowEndTime += 2 * HOUR_IN_MILLIS; + + rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob); + assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); + assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); + } + + /** + * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job + * with the correct delay and deadline constraints if the periodic job with a custom flex + * setting failed but then ran successfully and was rescheduled when run after its expected + * running window. + */ + @Test + public void testGetRescheduleJobForPeriodic_outsideWindow_flex_failedJob() { + JobStatus job = createJobStatus( + "testGetRescheduleJobForPeriodic_outsideWindow_flex_failedJob", + createJobInfo().setPeriodic(HOUR_IN_MILLIS, 30 * MINUTE_IN_MILLIS)); + JobStatus failedJob = mService.getRescheduleJobForFailureLocked(job); + // First window starts 30 minutes from now. + advanceElapsedClock(30 * MINUTE_IN_MILLIS); + long now = sElapsedRealtimeClock.millis(); + long nextWindowStartTime = now + HOUR_IN_MILLIS; + long nextWindowEndTime = now + 90 * MINUTE_IN_MILLIS; + + advanceElapsedClock(31 * MINUTE_IN_MILLIS); + // Say the job ran at the very end of its previous window. The intended JSS behavior is to + // have consistent windows, so the new window should start as soon as the previous window + // ended and end PERIOD time after the previous window ended. + JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob); + assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); + assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); + + // 5 minutes before the start of the next window. It's too close to the next window, so the + // returned job should be for the window after. + advanceElapsedClock(24 * MINUTE_IN_MILLIS); + nextWindowStartTime += HOUR_IN_MILLIS; + nextWindowEndTime += HOUR_IN_MILLIS; + rescheduledJob = mService.getRescheduleJobForPeriodic(job); + assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); + assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); + + advanceElapsedClock(2 * HOUR_IN_MILLIS); + // Say that the job ran at this point, possibly due to device idle. + // The next window should be consistent (start and end at the time it would have had the job + // run normally in previous windows). + nextWindowStartTime += 2 * HOUR_IN_MILLIS; + nextWindowEndTime += 2 * HOUR_IN_MILLIS; + + rescheduledJob = mService.getRescheduleJobForPeriodic(failedJob); + assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime()); + assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed()); + } +} diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java index 7c30f25c6281..2e7283c287bf 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java @@ -105,8 +105,9 @@ public class QuotaControllerTest { private static final int SOURCE_USER_ID = 0; private BroadcastReceiver mChargingReceiver; - private Constants mConstants; + private Constants mJsConstants; private QuotaController mQuotaController; + private QuotaController.QcConstants mQcConstants; private int mSourceUid; private IUidObserver mUidObserver; @@ -132,13 +133,13 @@ public class QuotaControllerTest { .mockStatic(LocalServices.class) .startMocking(); // Make sure constants turn on QuotaController. - mConstants = new Constants(); - mConstants.USE_HEARTBEATS = false; + mJsConstants = new Constants(); + mJsConstants.USE_HEARTBEATS = false; // Called in StateController constructor. when(mJobSchedulerService.getTestableContext()).thenReturn(mContext); when(mJobSchedulerService.getLock()).thenReturn(mJobSchedulerService); - when(mJobSchedulerService.getConstants()).thenReturn(mConstants); + when(mJobSchedulerService.getConstants()).thenReturn(mJsConstants); // Called in QuotaController constructor. IActivityManager activityManager = ActivityManager.getService(); spyOn(activityManager); @@ -196,6 +197,7 @@ public class QuotaControllerTest { } catch (RemoteException e) { fail(e.getMessage()); } + mQcConstants = mQuotaController.getQcConstants(); } @After @@ -531,7 +533,7 @@ public class QuotaControllerTest { expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS; expectedStats.bgJobCountInMaxPeriod = 15; expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS) - + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS; + + mQcConstants.IN_QUOTA_BUFFER_MS; mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats); assertEquals(expectedStats, inputStats); @@ -544,7 +546,7 @@ public class QuotaControllerTest { expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS; expectedStats.bgJobCountInMaxPeriod = 15; expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS) - + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS; + + mQcConstants.IN_QUOTA_BUFFER_MS; mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats); assertEquals(expectedStats, inputStats); @@ -557,7 +559,7 @@ public class QuotaControllerTest { expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS; expectedStats.bgJobCountInMaxPeriod = 15; expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS) - + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS; + + mQcConstants.IN_QUOTA_BUFFER_MS; mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats); assertEquals(expectedStats, inputStats); @@ -574,7 +576,7 @@ public class QuotaControllerTest { expectedStats.executionTimeInMaxPeriodMs = 23 * MINUTE_IN_MILLIS; expectedStats.bgJobCountInMaxPeriod = 18; expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS) - + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS; + + mQcConstants.IN_QUOTA_BUFFER_MS; mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats); assertEquals(expectedStats, inputStats); @@ -590,7 +592,7 @@ public class QuotaControllerTest { expectedStats.executionTimeInMaxPeriodMs = 24 * MINUTE_IN_MILLIS; expectedStats.bgJobCountInMaxPeriod = 20; expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS) - + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS; + + mQcConstants.IN_QUOTA_BUFFER_MS; mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats); assertEquals(expectedStats, inputStats); } @@ -630,7 +632,7 @@ public class QuotaControllerTest { expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS; expectedStats.bgJobCountInMaxPeriod = 20; expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - 3 * MINUTE_IN_MILLIS) - + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS; + + mQcConstants.IN_QUOTA_BUFFER_MS; assertEquals(expectedStats, mQuotaController.getExecutionStatsLocked(0, "com.android.test", WORKING_INDEX)); @@ -642,7 +644,7 @@ public class QuotaControllerTest { expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS; expectedStats.bgJobCountInMaxPeriod = 20; expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - 3 * MINUTE_IN_MILLIS) - + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS; + + mQcConstants.IN_QUOTA_BUFFER_MS; assertEquals(expectedStats, mQuotaController.getExecutionStatsLocked(0, "com.android.test", FREQUENT_INDEX)); @@ -654,7 +656,7 @@ public class QuotaControllerTest { expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS; expectedStats.bgJobCountInMaxPeriod = 20; expectedStats.quotaCutoffTimeElapsed = now - (2 * HOUR_IN_MILLIS - 3 * MINUTE_IN_MILLIS) - + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS; + + mQcConstants.IN_QUOTA_BUFFER_MS; assertEquals(expectedStats, mQuotaController.getExecutionStatsLocked(0, "com.android.test", RARE_INDEX)); } @@ -685,8 +687,8 @@ public class QuotaControllerTest { // Advance clock so that the working stats shouldn't be the same. advanceElapsedClock(MINUTE_IN_MILLIS); // Change frequent bucket size so that the stats need to be recalculated. - mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS = 6 * HOUR_IN_MILLIS; - mQuotaController.onConstantsUpdatedLocked(); + mQcConstants.WINDOW_SIZE_FREQUENT_MS = 6 * HOUR_IN_MILLIS; + mQcConstants.updateConstants(); ExecutionStats expectedStats = new ExecutionStats(); expectedStats.windowSizeMs = originalStatsActive.windowSizeMs; @@ -778,7 +780,7 @@ public class QuotaControllerTest { setStandbyBucket(ACTIVE_INDEX); assertEquals(7 * MINUTE_IN_MILLIS, mQuotaController.getRemainingExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); - assertEquals(mConstants.QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS - 9 * MINUTE_IN_MILLIS, + assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 9 * MINUTE_IN_MILLIS, mQuotaController.getTimeUntilQuotaConsumedLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); } @@ -901,7 +903,7 @@ public class QuotaControllerTest { public void testIsWithinQuotaLocked_UnderDuration_OverJobCount() { setDischarging(); final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); - final int jobCount = mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME; + final int jobCount = mQcConstants.MAX_JOB_COUNT_PER_ALLOWED_TIME; mQuotaController.saveTimingSession(0, "com.android.test.spam", createTimingSession(now - (HOUR_IN_MILLIS), 15 * MINUTE_IN_MILLIS, 25)); mQuotaController.saveTimingSession(0, "com.android.test.spam", @@ -936,7 +938,7 @@ public class QuotaControllerTest { public void testIsWithinQuotaLocked_OverDuration_OverJobCount() { setDischarging(); final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); - final int jobCount = mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME; + final int jobCount = mQcConstants.MAX_JOB_COUNT_PER_ALLOWED_TIME; mQuotaController.saveTimingSession(0, "com.android.test", createTimingSession(now - (HOUR_IN_MILLIS), 15 * MINUTE_IN_MILLIS, 25)); mQuotaController.saveTimingSession(0, "com.android.test", @@ -1098,8 +1100,7 @@ public class QuotaControllerTest { final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); // Test with timing sessions out of window but still under max execution limit. final long expectedAlarmTime = - (now - 18 * HOUR_IN_MILLIS) + 24 * HOUR_IN_MILLIS - + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS; + (now - 18 * HOUR_IN_MILLIS) + 24 * HOUR_IN_MILLIS + mQcConstants.IN_QUOTA_BUFFER_MS; mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, createTimingSession(now - 18 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, 1)); mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, @@ -1156,8 +1157,7 @@ public class QuotaControllerTest { final long end = now - (2 * HOUR_IN_MILLIS - 5 * MINUTE_IN_MILLIS); // Counting backwards, the quota will come back one minute before the end. final long expectedAlarmTime = - end - MINUTE_IN_MILLIS + 2 * HOUR_IN_MILLIS - + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS; + end - MINUTE_IN_MILLIS + 2 * HOUR_IN_MILLIS + mQcConstants.IN_QUOTA_BUFFER_MS; mQuotaController.saveTimingSession(0, "com.android.test", new TimingSession(now - 2 * HOUR_IN_MILLIS, end, 1)); mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); @@ -1207,8 +1207,7 @@ public class QuotaControllerTest { // Test with timing sessions in window but still in quota. final long start = now - (6 * HOUR_IN_MILLIS); - final long expectedAlarmTime = - start + 8 * HOUR_IN_MILLIS + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS; + final long expectedAlarmTime = start + 8 * HOUR_IN_MILLIS + mQcConstants.IN_QUOTA_BUFFER_MS; mQuotaController.saveTimingSession(0, "com.android.test", createTimingSession(start, 5 * MINUTE_IN_MILLIS, 1)); mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); @@ -1262,7 +1261,7 @@ public class QuotaControllerTest { // needs to be excluded. final long expectedAlarmTime = start + MINUTE_IN_MILLIS + 24 * HOUR_IN_MILLIS - + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS; + + mQcConstants.IN_QUOTA_BUFFER_MS; mQuotaController.saveTimingSession(0, "com.android.test", createTimingSession(start, 5 * MINUTE_IN_MILLIS, 1)); mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket); @@ -1324,21 +1323,21 @@ public class QuotaControllerTest { // And down from there. final long expectedWorkingAlarmTime = outOfQuotaTime + (2 * HOUR_IN_MILLIS) - + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS; + + mQcConstants.IN_QUOTA_BUFFER_MS; mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", WORKING_INDEX); inOrder.verify(mAlarmManager, times(1)) .set(anyInt(), eq(expectedWorkingAlarmTime), eq(TAG_QUOTA_CHECK), any(), any()); final long expectedFrequentAlarmTime = outOfQuotaTime + (8 * HOUR_IN_MILLIS) - + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS; + + mQcConstants.IN_QUOTA_BUFFER_MS; mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", FREQUENT_INDEX); inOrder.verify(mAlarmManager, times(1)) .set(anyInt(), eq(expectedFrequentAlarmTime), eq(TAG_QUOTA_CHECK), any(), any()); final long expectedRareAlarmTime = outOfQuotaTime + (24 * HOUR_IN_MILLIS) - + mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS; + + mQcConstants.IN_QUOTA_BUFFER_MS; mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", RARE_INDEX); inOrder.verify(mAlarmManager, times(1)) .set(anyInt(), eq(expectedRareAlarmTime), eq(TAG_QUOTA_CHECK), any(), any()); @@ -1365,7 +1364,7 @@ public class QuotaControllerTest { ExecutionStats stats = mQuotaController.getExecutionStatsLocked(SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); stats.jobCountInAllowedTime = - mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME + 2; + mQcConstants.MAX_JOB_COUNT_PER_ALLOWED_TIME + 2; // Invalid time in the past, so the count shouldn't be used. stats.jobCountExpirationTimeElapsed = @@ -1400,8 +1399,8 @@ public class QuotaControllerTest { @Test public void testMaybeScheduleStartAlarmLocked_SmallRollingQuota_UpdatedBufferSize() { // Make sure any new value is used correctly. - mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS *= 2; - mQuotaController.onConstantsUpdatedLocked(); + mQcConstants.IN_QUOTA_BUFFER_MS *= 2; + mQcConstants.updateConstants(); runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_AllowedTimeCheck(); mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear(); runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_MaxTimeCheck(); @@ -1410,8 +1409,8 @@ public class QuotaControllerTest { @Test public void testMaybeScheduleStartAlarmLocked_SmallRollingQuota_UpdatedAllowedTime() { // Make sure any new value is used correctly. - mConstants.QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS /= 2; - mQuotaController.onConstantsUpdatedLocked(); + mQcConstants.ALLOWED_TIME_PER_PERIOD_MS /= 2; + mQcConstants.updateConstants(); runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_AllowedTimeCheck(); mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear(); runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_MaxTimeCheck(); @@ -1420,8 +1419,8 @@ public class QuotaControllerTest { @Test public void testMaybeScheduleStartAlarmLocked_SmallRollingQuota_UpdatedMaxTime() { // Make sure any new value is used correctly. - mConstants.QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS /= 2; - mQuotaController.onConstantsUpdatedLocked(); + mQcConstants.MAX_EXECUTION_TIME_MS /= 2; + mQcConstants.updateConstants(); runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_AllowedTimeCheck(); mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear(); runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_MaxTimeCheck(); @@ -1430,10 +1429,10 @@ public class QuotaControllerTest { @Test public void testMaybeScheduleStartAlarmLocked_SmallRollingQuota_UpdatedEverything() { // Make sure any new value is used correctly. - mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS *= 2; - mConstants.QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS /= 2; - mConstants.QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS /= 2; - mQuotaController.onConstantsUpdatedLocked(); + mQcConstants.IN_QUOTA_BUFFER_MS *= 2; + mQcConstants.ALLOWED_TIME_PER_PERIOD_MS /= 2; + mQcConstants.MAX_EXECUTION_TIME_MS /= 2; + mQcConstants.updateConstants(); runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_AllowedTimeCheck(); mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear(); runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_MaxTimeCheck(); @@ -1448,9 +1447,8 @@ public class QuotaControllerTest { final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); // Working set window size is 2 hours. final int standbyBucket = WORKING_INDEX; - final long contributionMs = mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS / 2; - final long remainingTimeMs = - mConstants.QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS - contributionMs; + final long contributionMs = mQcConstants.IN_QUOTA_BUFFER_MS / 2; + final long remainingTimeMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_MS - contributionMs; // Session straddles edge of bucket window. Only the contribution should be counted towards // the quota. @@ -1462,7 +1460,7 @@ public class QuotaControllerTest { // Expected alarm time should be when the app will have QUOTA_BUFFER_MS time of quota, which // is 2 hours + (QUOTA_BUFFER_MS - contributionMs) after the start of the second session. final long expectedAlarmTime = now - HOUR_IN_MILLIS + 2 * HOUR_IN_MILLIS - + (mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS - contributionMs); + + (mQcConstants.IN_QUOTA_BUFFER_MS - contributionMs); mQuotaController.maybeScheduleStartAlarmLocked(SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); verify(mAlarmManager, times(1)) @@ -1479,9 +1477,8 @@ public class QuotaControllerTest { final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); // Working set window size is 2 hours. final int standbyBucket = WORKING_INDEX; - final long contributionMs = mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS / 2; - final long remainingTimeMs = - mConstants.QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS - contributionMs; + final long contributionMs = mQcConstants.IN_QUOTA_BUFFER_MS / 2; + final long remainingTimeMs = mQcConstants.MAX_EXECUTION_TIME_MS - contributionMs; // Session straddles edge of 24 hour window. Only the contribution should be counted towards // the quota. @@ -1493,9 +1490,8 @@ public class QuotaControllerTest { // Expected alarm time should be when the app will have QUOTA_BUFFER_MS time of quota, which // is 24 hours + (QUOTA_BUFFER_MS - contributionMs) after the start of the second session. final long expectedAlarmTime = now - 20 * HOUR_IN_MILLIS - //+ mConstants.QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS + 24 * HOUR_IN_MILLIS - + (mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS - contributionMs); + + (mQcConstants.IN_QUOTA_BUFFER_MS - contributionMs); mQuotaController.maybeScheduleStartAlarmLocked(SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); verify(mAlarmManager, times(1)) @@ -1515,12 +1511,12 @@ public class QuotaControllerTest { mQuotaController.maybeStartTrackingJobLocked(jobStatus, null); assertFalse(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); - mConstants.USE_HEARTBEATS = true; + mJsConstants.USE_HEARTBEATS = true; mQuotaController.onConstantsUpdatedLocked(); Thread.sleep(SECOND_IN_MILLIS); // Job updates are done in the background. assertTrue(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); - mConstants.USE_HEARTBEATS = false; + mJsConstants.USE_HEARTBEATS = false; mQuotaController.onConstantsUpdatedLocked(); Thread.sleep(SECOND_IN_MILLIS); // Job updates are done in the background. assertFalse(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); @@ -1528,20 +1524,20 @@ public class QuotaControllerTest { @Test public void testConstantsUpdating_ValidValues() { - mConstants.QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS = 5 * MINUTE_IN_MILLIS; - mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS = 2 * MINUTE_IN_MILLIS; - mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS = 15 * MINUTE_IN_MILLIS; - mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS = 30 * MINUTE_IN_MILLIS; - mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS = 45 * MINUTE_IN_MILLIS; - mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS = 60 * MINUTE_IN_MILLIS; - mConstants.QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS = 3 * HOUR_IN_MILLIS; - mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE = 5000; - mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING = 4000; - mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT = 3000; - mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE = 2000; - mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME = 500; - - mQuotaController.onConstantsUpdatedLocked(); + mQcConstants.ALLOWED_TIME_PER_PERIOD_MS = 5 * MINUTE_IN_MILLIS; + mQcConstants.IN_QUOTA_BUFFER_MS = 2 * MINUTE_IN_MILLIS; + mQcConstants.WINDOW_SIZE_ACTIVE_MS = 15 * MINUTE_IN_MILLIS; + mQcConstants.WINDOW_SIZE_WORKING_MS = 30 * MINUTE_IN_MILLIS; + mQcConstants.WINDOW_SIZE_FREQUENT_MS = 45 * MINUTE_IN_MILLIS; + mQcConstants.WINDOW_SIZE_RARE_MS = 60 * MINUTE_IN_MILLIS; + mQcConstants.MAX_EXECUTION_TIME_MS = 3 * HOUR_IN_MILLIS; + mQcConstants.MAX_JOB_COUNT_ACTIVE = 5000; + mQcConstants.MAX_JOB_COUNT_WORKING = 4000; + mQcConstants.MAX_JOB_COUNT_FREQUENT = 3000; + mQcConstants.MAX_JOB_COUNT_RARE = 2000; + mQcConstants.MAX_JOB_COUNT_PER_ALLOWED_TIME = 500; + + mQcConstants.updateConstants(); assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs()); assertEquals(2 * MINUTE_IN_MILLIS, mQuotaController.getInQuotaBufferMs()); @@ -1561,20 +1557,20 @@ public class QuotaControllerTest { @Test public void testConstantsUpdating_InvalidValues() { // Test negatives/too low. - mConstants.QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS = -MINUTE_IN_MILLIS; - mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS = -MINUTE_IN_MILLIS; - mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS = -MINUTE_IN_MILLIS; - mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS = -MINUTE_IN_MILLIS; - mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS = -MINUTE_IN_MILLIS; - mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS = -MINUTE_IN_MILLIS; - mConstants.QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS = -MINUTE_IN_MILLIS; - mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE = -1; - mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING = 1; - mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT = 1; - mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE = 1; - mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME = 0; - - mQuotaController.onConstantsUpdatedLocked(); + mQcConstants.ALLOWED_TIME_PER_PERIOD_MS = -MINUTE_IN_MILLIS; + mQcConstants.IN_QUOTA_BUFFER_MS = -MINUTE_IN_MILLIS; + mQcConstants.WINDOW_SIZE_ACTIVE_MS = -MINUTE_IN_MILLIS; + mQcConstants.WINDOW_SIZE_WORKING_MS = -MINUTE_IN_MILLIS; + mQcConstants.WINDOW_SIZE_FREQUENT_MS = -MINUTE_IN_MILLIS; + mQcConstants.WINDOW_SIZE_RARE_MS = -MINUTE_IN_MILLIS; + mQcConstants.MAX_EXECUTION_TIME_MS = -MINUTE_IN_MILLIS; + mQcConstants.MAX_JOB_COUNT_ACTIVE = -1; + mQcConstants.MAX_JOB_COUNT_WORKING = 1; + mQcConstants.MAX_JOB_COUNT_FREQUENT = 1; + mQcConstants.MAX_JOB_COUNT_RARE = 1; + mQcConstants.MAX_JOB_COUNT_PER_ALLOWED_TIME = 0; + + mQcConstants.updateConstants(); assertEquals(MINUTE_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs()); assertEquals(0, mQuotaController.getInQuotaBufferMs()); @@ -1590,15 +1586,15 @@ public class QuotaControllerTest { assertEquals(100, mQuotaController.getBucketMaxJobCounts()[RARE_INDEX]); // Test larger than a day. Controller should cap at one day. - mConstants.QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS = 25 * HOUR_IN_MILLIS; - mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS = 25 * HOUR_IN_MILLIS; - mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS = 25 * HOUR_IN_MILLIS; - mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS = 25 * HOUR_IN_MILLIS; - mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS = 25 * HOUR_IN_MILLIS; - mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS = 25 * HOUR_IN_MILLIS; - mConstants.QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS = 25 * HOUR_IN_MILLIS; + mQcConstants.ALLOWED_TIME_PER_PERIOD_MS = 25 * HOUR_IN_MILLIS; + mQcConstants.IN_QUOTA_BUFFER_MS = 25 * HOUR_IN_MILLIS; + mQcConstants.WINDOW_SIZE_ACTIVE_MS = 25 * HOUR_IN_MILLIS; + mQcConstants.WINDOW_SIZE_WORKING_MS = 25 * HOUR_IN_MILLIS; + mQcConstants.WINDOW_SIZE_FREQUENT_MS = 25 * HOUR_IN_MILLIS; + mQcConstants.WINDOW_SIZE_RARE_MS = 25 * HOUR_IN_MILLIS; + mQcConstants.MAX_EXECUTION_TIME_MS = 25 * HOUR_IN_MILLIS; - mQuotaController.onConstantsUpdatedLocked(); + mQcConstants.updateConstants(); assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs()); assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getInQuotaBufferMs()); @@ -2219,7 +2215,7 @@ public class QuotaControllerTest { verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any()); // Ran jobs up to the job limit. All of them should be allowed to run. - for (int i = 0; i < mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME; ++i) { + for (int i = 0; i < mQcConstants.MAX_JOB_COUNT_PER_ALLOWED_TIME; ++i) { JobStatus job = createJobStatus("testStartAlarmScheduled_JobCount_AllowedTime", i); mQuotaController.maybeStartTrackingJobLocked(job, null); assertTrue(job.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)); @@ -2240,7 +2236,7 @@ public class QuotaControllerTest { ExecutionStats stats = mQuotaController.getExecutionStatsLocked(SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); final long expectedWorkingAlarmTime = - stats.jobCountExpirationTimeElapsed + mConstants.QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS; + stats.jobCountExpirationTimeElapsed + mQcConstants.ALLOWED_TIME_PER_PERIOD_MS; verify(mAlarmManager, times(1)) .set(anyInt(), eq(expectedWorkingAlarmTime), eq(TAG_QUOTA_CHECK), any(), any()); } diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java index 494677d38c0e..19369dbe5f44 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java @@ -39,6 +39,7 @@ import android.app.job.JobInfo; import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageManagerInternal; +import android.os.Looper; import android.os.SystemClock; import androidx.test.runner.AndroidJUnit4; @@ -70,7 +71,7 @@ public class TimeControllerTest { private static final String SOURCE_PACKAGE = "com.android.frameworks.mockingservicestests"; private static final int SOURCE_USER_ID = 0; - private Constants mConstants; + private TimeController.TcConstants mConstants; private TimeController mTimeController; private MockitoSession mMockingSession; @@ -88,14 +89,13 @@ public class TimeControllerTest { .strictness(Strictness.LENIENT) .mockStatic(LocalServices.class) .startMocking(); - // Use default constants for now. - mConstants = new Constants(); // Called in StateController constructor. when(mJobSchedulerService.getTestableContext()).thenReturn(mContext); when(mJobSchedulerService.getLock()).thenReturn(mJobSchedulerService); - when(mJobSchedulerService.getConstants()).thenReturn(mConstants); + when(mJobSchedulerService.getConstants()).thenReturn(mock(Constants.class)); // Called in TimeController constructor. + when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper()); when(mContext.getSystemService(Context.ALARM_SERVICE)).thenReturn(mAlarmManager); // Used in JobStatus. doReturn(mock(PackageManagerInternal.class)) @@ -111,6 +111,7 @@ public class TimeControllerTest { // Initialize real objects. mTimeController = new TimeController(mJobSchedulerService); + mConstants = mTimeController.getTcConstants(); spyOn(mTimeController); } @@ -159,7 +160,7 @@ public class TimeControllerTest { @Test public void testMaybeStartTrackingJobLocked_DelayInOrder_NoSkipping() { - mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = false; + mConstants.SKIP_NOT_READY_JOBS = false; mTimeController.onConstantsUpdatedLocked(); runTestMaybeStartTrackingJobLocked_DelayInOrder(); @@ -167,7 +168,7 @@ public class TimeControllerTest { @Test public void testMaybeStartTrackingJobLocked_DelayInOrder_WithSkipping_AllReady() { - mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true; + mConstants.SKIP_NOT_READY_JOBS = true; mTimeController.onConstantsUpdatedLocked(); doReturn(true).when(mTimeController).wouldBeReadyWithConstraintLocked(any(), anyInt()); @@ -201,7 +202,7 @@ public class TimeControllerTest { @Test public void testMaybeStartTrackingJobLocked_DelayInOrder_WithSkipping_SomeNotReady() { - mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true; + mConstants.SKIP_NOT_READY_JOBS = true; mTimeController.onConstantsUpdatedLocked(); final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); @@ -235,7 +236,7 @@ public class TimeControllerTest { @Test public void testMaybeStartTrackingJobLocked_DelayReverseOrder_NoSkipping() { - mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = false; + mConstants.SKIP_NOT_READY_JOBS = false; mTimeController.onConstantsUpdatedLocked(); runTestMaybeStartTrackingJobLocked_DelayReverseOrder(); @@ -243,7 +244,7 @@ public class TimeControllerTest { @Test public void testMaybeStartTrackingJobLocked_DelayReverseOrder_WithSkipping_AllReady() { - mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true; + mConstants.SKIP_NOT_READY_JOBS = true; mTimeController.onConstantsUpdatedLocked(); doReturn(true).when(mTimeController).wouldBeReadyWithConstraintLocked(any(), anyInt()); @@ -279,7 +280,7 @@ public class TimeControllerTest { @Test public void testMaybeStartTrackingJobLocked_DelayReverseOrder_WithSkipping_SomeNotReady() { - mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true; + mConstants.SKIP_NOT_READY_JOBS = true; mTimeController.onConstantsUpdatedLocked(); final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); @@ -315,7 +316,7 @@ public class TimeControllerTest { @Test public void testMaybeStartTrackingJobLocked_DeadlineInOrder_NoSkipping() { - mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = false; + mConstants.SKIP_NOT_READY_JOBS = false; mTimeController.onConstantsUpdatedLocked(); runTestMaybeStartTrackingJobLocked_DeadlineInOrder(); @@ -323,7 +324,7 @@ public class TimeControllerTest { @Test public void testMaybeStartTrackingJobLocked_DeadlineInOrder_WithSkipping_AllReady() { - mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true; + mConstants.SKIP_NOT_READY_JOBS = true; mTimeController.onConstantsUpdatedLocked(); doReturn(true).when(mTimeController).wouldBeReadyWithConstraintLocked(any(), anyInt()); @@ -357,7 +358,7 @@ public class TimeControllerTest { @Test public void testMaybeStartTrackingJobLocked_DeadlineInOrder_WithSkipping_SomeNotReady() { - mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true; + mConstants.SKIP_NOT_READY_JOBS = true; mTimeController.onConstantsUpdatedLocked(); final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); @@ -391,7 +392,7 @@ public class TimeControllerTest { @Test public void testMaybeStartTrackingJobLocked_DeadlineReverseOrder_NoSkipping() { - mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = false; + mConstants.SKIP_NOT_READY_JOBS = false; mTimeController.onConstantsUpdatedLocked(); runTestMaybeStartTrackingJobLocked_DeadlineReverseOrder(); @@ -399,7 +400,7 @@ public class TimeControllerTest { @Test public void testMaybeStartTrackingJobLocked_DeadlineReverseOrder_WithSkipping_AllReady() { - mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true; + mConstants.SKIP_NOT_READY_JOBS = true; mTimeController.onConstantsUpdatedLocked(); doReturn(true).when(mTimeController).wouldBeReadyWithConstraintLocked(any(), anyInt()); @@ -438,7 +439,7 @@ public class TimeControllerTest { @Test public void testMaybeStartTrackingJobLocked_DeadlineReverseOrder_WithSkipping_SomeNotReady() { - mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true; + mConstants.SKIP_NOT_READY_JOBS = true; mTimeController.onConstantsUpdatedLocked(); final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); @@ -493,8 +494,8 @@ public class TimeControllerTest { .wouldBeReadyWithConstraintLocked(eq(jobEarliest), anyInt()); // Starting off with the skipping off, we should still set an alarm for the earlier job. - mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = false; - mTimeController.onConstantsUpdatedLocked(); + mConstants.SKIP_NOT_READY_JOBS = false; + mTimeController.recheckAlarmsLocked(); InOrder inOrder = inOrder(mAlarmManager); mTimeController.maybeStartTrackingJobLocked(jobEarliest, null); @@ -504,16 +505,16 @@ public class TimeControllerTest { eq(TAG_DEADLINE), any(), any(), any()); // Turn it on, use alarm for later job. - mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true; - mTimeController.onConstantsUpdatedLocked(); + mConstants.SKIP_NOT_READY_JOBS = true; + mTimeController.recheckAlarmsLocked(); inOrder.verify(mAlarmManager, times(1)) .set(anyInt(), eq(now + HOUR_IN_MILLIS), anyLong(), anyLong(), eq(TAG_DEADLINE), any(), any(), any()); // Back off, use alarm for earlier job. - mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = false; - mTimeController.onConstantsUpdatedLocked(); + mConstants.SKIP_NOT_READY_JOBS = false; + mTimeController.recheckAlarmsLocked(); inOrder.verify(mAlarmManager, times(1)) .set(anyInt(), eq(now + 5 * MINUTE_IN_MILLIS), anyLong(), anyLong(), @@ -522,16 +523,16 @@ public class TimeControllerTest { @Test public void testCheckExpiredDelaysAndResetAlarm_NoSkipping() { - mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = false; - mTimeController.onConstantsUpdatedLocked(); + mConstants.SKIP_NOT_READY_JOBS = false; + mTimeController.recheckAlarmsLocked(); runTestCheckExpiredDelaysAndResetAlarm(); } @Test public void testCheckExpiredDelaysAndResetAlarm_WithSkipping_AllReady() { - mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true; - mTimeController.onConstantsUpdatedLocked(); + mConstants.SKIP_NOT_READY_JOBS = true; + mTimeController.recheckAlarmsLocked(); doReturn(true).when(mTimeController).wouldBeReadyWithConstraintLocked(any(), anyInt()); @@ -589,8 +590,8 @@ public class TimeControllerTest { @Test public void testCheckExpiredDelaysAndResetAlarm_WithSkipping_SomeNotReady() { - mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true; - mTimeController.onConstantsUpdatedLocked(); + mConstants.SKIP_NOT_READY_JOBS = true; + mTimeController.recheckAlarmsLocked(); final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); JobStatus jobLatest = createJobStatus("testCheckExpiredDelaysAndResetAlarm", @@ -639,16 +640,16 @@ public class TimeControllerTest { @Test public void testCheckExpiredDeadlinesAndResetAlarm_NoSkipping() { - mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = false; - mTimeController.onConstantsUpdatedLocked(); + mConstants.SKIP_NOT_READY_JOBS = false; + mTimeController.recheckAlarmsLocked(); runTestCheckExpiredDeadlinesAndResetAlarm(); } @Test public void testCheckExpiredDeadlinesAndResetAlarm_WithSkipping_AllReady() { - mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true; - mTimeController.onConstantsUpdatedLocked(); + mConstants.SKIP_NOT_READY_JOBS = true; + mTimeController.recheckAlarmsLocked(); doReturn(true).when(mTimeController).wouldBeReadyWithConstraintLocked(any(), anyInt()); @@ -706,8 +707,8 @@ public class TimeControllerTest { @Test public void testCheckExpiredDeadlinesAndResetAlarm_WithSkipping_SomeNotReady() { - mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true; - mTimeController.onConstantsUpdatedLocked(); + mConstants.SKIP_NOT_READY_JOBS = true; + mTimeController.recheckAlarmsLocked(); final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); JobStatus jobLatest = createJobStatus("testCheckExpiredDeadlinesAndResetAlarm", @@ -756,8 +757,8 @@ public class TimeControllerTest { @Test public void testEvaluateStateLocked_SkippingOff() { - mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = false; - mTimeController.onConstantsUpdatedLocked(); + mConstants.SKIP_NOT_READY_JOBS = false; + mTimeController.recheckAlarmsLocked(); JobStatus job = createJobStatus("testEvaluateStateLocked_SkippingOff", createJob().setOverrideDeadline(HOUR_IN_MILLIS)); @@ -768,8 +769,8 @@ public class TimeControllerTest { @Test public void testEvaluateStateLocked_SkippingOn_Delay() { - mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true; - mTimeController.onConstantsUpdatedLocked(); + mConstants.SKIP_NOT_READY_JOBS = true; + mTimeController.recheckAlarmsLocked(); final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); JobStatus jobLatest = createJobStatus("testEvaluateStateLocked_SkippingOn_Delay", @@ -827,8 +828,8 @@ public class TimeControllerTest { @Test public void testEvaluateStateLocked_SkippingOn_Deadline() { - mConstants.TIME_CONTROLLER_SKIP_NOT_READY_JOBS = true; - mTimeController.onConstantsUpdatedLocked(); + mConstants.SKIP_NOT_READY_JOBS = true; + mTimeController.recheckAlarmsLocked(); final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); JobStatus jobLatest = createJobStatus("testEvaluateStateLocked_SkippingOn_Deadline", diff --git a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java index eb90295ed8c9..dae3d30c4fd1 100644 --- a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java +++ b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java @@ -137,7 +137,6 @@ public final class TestableDeviceConfig implements TestRule { return mKeyValueMap.get(getKey(namespace, name)); }).when(() -> DeviceConfig.getProperty(anyString(), anyString())); - return new TestWatcher() { @Override protected void succeeded(Description description) { diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java index bac8414ef205..7a20af45b999 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java @@ -329,6 +329,31 @@ public class RecoverableKeyStoreDbTest { assertEquals(serialNumber, mRecoverableKeyStoreDb.getUserSerialNumbers().get(userId)); } + @Test + public void setPlatformKeyGenerationId_invalidatesExistingKeysForUser() { + int userId = 42; + int generationId = 110; + int uid = 1009; + int status = 120; + String alias = "test"; + byte[] nonce = getUtf8Bytes("nonce"); + byte[] keyMaterial = getUtf8Bytes("keymaterial"); + byte[] keyMetadata = null; + + WrappedKey wrappedKey = + new WrappedKey(nonce, keyMaterial, keyMetadata, generationId, status); + mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKey); + + WrappedKey retrievedKey = mRecoverableKeyStoreDb.getKey(uid, alias); + assertThat(retrievedKey.getRecoveryStatus()).isEqualTo(status); + + mRecoverableKeyStoreDb.setPlatformKeyGenerationId(userId, generationId + 1); + + retrievedKey = mRecoverableKeyStoreDb.getKey(uid, alias); + assertThat(retrievedKey.getRecoveryStatus()) + .isEqualTo(RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE); + } + @Test public void removeUserFromAllTables_removesData() throws Exception { @@ -439,7 +464,7 @@ public class RecoverableKeyStoreDbTest { } @Test - public void testInvalidateKeysWithOldGenerationId_withSingleKey() { + public void testInvalidateKeysForUser_withSingleKey() { int userId = 12; int uid = 1009; int generationId = 6; @@ -458,7 +483,7 @@ public class RecoverableKeyStoreDbTest { assertThat(retrievedKey.getRecoveryStatus()).isEqualTo(status); mRecoverableKeyStoreDb.setRecoveryStatus(uid, alias, status2); - mRecoverableKeyStoreDb.invalidateKeysWithOldGenerationId(userId, generationId + 1); + mRecoverableKeyStoreDb.invalidateKeysForUser(userId); retrievedKey = mRecoverableKeyStoreDb.getKey(uid, alias); assertThat(retrievedKey.getRecoveryStatus()) diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java index 99b827c11853..bdc46ec808c1 100644 --- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java @@ -263,6 +263,7 @@ public class NetworkPolicyManagerServiceTest { private static final int INVALID_CARRIER_CONFIG_VALUE = -9999; private long mDefaultWarningBytes; // filled in with the actual default before tests are run private long mDefaultLimitBytes; // filled in with the actual default before tests are run + private PersistableBundle mCarrierConfig = CarrierConfigManager.getDefaultConfig(); private static final int APP_ID_A = android.os.Process.FIRST_APPLICATION_UID + 4; private static final int APP_ID_B = android.os.Process.FIRST_APPLICATION_UID + 8; @@ -409,6 +410,9 @@ public class NetworkPolicyManagerServiceTest { doNothing().when(mConnectivityManager) .registerNetworkCallback(any(), mNetworkCallbackCaptor.capture()); + // Create the expected carrier config + mCarrierConfig.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true); + // Prepare NPMS. mService.systemReady(mService.networkScoreAndNetworkManagementServiceReady()); @@ -1086,6 +1090,25 @@ public class NetworkPolicyManagerServiceTest { isA(Notification.class), eq(UserHandle.ALL)); } + // Push over warning, but with a config that isn't from an identified carrier + { + history.clear(); + history.recordData(start, end, + new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1799), 0L, 0L, 0L, 0)); + + reset(mTelephonyManager, mNetworkManager, mNotifManager); + expectMobileDefaults(); + expectDefaultCarrierConfig(); + + mService.updateNetworks(); + + verify(mTelephonyManager, atLeastOnce()).setPolicyDataEnabled(true, TEST_SUB_ID); + verify(mNetworkManager, atLeastOnce()).setInterfaceQuota(TEST_IFACE, + DataUnit.MEGABYTES.toBytes(1800 - 1799)); + // Since this isn't from the identified carrier, there should be no notifications + verify(mNotifManager, never()).notifyAsUser(any(), anyInt(), any(), any()); + } + // Push over limit { history.clear(); @@ -1812,7 +1835,7 @@ public class NetworkPolicyManagerServiceTest { private void expectNetworkState(boolean roaming) throws Exception { when(mCarrierConfigManager.getConfigForSubId(eq(TEST_SUB_ID))) - .thenReturn(CarrierConfigManager.getDefaultConfig()); + .thenReturn(mCarrierConfig); when(mConnManager.getAllNetworkState()).thenReturn(new NetworkState[] { new NetworkState(buildNetworkInfo(), buildLinkProperties(TEST_IFACE), @@ -1821,10 +1844,16 @@ public class NetworkPolicyManagerServiceTest { }); } + private void expectDefaultCarrierConfig() throws Exception { + when(mCarrierConfigManager.getConfigForSubId(eq(TEST_SUB_ID))) + .thenReturn(CarrierConfigManager.getDefaultConfig()); + } + private void expectMobileDefaults() throws Exception { when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn( new int[] { TEST_SUB_ID }); when(mTelephonyManager.getSubscriberId(TEST_SUB_ID)).thenReturn(TEST_IMSI); + doNothing().when(mTelephonyManager).setPolicyDataEnabled(anyBoolean(), anyInt()); expectNetworkState(false /* roaming */); } diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java index 6ec864c05258..f4a6231e43fb 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -16,6 +16,7 @@ package com.android.server.usage; +import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_START; import static android.app.usage.UsageEvents.Event.NOTIFICATION_SEEN; import static android.app.usage.UsageEvents.Event.SLICE_PINNED; import static android.app.usage.UsageEvents.Event.SLICE_PINNED_PRIV; @@ -821,6 +822,41 @@ public class AppStandbyControllerTests { } @Test + public void testInitialForegroundServiceTimeout() throws Exception { + setChargingState(mController, false); + + mInjector.mElapsedRealtime = 1 * RARE_THRESHOLD + 100; + // Make sure app is in NEVER bucket + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER, + REASON_MAIN_FORCED, mInjector.mElapsedRealtime); + mController.checkIdleStates(USER_ID); + assertBucket(STANDBY_BUCKET_NEVER); + + mInjector.mElapsedRealtime += 100; + + // Trigger a FOREGROUND_SERVICE_START and verify bucket + reportEvent(mController, FOREGROUND_SERVICE_START, mInjector.mElapsedRealtime, PACKAGE_1); + mController.checkIdleStates(USER_ID); + assertBucket(STANDBY_BUCKET_ACTIVE); + + // Verify it's still in ACTIVE close to end of timeout + mInjector.mElapsedRealtime += mController.mInitialForegroundServiceStartTimeoutMillis - 100; + mController.checkIdleStates(USER_ID); + assertBucket(STANDBY_BUCKET_ACTIVE); + + // Verify bucket moves to RARE after timeout + mInjector.mElapsedRealtime += 200; + mController.checkIdleStates(USER_ID); + assertBucket(STANDBY_BUCKET_RARE); + + // Trigger a FOREGROUND_SERVICE_START again + reportEvent(mController, FOREGROUND_SERVICE_START, mInjector.mElapsedRealtime, PACKAGE_1); + mController.checkIdleStates(USER_ID); + // Bucket should not be immediately elevated on subsequent service starts + assertBucket(STANDBY_BUCKET_RARE); + } + + @Test public void testPredictionNotOverridden() throws Exception { reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1); assertBucket(STANDBY_BUCKET_ACTIVE); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index cbca087872d9..8d56bc400d14 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -62,6 +62,7 @@ import static org.mockito.Matchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; @@ -85,6 +86,7 @@ import android.app.NotificationChannelGroup; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Person; +import android.app.RemoteInput; import android.app.admin.DevicePolicyManagerInternal; import android.app.usage.UsageStatsManagerInternal; import android.companion.ICompanionDeviceManager; @@ -96,6 +98,7 @@ import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; +import android.content.pm.UserInfo; import android.content.res.Resources; import android.graphics.Color; import android.graphics.drawable.Icon; @@ -327,6 +330,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { LocalServices.removeServiceForTest(WindowManagerInternal.class); LocalServices.addService(WindowManagerInternal.class, mWindowManagerInternal); + doNothing().when(mContext).sendBroadcastAsUser(any(), any(), any()); + mService = new TestableNotificationManagerService(mContext); // Use this testable looper. @@ -375,20 +380,16 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { when(mAssistants.isAdjustmentAllowed(anyString())).thenReturn(true); - try { - mService.init(mTestableLooper.getLooper(), - mPackageManager, mPackageManagerClient, mockLightsManager, - mListeners, mAssistants, mConditionProviders, - mCompanionMgr, mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager, - mGroupHelper, mAm, mAppUsageStats, - mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal, - mAppOpsManager, mUm); - mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY); - } catch (SecurityException e) { - if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { - throw e; - } - } + + mService.init(mTestableLooper.getLooper(), + mPackageManager, mPackageManagerClient, mockLightsManager, + mListeners, mAssistants, mConditionProviders, + mCompanionMgr, mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager, + mGroupHelper, mAm, mAppUsageStats, + mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal, + mAppOpsManager, mUm); + mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY); + mService.setAudioManager(mAudioManager); // Tests call directly into the Binder. @@ -2080,14 +2081,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { public void testSetListenerAccessForUser() throws Exception { UserHandle user = UserHandle.of(10); ComponentName c = ComponentName.unflattenFromString("package/Component"); - try { - mBinderService.setNotificationListenerAccessGrantedForUser( - c, user.getIdentifier(), true); - } catch (SecurityException e) { - if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { - throw e; - } - } + mBinderService.setNotificationListenerAccessGrantedForUser(c, user.getIdentifier(), true); + verify(mContext, times(1)).sendBroadcastAsUser(any(), eq(user), any()); verify(mListeners, times(1)).setPackageOrComponentEnabled( @@ -2101,15 +2096,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test public void testSetAssistantAccessForUser() throws Exception { UserHandle user = UserHandle.of(10); + List<UserInfo> uis = new ArrayList<>(); + UserInfo ui = new UserInfo(); + ui.id = 10; + uis.add(ui); ComponentName c = ComponentName.unflattenFromString("package/Component"); - try { - mBinderService.setNotificationAssistantAccessGrantedForUser( - c, user.getIdentifier(), true); - } catch (SecurityException e) { - if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { - throw e; - } - } + when(mUm.getEnabledProfiles(10)).thenReturn(uis); + + mBinderService.setNotificationAssistantAccessGrantedForUser(c, user.getIdentifier(), true); verify(mContext, times(1)).sendBroadcastAsUser(any(), eq(user), any()); verify(mAssistants, times(1)).setPackageOrComponentEnabled( @@ -2150,14 +2144,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { public void testSetDndAccessForUser() throws Exception { UserHandle user = UserHandle.of(10); ComponentName c = ComponentName.unflattenFromString("package/Component"); - try { - mBinderService.setNotificationPolicyAccessGrantedForUser( - c.getPackageName(), user.getIdentifier(), true); - } catch (SecurityException e) { - if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { - throw e; - } - } + mBinderService.setNotificationPolicyAccessGrantedForUser( + c.getPackageName(), user.getIdentifier(), true); verify(mContext, times(1)).sendBroadcastAsUser(any(), eq(user), any()); verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( @@ -2171,13 +2159,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test public void testSetListenerAccess() throws Exception { ComponentName c = ComponentName.unflattenFromString("package/Component"); - try { - mBinderService.setNotificationListenerAccessGranted(c, true); - } catch (SecurityException e) { - if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { - throw e; - } - } + mBinderService.setNotificationListenerAccessGranted(c, true); verify(mListeners, times(1)).setPackageOrComponentEnabled( c.flattenToString(), 0, true, true); @@ -2189,19 +2171,45 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test public void testSetAssistantAccess() throws Exception { + List<UserInfo> uis = new ArrayList<>(); + UserInfo ui = new UserInfo(); + ui.id = 0; + uis.add(ui); + when(mUm.getEnabledProfiles(ui.id)).thenReturn(uis); ComponentName c = ComponentName.unflattenFromString("package/Component"); - try { - mBinderService.setNotificationAssistantAccessGranted(c, true); - } catch (SecurityException e) { - if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { - throw e; - } - } + + mBinderService.setNotificationAssistantAccessGranted(c, true); + + verify(mAssistants, times(1)).setPackageOrComponentEnabled( + c.flattenToString(), 0, true, true); + verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( + c.flattenToString(), 0, false, true); + verify(mListeners, never()).setPackageOrComponentEnabled( + any(), anyInt(), anyBoolean(), anyBoolean()); + } + + @Test + public void testSetAssistantAccess_multiProfile() throws Exception { + List<UserInfo> uis = new ArrayList<>(); + UserInfo ui = new UserInfo(); + ui.id = 0; + uis.add(ui); + UserInfo ui10 = new UserInfo(); + ui10.id = 10; + uis.add(ui10); + when(mUm.getEnabledProfiles(ui.id)).thenReturn(uis); + ComponentName c = ComponentName.unflattenFromString("package/Component"); + + mBinderService.setNotificationAssistantAccessGranted(c, true); verify(mAssistants, times(1)).setPackageOrComponentEnabled( c.flattenToString(), 0, true, true); + verify(mAssistants, times(1)).setPackageOrComponentEnabled( + c.flattenToString(), 10, true, true); verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( c.flattenToString(), 0, false, true); + verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( + c.flattenToString(), 10, false, true); verify(mListeners, never()).setPackageOrComponentEnabled( any(), anyInt(), anyBoolean(), anyBoolean()); } @@ -2212,14 +2220,13 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { ComponentName c = ComponentName.unflattenFromString("package/Component"); componentList.add(c); when(mAssistants.getAllowedComponents(anyInt())).thenReturn(componentList); + List<UserInfo> uis = new ArrayList<>(); + UserInfo ui = new UserInfo(); + ui.id = 0; + uis.add(ui); + when(mUm.getEnabledProfiles(ui.id)).thenReturn(uis); - try { - mBinderService.setNotificationAssistantAccessGranted(null, true); - } catch (SecurityException e) { - if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { - throw e; - } - } + mBinderService.setNotificationAssistantAccessGranted(null, true); verify(mAssistants, times(1)).setPackageOrComponentEnabled( c.flattenToString(), 0, true, false); @@ -2231,25 +2238,59 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test public void testSetAssistantAccessForUser_nullWithAllowedAssistant() throws Exception { - UserHandle user = UserHandle.of(10); + List<UserInfo> uis = new ArrayList<>(); + UserInfo ui = new UserInfo(); + ui.id = 10; + uis.add(ui); + UserHandle user = ui.getUserHandle(); ArrayList<ComponentName> componentList = new ArrayList<>(); ComponentName c = ComponentName.unflattenFromString("package/Component"); componentList.add(c); when(mAssistants.getAllowedComponents(anyInt())).thenReturn(componentList); + when(mUm.getEnabledProfiles(10)).thenReturn(uis); - try { - mBinderService.setNotificationAssistantAccessGrantedForUser( + mBinderService.setNotificationAssistantAccessGrantedForUser( + null, user.getIdentifier(), true); + + verify(mAssistants, times(1)).setPackageOrComponentEnabled( + c.flattenToString(), user.getIdentifier(), true, false); + verify(mAssistants).setUserSet(10, true); + verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( + c.flattenToString(), user.getIdentifier(), false, false); + verify(mListeners, never()).setPackageOrComponentEnabled( + any(), anyInt(), anyBoolean(), anyBoolean()); + } + + @Test + public void testSetAssistantAccessForUser_workProfile_nullWithAllowedAssistant() + throws Exception { + List<UserInfo> uis = new ArrayList<>(); + UserInfo ui = new UserInfo(); + ui.id = 0; + uis.add(ui); + UserInfo ui10 = new UserInfo(); + ui10.id = 10; + uis.add(ui10); + UserHandle user = ui.getUserHandle(); + ArrayList<ComponentName> componentList = new ArrayList<>(); + ComponentName c = ComponentName.unflattenFromString("package/Component"); + componentList.add(c); + when(mAssistants.getAllowedComponents(anyInt())).thenReturn(componentList); + when(mUm.getEnabledProfiles(ui.id)).thenReturn(uis); + + mBinderService.setNotificationAssistantAccessGrantedForUser( null, user.getIdentifier(), true); - } catch (SecurityException e) { - if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { - throw e; - } - } verify(mAssistants, times(1)).setPackageOrComponentEnabled( c.flattenToString(), user.getIdentifier(), true, false); + verify(mAssistants, times(1)).setPackageOrComponentEnabled( + c.flattenToString(), ui10.id, true, false); + verify(mAssistants).setUserSet(0, true); + verify(mAssistants).setUserSet(10, true); verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( c.flattenToString(), user.getIdentifier(), false, false); + verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( + c.flattenToString(), ui10.id, false, false); verify(mListeners, never()).setPackageOrComponentEnabled( any(), anyInt(), anyBoolean(), anyBoolean()); } @@ -2257,13 +2298,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test public void testSetDndAccess() throws Exception { ComponentName c = ComponentName.unflattenFromString("package/Component"); - try { - mBinderService.setNotificationPolicyAccessGranted(c.getPackageName(), true); - } catch (SecurityException e) { - if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { - throw e; - } - } + + mBinderService.setNotificationPolicyAccessGranted(c.getPackageName(), true); verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( c.getPackageName(), 0, true, true); @@ -2291,6 +2327,12 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { public void testSetAssistantAccess_doesNothingOnLowRam() throws Exception { when(mActivityManager.isLowRamDevice()).thenReturn(true); ComponentName c = ComponentName.unflattenFromString("package/Component"); + List<UserInfo> uis = new ArrayList<>(); + UserInfo ui = new UserInfo(); + ui.id = 0; + uis.add(ui); + when(mUm.getEnabledProfiles(ui.id)).thenReturn(uis); + mBinderService.setNotificationAssistantAccessGranted(c, true); verify(mListeners, never()).setPackageOrComponentEnabled( @@ -2320,13 +2362,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(true); when(mActivityManager.isLowRamDevice()).thenReturn(true); ComponentName c = ComponentName.unflattenFromString("package/Component"); - try { - mBinderService.setNotificationListenerAccessGranted(c, true); - } catch (SecurityException e) { - if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { - throw e; - } - } + + mBinderService.setNotificationListenerAccessGranted(c, true); verify(mListeners, times(1)).setPackageOrComponentEnabled( c.flattenToString(), 0, true, true); @@ -2341,13 +2378,13 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(true); when(mActivityManager.isLowRamDevice()).thenReturn(true); ComponentName c = ComponentName.unflattenFromString("package/Component"); - try { - mBinderService.setNotificationAssistantAccessGranted(c, true); - } catch (SecurityException e) { - if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { - throw e; - } - } + List<UserInfo> uis = new ArrayList<>(); + UserInfo ui = new UserInfo(); + ui.id = 0; + uis.add(ui); + when(mUm.getEnabledProfiles(ui.id)).thenReturn(uis); + + mBinderService.setNotificationAssistantAccessGranted(c, true); verify(mListeners, never()).setPackageOrComponentEnabled( anyString(), anyInt(), anyBoolean(), anyBoolean()); @@ -2362,13 +2399,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(true); when(mActivityManager.isLowRamDevice()).thenReturn(true); ComponentName c = ComponentName.unflattenFromString("package/Component"); - try { - mBinderService.setNotificationPolicyAccessGranted(c.getPackageName(), true); - } catch (SecurityException e) { - if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { - throw e; - } - } + + mBinderService.setNotificationPolicyAccessGranted(c.getPackageName(), true); verify(mListeners, never()).setPackageOrComponentEnabled( anyString(), anyInt(), anyBoolean(), anyBoolean()); @@ -4492,6 +4524,13 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { Person person = new Person.Builder() .setName("bubblebot") .build(); + // It needs remote input to be bubble-able + RemoteInput remoteInput = new RemoteInput.Builder("reply_key").setLabel("reply").build(); + PendingIntent inputIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0); + Icon icon = Icon.createWithResource(mContext, android.R.drawable.sym_def_app_icon); + Notification.Action replyAction = new Notification.Action.Builder(icon, "Reply", + inputIntent).addRemoteInput(remoteInput) + .build(); // Make it messaging style Notification.Builder nb = new Notification.Builder(mContext, mTestNotificationChannel.getId()) @@ -4504,6 +4543,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { .addMessage("Is it me you're looking for?", SystemClock.currentThreadTimeMillis(), person) ) + .setActions(replyAction) .setSmallIcon(android.R.drawable.sym_def_app_icon); StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, null, mUid, 0, diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index 45d52195c5fd..5803385e13e2 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -79,7 +79,6 @@ import android.view.Gravity; import androidx.test.filters.SmallTest; import com.android.server.wm.LaunchParamsController.LaunchParamsModifier; -import com.android.server.wm.TaskRecord.TaskRecordFactory; import org.junit.Before; import org.junit.Test; @@ -330,21 +329,14 @@ public class ActivityStarterTests extends ActivityTestsBase { any(), any(), any(), anyInt(), anyInt(), anyInt(), any(), anyBoolean(), anyBoolean(), any(), any(), any()); - // instrument the stack and task used. - final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().createStack( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - final TaskRecord task = new TaskBuilder(mSupervisor) - .setCreateStack(false) - .build(); - - // use factory that only returns spy task. - final TaskRecordFactory factory = mock(TaskRecordFactory.class); - TaskRecord.setTaskRecordFactory(factory); - - // return task when created. - doReturn(task).when(factory).create(any(), anyInt(), any(), any(), any(), any()); + // Use factory that only returns spy task. + mockTaskRecordFactory(); if (mockGetLaunchStack) { + // Instrument the stack and task used. + final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().createStack( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + // Direct starter to use spy stack. doReturn(stack).when(mRootActivityContainer) .getLaunchStack(any(), any(), any(), anyBoolean()); @@ -707,6 +699,36 @@ public class ActivityStarterTests extends ActivityTestsBase { } /** + * This test ensures that {@link ActivityStarter#setTargetStackAndMoveToFrontIfNeeded} will + * move the existing task to front if the current focused stack doesn't have running task. + */ + @Test + public void testBringTaskToFrontWhenFocusedStackIsFinising() { + // Put 2 tasks in the same stack (simulate the behavior of home stack). + final ActivityRecord activity = new ActivityBuilder(mService) + .setCreateTask(true).build(); + new ActivityBuilder(mService) + .setStack(activity.getActivityStack()) + .setCreateTask(true).build(); + + // Create a top finishing activity. + final ActivityRecord finishingTopActivity = new ActivityBuilder(mService) + .setCreateTask(true).build(); + finishingTopActivity.getActivityStack().moveToFront("finishingTopActivity"); + + assertEquals(finishingTopActivity, mRootActivityContainer.topRunningActivity()); + finishingTopActivity.finishing = true; + + // Launch the bottom task of the target stack. + prepareStarter(FLAG_ACTIVITY_NEW_TASK, false /* mockGetLaunchStack */) + .setReason("testBringTaskToFrontWhenTopStackIsFinising") + .setIntent(activity.intent) + .execute(); + // The hierarchies of the activity should move to front. + assertEquals(activity, mRootActivityContainer.topRunningActivity()); + } + + /** * This test ensures that when starting an existing single task activity on secondary display * which is not the top focused display, it should deliver new intent to the activity and not * create a new stack. diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java index afd4ec160aad..09b511a20419 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java @@ -41,6 +41,7 @@ import static com.android.server.wm.ActivityStackSupervisor.ON_TOP; import android.app.ActivityManagerInternal; import android.app.ActivityOptions; +import android.app.AppOpsManager; import android.app.IApplicationThread; import android.content.ComponentName; import android.content.Context; @@ -71,6 +72,7 @@ import com.android.server.am.PendingIntentController; import com.android.server.appop.AppOpsService; import com.android.server.firewall.IntentFirewall; import com.android.server.uri.UriGrantsManagerInternal; +import com.android.server.wm.TaskRecord.TaskRecordFactory; import com.android.server.wm.utils.MockTracker; import org.junit.After; @@ -159,6 +161,19 @@ class ActivityTestsBase { } /** + * Delegates task creation to {@link #TaskBuilder} to avoid the dependency of window hierarchy + * when starting activity in unit tests. + */ + void mockTaskRecordFactory() { + final TaskRecord task = new TaskBuilder(mSupervisor).setCreateStack(false).build(); + final TaskRecordFactory factory = mock(TaskRecordFactory.class); + TaskRecord.setTaskRecordFactory(factory); + doReturn(task).when(factory).create(any() /* service */, anyInt() /* taskId */, + any() /* info */, any() /* intent */, any() /* voiceSession */, + any() /* voiceInteractor */); + } + + /** * Builder for creating new activities. */ protected static class ActivityBuilder { @@ -413,12 +428,18 @@ class ActivityTestsBase { ActivityStackSupervisor mTestStackSupervisor; ActivityDisplay mDefaultDisplay; + AppOpsService mAppOpsService; TestActivityTaskManagerService(Context context) { super(context); spyOn(this); mUgmInternal = mock(UriGrantsManagerInternal.class); + mAppOpsService = mock(AppOpsService.class); + + // Make sure permission checks aren't overridden. + doReturn(AppOpsManager.MODE_DEFAULT) + .when(mAppOpsService).noteOperation(anyInt(), anyInt(), anyString()); mSupportsMultiWindow = true; mSupportsMultiDisplay = true; @@ -482,6 +503,11 @@ class ActivityTestsBase { } @Override + AppOpsService getAppOpsService() { + return mAppOpsService; + } + + @Override void updateCpuStats() { } diff --git a/services/tests/wmtests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java b/services/tests/wmtests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java index 380f7c67402e..5cd649fc968f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java @@ -27,6 +27,7 @@ import static org.junit.Assert.assertTrue; import android.platform.test.annotations.Presubmit; +import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import org.junit.Before; @@ -80,6 +81,7 @@ public class AnimatingAppWindowTokenRegistryTest extends WindowTestsBase { } @Test + @FlakyTest(bugId = 131005232) public void testContainerRemoved() { final AppWindowToken window1 = createAppWindowToken(mDisplayContent, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); diff --git a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java index 83c0af92a6ee..777e4f4915b8 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java @@ -38,6 +38,7 @@ import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationDefinition; import android.view.RemoteAnimationTarget; +import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import org.junit.Test; @@ -96,6 +97,7 @@ public class AppChangeTransitionTests extends WindowTestsBase { } @Test + @FlakyTest(bugId = 131005232) public void testModeChangeRemoteAnimatorNoSnapshot() { // setup currently defaults to no snapshot. setUpOnDisplay(mDisplayContent); @@ -113,6 +115,7 @@ public class AppChangeTransitionTests extends WindowTestsBase { } @Test + @FlakyTest(bugId = 131005232) public void testCancelPendingChangeOnRemove() { // setup currently defaults to no snapshot. setUpOnDisplay(mDisplayContent); @@ -132,6 +135,7 @@ public class AppChangeTransitionTests extends WindowTestsBase { } @Test + @FlakyTest(bugId = 131005232) public void testNoChangeWhenMoveDisplay() { mDisplayContent.setWindowingMode(WINDOWING_MODE_FULLSCREEN); final DisplayContent dc1 = createNewDisplay(Display.STATE_ON); diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java index b1ffbbd4ffba..20379a2427be 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java @@ -31,6 +31,7 @@ import static org.junit.Assert.assertTrue; import android.platform.test.annotations.Presubmit; import android.view.WindowManager; +import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import org.junit.Before; @@ -52,6 +53,7 @@ public class AppTransitionControllerTest extends WindowTestsBase { } @Test + @FlakyTest(bugId = 131005232) public void testTranslucentOpen() { synchronized (mWm.mGlobalLock) { final AppWindowToken behind = createAppWindowToken(mDisplayContent, @@ -69,6 +71,7 @@ public class AppTransitionControllerTest extends WindowTestsBase { } @Test + @FlakyTest(bugId = 131005232) public void testTranslucentClose() { synchronized (mWm.mGlobalLock) { final AppWindowToken behind = createAppWindowToken(mDisplayContent, @@ -84,6 +87,7 @@ public class AppTransitionControllerTest extends WindowTestsBase { } @Test + @FlakyTest(bugId = 131005232) public void testChangeIsNotOverwritten() { synchronized (mWm.mGlobalLock) { final AppWindowToken behind = createAppWindowToken(mDisplayContent, @@ -101,6 +105,7 @@ public class AppTransitionControllerTest extends WindowTestsBase { } @Test + @FlakyTest(bugId = 131005232) public void testTransitWithinTask() { synchronized (mWm.mGlobalLock) { final AppWindowToken opening = createAppWindowToken(mDisplayContent, diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java index 6e09167f7cba..5fc7f44588e1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java @@ -39,6 +39,7 @@ import android.view.Display; import org.junit.Before; import org.junit.Test; +import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; /** @@ -64,6 +65,7 @@ public class AppTransitionTests extends WindowTestsBase { } @Test + @FlakyTest(bugId = 131005232) public void testKeyguardOverride() { mWm.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false /* alwaysKeepCurrent */); mWm.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */); @@ -71,6 +73,7 @@ public class AppTransitionTests extends WindowTestsBase { } @Test + @FlakyTest(bugId = 131005232) public void testKeyguardKeep() { mWm.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */); mWm.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false /* alwaysKeepCurrent */); @@ -78,6 +81,7 @@ public class AppTransitionTests extends WindowTestsBase { } @Test + @FlakyTest(bugId = 131005232) public void testForceOverride() { mWm.prepareAppTransition(TRANSIT_KEYGUARD_UNOCCLUDE, false /* alwaysKeepCurrent */); mDc.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, @@ -93,6 +97,7 @@ public class AppTransitionTests extends WindowTestsBase { } @Test + @FlakyTest(bugId = 131005232) public void testKeepKeyguard_withCrashing() { mWm.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */); mWm.prepareAppTransition(TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */); @@ -100,6 +105,7 @@ public class AppTransitionTests extends WindowTestsBase { } @Test + @FlakyTest(bugId = 131005232) public void testAppTransitionStateForMultiDisplay() { // Create 2 displays & presume both display the state is ON for ready to display & animate. final DisplayContent dc1 = createNewDisplay(Display.STATE_ON); @@ -168,6 +174,7 @@ public class AppTransitionTests extends WindowTestsBase { } @Test + @FlakyTest(bugId = 131005232) public void testLoadAnimationSafely() { DisplayContent dc = createNewDisplay(Display.STATE_ON); assertNull(dc.mAppTransition.loadAnimationSafely( diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowThumbnailTest.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowThumbnailTest.java index fa0c384ca89d..b8f8e21d64ac 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppWindowThumbnailTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowThumbnailTest.java @@ -28,6 +28,7 @@ import android.platform.test.annotations.Presubmit; import android.view.Surface; import android.view.SurfaceControl; +import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import org.junit.Test; @@ -53,6 +54,7 @@ public class AppWindowThumbnailTest extends WindowTestsBase { } @Test + @FlakyTest(bugId = 131005232) public void testDestroy_nullsSurface() { final AppWindowThumbnail t = buildThumbnail(); assertNotNull(t.getSurfaceControl()); diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java index 623559e41256..70d9b5fedbcb 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java @@ -30,6 +30,7 @@ import static org.mockito.ArgumentMatchers.intThat; import android.platform.test.annotations.Presubmit; import android.view.SurfaceControl; +import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import com.android.server.wm.WindowTestUtils.TestAppWindowToken; @@ -65,6 +66,7 @@ public class AppWindowTokenAnimationTests extends WindowTestsBase { } @Test + @FlakyTest(bugId = 131005232) public void clipAfterAnim_boundsLayerIsCreated() { mToken.mNeedsAnimationBoundsLayer = true; @@ -86,6 +88,7 @@ public class AppWindowTokenAnimationTests extends WindowTestsBase { } @Test + @FlakyTest(bugId = 131005232) public void clipAfterAnim_boundsLayerIsDestroyed() { mToken.mNeedsAnimationBoundsLayer = true; mToken.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */); @@ -116,6 +119,7 @@ public class AppWindowTokenAnimationTests extends WindowTestsBase { } @Test + @FlakyTest(bugId = 131005232) public void clipNoneAnim_boundsLayerIsNotCreated() { mToken.mNeedsAnimationBoundsLayer = false; diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java index 064b553bb314..fe4541183038 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java @@ -150,6 +150,7 @@ public class AppWindowTokenTests extends WindowTestsBase { } @Test + @FlakyTest(bugId = 131005232) public void testLandscapeSeascapeRotationByApp() { // Some plumbing to get the service ready for rotation updates. mWm.mDisplayReady = true; @@ -504,14 +505,14 @@ public class AppWindowTokenTests extends WindowTestsBase { private void assertHasStartingWindow(AppWindowToken atoken) { assertNotNull(atoken.startingSurface); - assertNotNull(atoken.startingData); + assertNotNull(atoken.mStartingData); assertNotNull(atoken.startingWindow); } private void assertNoStartingWindow(AppWindowToken atoken) { assertNull(atoken.startingSurface); assertNull(atoken.startingWindow); - assertNull(atoken.startingData); + assertNull(atoken.mStartingData); atoken.forAllWindows(windowState -> { assertFalse(windowState.getBaseType() == TYPE_APPLICATION_STARTING); }, true); diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java index 196cc211692c..0110e94eb1cd 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java @@ -39,6 +39,7 @@ import android.view.SurfaceControl; import android.view.SurfaceSession; import android.view.View; +import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import com.android.server.LocalServices; @@ -146,16 +147,19 @@ public class DragDropControllerTests extends WindowTestsBase { } @Test + @FlakyTest(bugId = 131005232) public void testDragFlow() { dragFlow(0, ClipData.newPlainText("label", "Test"), 0, 0); } @Test + @FlakyTest(bugId = 131005232) public void testPerformDrag_NullDataWithGrantUri() { dragFlow(View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ, null, 0, 0); } @Test + @FlakyTest(bugId = 131005232) public void testPerformDrag_NullDataToOtherUser() { final WindowState otherUsersWindow = createDropTargetWindow("Other user's window", 1 * UserHandle.PER_USER_RANGE); diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java index a0546d7623e7..86ee75ebf3df 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java @@ -31,6 +31,7 @@ import android.platform.test.annotations.Presubmit; import android.view.InsetsSource; import android.view.InsetsState; +import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import org.junit.Before; diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java index 2d906d1683d5..9fce78b292f5 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java @@ -58,6 +58,7 @@ public class InsetsStateControllerTest extends WindowTestsBase { } @Test + @FlakyTest(bugId = 131005232) public void testStripForDispatch_notOwn() { final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); final WindowState app = createWindow(null, TYPE_APPLICATION, "parentWindow"); diff --git a/services/tests/wmtests/src/com/android/server/wm/PendingRemoteAnimationRegistryTest.java b/services/tests/wmtests/src/com/android/server/wm/PendingRemoteAnimationRegistryTest.java index b769fcecc469..103c3ab7e541 100644 --- a/services/tests/wmtests/src/com/android/server/wm/PendingRemoteAnimationRegistryTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/PendingRemoteAnimationRegistryTest.java @@ -24,6 +24,7 @@ import android.app.ActivityOptions; import android.platform.test.annotations.Presubmit; import android.view.RemoteAnimationAdapter; +import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import com.android.server.testutils.OffsettableClock; @@ -73,6 +74,7 @@ public class PendingRemoteAnimationRegistryTest extends ActivityTestsBase { } @Test + @FlakyTest(bugId = 131005232) public void testTimeout() { mRegistry.addPendingAnimation("com.android.test", mAdapter); mClock.fastForward(5000); diff --git a/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java b/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java index 4673992f64ef..027f90333561 100644 --- a/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java @@ -110,6 +110,7 @@ public class PersisterQueueTests { } @Test + @FlakyTest(bugId = 131005232) public void testProcessOneItem_Flush() throws Exception { mFactory.setExpectedProcessedItemNumber(1); mListener.setExpectedOnPreProcessItemCallbackTimes(1); diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java index 0c2ce614b772..385748c21ff8 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java @@ -21,6 +21,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; +import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE; import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; @@ -166,6 +167,25 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { verify(mAnimationCallbacks).onAnimationFinished(REORDER_KEEP_IN_PLACE, true, false); } + @Test + public void testShouldAnimateWhenNoCancelWithDeferredScreenshot() { + mWm.setRecentsAnimationController(mController); + final AppWindowToken appWindow = createAppWindowToken(mDisplayContent, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); + final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, appWindow, "win1"); + appWindow.addWindow(win1); + assertEquals(appWindow.getTask().getTopVisibleAppToken(), appWindow); + assertEquals(appWindow.findMainWindow(), win1); + + mController.addAnimation(appWindow.getTask(), false /* isRecentTaskInvisible */); + assertTrue(mController.isAnimatingTask(appWindow.getTask())); + + // Assume appWindow transition should animate when no + // IRecentsAnimationController#setCancelWithDeferredScreenshot called. + assertFalse(mController.shouldCancelWithDeferredScreenshot()); + assertTrue(appWindow.shouldAnimate(TRANSIT_ACTIVITY_CLOSE)); + } + private static void verifyNoMoreInteractionsExceptAsBinder(IInterface binder) { verify(binder, atLeast(0)).asBinder(); verifyNoMoreInteractions(binder); diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java index 6d27e6d0852c..b2084f88bc43 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java @@ -25,13 +25,11 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMAR import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE; import static android.view.Display.DEFAULT_DISPLAY; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.server.wm.ActivityDisplay.POSITION_TOP; @@ -56,7 +54,6 @@ import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.ResolveInfo; -import android.content.res.Resources; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; import android.util.Pair; @@ -73,10 +70,10 @@ import java.util.Arrays; import java.util.List; /** - * Tests for the {@link ActivityStackSupervisor} class. + * Tests for the {@link RootActivityContainer} class. * * Build/Install/Run: - * atest WmTests:ActivityStackSupervisorTests + * atest WmTests:RootActivityContainerTests */ @MediumTest @Presubmit @@ -401,17 +398,15 @@ public class RootActivityContainerTests extends ActivityTestsBase { */ @Test public void testStartHomeOnAllDisplays() { + mockResolveHomeActivity(); + // Create secondary displays. final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay()); mRootActivityContainer.addChild(secondDisplay, POSITION_TOP); doReturn(true).when(secondDisplay).supportsSystemDecorations(); // Create mock tasks and other necessary mocks. - TaskBuilder taskBuilder = new TaskBuilder(mService.mStackSupervisor).setCreateStack(false); - final TaskRecord.TaskRecordFactory factory = mock(TaskRecord.TaskRecordFactory.class); - TaskRecord.setTaskRecordFactory(factory); - doAnswer(i -> taskBuilder.build()).when(factory) - .create(any(), anyInt(), any(), any(), any(), any()); + mockTaskRecordFactory(); doReturn(true).when(mRootActivityContainer) .ensureVisibilityAndConfig(any(), anyInt(), anyBoolean(), anyBoolean()); doReturn(true).when(mRootActivityContainer).canStartHomeOnDisplay( @@ -512,6 +507,26 @@ public class RootActivityContainerTests extends ActivityTestsBase { } /** + * Tests that when starting {@link #ResolverActivity} for home, it should use the standard + * activity type (in a new stack) so the order of back stack won't be broken. + */ + @Test + public void testStartResolverActivityForHome() { + final ActivityInfo info = new ActivityInfo(); + info.applicationInfo = new ApplicationInfo(); + info.applicationInfo.packageName = "android"; + info.name = ResolverActivity.class.getName(); + doReturn(info).when(mRootActivityContainer).resolveHomeActivity(anyInt(), any()); + mockTaskRecordFactory(); + + mRootActivityContainer.startHomeOnDisplay(0 /* userId */, "test", DEFAULT_DISPLAY); + final ActivityRecord resolverActivity = mRootActivityContainer.topRunningActivity(); + + assertEquals(info, resolverActivity.info); + assertEquals(ACTIVITY_TYPE_STANDARD, resolverActivity.getActivityStack().getActivityType()); + } + + /** * Tests that secondary home should be selected if default home not set. */ @Test @@ -539,83 +554,12 @@ public class RootActivityContainerTests extends ActivityTestsBase { } /** - * Tests that the default secondary home activity is always picked when it is in forced by - * config_useSystemProvidedLauncherForSecondary. - */ - @Test - public void testResolveSecondaryHomeActivityForced() { - Resources resources = mContext.getResources(); - spyOn(resources); - final String defaultSecondaryHome = - "com.android.test/com.android.test.TestDefaultSecondaryHome"; - final ComponentName secondaryComp = ComponentName.unflattenFromString(defaultSecondaryHome); - doReturn(defaultSecondaryHome).when(resources).getString( - com.android.internal.R.string.config_secondaryHomeComponent); - doReturn(true).when(resources).getBoolean( - com.android.internal.R.bool.config_useSystemProvidedLauncherForSecondary); - - final Intent secondaryHomeIntent = mService.getSecondaryHomeIntent(null); - assertEquals(secondaryComp, secondaryHomeIntent.getComponent()); - - final ActivityInfo aInfoSecondary = new ActivityInfo(); - aInfoSecondary.name = secondaryComp.getClassName(); - aInfoSecondary.applicationInfo = new ApplicationInfo(); - aInfoSecondary.applicationInfo.packageName = secondaryComp.getPackageName(); - doReturn(aInfoSecondary).when(mRootActivityContainer).resolveHomeActivity(anyInt(), - refEq(secondaryHomeIntent)); - - final Intent homeIntent = mService.getHomeIntent(); - final ActivityInfo aInfoDefault = new ActivityInfo(); - aInfoDefault.name = "fakeHomeActivity"; - aInfoDefault.applicationInfo = new ApplicationInfo(); - aInfoDefault.applicationInfo.packageName = "fakeHomePackage"; - doReturn(aInfoDefault).when(mRootActivityContainer).resolveHomeActivity(anyInt(), - refEq(homeIntent)); - - // Let resolveActivities call to validate both main launcher and second launcher so that - // resolveActivities call does not work as enabler for secondary. - final List<ResolveInfo> resolutions1 = new ArrayList<>(); - final ResolveInfo resolveInfo1 = new ResolveInfo(); - resolveInfo1.activityInfo = new ActivityInfo(); - resolveInfo1.activityInfo.name = aInfoDefault.name; - resolveInfo1.activityInfo.applicationInfo = aInfoDefault.applicationInfo; - resolutions1.add(resolveInfo1); - doReturn(resolutions1).when(mRootActivityContainer).resolveActivities(anyInt(), - refEq(homeIntent)); - final List<ResolveInfo> resolutions2 = new ArrayList<>(); - final ResolveInfo resolveInfo2 = new ResolveInfo(); - resolveInfo2.activityInfo = new ActivityInfo(); - resolveInfo2.activityInfo.name = aInfoSecondary.name; - resolveInfo2.activityInfo.applicationInfo = aInfoSecondary.applicationInfo; - resolutions2.add(resolveInfo2); - doReturn(resolutions2).when(mRootActivityContainer).resolveActivities(anyInt(), - refEq(secondaryHomeIntent)); - - doReturn(true).when(mRootActivityContainer).canStartHomeOnDisplay( - any(), anyInt(), anyBoolean()); - - final Pair<ActivityInfo, Intent> resolvedInfo = mRootActivityContainer - .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */); - - assertEquals(secondaryComp.getClassName(), resolvedInfo.first.name); - assertEquals(secondaryComp.getPackageName(), - resolvedInfo.first.applicationInfo.packageName); - assertEquals(aInfoSecondary.name, resolvedInfo.first.name); - } - - /** * Tests that secondary home should be selected if default home not support secondary displays * or there is no matched activity in the same package as selected default home. */ @Test public void testResolveSecondaryHomeActivityWhenDefaultHomeNotSupportMultiDisplay() { - final Intent defaultHomeIntent = mService.getHomeIntent(); - final ActivityInfo aInfoDefault = new ActivityInfo(); - aInfoDefault.name = "fakeHomeActivity"; - aInfoDefault.applicationInfo = new ApplicationInfo(); - aInfoDefault.applicationInfo.packageName = "fakeHomePackage"; - doReturn(aInfoDefault).when(mRootActivityContainer).resolveHomeActivity(anyInt(), - refEq(defaultHomeIntent)); + mockResolveHomeActivity(); final List<ResolveInfo> resolutions = new ArrayList<>(); doReturn(resolutions).when(mRootActivityContainer).resolveActivities(anyInt(), any()); @@ -642,13 +586,7 @@ public class RootActivityContainerTests extends ActivityTestsBase { */ @Test public void testResolveSecondaryHomeActivityWhenDefaultHomeSupportMultiDisplay() { - final Intent homeIntent = mService.getHomeIntent(); - final ActivityInfo aInfoDefault = new ActivityInfo(); - aInfoDefault.name = "fakeHomeActivity"; - aInfoDefault.applicationInfo = new ApplicationInfo(); - aInfoDefault.applicationInfo.packageName = "fakeHomePackage"; - doReturn(aInfoDefault).when(mRootActivityContainer).resolveHomeActivity(anyInt(), - refEq(homeIntent)); + final ActivityInfo aInfoDefault = mockResolveHomeActivity(); final List<ResolveInfo> resolutions = new ArrayList<>(); final ResolveInfo infoFake1 = new ResolveInfo(); @@ -679,13 +617,7 @@ public class RootActivityContainerTests extends ActivityTestsBase { */ @Test public void testResolveSecondaryHomeActivityWhenOtherActivitySupportMultiDisplay() { - final Intent homeIntent = mService.getHomeIntent(); - final ActivityInfo aInfoDefault = new ActivityInfo(); - aInfoDefault.name = "fakeHomeActivity"; - aInfoDefault.applicationInfo = new ApplicationInfo(); - aInfoDefault.applicationInfo.packageName = "fakeHomePackage"; - doReturn(aInfoDefault).when(mRootActivityContainer).resolveHomeActivity(anyInt(), - refEq(homeIntent)); + mockResolveHomeActivity(); final List<ResolveInfo> resolutions = new ArrayList<>(); final ResolveInfo infoFake1 = new ResolveInfo(); @@ -713,4 +645,19 @@ public class RootActivityContainerTests extends ActivityTestsBase { resolvedInfo.first.applicationInfo.packageName); assertEquals(infoFake1.activityInfo.name, resolvedInfo.first.name); } + + /** + * Mock {@link RootActivityContainerTests#resolveHomeActivity} for returning consistent activity + * info for test cases (the original implementation will resolve from the real package manager). + */ + private ActivityInfo mockResolveHomeActivity() { + final Intent homeIntent = mService.getHomeIntent(); + final ActivityInfo aInfoDefault = new ActivityInfo(); + aInfoDefault.name = "fakeHomeActivity"; + aInfoDefault.applicationInfo = new ApplicationInfo(); + aInfoDefault.applicationInfo.packageName = "fakeHomePackage"; + doReturn(aInfoDefault).when(mRootActivityContainer).resolveHomeActivity(anyInt(), + refEq(homeIntent)); + return aInfoDefault; + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPersisterTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskPersisterTest.java index 9dfeadf878e0..9fc160229d45 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskPersisterTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskPersisterTest.java @@ -29,6 +29,8 @@ import android.os.UserManager; import android.platform.test.annotations.Presubmit; import android.util.SparseBooleanArray; +import androidx.test.filters.FlakyTest; + import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -72,6 +74,7 @@ public class TaskPersisterTest { } @Test + @FlakyTest(bugId = 131005232) public void testTaskIdsPersistence() { SparseBooleanArray taskIdsOnFile = new SparseBooleanArray(); for (int i = 0; i < 100; i++) { diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index 715353e5d980..3a8d3b74c08f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -476,4 +476,47 @@ public class WindowStateTests extends WindowTestsBase { root.prepareWindowToDisplayDuringRelayout(wasVisible /*wasVisible*/); verify(sPowerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString()); } + + @Test + public void testVisibilityChangeSwitchUser() { + final WindowState window = createWindow(null, TYPE_APPLICATION, "app"); + window.mHasSurface = true; + window.setShowToOwnerOnlyLocked(true); + + mWm.mCurrentUserId = 1; + window.switchUser(); + assertFalse(window.isVisible()); + assertFalse(window.isVisibleByPolicy()); + + mWm.mCurrentUserId = 0; + window.switchUser(); + assertTrue(window.isVisible()); + assertTrue(window.isVisibleByPolicy()); + } + + @Test + public void testGetTransformationMatrix() { + synchronized (mWm.mGlobalLock) { + final WindowState win0 = createWindow(null, TYPE_APPLICATION, "win0"); + win0.getFrameLw().offsetTo(1, 0); + + final DisplayContent dc = createNewDisplay(); + dc.reparentDisplayContent(win0, win0.getSurfaceControl()); + dc.updateLocation(win0, 2, 0); + + final float[] values = new float[9]; + final Matrix matrix = new Matrix(); + final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class); + final WindowState win1 = createWindow(null, TYPE_APPLICATION, dc, "win1"); + win1.mHasSurface = true; + win1.mSurfaceControl = mock(SurfaceControl.class); + win1.getFrameLw().offsetTo(3, 0); + win1.updateSurfacePosition(t); + win1.getTransformationMatrix(values, matrix); + + matrix.getValues(values); + assertEquals(6f, values[Matrix.MTRANS_X], 0f); + assertEquals(0f, values[Matrix.MTRANS_Y], 0f); + } + } } diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java index 4b33e165bfeb..77866279a751 100644 --- a/services/usage/java/com/android/server/usage/AppStandbyController.java +++ b/services/usage/java/com/android/server/usage/AppStandbyController.java @@ -27,6 +27,7 @@ import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_ACTIVE_TIMEOU import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_DOZE; import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_NON_DOZE; import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_START; +import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_FOREGROUND_SERVICE_START; import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_BACKGROUND; import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND; import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_NOTIFICATION_SEEN; @@ -238,6 +239,11 @@ public class AppStandbyController { long mUnexemptedSyncScheduledTimeoutMillis; /** Maximum time a system interaction should keep the buckets elevated. */ long mSystemInteractionTimeoutMillis; + /** + * Maximum time a foreground service start should keep the buckets elevated if the service + * start is the first usage of the app + */ + long mInitialForegroundServiceStartTimeoutMillis; /** The length of time phone must be charging before considered stable enough to run jobs */ long mStableChargingThresholdMillis; @@ -877,7 +883,8 @@ public class AppStandbyController { || event.mEventType == UsageEvents.Event.USER_INTERACTION || event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN || event.mEventType == UsageEvents.Event.SLICE_PINNED - || event.mEventType == UsageEvents.Event.SLICE_PINNED_PRIV)) { + || event.mEventType == UsageEvents.Event.SLICE_PINNED_PRIV + || event.mEventType == UsageEvents.Event.FOREGROUND_SERVICE_START)) { final AppUsageHistory appHistory = mAppIdleHistory.getAppUsageHistory( event.mPackage, userId, elapsedRealtime); @@ -898,6 +905,13 @@ public class AppStandbyController { STANDBY_BUCKET_ACTIVE, subReason, 0, elapsedRealtime + mSystemInteractionTimeoutMillis); nextCheckTime = mSystemInteractionTimeoutMillis; + } else if (event.mEventType == UsageEvents.Event.FOREGROUND_SERVICE_START) { + // Only elevate bucket if this is the first usage of the app + if (prevBucket != STANDBY_BUCKET_NEVER) return; + mAppIdleHistory.reportUsage(appHistory, event.mPackage, + STANDBY_BUCKET_ACTIVE, subReason, + 0, elapsedRealtime + mInitialForegroundServiceStartTimeoutMillis); + nextCheckTime = mInitialForegroundServiceStartTimeoutMillis; } else { mAppIdleHistory.reportUsage(appHistory, event.mPackage, STANDBY_BUCKET_ACTIVE, subReason, @@ -930,6 +944,8 @@ public class AppStandbyController { case UsageEvents.Event.NOTIFICATION_SEEN: return REASON_SUB_USAGE_NOTIFICATION_SEEN; case UsageEvents.Event.SLICE_PINNED: return REASON_SUB_USAGE_SLICE_PINNED; case UsageEvents.Event.SLICE_PINNED_PRIV: return REASON_SUB_USAGE_SLICE_PINNED_PRIV; + case UsageEvents.Event.FOREGROUND_SERVICE_START: + return REASON_SUB_USAGE_FOREGROUND_SERVICE_START; default: return 0; } } @@ -1509,6 +1525,9 @@ public class AppStandbyController { pw.print(" mSystemInteractionTimeoutMillis="); TimeUtils.formatDuration(mSystemInteractionTimeoutMillis, pw); pw.println(); + pw.print(" mInitialForegroundServiceStartTimeoutMillis="); + TimeUtils.formatDuration(mInitialForegroundServiceStartTimeoutMillis, pw); + pw.println(); pw.print(" mPredictionTimeoutMillis="); TimeUtils.formatDuration(mPredictionTimeoutMillis, pw); @@ -1848,6 +1867,8 @@ public class AppStandbyController { "unexempted_sync_scheduled_duration"; private static final String KEY_SYSTEM_INTERACTION_HOLD_DURATION = "system_interaction_duration"; + private static final String KEY_INITIAL_FOREGROUND_SERVICE_START_HOLD_DURATION = + "initial_foreground_service_start_duration"; private static final String KEY_STABLE_CHARGING_THRESHOLD = "stable_charging_threshold"; public static final long DEFAULT_STRONG_USAGE_TIMEOUT = 1 * ONE_HOUR; public static final long DEFAULT_NOTIFICATION_TIMEOUT = 12 * ONE_HOUR; @@ -1859,6 +1880,7 @@ public class AppStandbyController { public static final long DEFAULT_EXEMPTED_SYNC_START_TIMEOUT = 10 * ONE_MINUTE; public static final long DEFAULT_UNEXEMPTED_SYNC_SCHEDULED_TIMEOUT = 10 * ONE_MINUTE; public static final long DEFAULT_STABLE_CHARGING_THRESHOLD = 10 * ONE_MINUTE; + public static final long DEFAULT_INITIAL_FOREGROUND_SERVICE_START_TIMEOUT = 30 * ONE_MINUTE; private final KeyValueListParser mParser = new KeyValueListParser(','); @@ -1964,6 +1986,12 @@ public class AppStandbyController { mSystemInteractionTimeoutMillis = mParser.getDurationMillis( KEY_SYSTEM_INTERACTION_HOLD_DURATION, COMPRESS_TIME ? ONE_MINUTE : DEFAULT_SYSTEM_INTERACTION_TIMEOUT); + + mInitialForegroundServiceStartTimeoutMillis = mParser.getDurationMillis( + KEY_INITIAL_FOREGROUND_SERVICE_START_HOLD_DURATION, + COMPRESS_TIME ? ONE_MINUTE : + DEFAULT_INITIAL_FOREGROUND_SERVICE_START_TIMEOUT); + mStableChargingThresholdMillis = mParser.getDurationMillis( KEY_STABLE_CHARGING_THRESHOLD, COMPRESS_TIME ? ONE_MINUTE : DEFAULT_STABLE_CHARGING_THRESHOLD); diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl index a814c03ff9ad..5cd46ca936a7 100644 --- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl +++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl @@ -301,4 +301,9 @@ interface ITelecomService { void setTestAutoModeApp(String packageName); + /** + * @see TelecomServiceImpl#setTestDefaultDialer + */ + void setTestDefaultDialer(in String packageName); + } diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 638733430ede..69500d77fd6f 100755 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -3313,7 +3313,7 @@ public class CarrierConfigManager { }); sDefaults.putStringArray(KEY_EMERGENCY_NUMBER_PREFIX_STRING_ARRAY, new String[0]); sDefaults.putBoolean(KEY_USE_USIM_BOOL, false); - sDefaults.putBoolean(KEY_SHOW_WFC_LOCATION_PRIVACY_POLICY_BOOL, true); + sDefaults.putBoolean(KEY_SHOW_WFC_LOCATION_PRIVACY_POLICY_BOOL, false); sDefaults.putBoolean(KEY_AUTO_CANCEL_CS_REJECT_NOTIFICATION, false); sDefaults.putString(KEY_SMART_FORWARDING_CONFIG_COMPONENT_NAME_STRING, ""); sDefaults.putBoolean(KEY_ALWAYS_SHOW_PRIMARY_SIGNAL_BAR_IN_OPPORTUNISTIC_NETWORK_BOOLEAN, diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java index 373c5d27eec8..3ce28a46a029 100644 --- a/telephony/java/android/telephony/PhoneStateListener.java +++ b/telephony/java/android/telephony/PhoneStateListener.java @@ -38,6 +38,8 @@ import java.util.List; import java.util.Map; import java.util.concurrent.Executor; +import dalvik.system.VMRuntime; + /** * A listener class for monitoring changes in specific telephony states * on the device, including service state, signal strength, message @@ -400,8 +402,12 @@ public class PhoneStateListener { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) public PhoneStateListener(Integer subId) { this(subId, Looper.myLooper()); + if (subId != null && VMRuntime.getRuntime().getTargetSdkVersion() + >= Build.VERSION_CODES.Q) { + throw new IllegalArgumentException("PhoneStateListener with subId: " + + subId + " is not supported, use default constructor"); + } } - /** * Create a PhoneStateListener for the Phone using the specified subscription * and non-null Looper. @@ -410,6 +416,11 @@ public class PhoneStateListener { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) public PhoneStateListener(Integer subId, Looper looper) { this(subId, new HandlerExecutor(new Handler(looper))); + if (subId != null && VMRuntime.getRuntime().getTargetSdkVersion() + >= Build.VERSION_CODES.Q) { + throw new IllegalArgumentException("PhoneStateListener with subId: " + + subId + " is not supported, use default constructor"); + } } /** diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index ccf49c919db8..903e53394c03 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -108,6 +108,8 @@ import java.util.function.Consumer; import java.util.regex.Matcher; import java.util.regex.Pattern; +import dalvik.system.VMRuntime; + /** * Provides access to information about the telephony services on * the device. Applications can use the methods in this class to @@ -3321,7 +3323,7 @@ public class TelephonyManager { } /** - * Gets information about currently inserted UICCs and enabled eUICCs. + * Gets information about currently inserted UICCs and eUICCs. * <p> * Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). * <p> @@ -4863,18 +4865,22 @@ public class TelephonyManager { * Registers a listener object to receive notification of changes * in specified telephony states. * <p> - * To register a listener, pass a {@link PhoneStateListener} - * and specify at least one telephony state of interest in - * the events argument. - * - * At registration, and when a specified telephony state - * changes, the telephony manager invokes the appropriate - * callback method on the listener object and passes the - * current (updated) values. + * To register a listener, pass a {@link PhoneStateListener} and specify at least one telephony + * state of interest in the events argument. + * + * At registration, and when a specified telephony state changes, the telephony manager invokes + * the appropriate callback method on the listener object and passes the current (updated) + * values. * <p> - * To unregister a listener, pass the listener object and set the - * events argument to + * To un-register a listener, pass the listener object and set the events argument to * {@link PhoneStateListener#LISTEN_NONE LISTEN_NONE} (0). + * + * If this TelephonyManager object has been created with {@link #createForSubscriptionId}, + * applies to the given subId. Otherwise, applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. To listen events for multiple subIds, + * pass a separate listener object to each TelephonyManager object created with + * {@link #createForSubscriptionId}. + * * Note: if you call this method while in the middle of a binder transaction, you <b>must</b> * call {@link android.os.Binder#clearCallingIdentity()} before calling this method. A * {@link SecurityException} will be thrown otherwise. @@ -4889,17 +4895,26 @@ public class TelephonyManager { if (mContext == null) return; try { boolean notifyNow = (getITelephony() != null); - // If the listener has not explicitly set the subId (for example, created with the - // default constructor), replace the subId so it will listen to the account the - // telephony manager is created with. - if (listener.mSubId == null) { - listener.mSubId = mSubId; - } - ITelephonyRegistry registry = getTelephonyRegistry(); if (registry != null) { - registry.listenForSubscriber(listener.mSubId, getOpPackageName(), + int subId; + // subId from phonestatelistner is deprecated Q on forward, use the subId from + // TelephonyManager instance. + if (VMRuntime.getRuntime().getTargetSdkVersion() >= Build.VERSION_CODES.Q + || listener.mSubId == null) { + subId = mSubId; + } else { + subId = listener.mSubId; + } + + registry.listenForSubscriber(subId, getOpPackageName(), listener.callback, events, notifyNow); + // TODO: remove this once we remove PhoneStateListener constructor with subId. + if (events == PhoneStateListener.LISTEN_NONE) { + listener.mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + } else { + listener.mSubId = subId; + } } else { Rlog.w(TAG, "telephony registry not ready."); } diff --git a/telephony/java/android/telephony/UiccCardInfo.java b/telephony/java/android/telephony/UiccCardInfo.java index 2c98c4d40347..b438920362ff 100644 --- a/telephony/java/android/telephony/UiccCardInfo.java +++ b/telephony/java/android/telephony/UiccCardInfo.java @@ -101,7 +101,7 @@ public final class UiccCardInfo implements Parcelable { /** * Get the embedded ID (EID) of the eUICC. If the UiccCardInfo is not an eUICC - * (see {@link #isEuicc()}), returns null. + * (see {@link #isEuicc()}), or the EID is not available, returns null. * <p> * Note that this field may be omitted if the caller does not have the correct permissions * (see {@link TelephonyManager#getUiccCardsInfo()}). @@ -115,7 +115,7 @@ public final class UiccCardInfo implements Parcelable { } /** - * Get the ICCID of the UICC. + * Get the ICCID of the UICC. If the ICCID is not availble, returns null. * <p> * Note that this field may be omitted if the caller does not have the correct permissions * (see {@link TelephonyManager#getUiccCardsInfo()}). diff --git a/telephony/java/android/telephony/UiccSlotInfo.java b/telephony/java/android/telephony/UiccSlotInfo.java index 785d7aea2a6f..9a8d7cd35cb0 100644 --- a/telephony/java/android/telephony/UiccSlotInfo.java +++ b/telephony/java/android/telephony/UiccSlotInfo.java @@ -140,6 +140,10 @@ public class UiccSlotInfo implements Parcelable { return mIsEuicc; } + /** + * Returns the ICCID of a the UICC in the given slot, or the EID if it is an eUICC. Note that if + * the value is unavailble this will return null. + */ public String getCardId() { return mCardId; } diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 0169c26d393c..f226bb1fa588 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -1594,7 +1594,7 @@ interface ITelephony { int getCardIdForDefaultEuicc(int subId, String callingPackage); /** - * Gets information about currently inserted UICCs and enabled eUICCs. + * Gets information about currently inserted UICCs and eUICCs. * <p> * Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). * <p> diff --git a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java index 12b20efcb0b3..64c1830c5e5c 100644 --- a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java +++ b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java @@ -419,7 +419,8 @@ public final class TelephonyPermissions { // settings to individually disable the new restrictions for privileged, preloaded // non-privileged, and non-preinstalled apps. if (!isIdentifierCheckDisabled() && ( - (!isPreinstalled && !relax3PDeviceIdentifierCheck) + (isPrivApp && !relaxPrivDeviceIdentifierCheck) + || (!isPreinstalled && !relax3PDeviceIdentifierCheck) || (isPreinstalled && !isPrivApp && !relaxNonPrivDeviceIdentifierCheck))) { // The current package should only be reported in StatsLog if it has not previously been // reported for the currently invoked device identifier method. diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java index 13e737e9fed3..eb19361d86a3 100644 --- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java +++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java @@ -18,25 +18,27 @@ package com.android.server; import static android.service.watchdog.ExplicitHealthCheckService.PackageConfig; -import static com.android.server.PackageWatchdog.MonitoredPackage; -import static com.android.server.PackageWatchdog.TRIGGER_FAILURE_COUNT; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import android.Manifest; import android.content.Context; import android.content.pm.VersionedPackage; import android.os.Handler; import android.os.test.TestLooper; +import android.provider.DeviceConfig; import android.util.AtomicFile; import androidx.test.InstrumentationRegistry; +import com.android.server.PackageWatchdog.MonitoredPackage; import com.android.server.PackageWatchdog.PackageHealthObserver; import com.android.server.PackageWatchdog.PackageHealthObserverImpact; +import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -73,8 +75,13 @@ public class PackageWatchdogTest { public void setUp() throws Exception { new File(InstrumentationRegistry.getContext().getFilesDir(), "package-watchdog.xml").delete(); + adoptShellPermissions(Manifest.permission.READ_DEVICE_CONFIG); mTestLooper = new TestLooper(); - mTestLooper.startAutoDispatch(); + } + + @After + public void tearDown() throws Exception { + dropShellPermissions(); } /** @@ -204,7 +211,7 @@ public class PackageWatchdogTest { // Verify random observer not saved returns null assertNull(watchdog2.getPackages(new TestObserver(OBSERVER_NAME_3))); - // Then regiser observer1 + // Then register observer1 watchdog2.registerHealthObserver(observer1); watchdog2.registerHealthObserver(observer2); @@ -231,7 +238,7 @@ public class PackageWatchdogTest { watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION); // Then fail APP_A below the threshold - for (int i = 0; i < TRIGGER_FAILURE_COUNT - 1; i++) { + for (int i = 0; i < watchdog.getTriggerFailureCount() - 1; i++) { watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); } @@ -258,7 +265,7 @@ public class PackageWatchdogTest { watchdog.startObservingHealth(observer1, Arrays.asList(APP_B), SHORT_DURATION); // Then fail APP_C (not observed) above the threshold - for (int i = 0; i < TRIGGER_FAILURE_COUNT; i++) { + for (int i = 0; i < watchdog.getTriggerFailureCount(); i++) { watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_C, VERSION_CODE))); } @@ -292,7 +299,7 @@ public class PackageWatchdogTest { watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION); // Then fail APP_A (different version) above the threshold - for (int i = 0; i < TRIGGER_FAILURE_COUNT; i++) { + for (int i = 0; i < watchdog.getTriggerFailureCount(); i++) { watchdog.onPackageFailure(Arrays.asList( new VersionedPackage(APP_A, differentVersionCode))); } @@ -331,7 +338,7 @@ public class PackageWatchdogTest { SHORT_DURATION); // Then fail all apps above the threshold - for (int i = 0; i < TRIGGER_FAILURE_COUNT; i++) { + for (int i = 0; i < watchdog.getTriggerFailureCount(); i++) { watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE), new VersionedPackage(APP_B, VERSION_CODE), new VersionedPackage(APP_C, VERSION_CODE), @@ -384,7 +391,7 @@ public class PackageWatchdogTest { watchdog.startObservingHealth(observerSecond, Arrays.asList(APP_A), LONG_DURATION); // Then fail APP_A above the threshold - for (int i = 0; i < TRIGGER_FAILURE_COUNT; i++) { + for (int i = 0; i < watchdog.getTriggerFailureCount(); i++) { watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); } // Run handler so package failures are dispatched to observers @@ -401,7 +408,7 @@ public class PackageWatchdogTest { observerSecond.mFailedPackages.clear(); // Then fail APP_A again above the threshold - for (int i = 0; i < TRIGGER_FAILURE_COUNT; i++) { + for (int i = 0; i < watchdog.getTriggerFailureCount(); i++) { watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); } // Run handler so package failures are dispatched to observers @@ -418,7 +425,7 @@ public class PackageWatchdogTest { observerSecond.mFailedPackages.clear(); // Then fail APP_A again above the threshold - for (int i = 0; i < TRIGGER_FAILURE_COUNT; i++) { + for (int i = 0; i < watchdog.getTriggerFailureCount(); i++) { watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); } // Run handler so package failures are dispatched to observers @@ -435,7 +442,7 @@ public class PackageWatchdogTest { observerSecond.mFailedPackages.clear(); // Then fail APP_A again above the threshold - for (int i = 0; i < TRIGGER_FAILURE_COUNT; i++) { + for (int i = 0; i < watchdog.getTriggerFailureCount(); i++) { watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); } // Run handler so package failures are dispatched to observers @@ -462,7 +469,7 @@ public class PackageWatchdogTest { watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION); // Then fail APP_A above the threshold - for (int i = 0; i < TRIGGER_FAILURE_COUNT; i++) { + for (int i = 0; i < watchdog.getTriggerFailureCount(); i++) { watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); } @@ -539,6 +546,10 @@ public class PackageWatchdogTest { */ @Test public void testExplicitHealthCheckStateChanges() throws Exception { + adoptShellPermissions( + Manifest.permission.WRITE_DEVICE_CONFIG, + Manifest.permission.READ_DEVICE_CONFIG); + TestController controller = new TestController(); PackageWatchdog watchdog = createWatchdog(controller, true /* withPackagesReady */); TestObserver observer = new TestObserver(OBSERVER_NAME_1, @@ -559,7 +570,7 @@ public class PackageWatchdogTest { assertEquals(APP_B, requestedPackages.get(1)); // Disable explicit health checks (marks APP_A and APP_B as passed) - watchdog.setExplicitHealthCheckEnabled(false); + setExplicitHealthCheckEnabled(false); // Run handler so requests/cancellations are dispatched to the controller mTestLooper.dispatchAll(); @@ -575,7 +586,7 @@ public class PackageWatchdogTest { assertEquals(0, observer.mFailedPackages.size()); // Re-enable explicit health checks - watchdog.setExplicitHealthCheckEnabled(true); + setExplicitHealthCheckEnabled(true); // Run handler so requests/cancellations are dispatched to the controller mTestLooper.dispatchAll(); @@ -643,11 +654,13 @@ public class PackageWatchdogTest { /** Tests {@link MonitoredPackage} health check state transitions. */ @Test public void testPackageHealthCheckStateTransitions() { - MonitoredPackage m1 = new MonitoredPackage(APP_A, LONG_DURATION, + TestController controller = new TestController(); + PackageWatchdog wd = createWatchdog(controller, true /* withPackagesReady */); + MonitoredPackage m1 = wd.new MonitoredPackage(APP_A, LONG_DURATION, false /* hasPassedHealthCheck */); - MonitoredPackage m2 = new MonitoredPackage(APP_B, LONG_DURATION, false); - MonitoredPackage m3 = new MonitoredPackage(APP_C, LONG_DURATION, false); - MonitoredPackage m4 = new MonitoredPackage(APP_D, LONG_DURATION, SHORT_DURATION, true); + MonitoredPackage m2 = wd.new MonitoredPackage(APP_B, LONG_DURATION, false); + MonitoredPackage m3 = wd.new MonitoredPackage(APP_C, LONG_DURATION, false); + MonitoredPackage m4 = wd.new MonitoredPackage(APP_D, LONG_DURATION, SHORT_DURATION, true); // Verify transition: inactive -> active -> passed // Verify initially inactive @@ -683,6 +696,32 @@ public class PackageWatchdogTest { assertEquals(MonitoredPackage.STATE_PASSED, m4.handleElapsedTimeLocked(LONG_DURATION)); } + private void adoptShellPermissions(String... permissions) { + InstrumentationRegistry + .getInstrumentation() + .getUiAutomation() + .adoptShellPermissionIdentity(permissions); + } + + private void dropShellPermissions() { + InstrumentationRegistry + .getInstrumentation() + .getUiAutomation() + .dropShellPermissionIdentity(); + } + + private void setExplicitHealthCheckEnabled(boolean enabled) { + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK, + PackageWatchdog.PROPERTY_WATCHDOG_EXPLICIT_HEALTH_CHECK_ENABLED, + Boolean.toString(enabled), /*makeDefault*/false); + //give time for DeviceConfig to broadcast the property value change + try { + Thread.sleep(SHORT_DURATION); + } catch (InterruptedException e) { + fail("Thread.sleep unexpectedly failed!"); + } + } + private PackageWatchdog createWatchdog() { return createWatchdog(new TestController(), true /* withPackagesReady */); } diff --git a/tests/RollbackTest/Android.bp b/tests/RollbackTest/Android.bp index dfc3b6e15b3e..1932871d86b9 100644 --- a/tests/RollbackTest/Android.bp +++ b/tests/RollbackTest/Android.bp @@ -29,6 +29,14 @@ android_test_helper_app { } android_test_helper_app { + name: "RollbackTestAppAv3", + manifest: "TestApp/Av3.xml", + sdk_version: "current", + srcs: ["TestApp/src/**/*.java"], + resource_dirs: ["TestApp/res_v3"], +} + +android_test_helper_app { name: "RollbackTestAppACrashingV2", manifest: "TestApp/ACrashingV2.xml", sdk_version: "current", @@ -88,6 +96,15 @@ apex { installable: false, } +apex { + name: "com.android.tests.rollback.testapex.RollbackTestApexV3", + manifest: "TestApex/RollbackTestApexV3.json", + file_contexts: "apex.test", + prebuilts: ["RollbackTestApex.prebuilt.txt"], + key: "RollbackTestApex.key", + installable: false, +} + apex_key { name: "RollbackTestApex.key", public_key: "TestApex/com.android.tests.rollback.testapex.avbpubkey", @@ -109,6 +126,7 @@ android_test { java_resources: [ ":RollbackTestAppAv1", ":RollbackTestAppAv2", + ":RollbackTestAppAv3", ":RollbackTestAppACrashingV2", ":RollbackTestAppBv1", ":RollbackTestAppBv2", @@ -116,6 +134,7 @@ android_test { ":RollbackTestAppASplitV2", ":com.android.tests.rollback.testapex.RollbackTestApexV1", ":com.android.tests.rollback.testapex.RollbackTestApexV2", + ":com.android.tests.rollback.testapex.RollbackTestApexV3", ], test_config: "RollbackTest.xml", sdk_version: "test_current", diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java index f9304f242ef3..13b5b9ac104e 100644 --- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java +++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java @@ -775,6 +775,42 @@ public class RollbackTest { } } + /** + * Test failure to enable rollback for multi-package installs. + * If any one of the packages fail to enable rollback, we shouldn't enable + * rollback for any package. + */ + @Test + public void testMultiPackageEnableFail() throws Exception { + try { + RollbackTestUtils.adoptShellPermissionIdentity( + Manifest.permission.INSTALL_PACKAGES, + Manifest.permission.DELETE_PACKAGES, + Manifest.permission.TEST_MANAGE_ROLLBACKS); + RollbackManager rm = RollbackTestUtils.getRollbackManager(); + + RollbackTestUtils.uninstall(TEST_APP_A); + RollbackTestUtils.uninstall(TEST_APP_B); + RollbackTestUtils.install("RollbackTestAppAv1.apk", false); + + // We should fail to enable rollback here because TestApp B is not + // already installed. + RollbackTestUtils.installMultiPackage(true, + "RollbackTestAppAv2.apk", + "RollbackTestAppBv2.apk"); + + assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); + assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_B)); + + assertNull(getUniqueRollbackInfoForPackage( + rm.getAvailableRollbacks(), TEST_APP_A)); + assertNull(getUniqueRollbackInfoForPackage( + rm.getAvailableRollbacks(), TEST_APP_B)); + } finally { + RollbackTestUtils.dropShellPermissionIdentity(); + } + } + @Test @Ignore("b/120200473") /** @@ -871,6 +907,51 @@ public class RollbackTest { } } + /** + * Test race between roll back and roll forward. + */ + @Test + public void testRollForwardRace() throws Exception { + try { + RollbackTestUtils.adoptShellPermissionIdentity( + Manifest.permission.INSTALL_PACKAGES, + Manifest.permission.DELETE_PACKAGES, + Manifest.permission.TEST_MANAGE_ROLLBACKS, + Manifest.permission.MANAGE_ROLLBACKS); + + RollbackManager rm = RollbackTestUtils.getRollbackManager(); + + RollbackTestUtils.uninstall(TEST_APP_A); + RollbackTestUtils.install("RollbackTestAppAv1.apk", false); + RollbackTestUtils.install("RollbackTestAppAv2.apk", true); + assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); + + RollbackInfo rollback = getUniqueRollbackInfoForPackage( + rm.getAvailableRollbacks(), TEST_APP_A); + assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback); + + // Install a new version of package A, then immediately rollback + // the previous version. We expect the rollback to fail, because + // it is no longer available. + // There are a couple different ways this could fail depending on + // thread interleaving, so don't ignore flaky failures. + RollbackTestUtils.install("RollbackTestAppAv3.apk", false); + try { + RollbackTestUtils.rollback(rollback.getRollbackId()); + // Note: Don't ignore flaky failures here. + fail("Expected rollback to fail, but it did not."); + } catch (AssertionError e) { + Log.i(TAG, "Note expected failure: ", e); + // Expected + } + + // Note: Don't ignore flaky failures here. + assertEquals(3, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); + } finally { + RollbackTestUtils.dropShellPermissionIdentity(); + } + } + // Helper function to test that the given rollback info is a rollback for // the atomic set {A2, B2} -> {A1, B1}. private void assertRollbackInfoForAandB(RollbackInfo rollback) { diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java index 7e711c290e5a..3b0e2a56db3c 100644 --- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java +++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java @@ -26,6 +26,7 @@ import android.content.rollback.RollbackManager; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import org.junit.After; @@ -54,6 +55,8 @@ public class StagedRollbackTest { "com.android.tests.rollback.testapex.RollbackTestApexV1.apex"; private static final String TEST_APEX_V2 = "com.android.tests.rollback.testapex.RollbackTestApexV2.apex"; + private static final String TEST_APEX_V3 = + "com.android.tests.rollback.testapex.RollbackTestApexV3.apex"; /** * Adopts common shell permissions needed for rollback tests. @@ -145,26 +148,13 @@ public class StagedRollbackTest { /** * Test rollbacks of staged installs an apk and an apex. - * Prepare apex (and apk) phase. - */ - @Test - public void testApkAndApexPrepare() throws Exception { - RollbackTestUtils.uninstall(TEST_APP_A); - assertEquals(-1, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); - - // Note: can't uninstall the apex. See note in #testApexOnlyPrepareApex(). - RollbackTestUtils.installStaged(false, TEST_APP_A_V1, TEST_APEX_V1); - - // At this point, the host test driver will reboot the device and run - // testApkAndApexEnableRollback(). - } - - /** - * Test rollbacks of staged installs an apk and an apex. * Enable rollback phase. */ @Test public void testApkAndApexEnableRollback() throws Exception { + RollbackTestUtils.uninstall(TEST_APP_A); + RollbackTestUtils.install(TEST_APP_A_V1, false); + assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APEX_PKG)); assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); @@ -225,22 +215,6 @@ public class StagedRollbackTest { /** * Test rollbacks of staged installs involving only apex. - * Prepare apex phase. - */ - @Test - public void testApexOnlyPrepareApex() throws Exception { - // Note: We can't uninstall the apex if it is already on device, - // because that isn't supported yet (b/123667725). As long as nothing - // is failing, this should be fine because we don't expect the tests - // to leave the device with v2 of the apex installed. - RollbackTestUtils.installStaged(false, TEST_APEX_V1); - - // At this point, the host test driver will reboot the device and run - // testApexOnlyEnableRollback(). - } - - /** - * Test rollbacks of staged installs involving only apex. * Enable rollback phase. */ @Test @@ -291,4 +265,51 @@ public class StagedRollbackTest { public void testApexOnlyConfirmRollback() throws Exception { assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APEX_PKG)); } + + /** + * Tests that apex update expires existing rollbacks for that apex. + * Enable rollback phase. + */ + @Test + public void testApexRollbackExpirationEnableRollback() throws Exception { + assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APEX_PKG)); + RollbackTestUtils.installStaged(true, TEST_APEX_V2); + + // At this point, the host test driver will reboot the device and run + // testApexRollbackExpirationUpdateApex(). + } + + /** + * Tests that apex update expires existing rollbacks for that apex. + * Update apex phase. + */ + @Test + public void testApexRollbackExpirationUpdateApex() throws Exception { + assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APEX_PKG)); + RollbackTestUtils.installStaged(false, TEST_APEX_V3); + + // At this point, the host test driver will reboot the device and run + // testApexRollbackExpirationConfirmExpiration(). + } + + /** + * Tests that apex update expires existing rollbacks for that apex. + * Confirm expiration phase. + */ + @Test + public void testApexRollbackExpirationConfirmExpiration() throws Exception { + assertEquals(3, RollbackTestUtils.getInstalledVersion(TEST_APEX_PKG)); + + RollbackManager rm = RollbackTestUtils.getRollbackManager(); + assertNull(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), TEST_APEX_PKG)); + } + + /** + * Helper function called by the host test to install v1 of the test apex, + * assuming the test apex is not installed. + */ + @Test + public void installTestApexV1() throws Exception { + RollbackTestUtils.installStaged(false, TEST_APEX_V1); + } } diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java index ac7f634d51f1..1f87ed863034 100644 --- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java +++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java @@ -18,6 +18,7 @@ package com.android.tests.rollback.host; import static org.junit.Assert.assertTrue; +import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; @@ -30,6 +31,8 @@ import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) public class StagedRollbackTest extends BaseHostJUnit4Test { + private static final String TEST_APEX_PKG = "com.android.tests.rollback.testapex"; + /** * Runs the given phase of a test by calling into the device. * Throws an exception if the test phase fails. @@ -59,8 +62,7 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { */ @Test public void testApexOnly() throws Exception { - runPhase("testApexOnlyPrepareApex"); - getDevice().reboot(); + installTestApexV1(); runPhase("testApexOnlyEnableRollback"); getDevice().reboot(); runPhase("testApexOnlyCommitRollback"); @@ -73,12 +75,45 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { */ @Test public void testApkAndApex() throws Exception { - runPhase("testApkAndApexPrepare"); - getDevice().reboot(); + installTestApexV1(); runPhase("testApkAndApexEnableRollback"); getDevice().reboot(); runPhase("testApkAndApexCommitRollback"); getDevice().reboot(); runPhase("testApkAndApexConfirmRollback"); } + + /** + * Tests that apex update expires existing rollbacks for that apex. + */ + @Test + public void testApexRollbackExpiration() throws Exception { + installTestApexV1(); + runPhase("testApexRollbackExpirationEnableRollback"); + getDevice().reboot(); + runPhase("testApexRollbackExpirationUpdateApex"); + getDevice().reboot(); + runPhase("testApexRollbackExpirationConfirmExpiration"); + } + + /** + * Do whatever is necessary to get version 1 of the test apex installed on + * the device. Try to do so without extra reboots where possible to keep + * the test execution time down. + */ + private void installTestApexV1() throws Exception { + for (ITestDevice.ApexInfo apexInfo : getDevice().getActiveApexes()) { + if (TEST_APEX_PKG.equals(apexInfo.name)) { + if (apexInfo.versionCode == 1) { + return; + } + getDevice().uninstallPackage(TEST_APEX_PKG); + getDevice().reboot(); + break; + } + } + + runPhase("installTestApexV1"); + getDevice().reboot(); + } } diff --git a/tests/RollbackTest/TestApex/RollbackTestApexV3.json b/tests/RollbackTest/TestApex/RollbackTestApexV3.json new file mode 100644 index 000000000000..87a2c9dbcb6a --- /dev/null +++ b/tests/RollbackTest/TestApex/RollbackTestApexV3.json @@ -0,0 +1,4 @@ +{ + "name": "com.android.tests.rollback.testapex", + "version": 3 +} diff --git a/tests/RollbackTest/TestApp/Av3.xml b/tests/RollbackTest/TestApp/Av3.xml new file mode 100644 index 000000000000..9725c9f7cf9e --- /dev/null +++ b/tests/RollbackTest/TestApp/Av3.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2018 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.tests.rollback.testapp.A" + android:versionCode="3" + android:versionName="3.0" > + + + <uses-sdk android:minSdkVersion="19" /> + + <application android:label="Rollback Test App A v3"> + <receiver android:name="com.android.tests.rollback.testapp.ProcessUserData" + android:exported="true" /> + <activity android:name="com.android.tests.rollback.testapp.MainActivity"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> +</manifest> diff --git a/tests/RollbackTest/TestApp/res_v3/values-anydpi/values.xml b/tests/RollbackTest/TestApp/res_v3/values-anydpi/values.xml new file mode 100644 index 000000000000..f2d8992bee37 --- /dev/null +++ b/tests/RollbackTest/TestApp/res_v3/values-anydpi/values.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<resources> + <integer name="split_version">3</integer> +</resources> diff --git a/tests/RollbackTest/TestApp/res_v3/values/values.xml b/tests/RollbackTest/TestApp/res_v3/values/values.xml new file mode 100644 index 000000000000..968168a4bf06 --- /dev/null +++ b/tests/RollbackTest/TestApp/res_v3/values/values.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<resources> + <integer name="app_version">3</integer> + <integer name="split_version">0</integer> +</resources> diff --git a/tests/net/common/Android.bp b/tests/net/common/Android.bp index 9ee58583779d..3b2e34a159ee 100644 --- a/tests/net/common/Android.bp +++ b/tests/net/common/Android.bp @@ -18,7 +18,7 @@ // They must be fast and stable, and exercise public or test APIs. java_library { name: "FrameworksNetCommonTests", - srcs: ["java/**/*.java"], + srcs: ["java/**/*.java", "java/**/*.kt"], static_libs: [ "androidx.test.rules", "frameworks-net-testutils", diff --git a/tests/net/common/java/android/net/LinkPropertiesTest.java b/tests/net/common/java/android/net/LinkPropertiesTest.java index 417729150be8..709f5f69aa2b 100644 --- a/tests/net/common/java/android/net/LinkPropertiesTest.java +++ b/tests/net/common/java/android/net/LinkPropertiesTest.java @@ -868,12 +868,12 @@ public class LinkPropertiesTest { source.setNat64Prefix(new IpPrefix("2001:db8:1:2:64:64::/96")); - TestUtils.assertParcelingIsLossless(source, LinkProperties.CREATOR); + TestUtils.assertParcelingIsLossless(source); } @Test public void testParcelUninitialized() throws Exception { LinkProperties empty = new LinkProperties(); - TestUtils.assertParcelingIsLossless(empty, LinkProperties.CREATOR); + TestUtils.assertParcelingIsLossless(empty); } } diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java index ad76388b3c9b..6bc7c1bb30f8 100644 --- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java +++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java @@ -33,6 +33,8 @@ 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.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.UNRESTRICTED_CAPABILITIES; @@ -585,4 +587,20 @@ public class NetworkCapabilitiesTest { nc2.set(nc1); // Overwrites, as opposed to combineCapabilities assertEquals(nc1, nc2); } + + @Test + public void testGetTransportTypes() { + final NetworkCapabilities nc = new NetworkCapabilities(); + nc.addTransportType(TRANSPORT_CELLULAR); + nc.addTransportType(TRANSPORT_WIFI); + nc.addTransportType(TRANSPORT_VPN); + nc.addTransportType(TRANSPORT_TEST); + + final int[] transportTypes = nc.getTransportTypes(); + assertEquals(4, transportTypes.length); + assertEquals(TRANSPORT_CELLULAR, transportTypes[0]); + assertEquals(TRANSPORT_WIFI, transportTypes[1]); + assertEquals(TRANSPORT_VPN, transportTypes[2]); + assertEquals(TRANSPORT_TEST, transportTypes[3]); + } } diff --git a/tests/net/common/java/android/net/NetworkTest.java b/tests/net/common/java/android/net/NetworkTest.java index 0bee7cd29d29..bef66b27df62 100644 --- a/tests/net/common/java/android/net/NetworkTest.java +++ b/tests/net/common/java/android/net/NetworkTest.java @@ -155,4 +155,12 @@ public class NetworkTest { private static <T> void assertNotEqual(T t1, T t2) { assertFalse(Objects.equals(t1, t2)); } + + @Test + public void testGetPrivateDnsBypassingCopy() { + final Network copy = mNetwork.getPrivateDnsBypassingCopy(); + assertEquals(mNetwork.netId, copy.netId); + assertNotEqual(copy.netId, copy.getNetIdForResolv()); + assertNotEqual(mNetwork.getNetIdForResolv(), copy.getNetIdForResolv()); + } } diff --git a/tests/net/common/java/android/net/StaticIpConfigurationTest.java b/tests/net/common/java/android/net/StaticIpConfigurationTest.java index 8449ca76d580..5096be221cbf 100644 --- a/tests/net/common/java/android/net/StaticIpConfigurationTest.java +++ b/tests/net/common/java/android/net/StaticIpConfigurationTest.java @@ -31,7 +31,9 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.net.InetAddress; +import java.util.ArrayList; import java.util.HashSet; +import java.util.List; import java.util.Objects; @RunWith(AndroidJUnit4.class) @@ -46,6 +48,7 @@ public class StaticIpConfigurationTest { private static final InetAddress DNS2 = IpAddress("8.8.4.4"); private static final InetAddress DNS3 = IpAddress("4.2.2.2"); private static final String IFACE = "eth0"; + private static final String FAKE_DOMAINS = "google.com"; private static InetAddress IpAddress(String addr) { return InetAddress.parseNumericAddress(addr); @@ -69,7 +72,7 @@ public class StaticIpConfigurationTest { s.dnsServers.add(DNS1); s.dnsServers.add(DNS2); s.dnsServers.add(DNS3); - s.domains = "google.com"; + s.domains = FAKE_DOMAINS; return s; } @@ -178,8 +181,8 @@ public class StaticIpConfigurationTest { expected.addDnsServer(DNS3); assertEquals(expected, s.toLinkProperties(IFACE)); - s.domains = "google.com"; - expected.setDomains("google.com"); + s.domains = FAKE_DOMAINS; + expected.setDomains(FAKE_DOMAINS); assertEquals(expected, s.toLinkProperties(IFACE)); s.gateway = null; @@ -218,4 +221,53 @@ public class StaticIpConfigurationTest { StaticIpConfiguration s2 = passThroughParcel(s); assertEquals(s, s2); } + + @Test + public void testBuilder() { + final ArrayList<InetAddress> dnsServers = new ArrayList<>(); + dnsServers.add(DNS1); + + final StaticIpConfiguration s = new StaticIpConfiguration.Builder() + .setIpAddress(ADDR) + .setGateway(GATEWAY) + .setDomains(FAKE_DOMAINS) + .setDnsServers(dnsServers) + .build(); + + assertEquals(s.ipAddress, s.getIpAddress()); + assertEquals(ADDR, s.getIpAddress()); + assertEquals(s.gateway, s.getGateway()); + assertEquals(GATEWAY, s.getGateway()); + assertEquals(s.domains, s.getDomains()); + assertEquals(FAKE_DOMAINS, s.getDomains()); + assertTrue(s.dnsServers.equals(s.getDnsServers())); + assertEquals(1, s.getDnsServers().size()); + assertEquals(DNS1, s.getDnsServers().get(0)); + } + + @Test + public void testAddDnsServers() { + final StaticIpConfiguration s = new StaticIpConfiguration((StaticIpConfiguration) null); + checkEmpty(s); + + s.addDnsServer(DNS1); + assertEquals(1, s.getDnsServers().size()); + assertEquals(DNS1, s.getDnsServers().get(0)); + + s.addDnsServer(DNS2); + s.addDnsServer(DNS3); + assertEquals(3, s.getDnsServers().size()); + assertEquals(DNS2, s.getDnsServers().get(1)); + assertEquals(DNS3, s.getDnsServers().get(2)); + } + + @Test + public void testGetRoutes() { + final StaticIpConfiguration s = makeTestObject(); + final List<RouteInfo> routeInfoList = s.getRoutes(IFACE); + + assertEquals(2, routeInfoList.size()); + assertEquals(new RouteInfo(ADDR, (InetAddress) null, IFACE), routeInfoList.get(0)); + assertEquals(new RouteInfo((IpPrefix) null, GATEWAY, IFACE), routeInfoList.get(1)); + } } diff --git a/tests/net/common/java/android/net/apf/ApfCapabilitiesTest.java b/tests/net/common/java/android/net/apf/ApfCapabilitiesTest.java index 7238895b3657..3ed8a86b2fb5 100644 --- a/tests/net/common/java/android/net/apf/ApfCapabilitiesTest.java +++ b/tests/net/common/java/android/net/apf/ApfCapabilitiesTest.java @@ -36,7 +36,7 @@ public class ApfCapabilitiesTest { final ApfCapabilities caps = new ApfCapabilities(123, 456, 789); ParcelableTestUtil.assertFieldCountEquals(3, ApfCapabilities.class); - TestUtils.assertParcelingIsLossless(caps, ApfCapabilities.CREATOR); + TestUtils.assertParcelingIsLossless(caps); } @Test diff --git a/tests/net/common/java/android/net/metrics/DhcpErrorEventTest.kt b/tests/net/common/java/android/net/metrics/DhcpErrorEventTest.kt new file mode 100644 index 000000000000..e19195322e1d --- /dev/null +++ b/tests/net/common/java/android/net/metrics/DhcpErrorEventTest.kt @@ -0,0 +1,67 @@ +package android.net.metrics + +import android.net.metrics.DhcpErrorEvent.errorCodeWithOption +import android.net.metrics.DhcpErrorEvent.DHCP_INVALID_OPTION_LENGTH +import androidx.test.filters.SmallTest +import androidx.test.runner.AndroidJUnit4 +import com.android.internal.util.TestUtils.parcelingRoundTrip +import java.lang.reflect.Modifier +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith + +private const val TEST_ERROR_CODE = 12345 +/** + * DHCP Optional Type: DHCP Subnet Mask (Copy from DhcpPacket.java) + */ +private const val DHCP_SUBNET_MASK = 1 + +@RunWith(AndroidJUnit4::class) +@SmallTest +class DhcpErrorEventTest { + + @Test + fun testConstructor() { + val event = DhcpErrorEvent(TEST_ERROR_CODE) + assertEquals(TEST_ERROR_CODE, event.errorCode) + } + + @Test + fun testParcelUnparcel() { + val event = DhcpErrorEvent(TEST_ERROR_CODE) + val parceled = parcelingRoundTrip(event) + assertEquals(TEST_ERROR_CODE, parceled.errorCode) + } + + @Test + fun testErrorCodeWithOption() { + val errorCode = errorCodeWithOption(DHCP_INVALID_OPTION_LENGTH, DHCP_SUBNET_MASK); + assertTrue((DHCP_INVALID_OPTION_LENGTH and errorCode) == DHCP_INVALID_OPTION_LENGTH); + assertTrue((DHCP_SUBNET_MASK and errorCode) == DHCP_SUBNET_MASK); + } + + @Test + fun testToString() { + val names = listOf("L2_ERROR", "L3_ERROR", "L4_ERROR", "DHCP_ERROR", "MISC_ERROR") + val errorFields = DhcpErrorEvent::class.java.declaredFields.filter { + it.type == Int::class.javaPrimitiveType + && Modifier.isPublic(it.modifiers) && Modifier.isStatic(it.modifiers) + && it.name !in names + } + + errorFields.forEach { + val intValue = it.getInt(null) + val stringValue = DhcpErrorEvent(intValue).toString() + assertTrue("Invalid string for error 0x%08X (field %s): %s".format(intValue, it.name, + stringValue), + stringValue.contains(it.name)) + } + } + + @Test + fun testToString_InvalidErrorCode() { + assertNotNull(DhcpErrorEvent(TEST_ERROR_CODE).toString()) + } +}
\ No newline at end of file diff --git a/tests/net/java/android/net/TcpKeepalivePacketDataTest.java b/tests/net/java/android/net/TcpKeepalivePacketDataTest.java index e0b722761c34..583d3fd536aa 100644 --- a/tests/net/java/android/net/TcpKeepalivePacketDataTest.java +++ b/tests/net/java/android/net/TcpKeepalivePacketDataTest.java @@ -79,7 +79,7 @@ public final class TcpKeepalivePacketDataTest { assertEquals(testInfo.tos, resultData.ipTos); assertEquals(testInfo.ttl, resultData.ipTtl); - TestUtils.assertParcelingIsLossless(resultData, TcpKeepalivePacketData.CREATOR); + TestUtils.assertParcelingIsLossless(resultData); final byte[] packet = resultData.getPacket(); // IP version and IHL diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index c15775fc4734..363ac9ce1d25 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -567,6 +567,16 @@ public class ConnectivityServiceTest { protected void preventAutomaticReconnect() { mPreventReconnectReceived.open(); } + + @Override + protected void addKeepalivePacketFilter(Message msg) { + Log.i(TAG, "Add keepalive packet filter."); + } + + @Override + protected void removeKeepalivePacketFilter(Message msg) { + Log.i(TAG, "Remove keepalive packet filter."); + } }; assertEquals(mNetworkAgent.netId, nmNetworkCaptor.getValue().netId); diff --git a/tests/net/util/java/com/android/internal/util/TestUtils.java b/tests/net/util/java/com/android/internal/util/TestUtils.java index 75329a805606..a99cd4716f9a 100644 --- a/tests/net/util/java/com/android/internal/util/TestUtils.java +++ b/tests/net/util/java/com/android/internal/util/TestUtils.java @@ -69,9 +69,17 @@ public final class TestUtils { } } - // TODO : fetch the creator through reflection or something instead of passing it - public static <T extends Parcelable, C extends Parcelable.Creator<T>> - void assertParcelingIsLossless(T source, C creator) { + /** + * Return a new instance of {@code T} after being parceled then unparceled. + */ + public static <T extends Parcelable> T parcelingRoundTrip(T source) { + final Parcelable.Creator<T> creator; + try { + creator = (Parcelable.Creator<T>) source.getClass().getField("CREATOR").get(null); + } catch (IllegalAccessException | NoSuchFieldException e) { + fail("Missing CREATOR field: " + e.getMessage()); + return null; + } Parcel p = Parcel.obtain(); source.writeToParcel(p, /* flags */ 0); p.setDataPosition(0); @@ -79,7 +87,14 @@ public final class TestUtils { p = Parcel.obtain(); p.unmarshall(marshalled, 0, marshalled.length); p.setDataPosition(0); - T dest = creator.createFromParcel(p); - assertEquals(source, dest); + return creator.createFromParcel(p); + } + + /** + * Assert that after being parceled then unparceled, {@code source} is equal to the original + * object. + */ + public static <T extends Parcelable> void assertParcelingIsLossless(T source) { + assertEquals(source, parcelingRoundTrip(source)); } } diff --git a/tools/bit/main.cpp b/tools/bit/main.cpp index 1a91f52bc6cf..d80c2e742fae 100644 --- a/tools/bit/main.cpp +++ b/tools/bit/main.cpp @@ -290,8 +290,14 @@ TestResults::OnTestStatus(TestStatus& status) m_currentAction->target->name.c_str(), className.c_str(), testName.c_str(), g_escapeEndColor); - string stack = get_bundle_string(results, &found, "stack", NULL); - if (found) { + bool stackFound; + string stack = get_bundle_string(results, &stackFound, "stack", NULL); + if (status.has_logcat()) { + const string logcat = status.logcat(); + if (logcat.length() > 0) { + printf("%s\n", logcat.c_str()); + } + } else if (stackFound) { printf("%s\n", stack.c_str()); } } |