diff options
104 files changed, 3249 insertions, 1200 deletions
diff --git a/Android.bp b/Android.bp index 47d06fa02ac8..f9b60e67e659 100644 --- a/Android.bp +++ b/Android.bp @@ -614,6 +614,7 @@ java_defaults { ":netd_aidl", ":vold_aidl", ":installd_aidl", + ":dumpstate_aidl", "lowpan/java/android/net/lowpan/ILowpanEnergyScanCallback.aidl", "lowpan/java/android/net/lowpan/ILowpanNetScanCallback.aidl", @@ -660,6 +661,7 @@ java_defaults { include_dirs: [ "system/update_engine/binder_bindings", "frameworks/native/aidl/binder", + "frameworks/native/cmds/dumpstate/binder", "frameworks/av/camera/aidl", "frameworks/av/media/libaudioclient/aidl", "frameworks/native/aidl/gui", @@ -682,9 +684,6 @@ java_defaults { no_framework_libs: true, libs: [ - "conscrypt", - "okhttp", - "bouncycastle", "ext", ], @@ -836,7 +835,6 @@ java_library_host { "cmds/statsd/src/**/*.proto", "core/proto/**/*.proto", "libs/incident/proto/**/*.proto", - "proto/src/stats_enums.proto", ], proto: { include_dirs: ["external/protobuf/src"], @@ -874,7 +872,6 @@ java_library { srcs: [ "core/proto/**/*.proto", "libs/incident/proto/android/os/**/*.proto", - "proto/src/stats_enums.proto", ], // Protos have lots of MissingOverride and similar. errorprone: { @@ -900,7 +897,6 @@ cc_library { srcs: [ "core/proto/**/*.proto", "libs/incident/**/*.proto", - "proto/src/stats_enums.proto", ], target: { @@ -1097,14 +1093,13 @@ framework_docs_args = "-android -manifest $(location core/res/AndroidManifest.xm "-federationapi SupportLib $(location current/support-api.txt) " framework_docs_only_args = " -android -manifest $(location core/res/AndroidManifest.xml) " + + "-werror -lerror -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 " + "-overview $(location core/java/overview.html) " + // Federate Support Library references against local API file. "-federate SupportLib https://developer.android.com " + "-federationapi SupportLib $(location current/support-api.txt) " framework_docs_only_libs = [ - "conscrypt", - "bouncycastle", "voip-common", "android.test.mock", "android-support-annotations", @@ -1184,7 +1179,8 @@ stubs_defaults { doc_defaults { name: "framework-docs-default", - libs: framework_docs_only_libs, + libs: framework_docs_only_libs + + ["stub-annotations"], html_dirs: [ "docs/html", ], @@ -1581,7 +1577,6 @@ filegroup { "core/java/org/apache/http/params/CoreConnectionPNames.java", "core/java/org/apache/http/params/HttpConnectionParams.java", "core/java/org/apache/http/params/HttpParams.java", - "core/java/android/net/http/HttpResponseCache.java", "core/java/android/net/http/SslCertificate.java", "core/java/android/net/http/SslError.java", "core/java/com/android/internal/util/HexDump.java", diff --git a/api/system-current.txt b/api/system-current.txt index 2188ec17bed4..83a789ad0294 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -1281,14 +1281,6 @@ package android.content.pm.permission { } -package android.graphics.drawable { - - public final class Icon implements android.os.Parcelable { - method public static android.graphics.drawable.Icon createWithResource(android.content.res.Resources, int); - } - -} - package android.hardware { public final class Sensor { @@ -4203,8 +4195,8 @@ package android.permission { } public static final class PermissionManager.SplitPermissionInfo { - method public java.lang.String[] getNewPermissions(); - method public java.lang.String getRootPermission(); + method public java.util.List<java.lang.String> getNewPermissions(); + method public java.lang.String getSplitPermission(); method public int getTargetSdk(); } @@ -5289,6 +5281,7 @@ package android.telephony { method public int getRejectCause(); method public int getTransportType(); method public boolean isEmergencyEnabled(); + method public boolean isRoaming(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.telephony.NetworkRegistrationState> CREATOR; field public static final int DOMAIN_CS = 1; // 0x1 @@ -5376,6 +5369,8 @@ package android.telephony { method public void setSubscriptionPlans(int, java.util.List<android.telephony.SubscriptionPlan>); field public static final java.lang.String ACTION_MANAGE_SUBSCRIPTION_PLANS = "android.telephony.action.MANAGE_SUBSCRIPTION_PLANS"; field public static final java.lang.String ACTION_REFRESH_SUBSCRIPTION_PLANS = "android.telephony.action.REFRESH_SUBSCRIPTION_PLANS"; + field public static final android.net.Uri ENHANCED_4G_ENABLED_CONTENT_URI; + field public static final android.net.Uri WFC_ENABLED_CONTENT_URI; } public final class SubscriptionPlan implements android.os.Parcelable { diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h index 4091d951ba06..6a9e8a1bf688 100644 --- a/cmds/statsd/src/StatsLogProcessor.h +++ b/cmds/statsd/src/StatsLogProcessor.h @@ -39,7 +39,8 @@ enum DumpReportReason { GET_DATA_CALLED = 4, ADB_DUMP = 5, CONFIG_RESET = 6, - STATSCOMPANION_DIED = 7 + STATSCOMPANION_DIED = 7, + TERMINATION_SIGNAL_RECEIVED = 8 }; class StatsLogProcessor : public ConfigListener { diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index 8da2d447a163..fb6f8c8d4590 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -867,6 +867,13 @@ void StatsService::Startup() { mConfigManager->Startup(); } +void StatsService::Terminate() { + ALOGI("StatsService::Terminating"); + if (mProcessor != nullptr) { + mProcessor->WriteDataToDisk(TERMINATION_SIGNAL_RECEIVED); + } +} + void StatsService::OnLogEvent(LogEvent* event) { mProcessor->OnLogEvent(event); if (mShellSubscriber != nullptr) { diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index 1f1d782af235..cbf34292c1d4 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -84,6 +84,11 @@ public: void Startup(); /** + * Called when terminiation signal received. + */ + void Terminate(); + + /** * Called by LogReader when there's a log event to process. */ virtual void OnLogEvent(LogEvent* event); diff --git a/cmds/statsd/src/atom_field_options.proto b/cmds/statsd/src/atom_field_options.proto index a2a03b14c073..453bf7e126cd 100644 --- a/cmds/statsd/src/atom_field_options.proto +++ b/cmds/statsd/src/atom_field_options.proto @@ -36,8 +36,8 @@ enum StateField { // exclusive state field, and any number of primary key fields. // For example, // message UidProcessStateChanged { -// optional int32 uid = 1 [(stateFieldOption).option = PRIMARY]; -// optional android.app.ProcessStateEnum state = 2 [(stateFieldOption).option = EXCLUSIVE]; +// optional int32 uid = 1 [(state_field_option).option = PRIMARY]; +// optional android.app.ProcessStateEnum state = 2 [(state_field_option).option = EXCLUSIVE]; // } // Each of this UidProcessStateChanged atom represents a state change for a specific uid. // A new state automatically overrides the previous state. @@ -46,16 +46,16 @@ enum StateField { // the primary key. // For example: // message ThreadStateChanged { -// optional int32 pid = 1 [(stateFieldOption).option = PRIMARY]; -// optional int32 tid = 2 [(stateFieldOption).option = PRIMARY]; -// optional int32 state = 3 [(stateFieldOption).option = EXCLUSIVE]; +// optional int32 pid = 1 [(state_field_option).option = PRIMARY]; +// optional int32 tid = 2 [(state_field_option).option = PRIMARY]; +// optional int32 state = 3 [(state_field_option).option = EXCLUSIVE]; // } // // Sometimes, there is no primary key field, when the state is GLOBAL. // For example, // // message ScreenStateChanged { -// optional android.view.DisplayStateEnum state = 1 [(stateFieldOption).option = EXCLUSIVE]; +// optional android.view.DisplayStateEnum state = 1 [(state_field_option).option = EXCLUSIVE]; // } // // Only fields of primary types can be annotated. AttributionNode cannot be primary keys (and they @@ -66,7 +66,7 @@ message StateAtomFieldOption { extend google.protobuf.FieldOptions { // Flags to decorate an atom that presents a state change. - optional StateAtomFieldOption stateFieldOption = 50000; + optional StateAtomFieldOption state_field_option = 50000; // Flags to decorate the uid fields in an atom. optional bool is_uid = 50001 [default = false]; diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 8eee9e6af237..957a9b434d05 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -26,12 +26,11 @@ import "frameworks/base/core/proto/android/app/job/enums.proto"; import "frameworks/base/core/proto/android/bluetooth/enums.proto"; import "frameworks/base/core/proto/android/os/enums.proto"; import "frameworks/base/core/proto/android/server/enums.proto"; +import "frameworks/base/core/proto/android/service/procstats_enum.proto"; +import "frameworks/base/core/proto/android/stats/enums.proto"; import "frameworks/base/core/proto/android/telecomm/enums.proto"; import "frameworks/base/core/proto/android/telephony/enums.proto"; import "frameworks/base/core/proto/android/view/enums.proto"; -import "frameworks/base/proto/src/stats_enums.proto"; -import "frameworks/base/core/proto/android/service/procstats.proto"; -import "frameworks/base/core/proto/android/internal/powerprofile.proto"; /** * The master atom class. This message defines all of the available @@ -129,8 +128,6 @@ message Atom { AppCrashOccurred app_crash_occurred = 78; ANROccurred anr_occurred = 79; WTFOccurred wtf_occurred = 80; - PhoneServiceStateChanged phone_service_state_changed = 94; - PhoneStateChanged phone_state_changed = 95; LowMemReported low_mem_reported = 81; GenericAtom generic_atom = 82; KeyValuePairsAtom key_value_pairs_atom = 83; @@ -144,6 +141,8 @@ message Atom { BatteryHealthSnapshot battery_health_snapshot = 91; SlowIo slow_io = 92; BatteryCausedShutdown battery_caused_shutdown = 93; + PhoneServiceStateChanged phone_service_state_changed = 94; + PhoneStateChanged phone_state_changed = 95; } // Pulled events will start at field 10000. @@ -275,7 +274,7 @@ message ThermalThrottlingStateChanged { */ message ScreenStateChanged { // New screen state, from frameworks/base/core/proto/android/view/enums.proto. - optional android.view.DisplayStateEnum state = 1 [(stateFieldOption).option = EXCLUSIVE]; + optional android.view.DisplayStateEnum state = 1 [(state_field_option).option = EXCLUSIVE]; } /** @@ -286,10 +285,10 @@ message ScreenStateChanged { * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java */ message UidProcessStateChanged { - optional int32 uid = 1 [(stateFieldOption).option = PRIMARY, (is_uid) = true]; + optional int32 uid = 1 [(state_field_option).option = PRIMARY, (is_uid) = true]; // The state, from frameworks/base/core/proto/android/app/enums.proto. - optional android.app.ProcessStateEnum state = 2 [(stateFieldOption).option = EXCLUSIVE]; + optional android.app.ProcessStateEnum state = 2 [(state_field_option).option = EXCLUSIVE]; } /** @@ -321,7 +320,7 @@ message ActivityManagerSleepStateChanged { ASLEEP = 1; AWAKE = 2; } - optional State state = 1 [(stateFieldOption).option = EXCLUSIVE]; + optional State state = 1 [(state_field_option).option = EXCLUSIVE]; } /** @@ -340,7 +339,7 @@ message MemoryFactorStateChanged { CRITICAL = 4; // critical memory. } - optional State factor = 1 [(stateFieldOption).option = EXCLUSIVE]; + optional State factor = 1 [(state_field_option).option = EXCLUSIVE]; } /** @@ -1941,7 +1940,7 @@ message LmkKillOccurred { */ message AppDied { // timestamp(elapsedRealtime) of record creation - optional uint64 timestamp_millis = 1 [(stateFieldOption).option = EXCLUSIVE]; + optional uint64 timestamp_millis = 1 [(state_field_option).option = EXCLUSIVE]; } /** @@ -1952,7 +1951,7 @@ message GenericAtom { optional int32 uid = 1 [(is_uid) = true]; // An event_id indicates the type of event. - optional android.os.statsd.EventType event_id = 2; + optional android.stats.EventType event_id = 2; } /** @@ -2691,11 +2690,288 @@ message NumFingerprints { optional int32 num_fingerprints = 2; } +message AggStats { + optional int64 min = 1; + + optional int64 average = 2; + + optional int64 max = 3; +} + +message ProcessStatsStateProto { + optional android.service.procstats.ScreenState screen_state = 1; + + optional android.service.procstats.MemoryState memory_state = 2; + + // this enum list is from frameworks/base/core/java/com/android/internal/app/procstats/ProcessStats.java + // and not frameworks/base/core/java/android/app/ActivityManager.java + optional android.service.procstats.ProcessState process_state = 3; + + // Millisecond uptime duration spent in this state + optional int64 duration_ms = 4; + + // Millisecond elapsed realtime duration spent in this state + optional int64 realtime_duration_ms = 9; + + // # of samples taken + optional int32 sample_size = 5; + + // PSS is memory reserved for this process + optional AggStats pss = 6; + + // USS is memory shared between processes, divided evenly for accounting + optional AggStats uss = 7; + + // RSS is memory resident for this process + optional AggStats rss = 8; +} + +// Next Tag: 7 +message ProcessStatsProto { + // Name of process. + optional string process = 1; + + // Uid of the process. + optional int32 uid = 2; + + // Information about how often kills occurred + message Kill { + // Count of excessive CPU kills + optional int32 cpu = 1; + + // Count of kills when cached + optional int32 cached = 2; + + // PSS stats during cached kill + optional AggStats cached_pss = 3; + } + optional Kill kill = 3; + + // Time and memory spent in various states. + repeated ProcessStatsStateProto states = 5; + + // Total time process has been running... screen_state, memory_state, and process_state + // will not be set. + optional ProcessStatsStateProto total_running_state = 6; +} + +message PackageServiceOperationStatsProto { + // Operate enum: Started, Foreground, Bound, Executing + optional android.service.procstats.ServiceOperationState operation = 1; + + // Number of times the service was in this operation. + optional int32 count = 2; + + // Information about a state the service can be in. + message StateStats { + // Screen state enum. + optional android.service.procstats.ScreenState screen_state = 1; + // Memory state enum. + optional android.service.procstats.MemoryState memory_state = 2; + + // duration in milliseconds. + optional int64 duration_ms = 3; + // Millisecond elapsed realtime duration spent in this state + optional int64 realtime_duration_ms = 4; + } + repeated StateStats state_stats = 3; +} + +message PackageServiceStatsProto { + // Name of service component. + optional string service_name = 1; + + // The operation stats. + // The package_name, package_uid, package_version, service_name will not be set to save space. + repeated PackageServiceOperationStatsProto operation_stats = 2; +} + +message PackageAssociationSourceProcessStatsProto { + // Uid of the process. + optional int32 process_uid = 1; + // Process name. + optional string process_name = 2; + + // Total count of the times this association appeared. + optional int32 total_count = 3; + + // Millisecond uptime total duration this association was around. + optional int64 total_duration_ms = 4; + + // Total count of the times this association became actively impacting its target process. + optional int32 active_count = 5; + + // Information on one source in this association. + message StateStats { + // Process state enum. + optional android.service.procstats.ProcessState process_state = 1; + // Millisecond uptime duration spent in this state + optional int64 duration_ms = 2; + // Millisecond elapsed realtime duration spent in this state + optional int64 realtime_duration_ms = 3; + } + repeated StateStats active_state_stats = 6; +} + +message PackageAssociationProcessStatsProto { + // Name of the target component. + optional string component_name = 1; + // Information on one source in this association. + repeated PackageAssociationSourceProcessStatsProto sources = 2; +} + + +message ProcessStatsPackageProto { + // Name of package. + optional string package = 1; + + // Uid of the package. + optional int32 uid = 2; + + // Version of the package. + optional int64 version = 3; + + // Stats for each process running with the package loaded in to it. + repeated ProcessStatsProto process_stats = 4; + + // Stats for each of the package's services. + repeated PackageServiceStatsProto service_stats = 5; + + // Stats for each association with the package. + repeated PackageAssociationProcessStatsProto association_stats = 6; +} + +message ProcessStatsSectionProto { + // Elapsed realtime at start of report. + optional int64 start_realtime_ms = 1; + + // Elapsed realtime at end of report. + optional int64 end_realtime_ms = 2; + + // CPU uptime at start of report. + optional int64 start_uptime_ms = 3; + + // CPU uptime at end of report. + optional int64 end_uptime_ms = 4; + + // System runtime library. e.g. "libdvm.so", "libart.so". + optional string runtime = 5; + + // whether kernel reports swapped pss. + optional bool has_swapped_pss = 6; + + // Data completeness. e.g. "complete", "partial", shutdown", or "sysprops". + enum Status { + STATUS_UNKNOWN = 0; + STATUS_COMPLETE = 1; + STATUS_PARTIAL = 2; + STATUS_SHUTDOWN = 3; + STATUS_SYSPROPS = 4; + } + repeated Status status = 7; + + // Stats for each process. + repeated ProcessStatsProto process_stats = 8; + + // Stats for each package. + repeated ProcessStatsPackageProto package_stats = 9; +} + /** * Pulled from ProcessStatsService.java */ message ProcStats { - optional android.service.procstats.ProcessStatsSectionProto proc_stats_section = 1; + optional ProcessStatsSectionProto proc_stats_section = 1; +} + +message PowerProfileProto { + optional double cpu_suspend = 1; + + optional double cpu_idle = 2; + + optional double cpu_active = 3; + + message CpuCluster { + optional int32 id = 1; + optional double cluster_power = 2; + optional int32 cores = 3; + repeated int64 speed = 4; + repeated double core_power = 5; + } + + repeated CpuCluster cpu_cluster = 40; + + optional double wifi_scan = 4; + + optional double wifi_on = 5; + + optional double wifi_active = 6; + + optional double wifi_controller_idle = 7; + + optional double wifi_controller_rx = 8; + + optional double wifi_controller_tx = 9; + + repeated double wifi_controller_tx_levels = 10; + + optional double wifi_controller_operating_voltage = 11; + + optional double bluetooth_controller_idle = 12; + + optional double bluetooth_controller_rx = 13; + + optional double bluetooth_controller_tx = 14; + + optional double bluetooth_controller_operating_voltage = 15; + + optional double modem_controller_sleep = 16; + + optional double modem_controller_idle = 17; + + optional double modem_controller_rx = 18; + + repeated double modem_controller_tx = 19; + + optional double modem_controller_operating_voltage = 20; + + optional double gps_on = 21; + + repeated double gps_signal_quality_based = 22; + + optional double gps_operating_voltage = 23; + + optional double bluetooth_on = 24; + + optional double bluetooth_active = 25; + + optional double bluetooth_at_cmd = 26; + + optional double ambient_display = 27; + + optional double screen_on = 28; + + optional double radio_on = 29; + + optional double radio_scanning = 30; + + optional double radio_active = 31; + + optional double screen_full = 32; + + optional double audio = 33; + + optional double video = 34; + + optional double flashlight = 35; + + optional double memory = 36; + + optional double camera = 37; + + optional double wifi_batched_scan = 38; + + optional double battery_capacity = 39; } /** @@ -2703,5 +2979,5 @@ message ProcStats { * Pulled from PowerProfile.java */ message PowerProfile { - optional com.android.internal.os.PowerProfileProto power_profile_proto = 1; -}
\ No newline at end of file + optional PowerProfileProto power_profile = 1; +} diff --git a/cmds/statsd/src/hash.cpp b/cmds/statsd/src/hash.cpp index c501c9f818b9..543a748adedb 100644 --- a/cmds/statsd/src/hash.cpp +++ b/cmds/statsd/src/hash.cpp @@ -16,6 +16,10 @@ #include "hash.h" +#ifndef FALLTHROUGH_INTENDED +#define FALLTHROUGH_INTENDED [[fallthrough]] +#endif + namespace android { namespace os { namespace statsd { @@ -67,8 +71,10 @@ uint32_t Hash32(const char *data, size_t n, uint32_t seed) { switch (n) { case 3: h ^= ByteAs32(data[2]) << 16; + FALLTHROUGH_INTENDED; case 2: h ^= ByteAs32(data[1]) << 8; + FALLTHROUGH_INTENDED; case 1: h ^= ByteAs32(data[0]); h *= m; @@ -104,16 +110,22 @@ uint64_t Hash64(const char* data, size_t n, uint64_t seed) { switch (n) { case 7: h ^= ByteAs64(data[6]) << 48; + FALLTHROUGH_INTENDED; case 6: h ^= ByteAs64(data[5]) << 40; + FALLTHROUGH_INTENDED; case 5: h ^= ByteAs64(data[4]) << 32; + FALLTHROUGH_INTENDED; case 4: h ^= ByteAs64(data[3]) << 24; + FALLTHROUGH_INTENDED; case 3: h ^= ByteAs64(data[2]) << 16; + FALLTHROUGH_INTENDED; case 2: h ^= ByteAs64(data[1]) << 8; + FALLTHROUGH_INTENDED; case 1: h ^= ByteAs64(data[0]); h *= m; diff --git a/cmds/statsd/src/main.cpp b/cmds/statsd/src/main.cpp index a5dac0836238..6b8c12a893e8 100644 --- a/cmds/statsd/src/main.cpp +++ b/cmds/statsd/src/main.cpp @@ -46,6 +46,27 @@ struct log_reader_thread_data { sp<StatsService> service; }; + +sp<StatsService> gStatsService = nullptr; + +void sigHandler(int sig) { + if (gStatsService != nullptr) { + gStatsService->Terminate(); + } +} + +void registerSigHandler() +{ + struct sigaction sa; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = sigHandler; + sigaction(SIGHUP, &sa, nullptr); + sigaction(SIGINT, &sa, nullptr); + sigaction(SIGQUIT, &sa, nullptr); + sigaction(SIGTERM, &sa, nullptr); +} + int main(int /*argc*/, char** /*argv*/) { // Set up the looper sp<Looper> looper(Looper::prepare(0 /* opts */)); @@ -60,23 +81,25 @@ int main(int /*argc*/, char** /*argv*/) { ::android::hardware::configureRpcThreadpool(1 /*threads*/, false /*willJoin*/); // Create the service - sp<StatsService> service = new StatsService(looper); - if (defaultServiceManager()->addService(String16("stats"), service) != 0) { + gStatsService = new StatsService(looper); + if (defaultServiceManager()->addService(String16("stats"), gStatsService) != 0) { ALOGE("Failed to add service as AIDL service"); return -1; } - auto ret = service->registerAsService(); + auto ret = gStatsService->registerAsService(); if (ret != ::android::OK) { ALOGE("Failed to add service as HIDL service"); return 1; // or handle error } - service->sayHiToStatsCompanion(); + registerSigHandler(); + + gStatsService->sayHiToStatsCompanion(); - service->Startup(); + gStatsService->Startup(); - sp<StatsSocketListener> socketListener = new StatsSocketListener(service); + sp<StatsSocketListener> socketListener = new StatsSocketListener(gStatsService); ALOGI("using statsd socket"); // Backlog and /proc/sys/net/unix/max_dgram_qlen set to large value diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto index ab0b23cc009d..10ed7f3ebaa1 100644 --- a/cmds/statsd/src/stats_log.proto +++ b/cmds/statsd/src/stats_log.proto @@ -271,6 +271,7 @@ message ConfigMetricsReport { ADB_DUMP = 5; CONFIG_RESET = 6; STATSCOMPANION_DIED = 7; + TERMINATION_SIGNAL_RECEIVED = 8; } optional DumpReportReason dump_report_reason = 8; diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index fc0c1dc43874..0aa053580a6d 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -107,20 +107,6 @@ public final class BluetoothDevice implements Parcelable { "android.bluetooth.device.action.FOUND"; /** - * Broadcast Action: Remote device disappeared. - * <p>Sent when a remote device that was found in the last discovery is not - * found in the current discovery. - * <p>Always contains the extra field {@link #EXTRA_DEVICE}. - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive. - * - * @hide - */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - @UnsupportedAppUsage - public static final String ACTION_DISAPPEARED = - "android.bluetooth.device.action.DISAPPEARED"; - - /** * Broadcast Action: Bluetooth class of a remote device has changed. * <p>Always contains the extra fields {@link #EXTRA_DEVICE} and {@link * #EXTRA_CLASS}. @@ -1971,9 +1957,7 @@ public final class BluetoothDevice implements Parcelable { * <p>The remote device will be authenticated and communication on this socket will be * encrypted. * <p> Use this socket if an authenticated socket link is possible. Authentication refers - * to the authentication of the link key to prevent man-in-the-middle type of attacks. When a - * secure socket connection is not possible, use {@link createInsecureLeL2capCocSocket(int, - * int)}. + * to the authentication of the link key to prevent man-in-the-middle type of attacks. * * @param psm dynamic PSM value from remote device * @return a CoC #BluetoothSocket ready for an outgoing connection diff --git a/core/java/android/bluetooth/BluetoothServerSocket.java b/core/java/android/bluetooth/BluetoothServerSocket.java index 758c68db1c5f..4e886250b4fa 100644 --- a/core/java/android/bluetooth/BluetoothServerSocket.java +++ b/core/java/android/bluetooth/BluetoothServerSocket.java @@ -203,8 +203,8 @@ public final class BluetoothServerSocket implements Closeable { /** * Returns the assigned dynamic protocol/service multiplexer (PSM) value for the listening L2CAP * Connection-oriented Channel (CoC) server socket. This server socket must be returned by the - * {@link BluetoothAdapter.listenUsingL2capChannel()} or {@link - * BluetoothAdapter.listenUsingInsecureL2capChannel()}. The returned value is undefined if this + * {@link BluetoothAdapter#listenUsingL2capChannel()} or {@link + * BluetoothAdapter#listenUsingInsecureL2capChannel()}. The returned value is undefined if this * method is called on non-L2CAP server sockets. * * @return the assigned PSM or LE_PSM value depending on transport diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index ca96d5026247..8913748e0c48 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -3955,6 +3955,12 @@ public class Intent implements Parcelable, Cloneable { public static final String EXTRA_LTE_EARFCN_RSRP_BOOST = "LteEarfcnRsrpBoost"; /** + * An parcelable extra used with {@link #ACTION_SERVICE_STATE} representing the service state. + * @hide + */ + public static final String EXTRA_SERVICE_STATE = "android.intent.extra.SERVICE_STATE"; + + /** * The name of the extra used to define the text to be processed, as a * CharSequence. Note that this may be a styled CharSequence, so you must use * {@link Bundle#getCharSequence(String) Bundle.getCharSequence()} to retrieve it. diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index 5926af6f9082..0a4f4eb8506c 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -1408,5 +1408,13 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { * @attr ref android.R.styleable#AndroidManifestLayout_minHeight */ public final int minHeight; + + /** + * Returns if this {@link WindowLayout} has specified bounds. + * @hide + */ + public boolean hasSpecifiedSize() { + return width >= 0 || height >= 0 || widthFraction >= 0 || heightFraction >= 0; + } } } diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 24675d301f4a..1673c28279b0 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -2440,17 +2440,17 @@ public class PackageParser { } - final int NS = PermissionManager.SPLIT_PERMISSIONS.length; + final int NS = PermissionManager.SPLIT_PERMISSIONS.size(); for (int is=0; is<NS; is++) { final PermissionManager.SplitPermissionInfo spi = - PermissionManager.SPLIT_PERMISSIONS[is]; + PermissionManager.SPLIT_PERMISSIONS.get(is); if (pkg.applicationInfo.targetSdkVersion >= spi.getTargetSdk() - || !pkg.requestedPermissions.contains(spi.getRootPermission())) { + || !pkg.requestedPermissions.contains(spi.getSplitPermission())) { continue; } - final String[] newPerms = spi.getNewPermissions(); - for (int in = 0; in < newPerms.length; in++) { - final String perm = newPerms[in]; + final List<String> newPerms = spi.getNewPermissions(); + for (int in = 0; in < newPerms.size(); in++) { + final String perm = newPerms.get(in); if (!pkg.requestedPermissions.contains(perm)) { pkg.requestedPermissions.add(perm); } diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index 486b054e1c00..d4dc181e5eec 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -2090,7 +2090,7 @@ public abstract class CameraMetadata<TKey> { /** * <p>Optimized for dim settings where the main light source - * is a flame.</p> + * is a candle.</p> * @see CaptureRequest#CONTROL_SCENE_MODE */ public static final int CONTROL_SCENE_MODE_CANDLELIGHT = 15; diff --git a/core/java/android/hardware/location/ContextHubClient.java b/core/java/android/hardware/location/ContextHubClient.java index 917644db4202..6609b76faac1 100644 --- a/core/java/android/hardware/location/ContextHubClient.java +++ b/core/java/android/hardware/location/ContextHubClient.java @@ -133,6 +133,7 @@ public class ContextHubClient implements Closeable { * * @hide */ + @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public boolean registerIntent(@NonNull PendingIntent intent, long nanoAppId) { // TODO: Implement this return false; @@ -146,6 +147,7 @@ public class ContextHubClient implements Closeable { * * @hide */ + @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public boolean unregisterIntent(@NonNull PendingIntent intent) { // TODO: Implement this return false; diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java index 36f3586aec2a..f94d69b7fa2d 100644 --- a/core/java/android/hardware/location/ContextHubManager.java +++ b/core/java/android/hardware/location/ContextHubManager.java @@ -749,6 +749,7 @@ public final class ContextHubManager { * * @see ContextHubClientCallback */ + @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) @NonNull public ContextHubClient createClient( @NonNull ContextHubInfo hubInfo, @NonNull ContextHubClientCallback callback, @NonNull @CallbackExecutor Executor executor) { @@ -785,6 +786,7 @@ public final class ContextHubManager { * * @see ContextHubClientCallback */ + @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) @NonNull public ContextHubClient createClient( @NonNull ContextHubInfo hubInfo, @NonNull ContextHubClientCallback callback) { return createClient(hubInfo, callback, new HandlerExecutor(Handler.getMain())); @@ -811,6 +813,7 @@ public final class ContextHubManager { * * @hide */ + @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) @NonNull public ContextHubClient createClient( @NonNull PendingIntent intent, @NonNull ContextHubInfo hubInfo, @NonNull ContextHubClientCallback callback, @@ -835,6 +838,7 @@ public final class ContextHubManager { * * @hide */ + @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) @NonNull public ContextHubClient createClient( @NonNull PendingIntent intent, @NonNull ContextHubInfo hubInfo, @NonNull ContextHubClientCallback callback) { diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index ee386127bd4d..c496ff4a3bd5 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -83,6 +83,7 @@ import java.util.Map; @SystemService(Context.CONNECTIVITY_SERVICE) public class ConnectivityManager { private static final String TAG = "ConnectivityManager"; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); /** * A change in network connectivity has occurred. A default connection has either @@ -2493,6 +2494,7 @@ public class ConnectivityManager { * {@hide} */ public void reportInetCondition(int networkType, int percentage) { + printStackTrace(); try { mService.reportInetCondition(networkType, percentage); } catch (RemoteException e) { @@ -2513,6 +2515,7 @@ public class ConnectivityManager { */ @Deprecated public void reportBadNetwork(Network network) { + printStackTrace(); try { // One of these will be ignored because it matches system's current state. // The other will trigger the necessary reevaluation. @@ -2535,6 +2538,7 @@ public class ConnectivityManager { * Internet using {@code network} or {@code false} if not. */ public void reportNetworkConnectivity(Network network, boolean hasConnectivity) { + printStackTrace(); try { mService.reportNetworkConnectivity(network, hasConnectivity); } catch (RemoteException e) { @@ -3073,6 +3077,7 @@ public class ConnectivityManager { private NetworkRequest sendRequestForNetwork(NetworkCapabilities need, NetworkCallback callback, int timeoutMs, int action, int legacyType, CallbackHandler handler) { + printStackTrace(); checkCallbackNotNull(callback); Preconditions.checkArgument(action == REQUEST || need != null, "null NetworkCapabilities"); final NetworkRequest request; @@ -3332,6 +3337,7 @@ public class ConnectivityManager { * {@link NetworkCapabilities#NET_CAPABILITY_CAPTIVE_PORTAL}. */ public void requestNetwork(NetworkRequest request, PendingIntent operation) { + printStackTrace(); checkPendingIntentNotNull(operation); try { mService.pendingRequestForNetwork(request.networkCapabilities, operation); @@ -3355,6 +3361,7 @@ public class ConnectivityManager { * corresponding NetworkRequest you'd like to remove. Cannot be null. */ public void releaseNetworkRequest(PendingIntent operation) { + printStackTrace(); checkPendingIntentNotNull(operation); try { mService.releasePendingNetworkRequest(operation); @@ -3439,6 +3446,7 @@ public class ConnectivityManager { */ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerNetworkCallback(NetworkRequest request, PendingIntent operation) { + printStackTrace(); checkPendingIntentNotNull(operation); try { mService.pendingListenForNetwork(request.networkCapabilities, operation); @@ -3520,6 +3528,7 @@ public class ConnectivityManager { * @param networkCallback The {@link NetworkCallback} used when making the request. */ public void unregisterNetworkCallback(NetworkCallback networkCallback) { + printStackTrace(); checkCallbackNotNull(networkCallback); final List<NetworkRequest> reqs = new ArrayList<>(); // Find all requests associated to this callback and stop callback triggers immediately. @@ -3948,4 +3957,19 @@ public class ConnectivityManager { throw e.rethrowFromSystemServer(); } } + + private void printStackTrace() { + if (DEBUG) { + final StackTraceElement[] callStack = Thread.currentThread().getStackTrace(); + final StringBuffer sb = new StringBuffer(); + for (int i = 3; i < callStack.length; i++) { + final String stackTrace = callStack[i].toString(); + if (stackTrace == null || stackTrace.contains("android.os")) { + break; + } + sb.append(" [").append(stackTrace).append("]"); + } + Log.d(TAG, "StackLog:" + sb.toString()); + } + } } diff --git a/core/java/android/os/DumpstateOptions.java b/core/java/android/os/DumpstateOptions.java new file mode 100644 index 000000000000..53037b2499cd --- /dev/null +++ b/core/java/android/os/DumpstateOptions.java @@ -0,0 +1,57 @@ +/* + * 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 android.os; + +/** + * Options passed to dumpstate service. + * + * @hide + */ +public final class DumpstateOptions implements Parcelable { + // If true the caller can get callbacks with per-section + // progress details. + private final boolean mGetSectionDetails; + // Name of the caller. + private final String mName; + + public DumpstateOptions(Parcel in) { + mGetSectionDetails = in.readBoolean(); + mName = in.readString(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeBoolean(mGetSectionDetails); + out.writeString(mName); + } + + public static final Parcelable.Creator<DumpstateOptions> CREATOR = + new Parcelable.Creator<DumpstateOptions>() { + public DumpstateOptions createFromParcel(Parcel in) { + return new DumpstateOptions(in); + } + + public DumpstateOptions[] newArray(int size) { + return new DumpstateOptions[size]; + } + }; +} diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java index df3aae2c901c..5f656207255a 100644 --- a/core/java/android/os/GraphicsEnvironment.java +++ b/core/java/android/os/GraphicsEnvironment.java @@ -55,7 +55,7 @@ public class GraphicsEnvironment { * Set up GraphicsEnvironment */ public void setup(Context context, Bundle coreSettings) { - setupGpuLayers(context, coreSettings); + setupGpuLayers(context); setupAngle(context, coreSettings); chooseDriver(context); } @@ -81,54 +81,27 @@ public class GraphicsEnvironment { } /** - * Return the debug layer app's on-disk and in-APK lib directories - */ - private static String getDebugLayerAppPaths(Context context, String app) { - ApplicationInfo appInfo; - try { - appInfo = context.getPackageManager().getApplicationInfo( - app, PackageManager.MATCH_ALL); - } catch (PackageManager.NameNotFoundException e) { - Log.w(TAG, "Debug layer app '" + app + "' not installed"); - - return null; - } - - String abi = chooseAbi(appInfo); - - StringBuilder sb = new StringBuilder(); - sb.append(appInfo.nativeLibraryDir) - .append(File.pathSeparator); - sb.append(appInfo.sourceDir) - .append("!/lib/") - .append(abi); - String paths = sb.toString(); - - if (DEBUG) Log.v(TAG, "Debug layer app libs: " + paths); - - return paths; - } - - /** * Set up layer search paths for all apps * If debuggable, check for additional debug settings */ - private void setupGpuLayers(Context context, Bundle coreSettings) { + private void setupGpuLayers(Context context) { String layerPaths = ""; // Only enable additional debug functionality if the following conditions are met: - // 1. App is debuggable or device is rooted + // 1. App is debuggable // 2. ENABLE_GPU_DEBUG_LAYERS is true // 3. Package name is equal to GPU_DEBUG_APP - if (isDebuggable(context) || (getCanLoadSystemLibraries() == 1)) { + if (isDebuggable(context)) { - int enable = coreSettings.getInt(Settings.Global.ENABLE_GPU_DEBUG_LAYERS, 0); + int enable = Settings.Global.getInt(context.getContentResolver(), + Settings.Global.ENABLE_GPU_DEBUG_LAYERS, 0); if (enable != 0) { - String gpuDebugApp = coreSettings.getString(Settings.Global.GPU_DEBUG_APP); + String gpuDebugApp = Settings.Global.getString(context.getContentResolver(), + Settings.Global.GPU_DEBUG_APP); String packageName = context.getPackageName(); @@ -142,22 +115,8 @@ public class GraphicsEnvironment { // the layers specified by the app. layerPaths = mDebugLayerPath + ":"; - - // If there is a debug layer app specified, add its path. - String gpuDebugLayerApp = - coreSettings.getString(Settings.Global.GPU_DEBUG_LAYER_APP); - - if (gpuDebugLayerApp != null && !gpuDebugLayerApp.isEmpty()) { - Log.i(TAG, "GPU debug layer app: " + gpuDebugLayerApp); - String paths = getDebugLayerAppPaths(context, gpuDebugLayerApp); - if (paths != null) { - // Append the path so files placed in the app's base directory will - // override the external path - layerPaths += paths + ":"; - } - } - - String layers = coreSettings.getString(Settings.Global.GPU_DEBUG_LAYERS); + String layers = Settings.Global.getString(context.getContentResolver(), + Settings.Global.GPU_DEBUG_LAYERS); Log.i(TAG, "Debug layer list: " + layers); if (layers != null && !layers.isEmpty()) { @@ -331,7 +290,6 @@ public class GraphicsEnvironment { return null; } - private static native int getCanLoadSystemLibraries(); private static native void setLayerPaths(ClassLoader classLoader, String layerPaths); private static native void setDebugLayers(String layers); private static native void setDriverPath(String path); diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java index 274b36aa1807..5d5e5e233b35 100644 --- a/core/java/android/os/Message.java +++ b/core/java/android/os/Message.java @@ -19,6 +19,8 @@ package android.os; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; +import com.android.internal.annotations.VisibleForTesting; + /** * * Defines a message containing a description and arbitrary data object that can be @@ -111,7 +113,13 @@ public final class Message implements Parcelable { /*package*/ int flags; - /*package*/ long when; + /** + * The targeted delivery time of this message. The time-base is + * {@link SystemClock#uptimeMillis}. + * @hide Only for use within the tests. + */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + public long when; /*package*/ Bundle data; diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java index aa44eb7e3a26..7ffb22f189b8 100644 --- a/core/java/android/permission/PermissionManager.java +++ b/core/java/android/permission/PermissionManager.java @@ -25,7 +25,9 @@ import android.content.Context; import com.android.internal.annotations.Immutable; import java.util.Arrays; +import java.util.Collections; import java.util.List; +import java.util.Objects; /** * System level service for accessing the permission capabilities of the platform. @@ -40,26 +42,28 @@ public final class PermissionManager { * * @hide */ - public static final SplitPermissionInfo[] SPLIT_PERMISSIONS = new SplitPermissionInfo[]{ + public static final List<SplitPermissionInfo> SPLIT_PERMISSIONS = Arrays.asList( // READ_EXTERNAL_STORAGE is always required when an app requests // WRITE_EXTERNAL_STORAGE, because we can't have an app that has // write access without read access. The hack here with the target // target SDK version ensures that this grant is always done. new SplitPermissionInfo(android.Manifest.permission.WRITE_EXTERNAL_STORAGE, - new String[]{android.Manifest.permission.READ_EXTERNAL_STORAGE}, + Collections.singletonList(android.Manifest.permission.READ_EXTERNAL_STORAGE), android.os.Build.VERSION_CODES.CUR_DEVELOPMENT + 1), new SplitPermissionInfo(android.Manifest.permission.READ_CONTACTS, - new String[]{android.Manifest.permission.READ_CALL_LOG}, + Collections.singletonList(android.Manifest.permission.READ_CALL_LOG), android.os.Build.VERSION_CODES.JELLY_BEAN), new SplitPermissionInfo(android.Manifest.permission.WRITE_CONTACTS, - new String[]{android.Manifest.permission.WRITE_CALL_LOG}, + Collections.singletonList(android.Manifest.permission.WRITE_CALL_LOG), android.os.Build.VERSION_CODES.JELLY_BEAN), new SplitPermissionInfo(Manifest.permission.ACCESS_FINE_LOCATION, - new String[]{android.Manifest.permission.ACCESS_BACKGROUND_LOCATION}, + Collections.singletonList( + android.Manifest.permission.ACCESS_BACKGROUND_LOCATION), android.os.Build.VERSION_CODES.P0), new SplitPermissionInfo(Manifest.permission.ACCESS_COARSE_LOCATION, - new String[]{android.Manifest.permission.ACCESS_BACKGROUND_LOCATION}, - android.os.Build.VERSION_CODES.P0)}; + Collections.singletonList( + android.Manifest.permission.ACCESS_BACKGROUND_LOCATION), + android.os.Build.VERSION_CODES.P0)); private final @NonNull Context mContext; @@ -74,7 +78,7 @@ public final class PermissionManager { } /** - * Get list of permissions that have been split into more granular or dependent permissions. + * 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 * {@link Manifest.permission#ACCESS_COARSE_LOCATION} could access he location while it was in @@ -82,7 +86,7 @@ public final class PermissionManager { * 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 * such an old app asks for a location permission (i.e. the - * {@link SplitPermissionInfo#getRootPermission()}), then the + * {@link SplitPermissionInfo#getSplitPermission()}), then the * {@link Manifest.permission#ACCESS_BACKGROUND_LOCATION} permission (inside * {@{@link SplitPermissionInfo#getNewPermissions}) is added. * @@ -91,8 +95,9 @@ public final class PermissionManager { * * @return All permissions that are split. */ - public @NonNull List<SplitPermissionInfo> getSplitPermissions() { - return Arrays.asList(SPLIT_PERMISSIONS); + public @NonNull + List<SplitPermissionInfo> getSplitPermissions() { + return SPLIT_PERMISSIONS; } /** @@ -101,21 +106,35 @@ public final class PermissionManager { */ @Immutable public static final class SplitPermissionInfo { - private final @NonNull String mRootPerm; - private final @NonNull String[] mNewPerms; + private final @NonNull String mSplitPerm; + private final @NonNull List<String> mNewPerms; private final int mTargetSdk; + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SplitPermissionInfo that = (SplitPermissionInfo) o; + return mTargetSdk == that.mTargetSdk + && Objects.equals(mSplitPerm, that.mSplitPerm); + } + + @Override + public int hashCode() { + return Objects.hash(mSplitPerm, mTargetSdk); + } + /** * Get the permission that is split. */ - public @NonNull String getRootPermission() { - return mRootPerm; + public @NonNull String getSplitPermission() { + return mSplitPerm; } /** * Get the permissions that are added. */ - public @NonNull String[] getNewPermissions() { + public @NonNull List<String> getNewPermissions() { return mNewPerms; } @@ -126,9 +145,9 @@ public final class PermissionManager { return mTargetSdk; } - private SplitPermissionInfo(@NonNull String rootPerm, @NonNull String[] newPerms, + private SplitPermissionInfo(@NonNull String rootPerm, @NonNull List<String> newPerms, int targetSdk) { - mRootPerm = rootPerm; + mSplitPerm = rootPerm; mNewPerms = newPerms; mTargetSdk = targetSdk; } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index c6e457436025..acb75778e08e 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -11631,12 +11631,6 @@ public final class Settings { public static final String GPU_DEBUG_LAYERS = "gpu_debug_layers"; /** - * Addition app for GPU layer discovery - * @hide - */ - public static final String GPU_DEBUG_LAYER_APP = "gpu_debug_layer_app"; - - /** * Control whether the process CPU usage meter should be shown. * * @deprecated This functionality is no longer available as of diff --git a/core/java/android/service/textclassifier/TextClassifierService.java b/core/java/android/service/textclassifier/TextClassifierService.java index b461c0daaca5..7af9db8ebf32 100644 --- a/core/java/android/service/textclassifier/TextClassifierService.java +++ b/core/java/android/service/textclassifier/TextClassifierService.java @@ -405,8 +405,8 @@ public abstract class TextClassifierService extends Service { PackageManager.MATCH_SYSTEM_ONLY); if ((ri == null) || (ri.serviceInfo == null)) { - Slog.w(LOG_TAG, String.format("Package or service not found in package %s", - packageName)); + Slog.w(LOG_TAG, String.format("Package or service not found in package %s for user %d", + packageName, context.getUserId())); return null; } final ServiceInfo si = ri.serviceInfo; diff --git a/core/java/android/text/style/TextAppearanceSpan.java b/core/java/android/text/style/TextAppearanceSpan.java index 2dc4f6001a06..648bd1bba20c 100644 --- a/core/java/android/text/style/TextAppearanceSpan.java +++ b/core/java/android/text/style/TextAppearanceSpan.java @@ -29,8 +29,33 @@ import android.text.TextPaint; import android.text.TextUtils; /** - * Sets the text color, size, style, and typeface to match a TextAppearance - * resource. + * Sets the text appearance using the given + * {@link android.R.styleable#TextAppearance TextAppearance} attributes. + * By default {@link TextAppearanceSpan} only changes the specified attributes in XML. + * {@link android.R.styleable#TextAppearance_textColorHighlight textColorHighlight}, + * {@link android.R.styleable#TextAppearance_textColorHint textColorHint}, + * {@link android.R.styleable#TextAppearance_textAllCaps textAllCaps} and + * {@link android.R.styleable#TextAppearance_fallbackLineSpacing fallbackLineSpacing} + * are not supported by {@link TextAppearanceSpan}. + * + * {@see android.widget.TextView#setTextAppearance(int)} + * + * @attr ref android.R.styleable#TextAppearance_fontFamily + * @attr ref android.R.styleable#TextAppearance_textColor + * @attr ref android.R.styleable#TextAppearance_textColorLink + * @attr ref android.R.styleable#TextAppearance_textFontWeight + * @attr ref android.R.styleable#TextAppearance_textSize + * @attr ref android.R.styleable#TextAppearance_textStyle + * @attr ref android.R.styleable#TextAppearance_typeface + * @attr ref android.R.styleable#TextAppearance_shadowColor + * @attr ref android.R.styleable#TextAppearance_shadowDx + * @attr ref android.R.styleable#TextAppearance_shadowDy + * @attr ref android.R.styleable#TextAppearance_shadowRadius + * @attr ref android.R.styleable#TextAppearance_elegantTextHeight + * @attr ref android.R.styleable#TextAppearance_letterSpacing + * @attr ref android.R.styleable#TextAppearance_fontFeatureSettings + * @attr ref android.R.styleable#TextAppearance_fontVariationSettings + * */ public class TextAppearanceSpan extends MetricAffectingSpan implements ParcelableSpan { private final String mFamilyName; diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java index f428c798d7f5..e9f1edf52b5f 100644 --- a/core/java/android/view/DisplayCutout.java +++ b/core/java/android/view/DisplayCutout.java @@ -245,9 +245,8 @@ public final class DisplayCutout { * passed, it's treated as an empty rectangle (0,0)-(0,0). */ // TODO(b/73953958): @VisibleForTesting(visibility = PRIVATE) - public DisplayCutout( - Insets safeInsets, @Nullable Rect boundLeft, @Nullable Rect boundTop, - @Nullable Rect boundRight, @Nullable Rect boundBottom) { + public DisplayCutout(@NonNull Insets safeInsets, @Nullable Rect boundLeft, + @Nullable Rect boundTop, @Nullable Rect boundRight, @Nullable Rect boundBottom) { this(safeInsets.toRect(), boundLeft, boundTop, boundRight, boundBottom, true); } @@ -262,7 +261,7 @@ public final class DisplayCutout { */ // TODO(b/73953958): @VisibleForTesting(visibility = PRIVATE) @Deprecated - public DisplayCutout(Rect safeInsets, List<Rect> boundingRects) { + public DisplayCutout(@Nullable Rect safeInsets, @Nullable List<Rect> boundingRects) { this(safeInsets, extractBoundsFromList(safeInsets, boundingRects), true /* copyArguments */); } @@ -313,18 +312,20 @@ public final class DisplayCutout { for (int i = 0; i < sortedBounds.length; ++i) { sortedBounds[i] = ZERO_RECT; } - for (Rect bound : boundingRects) { - // There will be at most one non-functional area per short edge of the device, and none - // on the long edges, so either safeInsets.right or safeInsets.bottom must be 0. - // TODO(b/117199965): Refine the logic to handle edge cases. - if (bound.left == 0) { - sortedBounds[BOUNDS_POSITION_LEFT] = bound; - } else if (bound.top == 0) { - sortedBounds[BOUNDS_POSITION_TOP] = bound; - } else if (safeInsets.right > 0) { - sortedBounds[BOUNDS_POSITION_RIGHT] = bound; - } else if (safeInsets.bottom > 0) { - sortedBounds[BOUNDS_POSITION_BOTTOM] = bound; + if (safeInsets != null && boundingRects != null) { + for (Rect bound : boundingRects) { + // There is at most one non-functional area per short edge of the device, but none + // on the long edges, so either safeInsets.right or safeInsets.bottom must be 0. + // TODO(b/117199965): Refine the logic to handle edge cases. + if (bound.left == 0) { + sortedBounds[BOUNDS_POSITION_LEFT] = bound; + } else if (bound.top == 0) { + sortedBounds[BOUNDS_POSITION_TOP] = bound; + } else if (safeInsets.right > 0) { + sortedBounds[BOUNDS_POSITION_RIGHT] = bound; + } else if (safeInsets.bottom > 0) { + sortedBounds[BOUNDS_POSITION_BOTTOM] = bound; + } } } return sortedBounds; @@ -389,6 +390,7 @@ public final class DisplayCutout { * @return a list of bounding {@code Rect}s, one for each display cutout area. No empty Rect is * returned. */ + @NonNull public List<Rect> getBoundingRects() { List<Rect> result = new ArrayList<>(); for (Rect bound : getBoundingRectsAll()) { diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 9d06680c4c07..f74c2341d816 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -3521,7 +3521,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener Typeface mFontTypeface = null; boolean mFontFamilyExplicit = false; int mTypefaceIndex = -1; - int mStyleIndex = -1; + int mTextStyle = 0; int mFontWeight = -1; boolean mAllCaps = false; int mShadowColor = 0; @@ -3547,7 +3547,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener + " mFontTypeface:" + mFontTypeface + "\n" + " mFontFamilyExplicit:" + mFontFamilyExplicit + "\n" + " mTypefaceIndex:" + mTypefaceIndex + "\n" - + " mStyleIndex:" + mStyleIndex + "\n" + + " mTextStyle:" + mTextStyle + "\n" + " mFontWeight:" + mFontWeight + "\n" + " mAllCaps:" + mAllCaps + "\n" + " mShadowColor:" + mShadowColor + "\n" @@ -3672,7 +3672,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener attributes.mFontFamilyExplicit = true; break; case com.android.internal.R.styleable.TextAppearance_textStyle: - attributes.mStyleIndex = appearance.getInt(attr, attributes.mStyleIndex); + attributes.mTextStyle = appearance.getInt(attr, attributes.mTextStyle); break; case com.android.internal.R.styleable.TextAppearance_textFontWeight: attributes.mFontWeight = appearance.getInt(attr, attributes.mFontWeight); @@ -3742,7 +3742,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener attributes.mFontFamily = null; } setTypefaceFromAttrs(attributes.mFontTypeface, attributes.mFontFamily, - attributes.mTypefaceIndex, attributes.mStyleIndex, attributes.mFontWeight); + attributes.mTypefaceIndex, attributes.mTextStyle, attributes.mFontWeight); if (attributes.mShadowColor != 0) { setShadowLayer(attributes.mShadowRadius, attributes.mShadowDx, attributes.mShadowDy, diff --git a/core/java/com/android/internal/os/LooperStats.java b/core/java/com/android/internal/os/LooperStats.java index e4724ff45b9e..8dc97fe86eb3 100644 --- a/core/java/com/android/internal/os/LooperStats.java +++ b/core/java/com/android/internal/os/LooperStats.java @@ -66,6 +66,7 @@ public class LooperStats implements Looper.Observer { session = session == null ? new DispatchSession() : session; session.startTimeMicro = getElapsedRealtimeMicro(); session.cpuStartMicro = getThreadTimeMicro(); + session.systemUptimeMillis = getSystemUptimeMillis(); return session; } @@ -85,12 +86,18 @@ public class LooperStats implements Looper.Observer { entry.messageCount++; if (session != DispatchSession.NOT_SAMPLED) { entry.recordedMessageCount++; - long latency = getElapsedRealtimeMicro() - session.startTimeMicro; - long cpuUsage = getThreadTimeMicro() - session.cpuStartMicro; + final long latency = getElapsedRealtimeMicro() - session.startTimeMicro; + final long cpuUsage = getThreadTimeMicro() - session.cpuStartMicro; entry.totalLatencyMicro += latency; entry.maxLatencyMicro = Math.max(entry.maxLatencyMicro, latency); entry.cpuUsageMicro += cpuUsage; entry.maxCpuUsageMicro = Math.max(entry.maxCpuUsageMicro, cpuUsage); + if (msg.getWhen() > 0) { + final long delay = Math.max(0L, session.systemUptimeMillis - msg.getWhen()); + entry.delayMillis += delay; + entry.maxDelayMillis = Math.max(entry.maxDelayMillis, delay); + entry.recordedDelayMessageCount++; + } } } } @@ -206,6 +213,10 @@ public class LooperStats implements Looper.Observer { return SystemClock.elapsedRealtimeNanos() / 1000; } + protected long getSystemUptimeMillis() { + return SystemClock.uptimeMillis(); + } + protected boolean shouldCollectDetailedData() { return ThreadLocalRandom.current().nextInt() % mSamplingInterval == 0; } @@ -214,6 +225,7 @@ public class LooperStats implements Looper.Observer { static final DispatchSession NOT_SAMPLED = new DispatchSession(); public long startTimeMicro; public long cpuStartMicro; + public long systemUptimeMillis; } private static class Entry { @@ -228,6 +240,9 @@ public class LooperStats implements Looper.Observer { public long maxLatencyMicro; public long cpuUsageMicro; public long maxCpuUsageMicro; + public long recordedDelayMessageCount; + public long delayMillis; + public long maxDelayMillis; Entry(Message msg, boolean isInteractive) { this.workSourceUid = msg.workSourceUid; @@ -251,6 +266,9 @@ public class LooperStats implements Looper.Observer { maxLatencyMicro = 0; cpuUsageMicro = 0; maxCpuUsageMicro = 0; + delayMillis = 0; + maxDelayMillis = 0; + recordedDelayMessageCount = 0; } static int idFor(Message msg, boolean isInteractive) { @@ -281,6 +299,9 @@ public class LooperStats implements Looper.Observer { public final long maxLatencyMicros; public final long cpuUsageMicros; public final long maxCpuUsageMicros; + public final long maxDelayMillis; + public final long delayMillis; + public final long recordedDelayMessageCount; ExportedEntry(Entry entry) { this.workSourceUid = entry.workSourceUid; @@ -301,6 +322,9 @@ public class LooperStats implements Looper.Observer { this.maxLatencyMicros = entry.maxLatencyMicro; this.cpuUsageMicros = entry.cpuUsageMicro; this.maxCpuUsageMicros = entry.maxCpuUsageMicro; + this.delayMillis = entry.delayMillis; + this.maxDelayMillis = entry.maxDelayMillis; + this.recordedDelayMessageCount = entry.recordedDelayMessageCount; } } } diff --git a/core/java/com/android/internal/os/ProcessCpuTracker.java b/core/java/com/android/internal/os/ProcessCpuTracker.java index bf31c7d8ad85..1ee4269d974b 100644 --- a/core/java/com/android/internal/os/ProcessCpuTracker.java +++ b/core/java/com/android/internal/os/ProcessCpuTracker.java @@ -29,7 +29,6 @@ import android.util.Slog; import com.android.internal.util.FastPrintWriter; import libcore.io.IoUtils; -import libcore.io.Libcore; import java.io.File; import java.io.FileInputStream; diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java index 0a787b99c0ac..d68e8f88fc5a 100644 --- a/core/java/com/android/server/SystemConfig.java +++ b/core/java/com/android/server/SystemConfig.java @@ -331,13 +331,11 @@ public class SystemConfig { readPermissions(Environment.buildPath( Environment.getOemDirectory(), "etc", "permissions"), oemPermissionFlag); - // Allow Product to customize system configs around libs, features, permissions and apps - int productPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_PERMISSIONS | - ALLOW_APP_CONFIGS | ALLOW_PRIVAPP_PERMISSIONS; + // Allow Product to customize all system configs readPermissions(Environment.buildPath( - Environment.getProductDirectory(), "etc", "sysconfig"), productPermissionFlag); + Environment.getProductDirectory(), "etc", "sysconfig"), ALLOW_ALL); readPermissions(Environment.buildPath( - Environment.getProductDirectory(), "etc", "permissions"), productPermissionFlag); + Environment.getProductDirectory(), "etc", "permissions"), ALLOW_ALL); // Allow /product_services to customize system configs around libs, features, permissions // and apps. diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp index 92235ad494a7..b70485d9a0ea 100644 --- a/core/jni/android_os_GraphicsEnvironment.cpp +++ b/core/jni/android_os_GraphicsEnvironment.cpp @@ -23,10 +23,6 @@ namespace { -int getCanLoadSystemLibraries_native() { - return android::GraphicsEnv::getInstance().getCanLoadSystemLibraries(); -} - void setDriverPath(JNIEnv* env, jobject clazz, jstring path) { ScopedUtfChars pathChars(env, path); android::GraphicsEnv::getInstance().setDriverPath(pathChars.c_str()); @@ -55,7 +51,6 @@ void setDebugLayers_native(JNIEnv* env, jobject clazz, jstring layers) { } const JNINativeMethod g_methods[] = { - { "getCanLoadSystemLibraries", "()I", reinterpret_cast<void*>(getCanLoadSystemLibraries_native) }, { "setDriverPath", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDriverPath) }, { "setAngleInfo", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V", reinterpret_cast<void*>(setAngleInfo_native) }, { "setLayerPaths", "(Ljava/lang/ClassLoader;Ljava/lang/String;)V", reinterpret_cast<void*>(setLayerPaths_native) }, diff --git a/core/proto/android/internal/powerprofile.proto b/core/proto/android/internal/powerprofile.proto index 9dd5e7ee556c..b0c8b5638a72 100644 --- a/core/proto/android/internal/powerprofile.proto +++ b/core/proto/android/internal/powerprofile.proto @@ -35,7 +35,7 @@ message PowerProfileProto { repeated double core_power = 5; } - repeated CpuCluster cpu_cluster = 41; + repeated CpuCluster cpu_cluster = 40; optional double wifi_scan = 4; @@ -85,27 +85,27 @@ message PowerProfileProto { optional double ambient_display = 27; - optional double screen_on = 29; + optional double screen_on = 28; - optional double radio_on = 30; + optional double radio_on = 29; - optional double radio_scanning = 31; + optional double radio_scanning = 30; - optional double radio_active = 32; + optional double radio_active = 31; - optional double screen_full = 33; + optional double screen_full = 32; - optional double audio = 34; + optional double audio = 33; - optional double video = 35; + optional double video = 34; - optional double flashlight = 36; + optional double flashlight = 35; - optional double memory = 37; + optional double memory = 36; - optional double camera = 38; + optional double camera = 37; - optional double wifi_batched_scan = 39; + optional double wifi_batched_scan = 38; - optional double battery_capacity = 40; + optional double battery_capacity = 39; } diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto index 47dbc0716c94..a02602ee5d99 100644 --- a/core/proto/android/providers/settings/global.proto +++ b/core/proto/android/providers/settings/global.proto @@ -397,10 +397,9 @@ message GlobalSettingsProto { // Ordered GPU debug layer list // i.e. <layer1>:<layer2>:...:<layerN> optional SettingProto debug_layers = 2 [ (android.privacy).dest = DEST_AUTOMATIC ]; + // App will load ANGLE instead of native GLES drivers. optional SettingProto angle_enabled_app = 3; - // App that can provide layer libraries. - optional SettingProto debug_layer_app = 4; } optional Gpu gpu = 59; diff --git a/proto/src/stats_enums.proto b/core/proto/android/stats/enums.proto index 6c892cfeae6c..2320a01b324c 100644 --- a/proto/src/stats_enums.proto +++ b/core/proto/android/stats/enums.proto @@ -16,8 +16,7 @@ syntax = "proto2"; -package android.os.statsd; -option java_package = "com.android.os"; +package android.stats; option java_outer_classname = "StatsEnums"; enum EventType { diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index d2c00f2a9547..401ffa36f5bd 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -138,7 +138,6 @@ <protected-broadcast android:name="android.bluetooth.device.action.MAS_INSTANCE" /> <protected-broadcast android:name="android.bluetooth.device.action.ALIAS_CHANGED" /> <protected-broadcast android:name="android.bluetooth.device.action.FOUND" /> - <protected-broadcast android:name="android.bluetooth.device.action.DISAPPEARED" /> <protected-broadcast android:name="android.bluetooth.device.action.CLASS_CHANGED" /> <protected-broadcast android:name="android.bluetooth.device.action.ACL_CONNECTED" /> <protected-broadcast android:name="android.bluetooth.device.action.ACL_DISCONNECT_REQUESTED" /> diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index 632c37fd54d2..60abd9468179 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -450,7 +450,6 @@ public class SettingsBackupTest { Settings.Global.GPU_DEBUG_APP, Settings.Global.GPU_DEBUG_LAYERS, Settings.Global.ANGLE_ENABLED_APP, - Settings.Global.GPU_DEBUG_LAYER_APP, Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING, Settings.Global.INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT, Settings.Global.INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS, diff --git a/core/tests/coretests/src/android/view/DisplayCutoutTest.java b/core/tests/coretests/src/android/view/DisplayCutoutTest.java index c8d994c4f6c1..8e4f2cd2fbff 100644 --- a/core/tests/coretests/src/android/view/DisplayCutoutTest.java +++ b/core/tests/coretests/src/android/view/DisplayCutoutTest.java @@ -103,6 +103,18 @@ public class DisplayCutoutTest { equalTo(new Rect[]{ZERO_RECT, boundTop, ZERO_RECT, boundBottom})); } + @Test + public void testExtractBoundsFromList_nullBoundingRects() { + Rect safeInsets = new Rect(0, 0, 0, 0); + assertThat(extractBoundsFromList(safeInsets, null /* boundingRects */), + equalTo(new Rect[]{ZERO_RECT, ZERO_RECT, ZERO_RECT, ZERO_RECT})); + } + + @Test + public void testExtractBoundsFromList_nullSafeInsets() { + assertThat(extractBoundsFromList(null /* safeInsets */, Collections.emptyList()), + equalTo(new Rect[]{ZERO_RECT, ZERO_RECT, ZERO_RECT, ZERO_RECT})); + } @Test public void hasCutout() throws Exception { diff --git a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java index 0c8dd9d6ed59..f637b7c7ec32 100644 --- a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java @@ -77,9 +77,13 @@ public final class LooperStatsTest { Message message = mHandlerFirst.obtainMessage(1000); message.workSourceUid = 1000; + message.when = looperStats.getSystemUptimeMillis(); + + looperStats.tickUptime(30); Object token = looperStats.messageDispatchStarting(); looperStats.tickRealtime(100); looperStats.tickThreadTime(10); + looperStats.tickUptime(200); looperStats.messageDispatched(token, message); List<LooperStats.ExportedEntry> entries = looperStats.getEntries(); @@ -98,6 +102,10 @@ public final class LooperStatsTest { assertThat(entry.maxLatencyMicros).isEqualTo(100); assertThat(entry.cpuUsageMicros).isEqualTo(10); assertThat(entry.maxCpuUsageMicros).isEqualTo(10); + assertThat(entry.recordedDelayMessageCount).isEqualTo(1); + assertThat(entry.delayMillis).isEqualTo(30); + assertThat(entry.maxDelayMillis).isEqualTo(30); + } @Test @@ -215,6 +223,56 @@ public final class LooperStatsTest { } @Test + public void testDispatchDelayIsRecorded() { + TestableLooperStats looperStats = new TestableLooperStats(1, 100); + + // Dispatched right on time. + Message message1 = mHandlerFirst.obtainMessage(1000); + message1.when = looperStats.getSystemUptimeMillis(); + Object token1 = looperStats.messageDispatchStarting(); + looperStats.tickUptime(10); + looperStats.messageDispatched(token1, message1); + + // Dispatched 100ms late. + Message message2 = mHandlerFirst.obtainMessage(1000); + message2.when = looperStats.getSystemUptimeMillis() - 100; + Object token2 = looperStats.messageDispatchStarting(); + looperStats.tickUptime(10); + looperStats.messageDispatched(token2, message2); + + // No target dispatching time. + Message message3 = mHandlerFirst.obtainMessage(1000); + message3.when = 0; + Object token3 = looperStats.messageDispatchStarting(); + looperStats.tickUptime(10); + looperStats.messageDispatched(token3, message3); + + // Dispatched too soon (should never happen). + Message message4 = mHandlerFirst.obtainMessage(1000); + message4.when = looperStats.getSystemUptimeMillis() + 200; + Object token4 = looperStats.messageDispatchStarting(); + looperStats.tickUptime(10); + looperStats.messageDispatched(token4, message4); + + // Dispatched 300ms late. + Message message5 = mHandlerFirst.obtainMessage(1000); + message5.when = looperStats.getSystemUptimeMillis() - 300; + Object token5 = looperStats.messageDispatchStarting(); + looperStats.tickUptime(10); + looperStats.messageDispatched(token5, message5); + + List<LooperStats.ExportedEntry> entries = looperStats.getEntries(); + assertThat(entries).hasSize(1); + + LooperStats.ExportedEntry entry = entries.get(0); + assertThat(entry.messageCount).isEqualTo(5); + assertThat(entry.recordedMessageCount).isEqualTo(5); + assertThat(entry.recordedDelayMessageCount).isEqualTo(4); + assertThat(entry.delayMillis).isEqualTo(400); + assertThat(entry.maxDelayMillis).isEqualTo(300); + } + + @Test public void testDataNotCollectedBeforeDeviceStateSet() { TestableLooperStats looperStats = new TestableLooperStats(1, 100); looperStats.setDeviceState(null); @@ -385,6 +443,7 @@ public final class LooperStatsTest { private int mCount; private long mRealtimeMicros; private long mThreadTimeMicros; + private long mUptimeMillis; private int mSamplingInterval; TestableLooperStats(int samplingInterval, int sizeCap) { @@ -401,6 +460,10 @@ public final class LooperStatsTest { mThreadTimeMicros += micros; } + void tickUptime(long millis) { + mUptimeMillis += millis; + } + @Override protected long getElapsedRealtimeMicro() { return INITIAL_MICROS + mRealtimeMicros; @@ -412,6 +475,11 @@ public final class LooperStatsTest { } @Override + protected long getSystemUptimeMillis() { + return INITIAL_MICROS / 1000 + mUptimeMillis; + } + + @Override protected boolean shouldCollectDetailedData() { return mCount++ % mSamplingInterval == 0; } diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java index 8414d6a6b866..b65fb9cd24ec 100644 --- a/graphics/java/android/graphics/ColorSpace.java +++ b/graphics/java/android/graphics/ColorSpace.java @@ -2525,9 +2525,7 @@ public abstract class ColorSpace { gamma == 1.0 ? DoubleUnaryOperator.identity() : x -> Math.pow(x < 0.0 ? 0.0 : x, gamma), min, max, id); - mTransferParameters = gamma == 1.0 ? - new TransferParameters(0.0, 0.0, 1.0, 1.0 + Math.ulp(1.0f), gamma) : - new TransferParameters(1.0, 0.0, 0.0, 0.0, gamma); + mTransferParameters = new TransferParameters(1.0, 0.0, 0.0, 0.0, gamma); } /** diff --git a/graphics/java/android/graphics/drawable/Icon.java b/graphics/java/android/graphics/drawable/Icon.java index 7efe5220873e..f41cc7ee5095 100644 --- a/graphics/java/android/graphics/drawable/Icon.java +++ b/graphics/java/android/graphics/drawable/Icon.java @@ -21,7 +21,6 @@ import android.annotation.DrawableRes; import android.annotation.IdRes; import android.annotation.IntDef; import android.annotation.NonNull; -import android.annotation.SystemApi; import android.annotation.UnsupportedAppUsage; import android.content.ContentResolver; import android.content.Context; @@ -570,7 +569,7 @@ public final class Icon implements Parcelable { * Version of createWithResource that takes Resources. Do not use. * @hide */ - @SystemApi + @UnsupportedAppUsage public static Icon createWithResource(Resources res, @DrawableRes int resId) { if (res == null) { throw new IllegalArgumentException("Resource must not be null."); diff --git a/graphics/java/android/graphics/pdf/PdfEditor.java b/graphics/java/android/graphics/pdf/PdfEditor.java index 3821bc7ab063..21ce1b8392d2 100644 --- a/graphics/java/android/graphics/pdf/PdfEditor.java +++ b/graphics/java/android/graphics/pdf/PdfEditor.java @@ -27,7 +27,6 @@ import android.system.Os; import android.system.OsConstants; import dalvik.system.CloseGuard; import libcore.io.IoUtils; -import libcore.io.Libcore; import java.io.IOException; diff --git a/keystore/OWNERS b/keystore/OWNERS new file mode 100644 index 000000000000..a63ca46df2a6 --- /dev/null +++ b/keystore/OWNERS @@ -0,0 +1,4 @@ +jbires@google.com +jdanis@google.com +robbarnes@google.com +swillden@google.com diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java index 2395b24f7136..b96a5853b9dc 100644 --- a/media/java/android/media/ExifInterface.java +++ b/media/java/android/media/ExifInterface.java @@ -3252,7 +3252,7 @@ public class ExifInterface { int thumbnailLength = jpegInterchangeFormatLengthAttribute.getIntValue(mExifByteOrder); // The following code limits the size of thumbnail size not to overflow EXIF data area. - thumbnailLength = Math.min(thumbnailLength, in.available() - thumbnailOffset); + thumbnailLength = Math.min(thumbnailLength, in.getLength() - thumbnailOffset); if (mMimeType == IMAGE_TYPE_JPEG || mMimeType == IMAGE_TYPE_RAF || mMimeType == IMAGE_TYPE_RW2) { thumbnailOffset += mExifOffset; @@ -3981,6 +3981,10 @@ public class ExifInterface { public double readDouble() throws IOException { return Double.longBitsToDouble(readLong()); } + + public int getLength() { + return mLength; + } } // An output stream to write EXIF data area, which can be written in either little or big endian diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java index 0e5cbe4e20b7..5604ffd83deb 100644 --- a/media/java/android/media/MediaPlayer2Impl.java +++ b/media/java/android/media/MediaPlayer2Impl.java @@ -3620,6 +3620,17 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { && getState() == PLAYER_STATE_ERROR) { status = CALL_STATUS_INVALID_OPERATION; } else { + if (mMediaCallType == CALL_COMPLETED_SEEK_TO) { + synchronized (mTaskLock) { + if (!mPendingTasks.isEmpty()) { + Task nextTask = mPendingTasks.get(0); + if (nextTask.mMediaCallType == mMediaCallType) { + throw new CommandSkippedException( + "consecutive seekTo is skipped except last one"); + } + } + } + } process(); } } catch (IllegalStateException e) { diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java index cdaabdcda20f..d0ca04bb07c2 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java @@ -184,7 +184,6 @@ public class DeviceDiscoveryService extends Service { if (shouldScan(mBluetoothFilters)) { final IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(BluetoothDevice.ACTION_FOUND); - intentFilter.addAction(BluetoothDevice.ACTION_DISAPPEARED); mBluetoothBroadcastReceiver = new BluetoothBroadcastReceiver(); registerReceiver(mBluetoothBroadcastReceiver, intentFilter); diff --git a/packages/EasterEgg/src/com/android/egg/paint/CutoutAvoidingToolbar.kt b/packages/EasterEgg/src/com/android/egg/paint/CutoutAvoidingToolbar.kt index 164fc5a5af3d..9855565c1335 100644 --- a/packages/EasterEgg/src/com/android/egg/paint/CutoutAvoidingToolbar.kt +++ b/packages/EasterEgg/src/com/android/egg/paint/CutoutAvoidingToolbar.kt @@ -26,15 +26,16 @@ class CutoutAvoidingToolbar : LinearLayout { private var _insets: WindowInsets? = null constructor(context: Context) : super(context) { - init(null, 0) } constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { - init(attrs, 0) } - constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) { - init(attrs, defStyle) + constructor( + context: Context, + attrs: AttributeSet, + defStyle: Int + ) : super(context, attrs, defStyle) { } override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { @@ -81,8 +82,4 @@ class CutoutAvoidingToolbar : LinearLayout { requestLayout() } } - - private fun init(attrs: AttributeSet?, defStyle: Int) { - } - } diff --git a/packages/EasterEgg/src/com/android/egg/paint/Painting.kt b/packages/EasterEgg/src/com/android/egg/paint/Painting.kt index a4a3d3d835e0..fc7e8b008a54 100644 --- a/packages/EasterEgg/src/com/android/egg/paint/Painting.kt +++ b/packages/EasterEgg/src/com/android/egg/paint/Painting.kt @@ -17,7 +17,6 @@ package com.android.egg.paint import android.content.Context -import android.content.res.Resources import android.graphics.* import android.provider.Settings import android.util.AttributeSet @@ -26,7 +25,6 @@ import android.view.MotionEvent import android.view.View import android.view.WindowInsets import java.util.concurrent.TimeUnit -import android.util.Log import android.provider.Settings.System import org.json.JSONObject @@ -86,11 +84,11 @@ public class Painting : View, SpotFilter.Plotter { } var bitmap: Bitmap? = null - var paperColor : Int = 0xFFFFFFFF.toInt() + var paperColor: Int = 0xFFFFFFFF.toInt() private var _paintCanvas: Canvas? = null private val _bitmapLock = Object() - + private var _drawPaint = Paint(Paint.ANTI_ALIAS_FLAG) private var _lastX = 0f private var _lastY = 0f @@ -113,7 +111,9 @@ public class Painting : View, SpotFilter.Plotter { FADE_TO_BLACK_CF synchronized(_bitmapLock) { - c.drawBitmap(bitmap, 0f, 0f, pt) + bitmap?.let { + c.drawBitmap(bitmap!!, 0f, 0f, pt) + } } invalidate() } @@ -122,18 +122,22 @@ public class Painting : View, SpotFilter.Plotter { } constructor(context: Context) : super(context) { - init(null, 0) + init() } constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { - init(attrs, 0) + init() } - constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) { - init(attrs, defStyle) + constructor( + context: Context, + attrs: AttributeSet, + defStyle: Int + ) : super(context, attrs, defStyle) { + init() } - private fun init(attrs: AttributeSet?, defStyle: Int) { + private fun init() { loadDevicePressureData() } @@ -264,7 +268,7 @@ public class Painting : View, SpotFilter.Plotter { super.onDraw(canvas) bitmap?.let { - canvas.drawBitmap(bitmap, 0f, 0f, _drawPaint); + canvas.drawBitmap(bitmap!!, 0f, 0f, _drawPaint) } } @@ -330,8 +334,8 @@ public class Painting : View, SpotFilter.Plotter { } if (bits.width != oldBits.height || bits.height != oldBits.width) { matrix.postScale( - bits.width.toFloat()/oldBits.height, - bits.height.toFloat()/oldBits.width) + bits.width.toFloat() / oldBits.height, + bits.height.toFloat() / oldBits.width) } c.matrix = matrix } @@ -350,9 +354,10 @@ public class Painting : View, SpotFilter.Plotter { val invertPaint = Paint() invertPaint.colorFilter = INVERT_CF synchronized(_bitmapLock) { - _paintCanvas?.drawBitmap(bitmap, 0f, 0f, invertPaint) + bitmap?.let { + _paintCanvas?.drawBitmap(bitmap!!, 0f, 0f, invertPaint) + } } invalidate() } } - diff --git a/packages/EasterEgg/src/com/android/egg/paint/ToolbarView.kt b/packages/EasterEgg/src/com/android/egg/paint/ToolbarView.kt index 86b11e7be81e..460fa3a7241f 100644 --- a/packages/EasterEgg/src/com/android/egg/paint/ToolbarView.kt +++ b/packages/EasterEgg/src/com/android/egg/paint/ToolbarView.kt @@ -17,19 +17,10 @@ package com.android.egg.paint import android.content.Context -import android.graphics.Canvas -import android.graphics.Color -import android.graphics.Paint -import android.graphics.Rect -import android.graphics.drawable.Drawable -import android.text.TextPaint -import android.transition.ChangeBounds import android.transition.Transition import android.transition.TransitionListenerAdapter -import android.transition.TransitionManager import android.util.AttributeSet import android.view.* -import android.view.animation.OvershootInterpolator import android.widget.FrameLayout class ToolbarView : FrameLayout { @@ -44,15 +35,16 @@ class ToolbarView : FrameLayout { } constructor(context: Context) : super(context) { - init(null, 0) } constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { - init(attrs, 0) } - constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) { - init(attrs, defStyle) + constructor( + context: Context, + attrs: AttributeSet, + defStyle: Int + ) : super(context, attrs, defStyle) { } override fun onApplyWindowInsets(insets: WindowInsets?): WindowInsets { @@ -70,8 +62,4 @@ class ToolbarView : FrameLayout { return super.onApplyWindowInsets(insets) } - - private fun init(attrs: AttributeSet?, defStyle: Int) { - } - } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index da1354be63d2..8745a330311a 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -662,9 +662,6 @@ class SettingsProtoDumpUtil { dumpSetting(s, p, Settings.Global.ANGLE_ENABLED_APP, GlobalSettingsProto.Gpu.ANGLE_ENABLED_APP); - dumpSetting(s, p, - Settings.Global.GPU_DEBUG_LAYER_APP, - GlobalSettingsProto.Gpu.DEBUG_LAYER_APP); p.end(gpuToken); final long hdmiToken = p.start(GlobalSettingsProto.HDMI); diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml index 7a38899f37f7..f138685e9810 100644 --- a/packages/SystemUI/res/layout/notification_info.xml +++ b/packages/SystemUI/res/layout/notification_info.xml @@ -24,8 +24,7 @@ android:clipChildren="false" android:clipToPadding="false" android:orientation="vertical" - android:background="@color/notification_guts_bg_color" - android:theme="@*android:style/Theme.DeviceDefault.Light"> + android:background="@color/notification_guts_bg_color"> <!-- Package Info --> <RelativeLayout diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml index f0436dea9207..d033057cdb78 100644 --- a/packages/SystemUI/res/layout/status_bar.xml +++ b/packages/SystemUI/res/layout/status_bar.xml @@ -49,11 +49,6 @@ android:paddingEnd="@dimen/status_bar_padding_end" android:orientation="horizontal" > - <ViewStub - android:id="@+id/operator_name" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:layout="@layout/operator_name" /> <FrameLayout android:layout_height="match_parent" android:layout_width="0dp" @@ -70,6 +65,12 @@ android:layout_width="match_parent" android:clipChildren="false" > + <ViewStub + android:id="@+id/operator_name" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout="@layout/operator_name" /> + <com.android.systemui.statusbar.policy.Clock android:id="@+id/clock" android:layout_width="wrap_content" diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index e1c71fae7559..42e19aace0c3 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -761,6 +761,8 @@ <string name="quick_settings_cast_device_default_description">Ready to cast</string> <!-- QuickSettings: Cast detail panel, text when there are no items [CHAR LIMIT=NONE] --> <string name="quick_settings_cast_detail_empty_text">No devices available</string> + <!-- QuickSettings: Cast unavailable, text when not connected to WiFi [CHAR LIMIT=NONE] --> + <string name="quick_settings_cast_no_wifi">Wi\u2011Fi not connected</string> <!-- QuickSettings: Brightness dialog title [CHAR LIMIT=NONE] --> <string name="quick_settings_brightness_dialog_title">Brightness</string> <!-- QuickSettings: Brightness dialog auto brightness button [CHAR LIMIT=NONE] --> @@ -1999,6 +2001,9 @@ <!-- accessibility label for quick settings items that open a details page [CHAR LIMIT=NONE] --> <string name="accessibility_quick_settings_open_details">Open details.</string> + <!-- accessibility label for quick settings items that are currently disabled. Must have a reason [CHAR LIMIT=NONE] --> + <string name="accessibility_quick_settings_not_available">Unvailable due to <xliff:g name="reason" id="reason" example="Wifi not available">%s</xliff:g></string> + <!-- accessibility label for quick settings items that open a details page [CHAR LIMIT=NONE] --> <string name="accessibility_quick_settings_open_settings">Open <xliff:g name="page" example="Bluetooth">%s</xliff:g> settings.</string> diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java index 4d24d82bd7ee..3007b6e68b78 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java @@ -736,14 +736,19 @@ public class ScreenDecorations extends SystemUI implements Tunable { switch (gravity) { case Gravity.TOP: out.set(displayCutout.getBoundingRectTop()); + break; case Gravity.LEFT: out.set(displayCutout.getBoundingRectLeft()); + break; case Gravity.BOTTOM: out.set(displayCutout.getBoundingRectBottom()); + break; case Gravity.RIGHT: out.set(displayCutout.getBoundingRectRight()); + break; + default: + out.setEmpty(); } - out.setEmpty(); } private void localBounds(Rect out) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java index ed78048c8746..921db6901626 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java @@ -46,6 +46,7 @@ import com.android.systemui.statusbar.phone.SystemUIDialog; import com.android.systemui.statusbar.policy.CastController; import com.android.systemui.statusbar.policy.CastController.CastDevice; import com.android.systemui.statusbar.policy.KeyguardMonitor; +import com.android.systemui.statusbar.policy.NetworkController; import java.util.LinkedHashMap; import java.util.Set; @@ -58,16 +59,18 @@ public class CastTile extends QSTileImpl<BooleanState> { private final CastController mController; private final CastDetailAdapter mDetailAdapter; private final KeyguardMonitor mKeyguard; + private final NetworkController mNetworkController; private final Callback mCallback = new Callback(); private final ActivityStarter mActivityStarter; private Dialog mDialog; - private boolean mRegistered; + private boolean mWifiConnected; public CastTile(QSHost host) { super(host); mController = Dependency.get(CastController.class); mDetailAdapter = new CastDetailAdapter(); mKeyguard = Dependency.get(KeyguardMonitor.class); + mNetworkController = Dependency.get(NetworkController.class); mActivityStarter = Dependency.get(ActivityStarter.class); } @@ -87,10 +90,12 @@ public class CastTile extends QSTileImpl<BooleanState> { if (listening) { mController.addCallback(mCallback); mKeyguard.addCallback(mCallback); + mNetworkController.addCallback(mSignalCallback); } else { mController.setDiscovering(false); mController.removeCallback(mCallback); mKeyguard.removeCallback(mCallback); + mNetworkController.removeCallback(mSignalCallback); } } @@ -112,6 +117,9 @@ public class CastTile extends QSTileImpl<BooleanState> { @Override protected void handleClick() { + if (getState().state == Tile.STATE_UNAVAILABLE) { + return; + } if (mKeyguard.isSecure() && !mKeyguard.canSkipBouncer()) { mActivityStarter.postQSRunnableDismissingKeyguard(() -> { showDetail(true); @@ -164,13 +172,22 @@ public class CastTile extends QSTileImpl<BooleanState> { if (!state.value && connecting) { state.label = mContext.getString(R.string.quick_settings_connecting); } - state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; state.icon = ResourceIcon.get(state.value ? R.drawable.ic_qs_cast_on : R.drawable.ic_qs_cast_off); + if (mWifiConnected) { + state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; + state.secondaryLabel = ""; + state.contentDescription = state.contentDescription + "," + + mContext.getString(R.string.accessibility_quick_settings_open_details); + state.expandedAccessibilityClassName = Button.class.getName(); + } else { + state.state = Tile.STATE_UNAVAILABLE; + String noWifi = mContext.getString(R.string.quick_settings_cast_no_wifi); + state.secondaryLabel = noWifi; + state.contentDescription = state.contentDescription + ", " + mContext.getString( + R.string.accessibility_quick_settings_not_available, noWifi); + } mDetailAdapter.updateItems(devices); - state.expandedAccessibilityClassName = Button.class.getName(); - state.contentDescription = state.contentDescription + "," - + mContext.getString(R.string.accessibility_quick_settings_open_details); } @Override @@ -192,6 +209,22 @@ public class CastTile extends QSTileImpl<BooleanState> { : mContext.getString(R.string.quick_settings_cast_device_default_name); } + private final NetworkController.SignalCallback mSignalCallback = + new NetworkController.SignalCallback() { + @Override + public void setWifiIndicators(boolean enabled, + NetworkController.IconState statusIcon, + NetworkController.IconState qsIcon, boolean activityIn, boolean activityOut, + String description, boolean isTransient, String statusLabel) { + // statusIcon.visible has the connected status information + boolean enabledAndConnected = enabled && qsIcon.visible; + if (enabledAndConnected != mWifiConnected) { + mWifiConnected = enabledAndConnected; + refreshState(); + } + } + }; + private final class Callback implements CastController.Callback, KeyguardMonitor.Callback { @Override public void onCastDevicesChanged() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java index 2450e448c4f7..24665eac76a7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java @@ -491,6 +491,10 @@ public class CarStatusBar extends StatusBar implements @Override public void onStateChanged(int newState) { super.onStateChanged(newState); + if (mFullscreenUserSwitcher == null) { + return; // Not using the full screen user switcher. + } + if (newState == StatusBarState.FULLSCREEN_USER_SWITCHER) { if (!mFullscreenUserSwitcher.isVisible()) { // Current execution path continues to set state after this, thus we deffer the diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java index 9acaf21c41f5..c66bbb1696ac 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java @@ -48,6 +48,7 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, private final NotificationStackScrollLayout mStackScroller; private final HeadsUpStatusBarView mHeadsUpStatusBarView; private final View mClockView; + private final View mOperatorNameView; private final DarkIconDispatcher mDarkIconDispatcher; private final NotificationPanelView mPanelView; private final Consumer<ExpandableNotificationRow> @@ -65,8 +66,10 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, private final View.OnLayoutChangeListener mStackScrollLayoutChangeListener = (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> updatePanelTranslation(); + private boolean mAnimationsEnabled = true; Point mPoint; + public HeadsUpAppearanceController( NotificationIconAreaController notificationIconAreaController, HeadsUpManagerPhone headsUpManager, @@ -75,7 +78,8 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, statusbarView.findViewById(R.id.heads_up_status_bar_view), statusbarView.findViewById(R.id.notification_stack_scroller), statusbarView.findViewById(R.id.notification_panel), - statusbarView.findViewById(R.id.clock)); + statusbarView.findViewById(R.id.clock), + statusbarView.findViewById(R.id.operator_name_frame)); } @VisibleForTesting @@ -85,7 +89,8 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, HeadsUpStatusBarView headsUpStatusBarView, NotificationStackScrollLayout stackScroller, NotificationPanelView panelView, - View clockView) { + View clockView, + View operatorNameView) { mNotificationIconAreaController = notificationIconAreaController; mHeadsUpManager = headsUpManager; mHeadsUpManager.addListener(this); @@ -101,6 +106,7 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, mStackScroller.addOnLayoutChangeListener(mStackScrollLayoutChangeListener); mStackScroller.setHeadsUpAppearanceController(this); mClockView = clockView; + mOperatorNameView = operatorNameView; mDarkIconDispatcher = Dependency.get(DarkIconDispatcher.class); mDarkIconDispatcher.addDarkReceiver(this); @@ -230,20 +236,52 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, mShown = isShown; if (isShown) { mHeadsUpStatusBarView.setVisibility(View.VISIBLE); - CrossFadeHelper.fadeIn(mHeadsUpStatusBarView, CONTENT_FADE_DURATION /* duration */, - CONTENT_FADE_DELAY /* delay */); - CrossFadeHelper.fadeOut(mClockView, CONTENT_FADE_DURATION/* duration */, - 0 /* delay */, () -> mClockView.setVisibility(View.INVISIBLE)); + show(mHeadsUpStatusBarView); + hide(mClockView, View.INVISIBLE); + if (mOperatorNameView != null) { + hide(mOperatorNameView, View.INVISIBLE); + } } else { - CrossFadeHelper.fadeIn(mClockView, CONTENT_FADE_DURATION /* duration */, - CONTENT_FADE_DELAY /* delay */); - CrossFadeHelper.fadeOut(mHeadsUpStatusBarView, CONTENT_FADE_DURATION/* duration */, - 0 /* delay */, () -> mHeadsUpStatusBarView.setVisibility(View.GONE)); - + show(mClockView); + if (mOperatorNameView != null) { + show(mOperatorNameView); + } + hide(mHeadsUpStatusBarView, View.GONE); } } } + /** + * Hides the view and sets the state to endState when finished. + * + * @param view The view to hide. + * @param endState One of {@link View#INVISIBLE} or {@link View#GONE}. + * @see View#setVisibility(int) + * + */ + private void hide(View view, int endState) { + if (mAnimationsEnabled) { + CrossFadeHelper.fadeOut(view, CONTENT_FADE_DURATION /* duration */, + 0 /* delay */, () -> view.setVisibility(endState)); + } else { + view.setVisibility(endState); + } + } + + private void show(View view) { + if (mAnimationsEnabled) { + CrossFadeHelper.fadeIn(view, CONTENT_FADE_DURATION /* duration */, + CONTENT_FADE_DELAY /* delay */); + } else { + view.setVisibility(View.VISIBLE); + } + } + + @VisibleForTesting + void setAnimationsEnabled(boolean enabled) { + mAnimationsEnabled = enabled; + } + @VisibleForTesting public boolean isShown() { return mShown; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java index 2a4595b980ac..8ac867727e65 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java @@ -361,6 +361,8 @@ public class KeyguardBouncer { } else if (fraction == EXPANSION_HIDDEN && oldExpansion != EXPANSION_HIDDEN) { onFullyHidden(); mExpansionCallback.onFullyHidden(); + } else if (fraction != EXPANSION_VISIBLE && oldExpansion == EXPANSION_VISIBLE) { + mExpansionCallback.onStartingToHide(); } } @@ -481,6 +483,7 @@ public class KeyguardBouncer { public interface BouncerExpansionCallback { void onFullyShown(); + void onStartingToHide(); void onFullyHidden(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java index 7c84df91fffa..e85ff8ef22c0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java @@ -519,7 +519,14 @@ public class KeyguardStatusBarView extends RelativeLayout mStatusIconContainer.setAlpha(alpha); mStatusIconContainer.setVisibility(visibility); - mSystemIconsContainer.setTranslationX(-mCurrentBurnInOffsetX * mDarkAmount); + float iconsX = -mCurrentBurnInOffsetX; + if (mMultiUserSwitch.getVisibility() == VISIBLE) { + // Squared alpha to add a nice easing curve and avoid overlap during animation. + mMultiUserAvatar.setAlpha(alpha * alpha); + iconsX += mMultiUserAvatar.getPaddingLeft() + mMultiUserAvatar.getWidth() + + mMultiUserAvatar.getPaddingRight(); + } + mSystemIconsContainer.setTranslationX(iconsX * mDarkAmount); mSystemIconsContainer.setTranslationY(mCurrentBurnInOffsetY * mDarkAmount); updateIconsAndTextColors(); } 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 3db1456446a0..ac3608bc622a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -84,6 +84,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } @Override + public void onStartingToHide() { + updateStates(); + } + + @Override public void onFullyHidden() { updateStates(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java new file mode 100644 index 000000000000..d9412ecd9a3a --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java @@ -0,0 +1,149 @@ +/* + * 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.qs.tiles; + +import static junit.framework.TestCase.assertEquals; +import static junit.framework.TestCase.assertTrue; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.service.quicksettings.Tile; +import android.support.test.filters.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import com.android.systemui.Dependency; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.qs.QSTileHost; +import com.android.systemui.statusbar.policy.CastController; +import com.android.systemui.statusbar.policy.KeyguardMonitor; +import com.android.systemui.statusbar.policy.NetworkController; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.HashSet; +import java.util.Set; + + +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +@SmallTest +public class CastTileTest extends SysuiTestCase { + + @Mock + private CastController mController; + @Mock + private ActivityStarter mActivityStarter; + @Mock + private KeyguardMonitor mKeyguard; + @Mock + private NetworkController mNetworkController; + @Mock + private QSTileHost mHost; + @Mock + NetworkController.SignalCallback mCallback; + + private TestableLooper mTestableLooper; + private CastTile mCastTile; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mTestableLooper = TestableLooper.get(this); + + mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper()); + mController = mDependency.injectMockDependency(CastController.class); + mActivityStarter = mDependency.injectMockDependency(ActivityStarter.class); + mKeyguard = mDependency.injectMockDependency(KeyguardMonitor.class); + mNetworkController = mDependency.injectMockDependency(NetworkController.class); + + when(mHost.getContext()).thenReturn(mContext); + + mCastTile = new CastTile(mHost); + + // We are not setting the mocks to listening, so we trigger a first refresh state to + // set the initial state + mCastTile.refreshState(); + + mCastTile.handleSetListening(true); + ArgumentCaptor<NetworkController.SignalCallback> signalCallbackArgumentCaptor = + ArgumentCaptor.forClass(NetworkController.SignalCallback.class); + verify(mNetworkController).addCallback(signalCallbackArgumentCaptor.capture()); + mCallback = signalCallbackArgumentCaptor.getValue(); + + } + + @Test + public void testStateUnavailable_wifiDisabled() { + NetworkController.IconState qsIcon = + new NetworkController.IconState(false, 0, ""); + mCallback.setWifiIndicators(false, mock(NetworkController.IconState.class), + qsIcon, false,false, "", + false, ""); + mTestableLooper.processAllMessages(); + + assertEquals(Tile.STATE_UNAVAILABLE, mCastTile.getState().state); + } + + @Test + public void testStateUnavailable_wifiNotConnected() { + NetworkController.IconState qsIcon = + new NetworkController.IconState(false, 0, ""); + mCallback.setWifiIndicators(true, mock(NetworkController.IconState.class), + qsIcon, false,false, "", + false, ""); + mTestableLooper.processAllMessages(); + + assertEquals(Tile.STATE_UNAVAILABLE, mCastTile.getState().state); + } + + @Test + public void testStateActive_wifiEnabledAndCasting() { + CastController.CastDevice device = mock(CastController.CastDevice.class); + device.state = CastController.CastDevice.STATE_CONNECTED; + Set<CastController.CastDevice> devices = new HashSet<>(); + devices.add(device); + when(mController.getCastDevices()).thenReturn(devices); + + NetworkController.IconState qsIcon = + new NetworkController.IconState(true, 0, ""); + mCallback.setWifiIndicators(true, mock(NetworkController.IconState.class), + qsIcon, false,false, "", + false, ""); + mTestableLooper.processAllMessages(); + + assertEquals(Tile.STATE_ACTIVE, mCastTile.getState().state); + } + + @Test + public void testStateInactive_wifiEnabledNotCasting() { + NetworkController.IconState qsIcon = + new NetworkController.IconState(true, 0, ""); + mCallback.setWifiIndicators(true, mock(NetworkController.IconState.class), + qsIcon, false,false, "", + false, ""); + mTestableLooper.processAllMessages(); + + assertEquals(Tile.STATE_INACTIVE, mCastTile.getState().state); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java index a4004ae2d7cc..10b0d834b36b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java @@ -54,6 +54,7 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase { private ExpandableNotificationRow mFirst; private HeadsUpStatusBarView mHeadsUpStatusBarView; private HeadsUpManagerPhone mHeadsUpManager; + private View mOperatorNameView; @Before public void setUp() throws Exception { @@ -63,13 +64,15 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase { mHeadsUpStatusBarView = new HeadsUpStatusBarView(mContext, mock(View.class), mock(TextView.class)); mHeadsUpManager = mock(HeadsUpManagerPhone.class); + mOperatorNameView = new View(mContext); mHeadsUpAppearanceController = new HeadsUpAppearanceController( mock(NotificationIconAreaController.class), mHeadsUpManager, mHeadsUpStatusBarView, mStackScroller, mPanelView, - new View(mContext)); + new View(mContext), + mOperatorNameView); mHeadsUpAppearanceController.setExpandedHeight(0.0f, 0.0f); } @@ -116,6 +119,22 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase { } @Test + public void testOperatorNameViewUpdated() { + mHeadsUpAppearanceController.setAnimationsEnabled(false); + + mFirst.setPinned(true); + when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true); + when(mHeadsUpManager.getTopEntry()).thenReturn(mFirst.getEntry()); + mHeadsUpAppearanceController.onHeadsUpPinned(mFirst); + Assert.assertEquals(View.INVISIBLE, mOperatorNameView.getVisibility()); + + mFirst.setPinned(false); + when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false); + mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst); + Assert.assertEquals(View.VISIBLE, mOperatorNameView.getVisibility()); + } + + @Test public void testHeaderReadFromOldController() { mHeadsUpAppearanceController.setExpandedHeight(1.0f, 1.0f); @@ -125,6 +144,7 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase { mHeadsUpStatusBarView, mStackScroller, mPanelView, + new View(mContext), new View(mContext)); newController.readFrom(mHeadsUpAppearanceController); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java index a7954f2aa199..020682b6b4e8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java @@ -170,13 +170,17 @@ public class KeyguardBouncerTest extends SysuiTestCase { mBouncer.ensureView(); mBouncer.setExpansion(0.5f); - mBouncer.setExpansion(1); + mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN); verify(mFalsingManager).onBouncerHidden(); verify(mExpansionCallback).onFullyHidden(); - mBouncer.setExpansion(0); + mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE); verify(mFalsingManager).onBouncerShown(); verify(mExpansionCallback).onFullyShown(); + + verify(mExpansionCallback, never()).onStartingToHide(); + mBouncer.setExpansion(0.9f); + verify(mExpansionCallback).onStartingToHide(); } @Test diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto index 5e87707c39e6..d86de5dfd799 100644 --- a/proto/src/metrics_constants/metrics_constants.proto +++ b/proto/src/metrics_constants/metrics_constants.proto @@ -6549,6 +6549,21 @@ message MetricsEvent { // Subtype: The importance of a notification has been changed ADJUSTMENT_KEY_IMPORTANCE = 1580; + // OPEN: Settings > Network & internet > Mobile network > Choose network + // CATEGORY: SETTINGS + // OS: Q + MOBILE_NETWORK_SELECT = 1581; + + // OPEN: Settings > Network & internet > Mobile network > Mobile Data > Dialog + // CATEGORY: SETTINGS + // OS: Q + MOBILE_DATA_DIALOG = 1582; + + // OPEN: Settings > Network & internet > Mobile network > Data roaming > Dialog + // CATEGORY: SETTINGS + // OS: Q + MOBILE_ROAMING_DIALOG = 1583; + // ---- End Q Constants, all Q constants go above this line ---- // Add new aosp constants above this line. diff --git a/services/core/Android.bp b/services/core/Android.bp index be353458cf44..2fa2941efe7f 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -4,6 +4,7 @@ java_library_static { aidl: { include_dirs: [ "frameworks/native/aidl/binder", + "frameworks/native/cmds/dumpstate/binder", "system/core/storaged/binder", "system/netd/server/binder", "system/vold/binder", @@ -11,6 +12,7 @@ java_library_static { }, srcs: [ "java/**/*.java", + ":dumpstate_aidl", ":netd_aidl", ":netd_metrics_aidl", ":installd_aidl", diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index b8db2d067a6e..4b77c69aba2b 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -5513,15 +5513,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (networkAgent.isVPN()) { // Temporarily disable the default proxy (not global). - synchronized (mProxyTracker.mProxyLock) { - if (!mProxyTracker.mDefaultProxyDisabled) { - mProxyTracker.mDefaultProxyDisabled = true; - if (mProxyTracker.mGlobalProxy == null - && mProxyTracker.mDefaultProxy != null) { - mProxyTracker.sendProxyBroadcast(null); - } - } - } + mProxyTracker.setDefaultProxyEnabled(false); // TODO: support proxy per network. } @@ -5543,15 +5535,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } else if (state == NetworkInfo.State.DISCONNECTED) { networkAgent.asyncChannel.disconnect(); if (networkAgent.isVPN()) { - synchronized (mProxyTracker.mProxyLock) { - if (mProxyTracker.mDefaultProxyDisabled) { - mProxyTracker.mDefaultProxyDisabled = false; - if (mProxyTracker.mGlobalProxy == null - && mProxyTracker.mDefaultProxy != null) { - mProxyTracker.sendProxyBroadcast(mProxyTracker.mDefaultProxy); - } - } - } + mProxyTracker.setDefaultProxyEnabled(true); updateUids(networkAgent, networkAgent.networkCapabilities, null); } disconnectAndDestroyNetwork(networkAgent); diff --git a/services/core/java/com/android/server/LooperStatsService.java b/services/core/java/com/android/server/LooperStatsService.java index 96ce6a4ee6a4..6677541b1b32 100644 --- a/services/core/java/com/android/server/LooperStatsService.java +++ b/services/core/java/com/android/server/LooperStatsService.java @@ -109,14 +109,28 @@ public class LooperStatsService extends Binder { "max_latency_micros", "total_cpu_micros", "max_cpu_micros", + "recorded_delay_message_count", + "total_delay_millis", + "max_delay_millis", "exception_count")); pw.println(header); for (LooperStats.ExportedEntry entry : entries) { - pw.printf("%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n", entry.workSourceUid, - entry.threadName, entry.handlerClassName, entry.messageName, - entry.isInteractive, entry.messageCount, entry.recordedMessageCount, - entry.totalLatencyMicros, entry.maxLatencyMicros, entry.cpuUsageMicros, - entry.maxCpuUsageMicros, entry.exceptionCount); + pw.printf("%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n", + entry.workSourceUid, + entry.threadName, + entry.handlerClassName, + entry.messageName, + entry.isInteractive, + entry.messageCount, + entry.recordedMessageCount, + entry.totalLatencyMicros, + entry.maxLatencyMicros, + entry.cpuUsageMicros, + entry.maxCpuUsageMicros, + entry.recordedDelayMessageCount, + entry.delayMillis, + entry.maxDelayMillis, + entry.exceptionCount); } } diff --git a/services/core/java/com/android/server/am/ActivityLaunchParamsModifier.java b/services/core/java/com/android/server/am/ActivityLaunchParamsModifier.java deleted file mode 100644 index f44ee7a234ca..000000000000 --- a/services/core/java/com/android/server/am/ActivityLaunchParamsModifier.java +++ /dev/null @@ -1,65 +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 - */ - -package com.android.server.am; - -import android.app.ActivityOptions; -import android.content.pm.ActivityInfo; -import android.graphics.Rect; - -import com.android.server.am.LaunchParamsController.LaunchParams; -import com.android.server.am.LaunchParamsController.LaunchParamsModifier; - -/** - * An implementation of {@link LaunchParamsModifier}, which applies the launch bounds specified - * inside {@link ActivityOptions#getLaunchBounds()}. - */ -public class ActivityLaunchParamsModifier implements LaunchParamsModifier { - private final ActivityStackSupervisor mSupervisor; - - ActivityLaunchParamsModifier(ActivityStackSupervisor activityStackSupervisor) { - mSupervisor = activityStackSupervisor; - } - - @Override - public int onCalculate(TaskRecord task, ActivityInfo.WindowLayout layout, - ActivityRecord activity, ActivityRecord source, ActivityOptions options, - LaunchParams currentParams, LaunchParams outParams) { - // We only care about figuring out bounds for activities. - if (activity == null) { - return RESULT_SKIP; - } - - // Activity must be resizeable in the specified task. - if (!(mSupervisor.canUseActivityOptionsLaunchBounds(options) - && (activity.isResizeable() || (task != null && task.isResizeable())))) { - return RESULT_SKIP; - } - - final Rect bounds = options.getLaunchBounds(); - - // Bounds weren't valid. - if (bounds == null || bounds.isEmpty()) { - return RESULT_SKIP; - } - - outParams.mBounds.set(bounds); - - // When this is the most explicit position specification so we should not allow further - // modification of the position. - return RESULT_DONE; - } -} diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 37167c688d87..acf7a73332b7 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -10184,6 +10184,7 @@ public class ActivityManagerService extends IActivityManager.Stub synchronized (this) { sb.append("Process: ").append(processName).append("\n"); sb.append("PID: ").append(process.pid).append("\n"); + sb.append("UID: ").append(process.uid).append("\n"); int flags = process.info.flags; IPackageManager pm = AppGlobals.getPackageManager(); sb.append("Flags: 0x").append(Integer.toHexString(flags)).append("\n"); diff --git a/services/core/java/com/android/server/am/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java index 18cdb054e648..7080e2bd58fd 100644 --- a/services/core/java/com/android/server/am/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/am/ActivityMetricsLogger.java @@ -110,7 +110,7 @@ import com.android.server.LocalServices; * data for Tron, logcat, event logs and {@link android.app.WaitResult}. * * Tests: - * atest SystemMetricsFunctionalTests + * atest CtsActivityManagerDeviceTestCases:ActivityMetricsLoggerTests */ class ActivityMetricsLogger { @@ -351,18 +351,24 @@ class ActivityMetricsLogger { + " processRunning=" + processRunning + " processSwitch=" + processSwitch); - // If we are already in an existing transition, only update the activity name, but not the - // other attributes. final int windowingMode = launchedActivity != null ? launchedActivity.getWindowingMode() : WINDOWING_MODE_UNDEFINED; - + final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(windowingMode); if (mCurrentTransitionStartTime == INVALID_START_TIME) { + // No transition is active ignore this launch. + return; + } + + if (launchedActivity != null && launchedActivity.nowVisible) { + // Launched activity is already visible. We cannot measure windows drawn delay. + reset(true /* abort */, info); return; } - final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(windowingMode); if (launchedActivity != null && info != null) { + // If we are already in an existing transition, only update the activity name, but not + // the other attributes. info.launchedActivity = launchedActivity; return; } @@ -371,7 +377,6 @@ class ActivityMetricsLogger { mWindowingModeTransitionInfo.size() > 0 && info == null; if ((!isLoggableResultCode(resultCode) || launchedActivity == null || !processSwitch || windowingMode == WINDOWING_MODE_UNDEFINED) && !otherWindowModesLaunching) { - // Failed to launch or it was not a process switch, so we don't care about the timing. reset(true /* abort */, info); return; diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index db9e59c23858..864bf2d77b59 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -227,7 +227,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } @Override - protected ConfigurationContainer getChildAt(int index) { + protected TaskRecord getChildAt(int index) { return mTaskHistory.get(index); } diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java index 13b16b2fb84c..33f949fccfb0 100644 --- a/services/core/java/com/android/server/am/ActivityStarter.java +++ b/services/core/java/com/android/server/am/ActivityStarter.java @@ -1606,13 +1606,18 @@ class ActivityStarter { mVoiceSession = voiceSession; mVoiceInteractor = voiceInteractor; - mPreferredDisplayId = getPreferedDisplayId(mSourceRecord, mStartActivity, options); - mLaunchParams.reset(); mSupervisor.getLaunchParamsController().calculate(inTask, null /*layout*/, r, sourceRecord, options, mLaunchParams); + if (mLaunchParams.hasPreferredDisplay()) { + mPreferredDisplayId = mLaunchParams.mPreferredDisplayId; + } else { + mPreferredDisplayId = DEFAULT_DISPLAY; + } + ensureValidPreferredDisplayId(r); + mLaunchMode = r.launchMode; mLaunchFlags = adjustLaunchFlagsToDocumentMode( @@ -1704,6 +1709,24 @@ class ActivityStarter { mNoAnimation = (mLaunchFlags & FLAG_ACTIVITY_NO_ANIMATION) != 0; } + /** + * Ensure preferred display ID matches the starting activity. + */ + private void ensureValidPreferredDisplayId(ActivityRecord startingActivity) { + // Check if the Activity is a VR activity. If so, the activity should be launched in + // main display. + if (startingActivity != null && startingActivity.requestedVrComponent != null) { + mPreferredDisplayId = DEFAULT_DISPLAY; + } + + // Get the virtual display ID from ActivityStackManagerService. If that's set we should + // always use that. + final int displayId = mService.mVr2dDisplayId; + if (displayId != INVALID_DISPLAY) { + mPreferredDisplayId = displayId; + } + } + private void sendNewTaskResultRequestIfNeeded() { final ActivityStack sourceStack = mStartActivity.resultTo != null ? mStartActivity.resultTo.getStack() : null; @@ -1883,44 +1906,6 @@ class ActivityStarter { } /** - * Returns the ID of the display to use for a new activity. If the device is in VR mode, - * then return the Vr mode's virtual display ID. If not, if the activity was started with - * a launchDisplayId, use that. Otherwise, if the source activity has a explicit display ID - * set, use that to launch the activity. - */ - private int getPreferedDisplayId( - ActivityRecord sourceRecord, ActivityRecord startingActivity, ActivityOptions options) { - // Check if the Activity is a VR activity. If so, the activity should be launched in - // main display. - if (startingActivity != null && startingActivity.requestedVrComponent != null) { - return DEFAULT_DISPLAY; - } - - // Get the virtual display id from ActivityManagerService. - int displayId = mService.mVr2dDisplayId; - if (displayId != INVALID_DISPLAY) { - if (DEBUG_STACK) { - Slog.d(TAG, "getSourceDisplayId :" + displayId); - } - return displayId; - } - - // If the caller requested a display, prefer that display. - final int launchDisplayId = - (options != null) ? options.getLaunchDisplayId() : INVALID_DISPLAY; - if (launchDisplayId != INVALID_DISPLAY) { - return launchDisplayId; - } - - displayId = sourceRecord != null ? sourceRecord.getDisplayId() : INVALID_DISPLAY; - // If the activity has a displayId set explicitly, launch it on the same displayId. - if (displayId != INVALID_DISPLAY) { - return displayId; - } - return DEFAULT_DISPLAY; - } - - /** * Figure out which task and activity to bring to front when we have found an existing matching * activity record in history. May also clear the task if needed. * @param intentActivity Existing matching activity. diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java index 2ca134465cd6..09c152ef5135 100644 --- a/services/core/java/com/android/server/am/CoreSettingsObserver.java +++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java @@ -56,10 +56,6 @@ final class CoreSettingsObserver extends ContentObserver { sGlobalSettingToTypeMap.put(Settings.Global.DEBUG_VIEW_ATTRIBUTES, int.class); sGlobalSettingToTypeMap.put(Settings.Global.ANGLE_ENABLED_APP, String.class); - sGlobalSettingToTypeMap.put(Settings.Global.ENABLE_GPU_DEBUG_LAYERS, int.class); - sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_APP, String.class); - sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_LAYERS, String.class); - sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_LAYER_APP, String.class); // add other global settings here... } diff --git a/services/core/java/com/android/server/am/LaunchParamsController.java b/services/core/java/com/android/server/am/LaunchParamsController.java index 6415c3ee7f72..218d9080c2c0 100644 --- a/services/core/java/com/android/server/am/LaunchParamsController.java +++ b/services/core/java/com/android/server/am/LaunchParamsController.java @@ -16,6 +16,13 @@ package com.android.server.am; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; +import static android.view.Display.INVALID_DISPLAY; + +import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE; +import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_DONE; +import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP; + import android.annotation.IntDef; import android.app.ActivityOptions; import android.content.pm.ActivityInfo.WindowLayout; @@ -26,13 +33,6 @@ import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; -import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; -import static android.view.Display.INVALID_DISPLAY; - -import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE; -import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_DONE; -import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP; - /** * {@link LaunchParamsController} calculates the {@link LaunchParams} by coordinating between * registered {@link LaunchParamsModifier}s. @@ -58,11 +58,7 @@ class LaunchParamsController { */ void registerDefaultModifiers(ActivityStackSupervisor supervisor) { // {@link TaskLaunchParamsModifier} handles window layout preferences. - registerModifier(new TaskLaunchParamsModifier()); - - // {@link ActivityLaunchParamsModifier} is the most specific modifier and thus should be - // registered last (applied first) out of the defaults. - registerModifier(new ActivityLaunchParamsModifier(supervisor)); + registerModifier(new TaskLaunchParamsModifier(supervisor)); } /** @@ -226,27 +222,41 @@ class LaunchParamsController { @IntDef({RESULT_SKIP, RESULT_DONE, RESULT_CONTINUE}) @interface Result {} - // Returned when the modifier does not want to influence the bounds calculation + /** Returned when the modifier does not want to influence the bounds calculation */ int RESULT_SKIP = 0; - // Returned when the modifier has changed the bounds and would like its results to be the - // final bounds applied. + /** + * Returned when the modifier has changed the bounds and would like its results to be the + * final bounds applied. + */ int RESULT_DONE = 1; - // Returned when the modifier has changed the bounds but is okay with other modifiers - // influencing the bounds. + /** + * Returned when the modifier has changed the bounds but is okay with other modifiers + * influencing the bounds. + */ int RESULT_CONTINUE = 2; /** - * Called when asked to calculate {@link LaunchParams}. - * @param task The {@link TaskRecord} currently being positioned. - * @param layout The specified {@link WindowLayout}. - * @param activity The {@link ActivityRecord} currently being positioned. - * @param source The {@link ActivityRecord} activity was started from. - * @param options The {@link ActivityOptions} specified for the activity. - * @param currentParams The current {@link LaunchParams}. This can differ from the initial - * params as it represents the modified params up to this point. - * @param outParams The resulting {@link LaunchParams} after all calculations. - * @return A {@link Result} representing the result of the - * {@link LaunchParams} calculation. + * Returns the launch params that the provided activity launch params should be overridden + * to. {@link LaunchParamsModifier} can use this for various purposes, including: 1) + * Providing default bounds if the launch bounds have not been provided. 2) Repositioning + * the task so it doesn't get placed over an existing task. 3) Resizing the task so that its + * dimensions match the activity's requested orientation. + * + * @param task Can be: 1) the target task in which the source activity wants to + * launch the target activity; 2) a newly created task that Android + * gives a chance to override its launching bounds; 3) {@code null} if + * this is called to override an activity's launching bounds. + * @param layout Desired layout when activity is first launched. + * @param activity Activity that is being started. This can be {@code null} on + * re-parenting an activity to a new task (e.g. for + * Picture-In-Picture). Tasks being created because an activity was + * launched should have this be non-null. + * @param source the Activity that launched a new task. Could be {@code null}. + * @param options {@link ActivityOptions} used to start the activity with. + * @param currentParams launching params after the process of last {@link + * LaunchParamsModifier}. + * @param outParams the result params to be set. + * @return see {@link LaunchParamsModifier.Result} */ @Result int onCalculate(TaskRecord task, WindowLayout layout, ActivityRecord activity, diff --git a/services/core/java/com/android/server/am/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/am/TaskLaunchParamsModifier.java index 92f1cc34be92..fd34d180ebc0 100644 --- a/services/core/java/com/android/server/am/TaskLaunchParamsModifier.java +++ b/services/core/java/com/android/server/am/TaskLaunchParamsModifier.java @@ -16,304 +16,770 @@ package com.android.server.am; +import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT; +import static android.util.DisplayMetrics.DENSITY_DEFAULT; +import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.Display.INVALID_DISPLAY; + import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.ActivityOptions; +import android.app.WindowConfiguration; import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; import android.graphics.Rect; +import android.os.Build; import android.util.Slog; import android.view.Gravity; -import com.android.internal.annotations.VisibleForTesting; + import com.android.server.am.LaunchParamsController.LaunchParams; import com.android.server.am.LaunchParamsController.LaunchParamsModifier; import java.util.ArrayList; +import java.util.List; /** - * Determines where a launching task should be positioned and sized on the display. - * - * The modifier is fairly simple. For the new task it tries default position based on the gravity - * and compares corners of the task with corners of existing tasks. If some two pairs of corners are - * sufficiently close enough, it shifts the bounds of the new task and tries again. When it exhausts - * all possible shifts, it gives up and puts the task in the original position. - * - * Note that the only gravities of concern are the corners and the center. + * The class that defines the default launch params for tasks. */ class TaskLaunchParamsModifier implements LaunchParamsModifier { private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskLaunchParamsModifier" : TAG_AM; + private static final boolean DEBUG = false; - // Determines how close window frames/corners have to be to call them colliding. - private static final int BOUNDS_CONFLICT_MIN_DISTANCE = 4; + // A mask for SUPPORTS_SCREEN that indicates the activity supports resize. + private static final int SUPPORTS_SCREEN_RESIZEABLE_MASK = + ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES + | ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS + | ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS + | ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS + | ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES + | ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS; - // Task will receive dimensions based on available dimensions divided by this. - private static final int WINDOW_SIZE_DENOMINATOR = 2; + // Screen size of Nexus 5x + private static final int DEFAULT_PORTRAIT_PHONE_WIDTH_DP = 412; + private static final int DEFAULT_PORTRAIT_PHONE_HEIGHT_DP = 732; - // Task will receive margins based on available dimensions divided by this. - private static final int MARGIN_SIZE_DENOMINATOR = 4; + // Allowance of size matching. + private static final int EPSILON = 2; - // If task bounds collide with some other, we will step and try again until we find a good - // position. The step will be determined by using dimensions and dividing it by this. + // Cascade window offset. + private static final int CASCADING_OFFSET_DP = 75; + + // Threshold how close window corners have to be to call them colliding. + private static final int BOUNDS_CONFLICT_THRESHOLD = 4; + + // Divide display size by this number to get each step to adjust bounds to avoid conflict. private static final int STEP_DENOMINATOR = 16; // We always want to step by at least this. private static final int MINIMAL_STEP = 1; - // Used to indicate if positioning algorithm is allowed to restart from the beginning, when it - // reaches the end of stack bounds. - private static final boolean ALLOW_RESTART = true; + private final ActivityStackSupervisor mSupervisor; + private final Rect mTmpBounds = new Rect(); + private final int[] mTmpDirections = new int[2]; - private static final int SHIFT_POLICY_DIAGONAL_DOWN = 1; - private static final int SHIFT_POLICY_HORIZONTAL_RIGHT = 2; - private static final int SHIFT_POLICY_HORIZONTAL_LEFT = 3; + private StringBuilder mLogBuilder; - private final Rect mAvailableRect = new Rect(); - private final Rect mTmpProposal = new Rect(); - private final Rect mTmpOriginal = new Rect(); + TaskLaunchParamsModifier(ActivityStackSupervisor supervisor) { + mSupervisor = supervisor; + } - /** - * Tries to set task's bound in a way that it won't collide with any other task. By colliding - * we mean that two tasks have left-top corner very close to each other, so one might get - * obfuscated by the other one. - */ @Override public int onCalculate(TaskRecord task, ActivityInfo.WindowLayout layout, ActivityRecord activity, ActivityRecord source, ActivityOptions options, LaunchParams currentParams, LaunchParams outParams) { - // We can only apply positioning if we're in a freeform stack. - if (task == null || task.getStack() == null || !task.inFreeformWindowingMode()) { - return RESULT_SKIP; - } + initLogBuilder(task, activity); + final int result = calculate(task, layout, activity, source, options, currentParams, + outParams); + outputLog(); + return result; + } - final ArrayList<TaskRecord> tasks = task.getStack().getAllTasks(); + private int calculate(TaskRecord task, ActivityInfo.WindowLayout layout, + ActivityRecord activity, ActivityRecord source, ActivityOptions options, + LaunchParams currentParams, LaunchParams outParams) { + // STEP 1: Determine the display to launch the activity/task. + final int displayId = getPreferredLaunchDisplay(options, source, currentParams); + outParams.mPreferredDisplayId = displayId; + ActivityDisplay display = mSupervisor.getActivityDisplay(displayId); + if (DEBUG) { + appendLog("display-id=" + outParams.mPreferredDisplayId + " display-windowing-mode=" + + display.getWindowingMode()); + } - mAvailableRect.set(task.getParent().getBounds()); + final ActivityRecord root; + if (task != null) { + root = (task.getRootActivity() == null ? activity : task.getRootActivity()); + } else { + root = activity; + } + // STEP 2: Resolve launch windowing mode. + // STEP 2.1: Determine if any parameter has specified initial bounds. That might be the + // launch bounds from activity options, or size/gravity passed in layout. It also treat the + // launch windowing mode in options as a suggestion for future resolution. + int launchMode = options != null ? options.getLaunchWindowingMode() + : WINDOWING_MODE_UNDEFINED; + // hasInitialBounds is set if either activity options or layout has specified bounds. If + // that's set we'll skip some adjustments later to avoid overriding the initial bounds. + boolean hasInitialBounds = false; + final boolean canApplyFreeformPolicy = + canApplyFreeformWindowPolicy(display, root, launchMode); + if (mSupervisor.canUseActivityOptionsLaunchBounds(options) && canApplyFreeformPolicy) { + hasInitialBounds = true; + launchMode = launchMode == WINDOWING_MODE_UNDEFINED + ? WINDOWING_MODE_FREEFORM + : launchMode; + outParams.mBounds.set(options.getLaunchBounds()); + if (DEBUG) appendLog("activity-options-bounds=" + outParams.mBounds); + } else if (launchMode == WINDOWING_MODE_PINNED) { + // System controls PIP window's bounds, so don't apply launch bounds. + if (DEBUG) appendLog("empty-window-layout-for-pip"); + } else if (launchMode == WINDOWING_MODE_FULLSCREEN) { + if (DEBUG) appendLog("activity-options-fullscreen=" + outParams.mBounds); + } else if (layout != null && canApplyFreeformPolicy) { + getLayoutBounds(display, root, layout, mTmpBounds); + if (!mTmpBounds.isEmpty()) { + launchMode = WINDOWING_MODE_FREEFORM; + outParams.mBounds.set(mTmpBounds); + hasInitialBounds = true; + if (DEBUG) appendLog("bounds-from-layout=" + outParams.mBounds); + } else { + if (DEBUG) appendLog("empty-window-layout"); + } + } - final Rect resultBounds = outParams.mBounds; + // STEP 2.2: Check if previous modifier or the controller (referred as "callers" below) has + // some opinions on launch mode and launch bounds. If they have opinions and there is no + // initial bounds set in parameters. Note the check on display ID is also input param + // related because we always defer to callers' suggestion if there is no specific display ID + // in options or from source activity. + // + // If opinions from callers don't need any further resolution, we try to honor that as is as + // much as possible later. + + // Flag to indicate if current param needs no further resolution. It's true it current + // param isn't freeform mode, or it already has launch bounds. + boolean fullyResolvedCurrentParam = false; + // We inherit launch params from previous modifiers or LaunchParamsController if options, + // layout and display conditions are not contradictory to their suggestions. It's important + // to carry over their values because LaunchParamsController doesn't automatically do that. + if (!currentParams.isEmpty() && !hasInitialBounds + && (!currentParams.hasPreferredDisplay() + || displayId == currentParams.mPreferredDisplayId)) { + if (currentParams.hasWindowingMode()) { + launchMode = currentParams.mWindowingMode; + fullyResolvedCurrentParam = (launchMode != WINDOWING_MODE_FREEFORM); + if (DEBUG) { + appendLog("inherit-" + WindowConfiguration.windowingModeToString(launchMode)); + } + } - if (layout == null) { - positionCenter(tasks, mAvailableRect, getFreeformWidth(mAvailableRect), - getFreeformHeight(mAvailableRect), resultBounds); - return RESULT_CONTINUE; + if (!currentParams.mBounds.isEmpty()) { + outParams.mBounds.set(currentParams.mBounds); + fullyResolvedCurrentParam = true; + if (DEBUG) appendLog("inherit-bounds=" + outParams.mBounds); + } } - int width = getFinalWidth(layout, mAvailableRect); - int height = getFinalHeight(layout, mAvailableRect); - int verticalGravity = layout.gravity & Gravity.VERTICAL_GRAVITY_MASK; - int horizontalGravity = layout.gravity & Gravity.HORIZONTAL_GRAVITY_MASK; - if (verticalGravity == Gravity.TOP) { - if (horizontalGravity == Gravity.RIGHT) { - positionTopRight(tasks, mAvailableRect, width, height, resultBounds); - } else { - positionTopLeft(tasks, mAvailableRect, width, height, resultBounds); - } - } else if (verticalGravity == Gravity.BOTTOM) { - if (horizontalGravity == Gravity.RIGHT) { - positionBottomRight(tasks, mAvailableRect, width, height, resultBounds); - } else { - positionBottomLeft(tasks, mAvailableRect, width, height, resultBounds); + // STEP 2.3: Adjust launch parameters as needed for freeform display. We enforce the policy + // that legacy (pre-D) apps and those apps that can't handle multiple screen density well + // are forced to be maximized. The rest of this step is to define the default policy when + // there is no initial bounds or a fully resolved current params from callers. Right now we + // launch all possible tasks/activities that can handle freeform into freeform mode. + if (display.inFreeformWindowingMode()) { + if (launchMode == WINDOWING_MODE_PINNED) { + if (DEBUG) appendLog("picture-in-picture"); + } else if (isTaskForcedMaximized(root)) { + // We're launching an activity that probably can't handle resizing nicely, so force + // it to be maximized even someone suggests launching it in freeform using launch + // options. + launchMode = WINDOWING_MODE_FULLSCREEN; + outParams.mBounds.setEmpty(); + if (DEBUG) appendLog("forced-maximize"); + } else if (fullyResolvedCurrentParam) { + // Don't adjust launch mode if that's inherited, except when we're launching an + // activity that should be forced to maximize. + if (DEBUG) appendLog("skip-adjustment-fully-resolved-params"); + } else if (launchMode != WINDOWING_MODE_FREEFORM + && (isNOrGreater(root) || isPreNResizeable(root))) { + // We're launching a pre-N and post-D activity that supports resizing, or a post-N + // activity. They can handle freeform nicely so launch them in freeform. + // Use undefined because we know we're in a freeform display. + launchMode = WINDOWING_MODE_UNDEFINED; + if (DEBUG) appendLog("should-be-freeform"); } } else { - // Some fancy gravity setting that we don't support yet. We just put the activity in the - // center. - Slog.w(TAG, "Received unsupported gravity: " + layout.gravity - + ", positioning in the center instead."); - positionCenter(tasks, mAvailableRect, width, height, resultBounds); + if (DEBUG) appendLog("non-freeform-display"); + } + // If launch mode matches display windowing mode, let it inherit from display. + outParams.mWindowingMode = launchMode == display.getWindowingMode() + ? WINDOWING_MODE_UNDEFINED : launchMode; + + // STEP 3: Determine final launch bounds based on resolved windowing mode and activity + // requested orientation. We set bounds to empty for fullscreen mode and keep bounds as is + // for all other windowing modes that's not freeform mode. One can read comments in + // relevant methods to further understand this step. + // + // We skip making adjustments if the params are fully resolved from previous results and + // trust that they are valid. + if (!fullyResolvedCurrentParam) { + final int resolvedMode = (launchMode != WINDOWING_MODE_UNDEFINED) ? launchMode + : display.getWindowingMode(); + if (source != null && source.inFreeformWindowingMode() + && resolvedMode == WINDOWING_MODE_FREEFORM + && outParams.mBounds.isEmpty() + && source.getDisplayId() == display.mDisplayId) { + // Set bounds to be not very far from source activity. + cascadeBounds(source.getBounds(), display, outParams.mBounds); + } + getTaskBounds(root, display, layout, resolvedMode, hasInitialBounds, outParams.mBounds); } return RESULT_CONTINUE; } - @VisibleForTesting - static int getFreeformStartLeft(Rect bounds) { - return bounds.left + bounds.width() / MARGIN_SIZE_DENOMINATOR; + private int getPreferredLaunchDisplay(@Nullable ActivityOptions options, + ActivityRecord source, LaunchParams currentParams) { + int displayId = INVALID_DISPLAY; + final int optionLaunchId = options != null ? options.getLaunchDisplayId() : INVALID_DISPLAY; + if (optionLaunchId != INVALID_DISPLAY) { + if (DEBUG) appendLog("display-from-option=" + optionLaunchId); + displayId = optionLaunchId; + } + + if (displayId == INVALID_DISPLAY && source != null) { + final int sourceDisplayId = source.getDisplayId(); + if (DEBUG) appendLog("display-from-source=" + sourceDisplayId); + displayId = sourceDisplayId; + } + + if (displayId != INVALID_DISPLAY && mSupervisor.getActivityDisplay(displayId) == null) { + displayId = INVALID_DISPLAY; + } + displayId = (displayId == INVALID_DISPLAY) ? currentParams.mPreferredDisplayId : displayId; + + displayId = (displayId == INVALID_DISPLAY) ? DEFAULT_DISPLAY : displayId; + + return displayId; + } + + private boolean canApplyFreeformWindowPolicy(@NonNull ActivityDisplay display, + @NonNull ActivityRecord root, int launchMode) { + return display.inFreeformWindowingMode() || launchMode == WINDOWING_MODE_FREEFORM + || root.isResizeable(); } - @VisibleForTesting - static int getFreeformStartTop(Rect bounds) { - return bounds.top + bounds.height() / MARGIN_SIZE_DENOMINATOR; + private void getLayoutBounds(@NonNull ActivityDisplay display, @NonNull ActivityRecord root, + @NonNull ActivityInfo.WindowLayout windowLayout, @NonNull Rect outBounds) { + final int verticalGravity = windowLayout.gravity & Gravity.VERTICAL_GRAVITY_MASK; + final int horizontalGravity = windowLayout.gravity & Gravity.HORIZONTAL_GRAVITY_MASK; + if (!windowLayout.hasSpecifiedSize() && verticalGravity == 0 && horizontalGravity == 0) { + outBounds.setEmpty(); + return; + } + + final Rect bounds = display.getBounds(); + final int defaultWidth = bounds.width(); + final int defaultHeight = bounds.height(); + + int width; + int height; + if (!windowLayout.hasSpecifiedSize()) { + outBounds.setEmpty(); + getTaskBounds(root, display, windowLayout, WINDOWING_MODE_FREEFORM, + /* hasInitialBounds */ false, outBounds); + width = outBounds.width(); + height = outBounds.height(); + } else { + width = defaultWidth; + if (windowLayout.width > 0 && windowLayout.width < defaultWidth) { + width = windowLayout.width; + } else if (windowLayout.widthFraction > 0 && windowLayout.widthFraction < 1.0f) { + width = (int) (width * windowLayout.widthFraction); + } + + height = defaultHeight; + if (windowLayout.height > 0 && windowLayout.height < defaultHeight) { + height = windowLayout.height; + } else if (windowLayout.heightFraction > 0 && windowLayout.heightFraction < 1.0f) { + height = (int) (height * windowLayout.heightFraction); + } + } + + final float fractionOfHorizontalOffset; + switch (horizontalGravity) { + case Gravity.LEFT: + fractionOfHorizontalOffset = 0f; + break; + case Gravity.RIGHT: + fractionOfHorizontalOffset = 1f; + break; + default: + fractionOfHorizontalOffset = 0.5f; + } + + final float fractionOfVerticalOffset; + switch (verticalGravity) { + case Gravity.TOP: + fractionOfVerticalOffset = 0f; + break; + case Gravity.BOTTOM: + fractionOfVerticalOffset = 1f; + break; + default: + fractionOfVerticalOffset = 0.5f; + } + + outBounds.set(0, 0, width, height); + final int xOffset = (int) (fractionOfHorizontalOffset * (defaultWidth - width)); + final int yOffset = (int) (fractionOfVerticalOffset * (defaultHeight - height)); + outBounds.offset(xOffset, yOffset); } - @VisibleForTesting - static int getFreeformWidth(Rect bounds) { - return bounds.width() / WINDOW_SIZE_DENOMINATOR; + /** + * Returns if task is forced to maximize. + * + * There are several cases where we force a task to maximize: + * 1) Root activity is targeting pre-Donut, which by default can't handle multiple screen + * densities, so resizing will likely cause issues; + * 2) Root activity doesn't declare any flag that it supports any screen density, so resizing + * may also cause issues; + * 3) Root activity is not resizeable, for which we shouldn't allow user resize it. + * + * @param root the root activity to check against. + * @return {@code true} if it should be forced to maximize; {@code false} otherwise. + */ + private boolean isTaskForcedMaximized(@NonNull ActivityRecord root) { + if (root.appInfo.targetSdkVersion < Build.VERSION_CODES.DONUT + || (root.appInfo.flags & SUPPORTS_SCREEN_RESIZEABLE_MASK) == 0) { + return true; + } + + return !root.isResizeable(); } - @VisibleForTesting - static int getFreeformHeight(Rect bounds) { - return bounds.height() / WINDOW_SIZE_DENOMINATOR; + private boolean isNOrGreater(@NonNull ActivityRecord root) { + return root.appInfo.targetSdkVersion >= Build.VERSION_CODES.N; + } + + /** + * Resolves activity requested orientation to 4 categories: + * 1) {@link ActivityInfo#SCREEN_ORIENTATION_LOCKED} indicating app wants to lock down + * orientation; + * 2) {@link ActivityInfo#SCREEN_ORIENTATION_LANDSCAPE} indicating app wants to be in landscape; + * 3) {@link ActivityInfo#SCREEN_ORIENTATION_PORTRAIT} indicating app wants to be in portrait; + * 4) {@link ActivityInfo#SCREEN_ORIENTATION_UNSPECIFIED} indicating app can handle any + * orientation. + * + * @param activity the activity to check + * @return corresponding resolved orientation value. + */ + private int resolveOrientation(@NonNull ActivityRecord activity) { + int orientation = activity.info.screenOrientation; + switch (orientation) { + case SCREEN_ORIENTATION_NOSENSOR: + case SCREEN_ORIENTATION_LOCKED: + orientation = SCREEN_ORIENTATION_LOCKED; + break; + case SCREEN_ORIENTATION_SENSOR_LANDSCAPE: + case SCREEN_ORIENTATION_REVERSE_LANDSCAPE: + case SCREEN_ORIENTATION_USER_LANDSCAPE: + case SCREEN_ORIENTATION_LANDSCAPE: + if (DEBUG) appendLog("activity-requested-landscape"); + orientation = SCREEN_ORIENTATION_LANDSCAPE; + break; + case SCREEN_ORIENTATION_SENSOR_PORTRAIT: + case SCREEN_ORIENTATION_REVERSE_PORTRAIT: + case SCREEN_ORIENTATION_USER_PORTRAIT: + case SCREEN_ORIENTATION_PORTRAIT: + if (DEBUG) appendLog("activity-requested-portrait"); + orientation = SCREEN_ORIENTATION_PORTRAIT; + break; + default: + orientation = SCREEN_ORIENTATION_UNSPECIFIED; + } + + return orientation; } - @VisibleForTesting - static int getHorizontalStep(Rect bounds) { - return Math.max(bounds.width() / STEP_DENOMINATOR, MINIMAL_STEP); + private boolean isPreNResizeable(ActivityRecord root) { + return root.appInfo.targetSdkVersion < Build.VERSION_CODES.N && root.isResizeable(); } - @VisibleForTesting - static int getVerticalStep(Rect bounds) { - return Math.max(bounds.height() / STEP_DENOMINATOR, MINIMAL_STEP); + private void cascadeBounds(@NonNull Rect srcBounds, @NonNull ActivityDisplay display, + @NonNull Rect outBounds) { + outBounds.set(srcBounds); + float density = (float) display.getConfiguration().densityDpi / DENSITY_DEFAULT; + final int defaultOffset = (int) (CASCADING_OFFSET_DP * density + 0.5f); + + display.getBounds(mTmpBounds); + final int dx = Math.min(defaultOffset, Math.max(0, mTmpBounds.right - srcBounds.right)); + final int dy = Math.min(defaultOffset, Math.max(0, mTmpBounds.bottom - srcBounds.bottom)); + outBounds.offset(dx, dy); } + private void getTaskBounds(@NonNull ActivityRecord root, @NonNull ActivityDisplay display, + @NonNull ActivityInfo.WindowLayout layout, int resolvedMode, boolean hasInitialBounds, + @NonNull Rect inOutBounds) { + if (resolvedMode == WINDOWING_MODE_FULLSCREEN) { + // We don't handle letterboxing here. Letterboxing will be handled by valid checks + // later. + inOutBounds.setEmpty(); + if (DEBUG) appendLog("maximized-bounds"); + return; + } + if (resolvedMode != WINDOWING_MODE_FREEFORM) { + // We don't apply freeform bounds adjustment to other windowing modes. + if (DEBUG) { + appendLog("skip-bounds-" + WindowConfiguration.windowingModeToString(resolvedMode)); + } + return; + } - private int getFinalWidth(ActivityInfo.WindowLayout windowLayout, Rect availableRect) { - int width = getFreeformWidth(availableRect); - if (windowLayout.width > 0) { - width = windowLayout.width; + final int orientation = resolveOrientation(root, display, inOutBounds); + if (orientation != SCREEN_ORIENTATION_PORTRAIT + && orientation != SCREEN_ORIENTATION_LANDSCAPE) { + throw new IllegalStateException( + "Orientation must be one of portrait or landscape, but it's " + + ActivityInfo.screenOrientationToString(orientation)); } - if (windowLayout.widthFraction > 0) { - width = (int) (availableRect.width() * windowLayout.widthFraction); + + // First we get the default size we want. + getDefaultFreeformSize(display, layout, orientation, mTmpBounds); + if (hasInitialBounds || sizeMatches(inOutBounds, mTmpBounds)) { + // We're here because either input parameters specified initial bounds, or the suggested + // bounds have the same size of the default freeform size. We should use the suggested + // bounds if possible -- so if app can handle the orientation we just use it, and if not + // we transpose the suggested bounds in-place. + if (orientation == orientationFromBounds(inOutBounds)) { + if (DEBUG) appendLog("freeform-size-orientation-match=" + inOutBounds); + } else { + // Meh, orientation doesn't match. Let's rotate inOutBounds in-place. + centerBounds(display, inOutBounds.height(), inOutBounds.width(), inOutBounds); + if (DEBUG) appendLog("freeform-orientation-mismatch=" + inOutBounds); + } + } else { + // We are here either because there is no suggested bounds, or the suggested bounds is + // a cascade from source activity. We should use the default freeform size and center it + // to the center of suggested bounds (or the display if no suggested bounds). The + // default size might be too big to center to source activity bounds in display, so we + // may need to move it back to the display. + centerBounds(display, mTmpBounds.width(), mTmpBounds.height(), inOutBounds); + adjustBoundsToFitInDisplay(display, inOutBounds); + if (DEBUG) appendLog("freeform-size-mismatch=" + inOutBounds); } - return width; + + // Lastly we adjust bounds to avoid conflicts with other tasks as much as possible. + adjustBoundsToAvoidConflict(display, inOutBounds); } - private int getFinalHeight(ActivityInfo.WindowLayout windowLayout, Rect availableRect) { - int height = getFreeformHeight(availableRect); - if (windowLayout.height > 0) { - height = windowLayout.height; + private int resolveOrientation(@NonNull ActivityRecord root, @NonNull ActivityDisplay display, + @NonNull Rect bounds) { + int orientation = resolveOrientation(root); + + if (orientation == SCREEN_ORIENTATION_LOCKED) { + orientation = bounds.isEmpty() ? display.getConfiguration().orientation + : orientationFromBounds(bounds); + if (DEBUG) { + appendLog(bounds.isEmpty() ? "locked-orientation-from-display=" + orientation + : "locked-orientation-from-bounds=" + bounds); + } } - if (windowLayout.heightFraction > 0) { - height = (int) (availableRect.height() * windowLayout.heightFraction); + + if (orientation == SCREEN_ORIENTATION_UNSPECIFIED) { + orientation = bounds.isEmpty() ? SCREEN_ORIENTATION_PORTRAIT + : orientationFromBounds(bounds); + if (DEBUG) { + appendLog(bounds.isEmpty() ? "default-portrait" + : "orientation-from-bounds=" + bounds); + } } - return height; - } - private void positionBottomLeft(ArrayList<TaskRecord> tasks, Rect availableRect, int width, - int height, Rect result) { - mTmpProposal.set(availableRect.left, availableRect.bottom - height, - availableRect.left + width, availableRect.bottom); - position(tasks, availableRect, mTmpProposal, !ALLOW_RESTART, SHIFT_POLICY_HORIZONTAL_RIGHT, - result); + return orientation; } - private void positionBottomRight(ArrayList<TaskRecord> tasks, Rect availableRect, int width, - int height, Rect result) { - mTmpProposal.set(availableRect.right - width, availableRect.bottom - height, - availableRect.right, availableRect.bottom); - position(tasks, availableRect, mTmpProposal, !ALLOW_RESTART, SHIFT_POLICY_HORIZONTAL_LEFT, - result); + private void getDefaultFreeformSize(@NonNull ActivityDisplay display, + @NonNull ActivityInfo.WindowLayout layout, int orientation, @NonNull Rect bounds) { + // Default size, which is letterboxing/pillarboxing in display. That's to say the large + // dimension of default size is the small dimension of display size, and the small dimension + // of default size is calculated to keep the same aspect ratio as the display's. + Rect displayBounds = display.getBounds(); + final int portraitHeight = Math.min(displayBounds.width(), displayBounds.height()); + final int otherDimension = Math.max(displayBounds.width(), displayBounds.height()); + final int portraitWidth = (portraitHeight * portraitHeight) / otherDimension; + final int defaultWidth = (orientation == SCREEN_ORIENTATION_LANDSCAPE) ? portraitHeight + : portraitWidth; + final int defaultHeight = (orientation == SCREEN_ORIENTATION_LANDSCAPE) ? portraitWidth + : portraitHeight; + + // Get window size based on Nexus 5x screen, we assume that this is enough to show content + // of activities. + final float density = (float) display.getConfiguration().densityDpi / DENSITY_DEFAULT; + final int phonePortraitWidth = (int) (DEFAULT_PORTRAIT_PHONE_WIDTH_DP * density + 0.5f); + final int phonePortraitHeight = (int) (DEFAULT_PORTRAIT_PHONE_HEIGHT_DP * density + 0.5f); + final int phoneWidth = (orientation == SCREEN_ORIENTATION_LANDSCAPE) ? phonePortraitHeight + : phonePortraitWidth; + final int phoneHeight = (orientation == SCREEN_ORIENTATION_LANDSCAPE) ? phonePortraitWidth + : phonePortraitHeight; + + // Minimum layout requirements. + final int layoutMinWidth = (layout == null) ? -1 : layout.minWidth; + final int layoutMinHeight = (layout == null) ? -1 : layout.minHeight; + + // Final result. + final int width = Math.min(defaultWidth, Math.max(phoneWidth, layoutMinWidth)); + final int height = Math.min(defaultHeight, Math.max(phoneHeight, layoutMinHeight)); + + bounds.set(0, 0, width, height); } - private void positionTopLeft(ArrayList<TaskRecord> tasks, Rect availableRect, int width, - int height, Rect result) { - mTmpProposal.set(availableRect.left, availableRect.top, - availableRect.left + width, availableRect.top + height); - position(tasks, availableRect, mTmpProposal, !ALLOW_RESTART, SHIFT_POLICY_HORIZONTAL_RIGHT, - result); + /** + * Gets centered bounds of width x height. If inOutBounds is not empty, the result bounds + * centers at its center or display's center if inOutBounds is empty. + */ + private void centerBounds(@NonNull ActivityDisplay display, int width, int height, + @NonNull Rect inOutBounds) { + if (inOutBounds.isEmpty()) { + display.getBounds(inOutBounds); + } + final int left = inOutBounds.centerX() - width / 2; + final int top = inOutBounds.centerY() - height / 2; + inOutBounds.set(left, top, left + width, top + height); } - private void positionTopRight(ArrayList<TaskRecord> tasks, Rect availableRect, int width, - int height, Rect result) { - mTmpProposal.set(availableRect.right - width, availableRect.top, - availableRect.right, availableRect.top + height); - position(tasks, availableRect, mTmpProposal, !ALLOW_RESTART, SHIFT_POLICY_HORIZONTAL_LEFT, - result); - } + private void adjustBoundsToFitInDisplay(@NonNull ActivityDisplay display, + @NonNull Rect inOutBounds) { + final Rect displayBounds = display.getBounds(); + + if (displayBounds.width() < inOutBounds.width() + || displayBounds.height() < inOutBounds.height()) { + // There is no way for us to fit the bounds in the display without changing width + // or height. Don't even try it. + return; + } + + final int dx; + if (inOutBounds.right > displayBounds.right) { + // Right edge is out of display. + dx = displayBounds.right - inOutBounds.right; + } else if (inOutBounds.left < displayBounds.left) { + // Left edge is out of display. + dx = displayBounds.left - inOutBounds.left; + } else { + // Vertical edges are all in display. + dx = 0; + } - private void positionCenter(ArrayList<TaskRecord> tasks, Rect availableRect, int width, - int height, Rect result) { - final int defaultFreeformLeft = getFreeformStartLeft(availableRect); - final int defaultFreeformTop = getFreeformStartTop(availableRect); - mTmpProposal.set(defaultFreeformLeft, defaultFreeformTop, - defaultFreeformLeft + width, defaultFreeformTop + height); - position(tasks, availableRect, mTmpProposal, ALLOW_RESTART, SHIFT_POLICY_DIAGONAL_DOWN, - result); + final int dy; + if (inOutBounds.top < displayBounds.top) { + // Top edge is out of display. + dy = displayBounds.top - inOutBounds.top; + } else if (inOutBounds.bottom > displayBounds.bottom) { + // Bottom edge is out of display. + dy = displayBounds.bottom - inOutBounds.bottom; + } else { + // Horizontal edges are all in display. + dy = 0; + } + inOutBounds.offset(dx, dy); } - private void position(ArrayList<TaskRecord> tasks, Rect availableRect, - Rect proposal, boolean allowRestart, int shiftPolicy, Rect result) { - mTmpOriginal.set(proposal); - boolean restarted = false; - while (boundsConflict(proposal, tasks)) { - // Unfortunately there is already a task at that spot, so we need to look for some - // other place. - shiftStartingPoint(proposal, availableRect, shiftPolicy); - if (shiftedTooFar(proposal, availableRect, shiftPolicy)) { - // We don't want the task to go outside of the stack, because it won't look - // nice. Depending on the starting point we either restart, or immediately give up. - if (!allowRestart) { - proposal.set(mTmpOriginal); - break; - } - // We must have started not from the top. Let's restart from there because there - // might be some space there. - proposal.set(availableRect.left, availableRect.top, - availableRect.left + proposal.width(), - availableRect.top + proposal.height()); - restarted = true; + /** + * Adjusts input bounds to avoid conflict with existing tasks in the display. + * + * If the input bounds conflict with existing tasks, this method scans the bounds in a series of + * directions to find a location where the we can put the bounds in display without conflict + * with any other tasks. + * + * It doesn't try to adjust bounds that's not fully in the given display. + * + * @param display the display which tasks are to check + * @param inOutBounds the bounds used to input initial bounds and output result bounds + */ + private void adjustBoundsToAvoidConflict(@NonNull ActivityDisplay display, + @NonNull Rect inOutBounds) { + final Rect displayBounds = display.getBounds(); + if (!displayBounds.contains(inOutBounds)) { + // The initial bounds are already out of display. The scanning algorithm below doesn't + // work so well with them. + return; + } + + final List<TaskRecord> tasksToCheck = new ArrayList<>(); + for (int i = 0; i < display.getChildCount(); ++i) { + ActivityStack<?> stack = display.getChildAt(i); + if (!stack.inFreeformWindowingMode()) { + continue; } - if (restarted && (proposal.left > getFreeformStartLeft(availableRect) - || proposal.top > getFreeformStartTop(availableRect))) { - // If we restarted and crossed the initial position, let's not struggle anymore. - // The user already must have ton of tasks visible, we can just smack the new - // one in the center. - proposal.set(mTmpOriginal); + + for (int j = 0; j < stack.getChildCount(); ++j) { + tasksToCheck.add(stack.getChildAt(j)); + } + } + + if (!boundsConflict(tasksToCheck, inOutBounds)) { + // Current proposal doesn't conflict with any task. Early return to avoid unnecessary + // calculation. + return; + } + + calculateCandidateShiftDirections(displayBounds, inOutBounds); + for (int direction : mTmpDirections) { + if (direction == Gravity.NO_GRAVITY) { + // We exhausted candidate directions, give up. break; } + + mTmpBounds.set(inOutBounds); + while (boundsConflict(tasksToCheck, mTmpBounds) && displayBounds.contains(mTmpBounds)) { + shiftBounds(direction, displayBounds, mTmpBounds); + } + + if (!boundsConflict(tasksToCheck, mTmpBounds) && displayBounds.contains(mTmpBounds)) { + // Found a candidate. Just use this. + inOutBounds.set(mTmpBounds); + if (DEBUG) appendLog("avoid-bounds-conflict=" + inOutBounds); + return; + } + + // Didn't find a conflict free bounds here. Try the next candidate direction. } - result.set(proposal); + + // We failed to find a conflict free location. Just keep the original result. } - private boolean shiftedTooFar(Rect start, Rect availableRect, int shiftPolicy) { - switch (shiftPolicy) { - case SHIFT_POLICY_HORIZONTAL_LEFT: - return start.left < availableRect.left; - case SHIFT_POLICY_HORIZONTAL_RIGHT: - return start.right > availableRect.right; - default: // SHIFT_POLICY_DIAGONAL_DOWN - return start.right > availableRect.right || start.bottom > availableRect.bottom; + /** + * Determines scanning directions and their priorities to avoid bounds conflict. + * + * @param availableBounds bounds that the result must be in + * @param initialBounds initial bounds when start scanning + */ + private void calculateCandidateShiftDirections(@NonNull Rect availableBounds, + @NonNull Rect initialBounds) { + for (int i = 0; i < mTmpDirections.length; ++i) { + mTmpDirections[i] = Gravity.NO_GRAVITY; + } + + final int oneThirdWidth = (2 * availableBounds.left + availableBounds.right) / 3; + final int twoThirdWidth = (availableBounds.left + 2 * availableBounds.right) / 3; + final int centerX = initialBounds.centerX(); + if (centerX < oneThirdWidth) { + // Too close to left, just scan to the right. + mTmpDirections[0] = Gravity.RIGHT; + return; + } else if (centerX > twoThirdWidth) { + // Too close to right, just scan to the left. + mTmpDirections[0] = Gravity.LEFT; + return; } + + final int oneThirdHeight = (2 * availableBounds.top + availableBounds.bottom) / 3; + final int twoThirdHeight = (availableBounds.top + 2 * availableBounds.bottom) / 3; + final int centerY = initialBounds.centerY(); + if (centerY < oneThirdHeight || centerY > twoThirdHeight) { + // Too close to top or bottom boundary and we're in the middle horizontally, scan + // horizontally in both directions. + mTmpDirections[0] = Gravity.RIGHT; + mTmpDirections[1] = Gravity.LEFT; + return; + } + + // We're in the center region both horizontally and vertically. Scan in both directions of + // primary diagonal. + mTmpDirections[0] = Gravity.BOTTOM | Gravity.RIGHT; + mTmpDirections[1] = Gravity.TOP | Gravity.LEFT; } - private void shiftStartingPoint(Rect posposal, Rect availableRect, int shiftPolicy) { - final int defaultFreeformStepHorizontal = getHorizontalStep(availableRect); - final int defaultFreeformStepVertical = getVerticalStep(availableRect); + private boolean boundsConflict(@NonNull List<TaskRecord> tasks, @NonNull Rect bounds) { + for (TaskRecord task : tasks) { + final Rect taskBounds = task.getBounds(); + final boolean leftClose = Math.abs(taskBounds.left - bounds.left) + < BOUNDS_CONFLICT_THRESHOLD; + final boolean topClose = Math.abs(taskBounds.top - bounds.top) + < BOUNDS_CONFLICT_THRESHOLD; + final boolean rightClose = Math.abs(taskBounds.right - bounds.right) + < BOUNDS_CONFLICT_THRESHOLD; + final boolean bottomClose = Math.abs(taskBounds.bottom - bounds.bottom) + < BOUNDS_CONFLICT_THRESHOLD; + + if ((leftClose && topClose) || (leftClose && bottomClose) || (rightClose && topClose) + || (rightClose && bottomClose)) { + return true; + } + } + + return false; + } - switch (shiftPolicy) { - case SHIFT_POLICY_HORIZONTAL_LEFT: - posposal.offset(-defaultFreeformStepHorizontal, 0); + private void shiftBounds(int direction, @NonNull Rect availableRect, + @NonNull Rect inOutBounds) { + final int horizontalOffset; + switch (direction & Gravity.HORIZONTAL_GRAVITY_MASK) { + case Gravity.LEFT: + horizontalOffset = -Math.max(MINIMAL_STEP, + availableRect.width() / STEP_DENOMINATOR); break; - case SHIFT_POLICY_HORIZONTAL_RIGHT: - posposal.offset(defaultFreeformStepHorizontal, 0); + case Gravity.RIGHT: + horizontalOffset = Math.max(MINIMAL_STEP, availableRect.width() / STEP_DENOMINATOR); break; - default: // SHIFT_POLICY_DIAGONAL_DOWN: - posposal.offset(defaultFreeformStepHorizontal, defaultFreeformStepVertical); + default: + horizontalOffset = 0; + } + + final int verticalOffset; + switch (direction & Gravity.VERTICAL_GRAVITY_MASK) { + case Gravity.TOP: + verticalOffset = -Math.max(MINIMAL_STEP, availableRect.height() / STEP_DENOMINATOR); break; + case Gravity.BOTTOM: + verticalOffset = Math.max(MINIMAL_STEP, availableRect.height() / STEP_DENOMINATOR); + break; + default: + verticalOffset = 0; } + + inOutBounds.offset(horizontalOffset, verticalOffset); } - private static boolean boundsConflict(Rect proposal, ArrayList<TaskRecord> tasks) { - for (int i = tasks.size() - 1; i >= 0; i--) { - final TaskRecord task = tasks.get(i); - if (!task.mActivities.isEmpty() && !task.matchParentBounds()) { - final Rect bounds = task.getOverrideBounds(); - if (closeLeftTopCorner(proposal, bounds) || closeRightTopCorner(proposal, bounds) - || closeLeftBottomCorner(proposal, bounds) - || closeRightBottomCorner(proposal, bounds)) { - return true; - } - } + private void initLogBuilder(TaskRecord task, ActivityRecord activity) { + if (DEBUG) { + mLogBuilder = new StringBuilder("TaskLaunchParamsModifier:task=" + task + + " activity=" + activity); } - return false; } - private static final boolean closeLeftTopCorner(Rect first, Rect second) { - return Math.abs(first.left - second.left) < BOUNDS_CONFLICT_MIN_DISTANCE - && Math.abs(first.top - second.top) < BOUNDS_CONFLICT_MIN_DISTANCE; + private void appendLog(String log) { + if (DEBUG) mLogBuilder.append(" ").append(log); } - private static final boolean closeRightTopCorner(Rect first, Rect second) { - return Math.abs(first.right - second.right) < BOUNDS_CONFLICT_MIN_DISTANCE - && Math.abs(first.top - second.top) < BOUNDS_CONFLICT_MIN_DISTANCE; + private void outputLog() { + if (DEBUG) Slog.d(TAG, mLogBuilder.toString()); } - private static final boolean closeLeftBottomCorner(Rect first, Rect second) { - return Math.abs(first.left - second.left) < BOUNDS_CONFLICT_MIN_DISTANCE - && Math.abs(first.bottom - second.bottom) < BOUNDS_CONFLICT_MIN_DISTANCE; + private static int orientationFromBounds(Rect bounds) { + return bounds.width() > bounds.height() ? SCREEN_ORIENTATION_LANDSCAPE + : SCREEN_ORIENTATION_PORTRAIT; } - private static final boolean closeRightBottomCorner(Rect first, Rect second) { - return Math.abs(first.right - second.right) < BOUNDS_CONFLICT_MIN_DISTANCE - && Math.abs(first.bottom - second.bottom) < BOUNDS_CONFLICT_MIN_DISTANCE; + private static boolean sizeMatches(Rect left, Rect right) { + return (Math.abs(right.width() - left.width()) < EPSILON) + && (Math.abs(right.height() - left.height()) < EPSILON); } } diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java index ca9b25699c1c..30659c1e77a3 100644 --- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java +++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java @@ -1031,6 +1031,11 @@ public class NetworkMonitor extends StateMachine { result.isPortal() /* isCaptivePortal */, startTime, endTime); + log("isCaptivePortal: isSuccessful()=" + result.isSuccessful() + + " isPortal()=" + result.isPortal() + + " RedirectUrl=" + result.redirectUrl + + " StartTime=" + startTime + " EndTime=" + endTime); + return result; } diff --git a/services/core/java/com/android/server/connectivity/ProxyTracker.java b/services/core/java/com/android/server/connectivity/ProxyTracker.java index 111c6d2de537..47e85b5fd3ab 100644 --- a/services/core/java/com/android/server/connectivity/ProxyTracker.java +++ b/services/core/java/com/android/server/connectivity/ProxyTracker.java @@ -16,6 +16,12 @@ package com.android.server.connectivity; +import static android.provider.Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST; +import static android.provider.Settings.Global.GLOBAL_HTTP_PROXY_HOST; +import static android.provider.Settings.Global.GLOBAL_HTTP_PROXY_PAC; +import static android.provider.Settings.Global.GLOBAL_HTTP_PROXY_PORT; +import static android.provider.Settings.Global.HTTP_PROXY; + import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ContentResolver; @@ -47,16 +53,14 @@ public class ProxyTracker { @NonNull private final Context mContext; - // TODO : make this private and import as much managing logic from ConnectivityService as - // possible @NonNull - public final Object mProxyLock = new Object(); + private final Object mProxyLock = new Object(); // The global proxy is the proxy that is set device-wide, overriding any network-specific // proxy. Note however that proxies are hints ; the system does not enforce their use. Hence // this value is only for querying. @Nullable @GuardedBy("mProxyLock") - public ProxyInfo mGlobalProxy = null; + private ProxyInfo mGlobalProxy = null; // The default proxy is the proxy that applies to no particular network if the global proxy // is not set. Individual networks have their own settings that override this. This member // is set through setDefaultProxy, which is called when the default network changes proxies @@ -64,10 +68,10 @@ public class ProxyTracker { // when PacManager resolves the proxy. @Nullable @GuardedBy("mProxyLock") - public volatile ProxyInfo mDefaultProxy = null; - // Whether the default proxy is disabled. TODO : make this mDefaultProxyEnabled + private volatile ProxyInfo mDefaultProxy = null; + // Whether the default proxy is enabled. @GuardedBy("mProxyLock") - public boolean mDefaultProxyDisabled = false; + private boolean mDefaultProxyEnabled = true; // The object responsible for Proxy Auto Configuration (PAC). @NonNull @@ -85,7 +89,7 @@ public class ProxyTracker { @Nullable private static ProxyInfo canonicalizeProxyInfo(@Nullable final ProxyInfo proxy) { if (proxy != null && TextUtils.isEmpty(proxy.getHost()) - && (proxy.getPacFileUrl() == null || Uri.EMPTY.equals(proxy.getPacFileUrl()))) { + && Uri.EMPTY.equals(proxy.getPacFileUrl())) { return null; } return proxy; @@ -123,7 +127,7 @@ public class ProxyTracker { // This information is already available as a world read/writable jvm property. synchronized (mProxyLock) { final ProxyInfo ret = mGlobalProxy; - if ((ret == null) && !mDefaultProxyDisabled) return mDefaultProxy; + if ((ret == null) && mDefaultProxyEnabled) return mDefaultProxy; return ret; } } @@ -146,11 +150,10 @@ public class ProxyTracker { */ public void loadGlobalProxy() { ContentResolver res = mContext.getContentResolver(); - String host = Settings.Global.getString(res, Settings.Global.GLOBAL_HTTP_PROXY_HOST); - int port = Settings.Global.getInt(res, Settings.Global.GLOBAL_HTTP_PROXY_PORT, 0); - String exclList = Settings.Global.getString(res, - Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST); - String pacFileUrl = Settings.Global.getString(res, Settings.Global.GLOBAL_HTTP_PROXY_PAC); + String host = Settings.Global.getString(res, GLOBAL_HTTP_PROXY_HOST); + int port = Settings.Global.getInt(res, GLOBAL_HTTP_PROXY_PORT, 0); + String exclList = Settings.Global.getString(res, GLOBAL_HTTP_PROXY_EXCLUSION_LIST); + String pacFileUrl = Settings.Global.getString(res, GLOBAL_HTTP_PROXY_PAC); if (!TextUtils.isEmpty(host) || !TextUtils.isEmpty(pacFileUrl)) { ProxyInfo proxyProperties; if (!TextUtils.isEmpty(pacFileUrl)) { @@ -175,8 +178,7 @@ public class ProxyTracker { * Read the global proxy from the deprecated Settings.Global.HTTP_PROXY setting and apply it. */ public void loadDeprecatedGlobalHttpProxy() { - final String proxy = Settings.Global.getString(mContext.getContentResolver(), - Settings.Global.HTTP_PROXY); + final String proxy = Settings.Global.getString(mContext.getContentResolver(), HTTP_PROXY); if (!TextUtils.isEmpty(proxy)) { String data[] = proxy.split(":"); if (data.length == 0) { @@ -259,11 +261,10 @@ public class ProxyTracker { final ContentResolver res = mContext.getContentResolver(); final long token = Binder.clearCallingIdentity(); try { - Settings.Global.putString(res, Settings.Global.GLOBAL_HTTP_PROXY_HOST, host); - Settings.Global.putInt(res, Settings.Global.GLOBAL_HTTP_PROXY_PORT, port); - Settings.Global.putString(res, Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST, - exclList); - Settings.Global.putString(res, Settings.Global.GLOBAL_HTTP_PROXY_PAC, pacFileUrl); + Settings.Global.putString(res, GLOBAL_HTTP_PROXY_HOST, host); + Settings.Global.putInt(res, GLOBAL_HTTP_PROXY_PORT, port); + Settings.Global.putString(res, GLOBAL_HTTP_PROXY_EXCLUSION_LIST, exclList); + Settings.Global.putString(res, GLOBAL_HTTP_PROXY_PAC, pacFileUrl); } finally { Binder.restoreCallingIdentity(token); } @@ -280,10 +281,7 @@ public class ProxyTracker { */ public void setDefaultProxy(@Nullable ProxyInfo proxyInfo) { synchronized (mProxyLock) { - if (mDefaultProxy != null && mDefaultProxy.equals(proxyInfo)) { - return; - } - if (mDefaultProxy == proxyInfo) return; // catches repeated nulls + if (Objects.equals(mDefaultProxy, proxyInfo)) return; if (proxyInfo != null && !proxyInfo.isValid()) { if (DBG) Slog.d(TAG, "Invalid proxy properties, ignoring: " + proxyInfo); return; @@ -304,9 +302,27 @@ public class ProxyTracker { mDefaultProxy = proxyInfo; if (mGlobalProxy != null) return; - if (!mDefaultProxyDisabled) { + if (mDefaultProxyEnabled) { sendProxyBroadcast(proxyInfo); } } } + + /** + * Enable or disable the default proxy. + * + * This sets the flag for enabling/disabling the default proxy and sends the broadcast + * if applicable. + * @param enabled whether the default proxy should be enabled. + */ + public void setDefaultProxyEnabled(final boolean enabled) { + synchronized (mProxyLock) { + if (mDefaultProxyEnabled != enabled) { + mDefaultProxyEnabled = enabled; + if (mGlobalProxy == null && mDefaultProxy != null) { + sendProxyBroadcast(enabled ? mDefaultProxy : null); + } + } + } + } } diff --git a/services/core/java/com/android/server/location/ContextHubClientBroker.java b/services/core/java/com/android/server/location/ContextHubClientBroker.java index 7a1da662961c..b29b7cf5a712 100644 --- a/services/core/java/com/android/server/location/ContextHubClientBroker.java +++ b/services/core/java/com/android/server/location/ContextHubClientBroker.java @@ -147,12 +147,9 @@ public class ContextHubClientBroker extends IContextHubClient.Stub /** * Invoked when the underlying binder of this broker has died at the client process. */ + @Override public void binderDied() { - try { - IContextHubClient.Stub.asInterface(this).close(); - } catch (RemoteException e) { - Log.e(TAG, "RemoteException while closing client on death", e); - } + close(); } /** diff --git a/services/core/java/com/android/server/os/DeviceIdentifiersPolicyService.java b/services/core/java/com/android/server/os/DeviceIdentifiersPolicyService.java index 3ffc5c53eab6..9c1ac346ce32 100644 --- a/services/core/java/com/android/server/os/DeviceIdentifiersPolicyService.java +++ b/services/core/java/com/android/server/os/DeviceIdentifiersPolicyService.java @@ -57,7 +57,7 @@ public final class DeviceIdentifiersPolicyService extends SystemService { // should use the getSerialForPackage method with the calling package specified. if (!TelephonyPermissions.checkCallingOrSelfReadDeviceIdentifiers(mContext, /* callingPackage */ null, "getSerial")) { - return null; + return Build.UNKNOWN; } return SystemProperties.get("ro.serialno", Build.UNKNOWN); } @@ -66,7 +66,7 @@ public final class DeviceIdentifiersPolicyService extends SystemService { public @Nullable String getSerialForPackage(String callingPackage) throws RemoteException { if (!TelephonyPermissions.checkCallingOrSelfReadDeviceIdentifiers(mContext, callingPackage, "getSerial")) { - return null; + return Build.UNKNOWN; } return SystemProperties.get("ro.serialno", Build.UNKNOWN); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 13f084e67494..910860363c26 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -2920,7 +2920,7 @@ public class PackageManagerService extends IPackageManager.Stub for (int splitPermNum = 0; splitPermNum < numSplitPerms; splitPermNum++) { final PermissionManager.SplitPermissionInfo splitPerm = splitPermissions.get(splitPermNum); - final String rootPerm = splitPerm.getRootPermission(); + final String rootPerm = splitPerm.getSplitPermission(); if (preUpgradeSdkVersion >= splitPerm.getTargetSdk()) { continue; @@ -2942,11 +2942,11 @@ public class PackageManagerService extends IPackageManager.Stub continue; } - final String[] newPerms = splitPerm.getNewPermissions(); + final List<String> newPerms = splitPerm.getNewPermissions(); - final int numNewPerms = newPerms.length; + final int numNewPerms = newPerms.size(); for (int newPermNum = 0; newPermNum < numNewPerms; newPermNum++) { - final String newPerm = newPerms[newPermNum]; + final String newPerm = newPerms.get(newPermNum); if (checkPermission(newPerm, pkgName, userId) == PERMISSION_GRANTED) { continue; } diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java index 6f644dd70339..0a93653ede56 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -1033,8 +1033,8 @@ public final class DefaultPermissionGrantPolicy { if (applicationInfo != null && applicationInfo.targetSdkVersion < splitPerm.getTargetSdk() - && permissionsWithoutSplits.contains(splitPerm.getRootPermission())) { - Collections.addAll(permissions, splitPerm.getNewPermissions()); + && permissionsWithoutSplits.contains(splitPerm.getSplitPermission())) { + permissions.addAll(splitPerm.getNewPermissions()); } } diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java index 1cba1c7bed1b..a55b49fe028d 100644 --- a/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java +++ b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java @@ -95,10 +95,22 @@ public class KeyguardStateMonitor extends IKeyguardStateCallback.Stub { mIsShowing = showing; mCallback.onShowingChanged(); - try { - mKeystoreService.onKeyguardVisibilityChanged(showing, mCurrentUserId); - } catch (RemoteException e) { - Slog.e(TAG, "Error informing keystore of screen lock", e); + int retry = 2; + while (retry > 0) { + try { + mKeystoreService.onKeyguardVisibilityChanged(showing, mCurrentUserId); + break; + } catch (RemoteException e) { + if (retry == 2) { + Slog.w(TAG, "Error informing keystore of screen lock. Keystore may have died" + + " -> refreshing service token and retrying"); + mKeystoreService = IKeystoreService.Stub.asInterface(ServiceManager + .getService("android.security.keystore")); + } else { + Slog.e(TAG, "Error informing keystore of screen lock after retrying once", e); + } + --retry; + } } } diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java index d36ab3f09399..5ce8145fa00e 100644 --- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java +++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java @@ -28,7 +28,6 @@ import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; -import android.provider.Settings; import android.service.textclassifier.ITextClassificationCallback; import android.service.textclassifier.ITextClassifierService; import android.service.textclassifier.ITextLinksCallback; @@ -41,7 +40,6 @@ import android.view.textclassifier.TextClassification; import android.view.textclassifier.TextClassificationContext; import android.view.textclassifier.TextClassificationManager; import android.view.textclassifier.TextClassificationSessionId; -import android.view.textclassifier.TextClassifier; import android.view.textclassifier.TextLinks; import android.view.textclassifier.TextSelection; @@ -275,6 +273,20 @@ public final class TextClassificationManagerService extends ITextClassifierServi IndentingPrintWriter pw = new IndentingPrintWriter(fout, " "); TextClassificationManager tcm = mContext.getSystemService(TextClassificationManager.class); tcm.dump(pw); + + pw.printPair("context", mContext); pw.println(); + synchronized (mLock) { + int size = mUserStates.size(); + pw.print("Number user states: "); pw.println(size); + if (size > 0) { + for (int i = 0; i < size; i++) { + pw.increaseIndent(); + UserState userState = mUserStates.valueAt(i); + pw.print(i); pw.print(":"); userState.dump(pw); pw.println(); + pw.decreaseIndent(); + } + } + } } private static final class PendingRequest implements IBinder.DeathRecipient { @@ -431,6 +443,15 @@ public final class TextClassificationManagerService extends ITextClassifierServi return willBind; } + private void dump(IndentingPrintWriter pw) { + pw.printPair("context", mContext); + pw.printPair("userId", mUserId); + synchronized (mLock) { + pw.printPair("binding", mBinding); + pw.printPair("numberRequests", mPendingRequests.size()); + } + } + private final class TextClassifierServiceConnection implements ServiceConnection { @Override public void onServiceConnected(ComponentName name, IBinder service) { diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 6f728fcf1f00..642f57814c61 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -92,7 +92,6 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREEN_ON; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT; import static com.android.server.wm.WindowManagerDebugConfig.SHOW_STACK_CRAWLS; import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS; @@ -119,7 +118,6 @@ import static com.android.server.wm.WindowManagerService.logSurface; import static com.android.server.wm.WindowState.RESIZE_HANDLE_WIDTH_IN_DP; import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING; import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW; -import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_MAY_CHANGE; import android.annotation.CallSuper; import android.annotation.NonNull; @@ -2065,9 +2063,25 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo layoutAndAssignWindowLayersIfNeeded(); } - int taskIdFromPoint(int x, int y) { + /** + * Used to obtain task ID when user taps on coordinate (x, y) in this display, and outside + * current task in focus. + * + * This returns the task ID of the foremost task at (x, y) if the task is not home. Otherwise it + * returns -1. + * + * @param x horizontal coordinate of the tap position + * @param y vertical coordinate of the tap position + * @return the task ID if a non-home task is found; -1 if not + */ + int taskForTapOutside(int x, int y) { for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) { final TaskStack stack = mTaskStackContainers.getChildAt(stackNdx); + if (stack.isActivityTypeHome()) { + // We skip not only home stack, but also everything behind home because user can't + // see them. + break; + } final int taskId = stack.taskIdFromPoint(x, y); if (taskId != -1) { return taskId; diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java index b7e37b2fd47a..25148d15343a 100644 --- a/services/core/java/com/android/server/wm/TaskPositioningController.java +++ b/services/core/java/com/android/server/wm/TaskPositioningController.java @@ -88,7 +88,7 @@ class TaskPositioningController { } taskId = task.mTaskId; } else { - taskId = displayContent.taskIdFromPoint(x, y); + taskId = displayContent.taskForTapOutside(x, y); } } if (taskId >= 0) { diff --git a/services/net/java/android/net/ip/IpNeighborMonitor.java b/services/net/java/android/net/ip/IpNeighborMonitor.java index fc07aa1ecd17..9512f1be66ef 100644 --- a/services/net/java/android/net/ip/IpNeighborMonitor.java +++ b/services/net/java/android/net/ip/IpNeighborMonitor.java @@ -40,7 +40,6 @@ import android.util.Log; import com.android.internal.util.BitUtils; import libcore.io.IoUtils; -import libcore.io.Libcore; import java.io.FileDescriptor; import java.net.InetAddress; diff --git a/services/net/java/android/net/netlink/NetlinkSocket.java b/services/net/java/android/net/netlink/NetlinkSocket.java index cfcba3a84f51..40098c1532b1 100644 --- a/services/net/java/android/net/netlink/NetlinkSocket.java +++ b/services/net/java/android/net/netlink/NetlinkSocket.java @@ -32,7 +32,6 @@ import android.system.Os; import android.system.StructTimeval; import android.util.Log; import libcore.io.IoUtils; -import libcore.io.Libcore; import java.io.FileDescriptor; import java.io.InterruptedIOException; diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityLaunchParamsModifierTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityLaunchParamsModifierTests.java deleted file mode 100644 index 9de64f2cf211..000000000000 --- a/services/tests/servicestests/src/com/android/server/am/ActivityLaunchParamsModifierTests.java +++ /dev/null @@ -1,144 +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 - */ - -package com.android.server.am; - -import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; - -import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_DONE; -import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import android.app.ActivityOptions; -import android.content.pm.ActivityInfo; -import android.graphics.Rect; -import android.platform.test.annotations.Presubmit; - -import androidx.test.filters.MediumTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.server.am.LaunchParamsController.LaunchParams; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -/** - * Tests for exercising resizing bounds due to activity options. - * - * Build/Install/Run: - * atest FrameworksServicesTests:ActivityLaunchParamsModifierTests - */ -@MediumTest -@Presubmit -@RunWith(AndroidJUnit4.class) -public class ActivityLaunchParamsModifierTests extends ActivityTestsBase { - private ActivityLaunchParamsModifier mModifier; - private ActivityTaskManagerService mService; - private ActivityStack mStack; - private TaskRecord mTask; - private ActivityRecord mActivity; - - private LaunchParams mCurrent; - private LaunchParams mResult; - - @Before - @Override - public void setUp() throws Exception { - super.setUp(); - mService = createActivityTaskManagerService(); - mModifier = new ActivityLaunchParamsModifier(mService.mStackSupervisor); - mCurrent = new LaunchParams(); - mResult = new LaunchParams(); - - - mStack = mService.mStackSupervisor.getDefaultDisplay().createStack( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - mTask = new TaskBuilder(mService.mStackSupervisor).setStack(mStack).build(); - mActivity = new ActivityBuilder(mService).setTask(mTask).build(); - } - - - @Test - public void testSkippedInvocations() throws Exception { - // No specified activity should be ignored - assertEquals(RESULT_SKIP, mModifier.onCalculate(null /*task*/, null /*layout*/, - null /*activity*/, null /*source*/, null /*options*/, mCurrent, mResult)); - - // No specified activity options should be ignored - assertEquals(RESULT_SKIP, mModifier.onCalculate(null /*task*/, null /*layout*/, - mActivity, null /*source*/, null /*options*/, mCurrent, mResult)); - - // launch bounds specified should be ignored. - final ActivityOptions options = ActivityOptions.makeBasic(); - assertEquals(RESULT_SKIP, mModifier.onCalculate(null /*task*/, null /*layout*/, - mActivity, null /*source*/, options /*options*/, mCurrent, mResult)); - - // Non-resizeable records should be ignored - mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE; - assertFalse(mActivity.isResizeable()); - assertEquals(RESULT_SKIP, mModifier.onCalculate(null /*task*/, null /*layout*/, - mActivity, null /*source*/, options /*options*/, mCurrent, mResult)); - - // make record resizeable - mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE; - assertTrue(mActivity.isResizeable()); - - assertEquals(RESULT_SKIP, mModifier.onCalculate(null /*task*/, null /*layout*/, - mActivity, null /*source*/, options /*options*/, mCurrent, mResult)); - - // Does not support freeform - mService.mSupportsFreeformWindowManagement = false; - assertFalse(mService.mStackSupervisor.canUseActivityOptionsLaunchBounds(options)); - assertEquals(RESULT_SKIP, mModifier.onCalculate(null /*task*/, null /*layout*/, - mActivity, null /*source*/, options /*options*/, mCurrent, mResult)); - - mService.mSupportsFreeformWindowManagement = true; - options.setLaunchBounds(new Rect()); - assertTrue(mService.mStackSupervisor.canUseActivityOptionsLaunchBounds(options)); - - // Invalid bounds - assertEquals(RESULT_SKIP, mModifier.onCalculate(null /*task*/, null /*layout*/, - mActivity, null /*source*/, options /*options*/, mCurrent, mResult)); - options.setLaunchBounds(new Rect(0, 0, -1, -1)); - assertEquals(RESULT_SKIP, mModifier.onCalculate(null /*task*/, null /*layout*/, - mActivity, null /*source*/, options /*options*/, mCurrent, mResult)); - - // Valid bounds should cause the positioner to be applied. - options.setLaunchBounds(new Rect(0, 0, 100, 100)); - assertEquals(RESULT_DONE, mModifier.onCalculate(null /*task*/, null /*layout*/, - mActivity, null /*source*/, options /*options*/, mCurrent, mResult)); - } - - @Test - public void testBoundsExtraction() throws Exception { - // Make activity resizeable and enable freeform mode. - mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE; - mService.mSupportsFreeformWindowManagement = true; - - ActivityOptions options = ActivityOptions.makeBasic(); - final Rect proposedBounds = new Rect(20, 30, 45, 40); - options.setLaunchBounds(proposedBounds); - - assertEquals(RESULT_DONE, mModifier.onCalculate(null /*task*/, null /*layout*/, - mActivity, null /*source*/, options /*options*/, mCurrent, mResult)); - assertEquals(mResult.mBounds, proposedBounds); - } -} diff --git a/services/tests/servicestests/src/com/android/server/am/TaskLaunchParamsModifierTests.java b/services/tests/servicestests/src/com/android/server/am/TaskLaunchParamsModifierTests.java index f5b8f78cfd53..0d1302f78316 100644 --- a/services/tests/servicestests/src/com/android/server/am/TaskLaunchParamsModifierTests.java +++ b/services/tests/servicestests/src/com/android/server/am/TaskLaunchParamsModifierTests.java @@ -11,239 +11,1098 @@ * 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 + * limitations under the License. */ package com.android.server.am; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; +import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; +import static android.util.DisplayMetrics.DENSITY_DEFAULT; +import static android.view.Display.DEFAULT_DISPLAY; import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; -import android.content.pm.ActivityInfo.WindowLayout; +import android.app.ActivityOptions; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.res.Configuration; import android.graphics.Rect; +import android.os.Build; import android.platform.test.annotations.Presubmit; import android.view.Gravity; -import androidx.test.filters.MediumTest; +import androidx.test.filters.SmallTest; +import androidx.test.filters.FlakyTest; import androidx.test.runner.AndroidJUnit4; +import com.android.server.am.LaunchParamsController.LaunchParams; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.Locale; + /** - * Tests for exercising resizing task bounds. + * Tests for default task bounds. * * Build/Install/Run: * atest FrameworksServicesTests:TaskLaunchParamsModifierTests */ -@MediumTest +@SmallTest @Presubmit @RunWith(AndroidJUnit4.class) +@FlakyTest(detail = "Confirm stable in post-submit before removing") public class TaskLaunchParamsModifierTests extends ActivityTestsBase { - private final static int STACK_WIDTH = 100; - private final static int STACK_HEIGHT = 200; - - private final static Rect STACK_BOUNDS = new Rect(0, 0, STACK_WIDTH, STACK_HEIGHT); - private ActivityTaskManagerService mService; - private ActivityStack mStack; - private TaskRecord mTask; + private ActivityRecord mActivity; - private TaskLaunchParamsModifier mPositioner; + private TaskLaunchParamsModifier mTarget; - private LaunchParamsController.LaunchParams mCurrent; - private LaunchParamsController.LaunchParams mResult; + private LaunchParams mCurrent; + private LaunchParams mResult; @Before @Override public void setUp() throws Exception { super.setUp(); - mService = createActivityTaskManagerService(); - mStack = mService.mStackSupervisor.getDefaultDisplay().createStack( - WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */); - mStack.requestResize(STACK_BOUNDS); + setupActivityTaskManagerService(); + mService.mSupportsFreeformWindowManagement = true; + when(mSupervisor.canUseActivityOptionsLaunchBounds(any())).thenCallRealMethod(); + + mActivity = new ActivityBuilder(mService).build(); + mActivity.appInfo.targetSdkVersion = Build.VERSION_CODES.N_MR1; + mActivity.info.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES; + + mTarget = new TaskLaunchParamsModifier(mSupervisor); + + mCurrent = new LaunchParams(); + mCurrent.reset(); + mResult = new LaunchParams(); + mResult.reset(); + } - // We must create the task after resizing to make sure it does not inherit the stack - // dimensions on resize. - mTask = new TaskBuilder(mService.mStackSupervisor).setStack(mStack).build(); + // ============================= + // Display ID Related Tests + // ============================= + @Test + public void testDefaultToPrimaryDisplay() { + createNewActivityDisplay(WINDOWING_MODE_FREEFORM); - mPositioner = new TaskLaunchParamsModifier(); + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null, + mActivity, /* source */ null, /* options */ null, mCurrent, mResult)); - mResult = new LaunchParamsController.LaunchParams(); - mCurrent = new LaunchParamsController.LaunchParams(); + assertEquals(DEFAULT_DISPLAY, mResult.mPreferredDisplayId); } - /** - * Ensures that the setup bounds are set as expected with the stack bounds set and the task - * bounds still {@code null}. - * @throws Exception - */ @Test - public void testInitialBounds() throws Exception { - assertEquals(mStack.getOverrideBounds(), STACK_BOUNDS); - assertEquals(mTask.getOverrideBounds(), new Rect()); + public void testUsesPreviousDisplayIdIfSet() { + createNewActivityDisplay(WINDOWING_MODE_FREEFORM); + final TestActivityDisplay display = createNewActivityDisplay(WINDOWING_MODE_FULLSCREEN); + + mCurrent.mPreferredDisplayId = display.mDisplayId; + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null, + mActivity, /* source */ null, /* options */ null, mCurrent, mResult)); + + assertEquals(display.mDisplayId, mResult.mPreferredDisplayId); } - /** - * Ensures that a task positioned with no {@link WindowLayout} receives the default launch - * position. - * @throws Exception - */ @Test - public void testLaunchNoWindowLayout() throws Exception { - assertEquals(RESULT_CONTINUE, mPositioner.onCalculate(mTask, null /*layout*/, - null /*record*/, null /*source*/, null /*options*/, mCurrent, mResult)); - assertEquals(getDefaultBounds(Gravity.NO_GRAVITY), mResult.mBounds); + public void testUsesSourcesDisplayIdIfSet() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + final TestActivityDisplay fullscreenDisplay = createNewActivityDisplay( + WINDOWING_MODE_FULLSCREEN); + + mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId; + + ActivityRecord source = createSourceActivity(fullscreenDisplay); + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null, + mActivity, source, /* options */ null, mCurrent, mResult)); + + assertEquals(fullscreenDisplay.mDisplayId, mResult.mPreferredDisplayId); } - /** - * Ensures that a task positioned with an empty {@link WindowLayout} receives the default launch - * position. - * @throws Exception - */ @Test - public void testlaunchEmptyWindowLayout() throws Exception { - assertEquals(RESULT_CONTINUE, mPositioner.onCalculate(mTask, - new WindowLayout(0, 0, 0, 0, Gravity.NO_GRAVITY, 0, 0), null /*activity*/, - null /*source*/, null /*options*/, mCurrent, mResult)); - assertEquals(mResult.mBounds, getDefaultBounds(Gravity.NO_GRAVITY)); + public void testUsesOptionsDisplayIdIfSet() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + final TestActivityDisplay fullscreenDisplay = createNewActivityDisplay( + WINDOWING_MODE_FULLSCREEN); + + mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId; + ActivityRecord source = createSourceActivity(freeformDisplay); + + ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchDisplayId(fullscreenDisplay.mDisplayId); + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null, + mActivity, source, options, mCurrent, mResult)); + + assertEquals(fullscreenDisplay.mDisplayId, mResult.mPreferredDisplayId); } - /** - * Ensures that a task positioned with a {@link WindowLayout} gravity specified is positioned - * according to specification. - * @throws Exception - */ + // ===================================== + // Launch Windowing Mode Related Tests + // ===================================== @Test - public void testlaunchWindowLayoutGravity() throws Exception { - // Unspecified gravity should be ignored - testGravity(Gravity.NO_GRAVITY); + public void testBoundsInOptionsInfersFreeformOnFreeformDisplay() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchBounds(new Rect(0, 0, 100, 100)); - // Unsupported gravity should be ignored - testGravity(Gravity.LEFT); - testGravity(Gravity.RIGHT); + mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId; - // Test defaults for vertical gravities - testGravity(Gravity.TOP); - testGravity(Gravity.BOTTOM); + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null, + mActivity, /* source */ null, options, mCurrent, mResult)); - // Test corners - testGravity(Gravity.TOP | Gravity.LEFT); - testGravity(Gravity.TOP | Gravity.RIGHT); - testGravity(Gravity.BOTTOM | Gravity.LEFT); - testGravity(Gravity.BOTTOM | Gravity.RIGHT); + assertEquivalentWindowingMode(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode, + WINDOWING_MODE_FREEFORM); } - private void testGravity(int gravity) { - try { - assertEquals(RESULT_CONTINUE, mPositioner.onCalculate(mTask, - new WindowLayout(0, 0, 0, 0, gravity, 0, 0), null /*activity*/, - null /*source*/, null /*options*/, mCurrent, mResult)); - assertEquals(mResult.mBounds, getDefaultBounds(gravity)); - } finally { - mCurrent.reset(); - mResult.reset(); - } + @Test + public void testBoundsInOptionsInfersFreeformWithResizeableActivity() { + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchBounds(new Rect(0, 0, 100, 100)); + + mCurrent.mPreferredDisplayId = DEFAULT_DISPLAY; + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null, + mActivity, /* source */ null, options, mCurrent, mResult)); + + assertEquivalentWindowingMode(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode, + WINDOWING_MODE_FULLSCREEN); + } + + @Test + public void testKeepsPictureInPictureLaunchModeInOptions() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchWindowingMode(WINDOWING_MODE_PINNED); + + mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId; + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null, + mActivity, /* source */ null, options, mCurrent, mResult)); + + assertEquivalentWindowingMode(WINDOWING_MODE_PINNED, mResult.mWindowingMode, + WINDOWING_MODE_FREEFORM); + } + + @Test + public void testKeepsPictureInPictureLaunchModeWithBoundsInOptions() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchWindowingMode(WINDOWING_MODE_PINNED); + options.setLaunchBounds(new Rect(0, 0, 100, 100)); + + mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId; + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null, + mActivity, /* source */ null, options, mCurrent, mResult)); + + assertEquivalentWindowingMode(WINDOWING_MODE_PINNED, mResult.mWindowingMode, + WINDOWING_MODE_FREEFORM); + } + + @Test + public void testKeepsFullscreenLaunchModeInOptionsOnNonFreeformDisplay() { + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN); + + mCurrent.mPreferredDisplayId = DEFAULT_DISPLAY; + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null, + mActivity, /* source */ null, options, mCurrent, mResult)); + + assertEquivalentWindowingMode(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode, + WINDOWING_MODE_FULLSCREEN); + } + + @Test + public void testNonEmptyLayoutInfersFreeformOnFreeformDisplay() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + + mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId; + + final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder() + .setWidth(120).setHeight(80).build(); + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity, + /* source */ null, /* options */ null, mCurrent, mResult)); + + assertEquivalentWindowingMode(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode, + WINDOWING_MODE_FREEFORM); + } + + @Test + public void testNonEmptyLayoutInfersFreeformWithEmptySize() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + + mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId; + + final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder() + .setGravity(Gravity.LEFT).build(); + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity, + /* source */ null, /* options */ null, mCurrent, mResult)); + + assertEquivalentWindowingMode(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode, + WINDOWING_MODE_FREEFORM); + } + + @Test + public void testNonEmptyLayoutInfersFreeformWithResizeableActivity() { + final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder() + .setWidth(120).setHeight(80).build(); + + mCurrent.mPreferredDisplayId = DEFAULT_DISPLAY; + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity, + /* source */ null, /* options */ null, mCurrent, mResult)); + + assertEquivalentWindowingMode(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode, + WINDOWING_MODE_FULLSCREEN); + } + + @Test + public void testRespectsFullyResolvedCurrentParam_Fullscreen() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + + mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId; + mCurrent.mWindowingMode = WINDOWING_MODE_FULLSCREEN; + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null, + mActivity, /* source */ null, /* options */ null, mCurrent, mResult)); + + assertEquivalentWindowingMode(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode, + WINDOWING_MODE_FREEFORM); + } + + @Test + public void testRespectsModeFromFullyResolvedCurrentParam_NonEmptyBounds() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + + mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId; + mCurrent.mWindowingMode = WINDOWING_MODE_FREEFORM; + mCurrent.mBounds.set(0, 0, 200, 100); + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null, + mActivity, /* source */ null, /* options */ null, mCurrent, mResult)); + + assertEquivalentWindowingMode(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode, + WINDOWING_MODE_FREEFORM); + } + + @Test + public void testForceMaximizesPreDApp() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM); + options.setLaunchBounds(new Rect(0, 0, 200, 100)); + + mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId; + mCurrent.mWindowingMode = WINDOWING_MODE_FREEFORM; + mCurrent.mBounds.set(0, 0, 200, 100); + + mActivity.appInfo.targetSdkVersion = Build.VERSION_CODES.CUPCAKE; + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null, + mActivity, /* source */ null, options, mCurrent, mResult)); + + assertEquivalentWindowingMode(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode, + WINDOWING_MODE_FREEFORM); + } + + @Test + public void testForceMaximizesAppWithoutMultipleDensitySupport() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM); + options.setLaunchBounds(new Rect(0, 0, 200, 100)); + + mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId; + mCurrent.mWindowingMode = WINDOWING_MODE_FREEFORM; + mCurrent.mBounds.set(0, 0, 200, 100); + + mActivity.appInfo.flags = 0; + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null, + mActivity, /* source */ null, options, mCurrent, mResult)); + + assertEquivalentWindowingMode(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode, + WINDOWING_MODE_FREEFORM); + } + + @Test + public void testForceMaximizesUnresizeableApp() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM); + options.setLaunchBounds(new Rect(0, 0, 200, 100)); + + mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId; + mCurrent.mWindowingMode = WINDOWING_MODE_FREEFORM; + mCurrent.mBounds.set(0, 0, 200, 100); + + mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE; + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null, + mActivity, /* source */ null, options, mCurrent, mResult)); + + assertEquivalentWindowingMode(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode, + WINDOWING_MODE_FREEFORM); + } + + @Test + public void testSkipsForceMaximizingAppsOnNonFreeformDisplay() { + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM); + options.setLaunchBounds(new Rect(0, 0, 200, 100)); + + mCurrent.mPreferredDisplayId = DEFAULT_DISPLAY; + mCurrent.mWindowingMode = WINDOWING_MODE_FREEFORM; + mCurrent.mBounds.set(0, 0, 200, 100); + + mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE; + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null, + mActivity, /* source */ null, options, mCurrent, mResult)); + + assertEquivalentWindowingMode(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode, + WINDOWING_MODE_FULLSCREEN); + } + + @Test + public void testUsesFullscreenOnNonFreeformDisplay() { + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchDisplayId(DEFAULT_DISPLAY); + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null, + mActivity, /* source */ null, options, mCurrent, mResult)); + + assertEquivalentWindowingMode(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode, + WINDOWING_MODE_FULLSCREEN); + } + + @Test + public void testUsesFreeformByDefaultForPostNApp() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchDisplayId(freeformDisplay.mDisplayId); + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null, + mActivity, /* source */ null, options, mCurrent, mResult)); + + assertEquivalentWindowingMode(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode, + WINDOWING_MODE_FREEFORM); + } + + @Test + public void testUsesFreeformByDefaultForPreNResizeableAppWithoutOrientationRequest() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchDisplayId(freeformDisplay.mDisplayId); + + mActivity.appInfo.targetSdkVersion = Build.VERSION_CODES.LOLLIPOP; + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null, + mActivity, /* source */ null, options, mCurrent, mResult)); + + assertEquivalentWindowingMode(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode, + WINDOWING_MODE_FREEFORM); + } + + @Test + public void testSkipsFreeformForPreNResizeableAppOnNonFullscreenDisplay() { + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchDisplayId(DEFAULT_DISPLAY); + + mActivity.appInfo.targetSdkVersion = Build.VERSION_CODES.LOLLIPOP; + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null, + mActivity, /* source */ null, options, mCurrent, mResult)); + + assertEquivalentWindowingMode(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode, + WINDOWING_MODE_FULLSCREEN); + } + + // ================================ + // Launching Bounds Related Tests + // =============================== + @Test + public void testKeepsBoundsWithPictureInPictureLaunchModeInOptions() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchWindowingMode(WINDOWING_MODE_PINNED); + + final Rect expected = new Rect(0, 0, 100, 100); + options.setLaunchBounds(expected); + + mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId; + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null, + mActivity, /* source */ null, options, mCurrent, mResult)); + + assertEquals(expected, mResult.mBounds); + } + + @Test + public void testNonEmptyLayoutBoundsRespectsGravityWithEmptySize_LeftGravity() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + + mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId; + + final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder() + .setGravity(Gravity.LEFT).build(); + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity, + /* source */ null, /* options */ null, mCurrent, mResult)); + + assertEquals(0, mResult.mBounds.left); + } + + @Test + public void testNonEmptyLayoutBoundsRespectsGravityWithEmptySize_TopGravity() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + + mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId; + + final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder() + .setGravity(Gravity.TOP).build(); + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity, + /* source */ null, /* options */ null, mCurrent, mResult)); + + assertEquals(0, mResult.mBounds.top); + } + + @Test + public void testNonEmptyLayoutBoundsRespectsGravityWithEmptySize_TopLeftGravity() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + + mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId; + + final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder() + .setGravity(Gravity.TOP | Gravity.LEFT).build(); + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity, + /* source */ null, /* options */ null, mCurrent, mResult)); + + assertEquals(0, mResult.mBounds.left); + assertEquals(0, mResult.mBounds.top); + } + + @Test + public void testNonEmptyLayoutBoundsRespectsGravityWithEmptySize_RightGravity() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + + mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId; + + final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder() + .setGravity(Gravity.RIGHT).build(); + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity, + /* source */ null, /* options */ null, mCurrent, mResult)); + + assertEquals(1920, mResult.mBounds.right); + } + + @Test + public void testNonEmptyLayoutBoundsRespectsGravityWithEmptySize_BottomGravity() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + + mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId; + + final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder() + .setGravity(Gravity.BOTTOM).build(); + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity, + /* source */ null, /* options */ null, mCurrent, mResult)); + + assertEquals(1080, mResult.mBounds.bottom); + } + + @Test + public void testNonEmptyLayoutBoundsRespectsGravityWithEmptySize_BottomRightGravity() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + + mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId; + + final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder() + .setGravity(Gravity.BOTTOM | Gravity.RIGHT).build(); + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity, + /* source */ null, /* options */ null, mCurrent, mResult)); + + assertEquals(1920, mResult.mBounds.right); + assertEquals(1080, mResult.mBounds.bottom); + } + + @Test + public void testNonEmptyLayoutBoundsOnFreeformDisplay_CenterToDisplay() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + + mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId; + + final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder() + .setWidth(120).setHeight(80).build(); + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity, + /* source */ null, /* options */ null, mCurrent, mResult)); + + assertEquals(new Rect(900, 500, 1020, 580), mResult.mBounds); + } + + @Test + public void testNonEmptyLayoutBoundsOnFreeformDisplay_LeftGravity() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + + mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId; + + final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder() + .setWidth(120).setHeight(80).setGravity(Gravity.LEFT).build(); + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity, + /* source */ null, /* options */ null, mCurrent, mResult)); + + assertEquals(new Rect(0, 500, 120, 580), mResult.mBounds); + } + + @Test + public void testNonEmptyLayoutBoundsOnFreeformDisplay_TopGravity() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + + mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId; + + final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder() + .setWidth(120).setHeight(80).setGravity(Gravity.TOP).build(); + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity, + /* source */ null, /* options */ null, mCurrent, mResult)); + + assertEquals(new Rect(900, 0, 1020, 80), mResult.mBounds); + } + + @Test + public void testNonEmptyLayoutBoundsOnFreeformDisplay_TopLeftGravity() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + + mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId; + + final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder() + .setWidth(120).setHeight(80).setGravity(Gravity.TOP | Gravity.LEFT).build(); + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity, + /* source */ null, /* options */ null, mCurrent, mResult)); + + assertEquals(new Rect(0, 0, 120, 80), mResult.mBounds); + } + + @Test + public void testNonEmptyLayoutBoundsOnFreeformDisplay_RightGravity() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + + mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId; + + final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder() + .setWidth(120).setHeight(80).setGravity(Gravity.RIGHT).build(); + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity, + /* source */ null, /* options */ null, mCurrent, mResult)); + + assertEquals(new Rect(1800, 500, 1920, 580), mResult.mBounds); + } + + @Test + public void testNonEmptyLayoutBoundsOnFreeformDisplay_BottomGravity() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + + mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId; + + final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder() + .setWidth(120).setHeight(80).setGravity(Gravity.BOTTOM).build(); + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity, + /* source */ null, /* options */ null, mCurrent, mResult)); + + assertEquals(new Rect(900, 1000, 1020, 1080), mResult.mBounds); + } + + @Test + public void testNonEmptyLayoutBoundsOnFreeformDisplay_RightBottomGravity() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + + mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId; + + final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder() + .setWidth(120).setHeight(80).setGravity(Gravity.BOTTOM | Gravity.RIGHT).build(); + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity, + /* source */ null, /* options */ null, mCurrent, mResult)); + + assertEquals(new Rect(1800, 1000, 1920, 1080), mResult.mBounds); + } + + @Test + public void testNonEmptyLayoutFractionBoundsOnFreeformDisplay() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + + mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId; + + final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder() + .setWidthFraction(0.0625f).setHeightFraction(0.1f).build(); + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity, + /* source */ null, /* options */ null, mCurrent, mResult)); + + assertEquals(new Rect(900, 486, 1020, 594), mResult.mBounds); + } + + @Test + public void testNonEmptyLayoutBoundsWithResizeableActivity() { + final ActivityDisplay display = mSupervisor.getActivityDisplay(DEFAULT_DISPLAY); + display.setBounds(new Rect(0, 0, 1920, 1080)); + final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder() + .setWidth(120).setHeight(80).build(); + + mCurrent.mPreferredDisplayId = DEFAULT_DISPLAY; + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity, + /* source */ null, /* options */ null, mCurrent, mResult)); + + assertEquals(new Rect(900, 500, 1020, 580), mResult.mBounds); + } + + @Test + public void testRespectBoundsFromFullyResolvedCurrentParam_NonEmptyBounds() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + + mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId; + mCurrent.mWindowingMode = WINDOWING_MODE_FREEFORM; + mCurrent.mBounds.set(0, 0, 200, 100); + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null, + mActivity, /* source */ null, /* options */ null, mCurrent, mResult)); + + assertEquals(new Rect(0, 0, 200, 100), mResult.mBounds); + } + + @Test + public void testDefaultSizeSmallerThanBigScreen() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchDisplayId(freeformDisplay.mDisplayId); + + mActivity.appInfo.targetSdkVersion = Build.VERSION_CODES.LOLLIPOP; + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null, + mActivity, /* source */ null, options, mCurrent, mResult)); + + final int resultWidth = mResult.mBounds.width(); + final int displayWidth = freeformDisplay.getBounds().width(); + assertTrue("Result width " + resultWidth + " is not smaller than " + displayWidth, + resultWidth < displayWidth); + + final int resultHeight = mResult.mBounds.height(); + final int displayHeight = freeformDisplay.getBounds().height(); + assertTrue("Result width " + resultHeight + " is not smaller than " + + displayHeight, resultHeight < displayHeight); + } + + @Test + public void testDefaultFreeformSizeCenteredToDisplay() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchDisplayId(freeformDisplay.mDisplayId); + + mActivity.appInfo.targetSdkVersion = Build.VERSION_CODES.LOLLIPOP; + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null, + mActivity, /* source */ null, options, mCurrent, mResult)); + + final Rect displayBounds = freeformDisplay.getBounds(); + assertEquals("Distance to left and right should be equal.", + mResult.mBounds.left - displayBounds.left, + displayBounds.right - mResult.mBounds.right); + assertEquals("Distance to top and bottom should be equal.", + mResult.mBounds.top - displayBounds.top, + displayBounds.bottom - mResult.mBounds.bottom); + } + + @Test + public void testCascadesToSourceSizeForFreeform() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchDisplayId(freeformDisplay.mDisplayId); + + final ActivityRecord source = createSourceActivity(freeformDisplay); + source.setBounds(0, 0, 412, 732); + + mActivity.appInfo.targetSdkVersion = Build.VERSION_CODES.LOLLIPOP; + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null, + mActivity, source, options, mCurrent, mResult)); + + final Rect displayBounds = freeformDisplay.getBounds(); + assertTrue("Left bounds should be larger than 0.", mResult.mBounds.left > 0); + assertTrue("Top bounds should be larger than 0.", mResult.mBounds.top > 0); + assertTrue("Bounds should be centered at somewhere in the left half, but it's " + + "centerX is " + mResult.mBounds.centerX(), + mResult.mBounds.centerX() < displayBounds.centerX()); + assertTrue("Bounds should be centered at somewhere in the top half, but it's " + + "centerY is " + mResult.mBounds.centerY(), + mResult.mBounds.centerY() < displayBounds.centerY()); + } + + @Test + public void testAdjustBoundsToFitDisplay_TopLeftOutOfDisplay() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchDisplayId(freeformDisplay.mDisplayId); + + final ActivityRecord source = createSourceActivity(freeformDisplay); + source.setBounds(0, 0, 200, 400); + + mActivity.appInfo.targetSdkVersion = Build.VERSION_CODES.LOLLIPOP; + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null, + mActivity, source, options, mCurrent, mResult)); + + final Rect displayBounds = freeformDisplay.getBounds(); + assertTrue("display bounds doesn't contain result. display bounds: " + + displayBounds + " result: " + mResult.mBounds, + displayBounds.contains(mResult.mBounds)); + } + + @Test + public void testAdjustBoundsToFitDisplay_BottomRightOutOfDisplay() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchDisplayId(freeformDisplay.mDisplayId); + + final ActivityRecord source = createSourceActivity(freeformDisplay); + source.setBounds(1720, 680, 1920, 1080); + + mActivity.appInfo.targetSdkVersion = Build.VERSION_CODES.LOLLIPOP; + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null, + mActivity, source, options, mCurrent, mResult)); + + final Rect displayBounds = freeformDisplay.getBounds(); + assertTrue("display bounds doesn't contain result. display bounds: " + + displayBounds + " result: " + mResult.mBounds, + displayBounds.contains(mResult.mBounds)); + } + + @Test + public void testAdjustBoundsToFitDisplay_LargerThanDisplay() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + + Configuration overrideConfig = new Configuration(); + overrideConfig.setTo(mSupervisor.getOverrideConfiguration()); + overrideConfig.setLayoutDirection(new Locale("ar")); + mSupervisor.onConfigurationChanged(overrideConfig); + + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchDisplayId(freeformDisplay.mDisplayId); + + final ActivityRecord source = createSourceActivity(freeformDisplay); + source.setBounds(1720, 680, 1920, 1080); + + mActivity.appInfo.targetSdkVersion = Build.VERSION_CODES.LOLLIPOP; + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null, + mActivity, source, options, mCurrent, mResult)); + + final Rect displayBounds = freeformDisplay.getBounds(); + assertTrue("display bounds doesn't contain result. display bounds: " + + displayBounds + " result: " + mResult.mBounds, + displayBounds.contains(mResult.mBounds)); } - /** - * Ensures that a task which causes a conflict with another task when positioned is adjusted as - * expected. - * @throws Exception - */ - @Test - public void testLaunchWindowCenterConflict() throws Exception { - testConflict(Gravity.NO_GRAVITY); - testConflict(Gravity.TOP); - testConflict(Gravity.BOTTOM); - testConflict(Gravity.TOP | Gravity.LEFT); - testConflict(Gravity.TOP | Gravity.RIGHT); - testConflict(Gravity.BOTTOM | Gravity.LEFT); - testConflict(Gravity.BOTTOM | Gravity.RIGHT); - } - - private void testConflict(int gravity) { - final WindowLayout layout = new WindowLayout(0, 0, 0, 0, gravity, 0, 0); - - // layout first task - mService.mStackSupervisor.getLaunchParamsController().layoutTask(mTask, layout); - - // Second task will be laid out on top of the first so starting bounds is the same. - final Rect expectedBounds = new Rect(mTask.getOverrideBounds()); - - ActivityRecord activity = null; - TaskRecord secondTask = null; - - // wrap with try/finally to ensure cleanup of activity/stack. - try { - // empty tasks are ignored in conflicts - activity = new ActivityBuilder(mService).setTask(mTask).build(); - - // Create secondary task - secondTask = new TaskBuilder(mService.mStackSupervisor).setStack(mStack).build(); - - // layout second task - assertEquals(RESULT_CONTINUE, - mPositioner.onCalculate(secondTask, layout, null /*activity*/, - null /*source*/, null /*options*/, mCurrent, mResult)); - - if ((gravity & (Gravity.TOP | Gravity.RIGHT)) == (Gravity.TOP | Gravity.RIGHT) - || (gravity & (Gravity.BOTTOM | Gravity.RIGHT)) - == (Gravity.BOTTOM | Gravity.RIGHT)) { - expectedBounds.offset(-TaskLaunchParamsModifier.getHorizontalStep( - mStack.getOverrideBounds()), 0); - } else if ((gravity & Gravity.TOP) == Gravity.TOP - || (gravity & Gravity.BOTTOM) == Gravity.BOTTOM) { - expectedBounds.offset( - TaskLaunchParamsModifier.getHorizontalStep(mStack.getOverrideBounds()), 0); - } else { - expectedBounds.offset( - TaskLaunchParamsModifier.getHorizontalStep(mStack.getOverrideBounds()), - TaskLaunchParamsModifier.getVerticalStep(mStack.getOverrideBounds())); - } - - assertEquals(mResult.mBounds, expectedBounds); - } finally { - // Remove task and activity to prevent influencing future tests - if (activity != null) { - mTask.removeActivity(activity); - } - - if (secondTask != null) { - mStack.removeTask(secondTask, "cleanup", ActivityStack.REMOVE_TASK_MODE_DESTROYING); - } + @Test + public void testRespectsLayoutMinDimensions() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchDisplayId(freeformDisplay.mDisplayId); + + final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder() + .setMinWidth(500).setMinHeight(800).build(); + + mActivity.appInfo.targetSdkVersion = Build.VERSION_CODES.LOLLIPOP; + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity, + /* source */ null, options, mCurrent, mResult)); + + assertEquals(500, mResult.mBounds.width()); + assertEquals(800, mResult.mBounds.height()); + } + + @Test + public void testRotatesInPlaceInitialBoundsMismatchOrientation() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchDisplayId(freeformDisplay.mDisplayId); + options.setLaunchBounds(new Rect(100, 100, 500, 300)); + + mActivity.info.screenOrientation = SCREEN_ORIENTATION_PORTRAIT; + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null, + mActivity, /* source */ null, options, mCurrent, mResult)); + + assertEquals(new Rect(200, 0, 400, 400), mResult.mBounds); + } + + @Test + public void testShiftsToRightForCloseToLeftBoundsWhenConflict() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + + addFreeformTaskTo(freeformDisplay, new Rect(50, 50, 100, 150)); + + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchDisplayId(freeformDisplay.mDisplayId); + options.setLaunchBounds(new Rect(50, 50, 500, 300)); + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null, + mActivity, /* source */ null, options, mCurrent, mResult)); + + assertEquals(new Rect(170, 50, 620, 300), mResult.mBounds); + } + + @Test + public void testShiftsToLeftForCloseToRightBoundsWhenConflict() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + + addFreeformTaskTo(freeformDisplay, new Rect(1720, 50, 1830, 150)); + + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchDisplayId(freeformDisplay.mDisplayId); + options.setLaunchBounds(new Rect(1720, 50, 1850, 300)); + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null, + mActivity, /* source */ null, options, mCurrent, mResult)); + + assertEquals(new Rect(1600, 50, 1730, 300), mResult.mBounds); + } + + @Test + public void testShiftsToRightFirstForHorizontallyCenteredAndCloseToTopWhenConflict() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + + addFreeformTaskTo(freeformDisplay, new Rect(0, 0, 100, 300)); + + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchDisplayId(freeformDisplay.mDisplayId); + options.setLaunchBounds(new Rect(0, 0, 1800, 200)); + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null, + mActivity, /* source */ null, options, mCurrent, mResult)); + + assertEquals(new Rect(120, 0, 1920, 200), mResult.mBounds); + } + + @Test + public void testShiftsToLeftNoSpaceOnRightForHorizontallyCenteredAndCloseToTopWhenConflict() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + + addFreeformTaskTo(freeformDisplay, new Rect(120, 0, 240, 300)); + + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchDisplayId(freeformDisplay.mDisplayId); + options.setLaunchBounds(new Rect(120, 0, 1860, 200)); + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null, + mActivity, /* source */ null, options, mCurrent, mResult)); + + assertEquals(new Rect(0, 0, 1740, 200), mResult.mBounds); + } + + @Test + public void testShiftsToBottomRightFirstForCenteredBoundsWhenConflict() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + + addFreeformTaskTo(freeformDisplay, new Rect(120, 0, 240, 100)); + + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchDisplayId(freeformDisplay.mDisplayId); + options.setLaunchBounds(new Rect(120, 0, 1800, 1013)); + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null, + mActivity, /* source */ null, options, mCurrent, mResult)); + + assertEquals(new Rect(240, 67, 1920, 1080), mResult.mBounds); + } + + @Test + public void testShiftsToTopLeftIfNoSpaceOnBottomRightForCenteredBoundsWhenConflict() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + + addFreeformTaskTo(freeformDisplay, new Rect(120, 67, 240, 100)); + + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchDisplayId(freeformDisplay.mDisplayId); + options.setLaunchBounds(new Rect(120, 67, 1800, 1020)); + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null, + mActivity, /* source */ null, options, mCurrent, mResult)); + + assertEquals(new Rect(0, 0, 1680, + 953), mResult.mBounds); + } + + private TestActivityDisplay createNewActivityDisplay(int windowingMode) { + final TestActivityDisplay display = addNewActivityDisplayAt(ActivityDisplay.POSITION_TOP); + display.setWindowingMode(windowingMode); + display.setBounds(/* left */ 0, /* top */ 0, /* right */ 1920, /* bottom */ 1080); + display.getConfiguration().densityDpi = DENSITY_DEFAULT; + return display; + } + + private ActivityRecord createSourceActivity(TestActivityDisplay display) { + final TestActivityStack stack = display.createStack(display.getWindowingMode(), + ACTIVITY_TYPE_STANDARD, true); + return new ActivityBuilder(mService).setStack(stack).setCreateTask(true).build(); + } + + private void addFreeformTaskTo(TestActivityDisplay display, Rect bounds) { + final TestActivityStack stack = display.createStack(display.getWindowingMode(), + ACTIVITY_TYPE_STANDARD, true); + stack.setWindowingMode(WINDOWING_MODE_FREEFORM); + final TaskRecord task = new TaskBuilder(mSupervisor).setStack(stack).build(); + task.setBounds(bounds); + } + + private void assertEquivalentWindowingMode(int expected, int actual, int parentWindowingMode) { + if (expected != parentWindowingMode) { + assertEquals(expected, actual); + } else { + assertEquals(WINDOWING_MODE_UNDEFINED, actual); } } - private Rect getDefaultBounds(int gravity) { - final Rect bounds = new Rect(); - bounds.set(mStack.getOverrideBounds()); + private static class WindowLayoutBuilder { + private int mWidth = -1; + private int mHeight = -1; + private float mWidthFraction = -1f; + private float mHeightFraction = -1f; + private int mGravity = Gravity.NO_GRAVITY; + private int mMinWidth = -1; + private int mMinHeight = -1; - final int verticalInset = - TaskLaunchParamsModifier.getFreeformStartTop(mStack.getOverrideBounds()); - final int horizontalInset = - TaskLaunchParamsModifier.getFreeformStartLeft(mStack.getOverrideBounds()); + private WindowLayoutBuilder setWidth(int width) { + mWidth = width; + return this; + } + + private WindowLayoutBuilder setHeight(int height) { + mHeight = height; + return this; + } - bounds.inset(horizontalInset, verticalInset); + private WindowLayoutBuilder setWidthFraction(float widthFraction) { + mWidthFraction = widthFraction; + return this; + } + + private WindowLayoutBuilder setHeightFraction(float heightFraction) { + mHeightFraction = heightFraction; + return this; + } - if ((gravity & (Gravity.TOP | Gravity.RIGHT)) == (Gravity.TOP | Gravity.RIGHT)) { - bounds.offsetTo(horizontalInset * 2, 0); - } else if ((gravity & Gravity.TOP) == Gravity.TOP) { - bounds.offsetTo(0, 0); - } else if ((gravity & (Gravity.BOTTOM | Gravity.RIGHT)) - == (Gravity.BOTTOM | Gravity.RIGHT)) { - bounds.offsetTo(horizontalInset * 2, verticalInset * 2); - } else if ((gravity & Gravity.BOTTOM) == Gravity.BOTTOM) { - bounds.offsetTo(0, verticalInset * 2); + private WindowLayoutBuilder setGravity(int gravity) { + mGravity = gravity; + return this; } - return bounds; + private WindowLayoutBuilder setMinWidth(int minWidth) { + mMinWidth = minWidth; + return this; + } + + private WindowLayoutBuilder setMinHeight(int minHeight) { + mMinHeight = minHeight; + return this; + } + + private ActivityInfo.WindowLayout build() { + return new ActivityInfo.WindowLayout(mWidth, mWidthFraction, mHeight, mHeightFraction, + mGravity, mMinWidth, mMinHeight); + } } } diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java index cd0f385ce04e..0e9a63ce728b 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java @@ -19,6 +19,8 @@ package com.android.server.wm; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static com.google.common.truth.Truth.assertThat; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -68,7 +70,7 @@ public class TaskStackContainersTests extends WindowTestsBase { } @Test - public void testStackPositionChildAt() throws Exception { + public void testStackPositionChildAt() { // Test that always-on-top stack can't be moved to position other than top. final TaskStack stack1 = createTaskStackOnDisplay(mDisplayContent); final TaskStack stack2 = createTaskStackOnDisplay(mDisplayContent); @@ -78,8 +80,8 @@ public class TaskStackContainersTests extends WindowTestsBase { final int stack1Pos = taskStackContainer.mChildren.indexOf(stack1); final int stack2Pos = taskStackContainer.mChildren.indexOf(stack2); final int pinnedStackPos = taskStackContainer.mChildren.indexOf(mPinnedStack); - assertGreaterThan(pinnedStackPos, stack2Pos); - assertGreaterThan(stack2Pos, stack1Pos); + assertThat(pinnedStackPos).isGreaterThan(stack2Pos); + assertThat(stack2Pos).isGreaterThan(stack1Pos); taskStackContainer.positionChildAt(WindowContainer.POSITION_BOTTOM, mPinnedStack, false); assertEquals(taskStackContainer.mChildren.get(stack1Pos), stack1); @@ -93,7 +95,7 @@ public class TaskStackContainersTests extends WindowTestsBase { } @Test - public void testStackPositionBelowPinnedStack() throws Exception { + public void testStackPositionBelowPinnedStack() { // Test that no stack can be above pinned stack. final TaskStack stack1 = createTaskStackOnDisplay(mDisplayContent); @@ -101,7 +103,7 @@ public class TaskStackContainersTests extends WindowTestsBase { final int stackPos = taskStackContainer.mChildren.indexOf(stack1); final int pinnedStackPos = taskStackContainer.mChildren.indexOf(mPinnedStack); - assertGreaterThan(pinnedStackPos, stackPos); + assertThat(pinnedStackPos).isGreaterThan(stackPos); taskStackContainer.positionChildAt(WindowContainer.POSITION_TOP, stack1, false); assertEquals(taskStackContainer.mChildren.get(stackPos), stack1); diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java index 70e4ce419e0a..cf67d786056c 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java @@ -48,18 +48,17 @@ import android.view.DisplayInfo; import android.view.IWindow; import android.view.WindowManager; -import androidx.test.InstrumentationRegistry; - import com.android.server.AttributeCache; import org.junit.After; -import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import java.util.HashSet; import java.util.LinkedList; +import androidx.test.InstrumentationRegistry; + /** * Common base class for window manager unit test classes. * @@ -194,14 +193,6 @@ class WindowTestsBase { } } - /** - * @return A SurfaceBuilderFactory to inject in to the WindowManagerService during - * set-up (or null). - */ - SurfaceBuilderFactory getSurfaceBuilderFactory() { - return null; - } - private WindowState createCommonWindow(WindowState parent, int type, String name) { synchronized (sWm.mWindowMap) { final WindowState win = createWindow(parent, type, name); @@ -212,16 +203,6 @@ class WindowTestsBase { } } - /** Asserts that the first entry is greater than the second entry. */ - void assertGreaterThan(int first, int second) throws Exception { - Assert.assertTrue("Excepted " + first + " to be greater than " + second, first > second); - } - - /** Asserts that the first entry is greater than the second entry. */ - void assertLessThan(int first, int second) throws Exception { - Assert.assertTrue("Excepted " + first + " to be less than " + second, first < second); - } - /** * Waits until the main handler for WM has processed all messages. */ diff --git a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java index 8f9fb1b7d5ec..a610e6efda8c 100644 --- a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java @@ -32,36 +32,35 @@ import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY; +import static com.google.common.truth.Truth.assertThat; + import android.platform.test.annotations.Presubmit; import android.view.SurfaceControl; import android.view.SurfaceSession; -import androidx.test.filters.FlakyTest; -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - import org.junit.After; import org.junit.Test; -import org.junit.runner.RunWith; import java.util.HashMap; import java.util.LinkedList; +import androidx.test.filters.FlakyTest; +import androidx.test.filters.SmallTest; + /** * Tests for the {@link WindowLayersController} class. * * Build/Install/Run: - * bit FrameworksServicesTests:com.android.server.wm.ZOrderingTests + * atest FrameworksServicesTests:com.android.server.wm.ZOrderingTests */ @SmallTest @FlakyTest(bugId = 74078662) @Presubmit -@RunWith(AndroidJUnit4.class) public class ZOrderingTests extends WindowTestsBase { private class LayerRecordingTransaction extends SurfaceControl.Transaction { - HashMap<SurfaceControl, Integer> mLayersForControl = new HashMap(); - HashMap<SurfaceControl, SurfaceControl> mRelativeLayersForControl = new HashMap(); + HashMap<SurfaceControl, Integer> mLayersForControl = new HashMap<>(); + HashMap<SurfaceControl, SurfaceControl> mRelativeLayersForControl = new HashMap<>(); @Override public SurfaceControl.Transaction setLayer(SurfaceControl sc, int layer) { @@ -86,11 +85,11 @@ public class ZOrderingTests extends WindowTestsBase { private SurfaceControl getRelativeLayer(SurfaceControl sc) { return mRelativeLayersForControl.get(sc); } - }; + } // We have WM use our Hierarchy recording subclass of SurfaceControl.Builder // such that we can keep track of the parents of Surfaces as they are constructed. - private HashMap<SurfaceControl, SurfaceControl> mParentFor = new HashMap(); + private HashMap<SurfaceControl, SurfaceControl> mParentFor = new HashMap<>(); private class HierarchyRecorder extends SurfaceControl.Builder { SurfaceControl mPendingParent; @@ -109,13 +108,13 @@ public class ZOrderingTests extends WindowTestsBase { mPendingParent = null; return sc; } - }; + } class HierarchyRecordingBuilderFactory implements SurfaceBuilderFactory { public SurfaceControl.Builder make(SurfaceSession s) { return new HierarchyRecorder(s); } - }; + } private LayerRecordingTransaction mTransaction; @@ -136,7 +135,7 @@ public class ZOrderingTests extends WindowTestsBase { } LinkedList<SurfaceControl> getAncestors(LayerRecordingTransaction t, SurfaceControl sc) { - LinkedList<SurfaceControl> p = new LinkedList(); + LinkedList<SurfaceControl> p = new LinkedList<>(); SurfaceControl current = sc; do { p.addLast(current); @@ -153,7 +152,7 @@ public class ZOrderingTests extends WindowTestsBase { void assertZOrderGreaterThan(LayerRecordingTransaction t, SurfaceControl left, - SurfaceControl right) throws Exception { + SurfaceControl right) { final LinkedList<SurfaceControl> leftParentChain = getAncestors(t, left); final LinkedList<SurfaceControl> rightParentChain = getAncestors(t, right); @@ -168,16 +167,15 @@ public class ZOrderingTests extends WindowTestsBase { } if (rightTop == null) { // right is the parent of left. - assertGreaterThan(t.getLayer(leftTop), 0); + assertThat(t.getLayer(leftTop)).isGreaterThan(0); } else if (leftTop == null) { // left is the parent of right. - assertGreaterThan(0, t.getLayer(rightTop)); + assertThat(t.getLayer(rightTop)).isLessThan(0); } else { - assertGreaterThan(t.getLayer(leftTop), - t.getLayer(rightTop)); + assertThat(t.getLayer(leftTop)).isGreaterThan(t.getLayer(rightTop)); } } - void assertWindowHigher(WindowState left, WindowState right) throws Exception { + void assertWindowHigher(WindowState left, WindowState right) { assertZOrderGreaterThan(mTransaction, left.getSurfaceControl(), right.getSurfaceControl()); } @@ -186,7 +184,7 @@ public class ZOrderingTests extends WindowTestsBase { } @Test - public void testAssignWindowLayers_ForImeWithNoTarget() throws Exception { + public void testAssignWindowLayers_ForImeWithNoTarget() { sWm.mInputMethodTarget = null; mDisplayContent.assignChildLayers(mTransaction); @@ -203,7 +201,7 @@ public class ZOrderingTests extends WindowTestsBase { } @Test - public void testAssignWindowLayers_ForImeWithAppTarget() throws Exception { + public void testAssignWindowLayers_ForImeWithAppTarget() { final WindowState imeAppTarget = createWindow("imeAppTarget"); sWm.mInputMethodTarget = imeAppTarget; @@ -222,7 +220,7 @@ public class ZOrderingTests extends WindowTestsBase { } @Test - public void testAssignWindowLayers_ForImeWithAppTargetWithChildWindows() throws Exception { + public void testAssignWindowLayers_ForImeWithAppTargetWithChildWindows() { final WindowState imeAppTarget = createWindow("imeAppTarget"); final WindowState imeAppTargetChildAboveWindow = createWindow(imeAppTarget, TYPE_APPLICATION_ATTACHED_DIALOG, imeAppTarget.mToken, @@ -248,7 +246,7 @@ public class ZOrderingTests extends WindowTestsBase { } @Test - public void testAssignWindowLayers_ForImeWithAppTargetAndAppAbove() throws Exception { + public void testAssignWindowLayers_ForImeWithAppTargetAndAppAbove() { final WindowState appBelowImeTarget = createWindow("appBelowImeTarget"); final WindowState imeAppTarget = createWindow("imeAppTarget"); final WindowState appAboveImeTarget = createWindow("appAboveImeTarget"); @@ -271,7 +269,7 @@ public class ZOrderingTests extends WindowTestsBase { } @Test - public void testAssignWindowLayers_ForImeNonAppImeTarget() throws Exception { + public void testAssignWindowLayers_ForImeNonAppImeTarget() { final WindowState imeSystemOverlayTarget = createWindow(null, TYPE_SYSTEM_OVERLAY, mDisplayContent, "imeSystemOverlayTarget", true /* ownerCanAddInternalSystemWindow */); @@ -298,7 +296,7 @@ public class ZOrderingTests extends WindowTestsBase { } @Test - public void testAssignWindowLayers_ForStatusBarImeTarget() throws Exception { + public void testAssignWindowLayers_ForStatusBarImeTarget() { sWm.mInputMethodTarget = mStatusBarWindow; mDisplayContent.assignChildLayers(mTransaction); @@ -312,7 +310,7 @@ public class ZOrderingTests extends WindowTestsBase { } @Test - public void testStackLayers() throws Exception { + public void testStackLayers() { final WindowState anyWindow1 = createWindow("anyWindow"); final WindowState pinnedStackWindow = createWindowOnStack(null, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent, @@ -342,7 +340,7 @@ public class ZOrderingTests extends WindowTestsBase { } @Test - public void testAssignWindowLayers_ForSysUiPanels() throws Exception { + public void testAssignWindowLayers_ForSysUiPanels() { final WindowState navBarPanel = createWindow(null, TYPE_NAVIGATION_BAR_PANEL, mDisplayContent, "NavBarPanel"); final WindowState statusBarPanel = @@ -359,7 +357,7 @@ public class ZOrderingTests extends WindowTestsBase { } @Test - public void testAssignWindowLayers_ForNegativelyZOrderedSubtype() throws Exception { + public void testAssignWindowLayers_ForNegativelyZOrderedSubtype() { // TODO(b/70040778): We should aim to eliminate the last user of TYPE_APPLICATION_MEDIA // then we can drop all negative layering on the windowing side. @@ -376,7 +374,7 @@ public class ZOrderingTests extends WindowTestsBase { } @Test - public void testDockedDividerPosition() throws Exception { + public void testDockedDividerPosition() { final WindowState pinnedStackWindow = createWindowOnStack(null, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent, "pinnedStackWindow"); diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java index 08bc9bcc4003..daa09f574651 100644 --- a/telecomm/java/android/telecom/Call.java +++ b/telecomm/java/android/telecom/Call.java @@ -397,7 +397,19 @@ public final class Call { public static final int PROPERTY_WIFI = 0x00000008; /** - * Call is using high definition audio. + * When set, the UI should indicate to the user that a call is using high definition + * audio. + * <p> + * The underlying {@link ConnectionService} is responsible for reporting this + * property. It is important to note that this property is not intended to report the + * actual audio codec being used for a Call, but whether the call should be indicated + * to the user as high definition. + * <p> + * The Android Telephony stack reports this property for calls based on a number + * of factors, including which audio codec is used and whether a call is using an HD + * codec end-to-end. Some mobile operators choose to suppress display of an HD indication, + * and in these cases this property will not be set for a call even if the underlying audio + * codec is in fact "high definition". */ public static final int PROPERTY_HIGH_DEF_AUDIO = 0x00000010; diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java index 4846092286ba..8454d12ac317 100644 --- a/telephony/java/android/provider/Telephony.java +++ b/telephony/java/android/provider/Telephony.java @@ -3289,7 +3289,6 @@ public final class Telephony { values.put(CDMA_ERI_ICON_INDEX, state.getCdmaEriIconIndex()); values.put(CDMA_ERI_ICON_MODE, state.getCdmaEriIconMode()); values.put(IS_EMERGENCY_ONLY, state.isEmergencyOnly()); - values.put(IS_DATA_ROAMING_FROM_REGISTRATION, state.getDataRoamingFromRegistration()); values.put(IS_USING_CARRIER_AGGREGATION, state.isUsingCarrierAggregation()); return values; } diff --git a/telephony/java/android/telephony/NetworkRegistrationState.java b/telephony/java/android/telephony/NetworkRegistrationState.java index c3931557feb3..b312f8478413 100644 --- a/telephony/java/android/telephony/NetworkRegistrationState.java +++ b/telephony/java/android/telephony/NetworkRegistrationState.java @@ -95,6 +95,13 @@ public class NetworkRegistrationState implements Parcelable { @RegState private final int mRegState; + /** + * Save the {@link ServiceState.RoamingType roaming type}. it can be overridden roaming type + * from resource overlay or carrier config. + */ + @ServiceState.RoamingType + private int mRoamingType; + private final int mAccessNetworkTechnology; private final int mRejectCause; @@ -140,6 +147,8 @@ public class NetworkRegistrationState implements Parcelable { mDomain = domain; mTransportType = transportType; mRegState = regState; + mRoamingType = (regState == REG_STATE_ROAMING) + ? ServiceState.ROAMING_TYPE_UNKNOWN : ServiceState.ROAMING_TYPE_NOT_ROAMING; mAccessNetworkTechnology = accessNetworkTechnology; mRejectCause = rejectCause; mAvailableServices = availableServices; @@ -182,6 +191,7 @@ public class NetworkRegistrationState implements Parcelable { mDomain = source.readInt(); mTransportType = source.readInt(); mRegState = source.readInt(); + mRoamingType = source.readInt(); mAccessNetworkTechnology = source.readInt(); mRejectCause = source.readInt(); mEmergencyOnly = source.readBoolean(); @@ -211,6 +221,31 @@ public class NetworkRegistrationState implements Parcelable { } /** + * @return {@code true} if registered on roaming network, {@code false} otherwise. + */ + public boolean isRoaming() { + return mRoamingType != ServiceState.ROAMING_TYPE_NOT_ROAMING; + } + + /** + * Set {@link ServiceState.RoamingType roaming type}. This could override + * roaming type based on resource overlay or carrier config. + * @hide + */ + public void setRoamingType(@ServiceState.RoamingType int roamingType) { + mRoamingType = roamingType; + } + + /** + * @return {@link ServiceState.RoamingType roaming type}. This could return + * overridden roaming type based on resource overlay or carrier config. + * @hide + */ + public @ServiceState.RoamingType int getRoamingType() { + return mRoamingType; + } + + /** * @return Whether emergency is enabled. */ public boolean isEmergencyEnabled() { return mEmergencyOnly; } @@ -280,6 +315,7 @@ public class NetworkRegistrationState implements Parcelable { .append(" domain=").append((mDomain == DOMAIN_CS) ? "CS" : "PS") .append("transportType=").append(mTransportType) .append(" regState=").append(regStateToString(mRegState)) + .append(" roamingType=").append(mRoamingType) .append(" accessNetworkTechnology=") .append(TelephonyManager.getNetworkTypeName(mAccessNetworkTechnology)) .append(" rejectCause=").append(mRejectCause) @@ -293,9 +329,9 @@ public class NetworkRegistrationState implements Parcelable { @Override public int hashCode() { - return Objects.hash(mDomain, mTransportType, mRegState, mAccessNetworkTechnology, - mRejectCause, mEmergencyOnly, mAvailableServices, mCellIdentity, - mVoiceSpecificStates, mDataSpecificStates); + return Objects.hash(mDomain, mTransportType, mRegState, mRoamingType, + mAccessNetworkTechnology, mRejectCause, mEmergencyOnly, mAvailableServices, + mCellIdentity, mVoiceSpecificStates, mDataSpecificStates); } @Override @@ -310,6 +346,7 @@ public class NetworkRegistrationState implements Parcelable { return mDomain == other.mDomain && mTransportType == other.mTransportType && mRegState == other.mRegState + && mRoamingType == other.mRoamingType && mAccessNetworkTechnology == other.mAccessNetworkTechnology && mRejectCause == other.mRejectCause && mEmergencyOnly == other.mEmergencyOnly @@ -325,6 +362,7 @@ public class NetworkRegistrationState implements Parcelable { dest.writeInt(mDomain); dest.writeInt(mTransportType); dest.writeInt(mRegState); + dest.writeInt(mRoamingType); dest.writeInt(mAccessNetworkTechnology); dest.writeInt(mRejectCause); dest.writeBoolean(mEmergencyOnly); diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index 60e103aa62e0..e0ec2c50ab5b 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -20,6 +20,7 @@ import android.annotation.IntDef; import android.annotation.SystemApi; import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; +import android.content.Intent; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -200,6 +201,15 @@ public class ServiceState implements Parcelable { private int mVoiceRegState = STATE_OUT_OF_SERVICE; private int mDataRegState = STATE_OUT_OF_SERVICE; + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "ROAMING_TYPE_" }, value = { + ROAMING_TYPE_NOT_ROAMING, + ROAMING_TYPE_UNKNOWN, + ROAMING_TYPE_DOMESTIC, + ROAMING_TYPE_INTERNATIONAL + }) + public @interface RoamingType {} /** * Roaming type * HOME : in home network @@ -230,8 +240,6 @@ public class ServiceState implements Parcelable { */ public static final int UNKNOWN_ID = -1; - private int mVoiceRoamingType; - private int mDataRoamingType; private String mVoiceOperatorAlphaLong; private String mVoiceOperatorAlphaShort; private String mVoiceOperatorNumeric; @@ -261,8 +269,6 @@ public class ServiceState implements Parcelable { @UnsupportedAppUsage private int mCdmaEriIconMode; - private boolean mIsDataRoamingFromRegistration; - @UnsupportedAppUsage private boolean mIsUsingCarrierAggregation; @@ -334,8 +340,6 @@ public class ServiceState implements Parcelable { protected void copyFrom(ServiceState s) { mVoiceRegState = s.mVoiceRegState; mDataRegState = s.mDataRegState; - mVoiceRoamingType = s.mVoiceRoamingType; - mDataRoamingType = s.mDataRoamingType; mVoiceOperatorAlphaLong = s.mVoiceOperatorAlphaLong; mVoiceOperatorAlphaShort = s.mVoiceOperatorAlphaShort; mVoiceOperatorNumeric = s.mVoiceOperatorNumeric; @@ -353,7 +357,6 @@ public class ServiceState implements Parcelable { mCdmaEriIconIndex = s.mCdmaEriIconIndex; mCdmaEriIconMode = s.mCdmaEriIconMode; mIsEmergencyOnly = s.mIsEmergencyOnly; - mIsDataRoamingFromRegistration = s.mIsDataRoamingFromRegistration; mIsUsingCarrierAggregation = s.mIsUsingCarrierAggregation; mChannelNumber = s.mChannelNumber; mCellBandwidths = s.mCellBandwidths == null ? null : @@ -369,8 +372,6 @@ public class ServiceState implements Parcelable { public ServiceState(Parcel in) { mVoiceRegState = in.readInt(); mDataRegState = in.readInt(); - mVoiceRoamingType = in.readInt(); - mDataRoamingType = in.readInt(); mVoiceOperatorAlphaLong = in.readString(); mVoiceOperatorAlphaShort = in.readString(); mVoiceOperatorNumeric = in.readString(); @@ -388,7 +389,6 @@ public class ServiceState implements Parcelable { mCdmaEriIconIndex = in.readInt(); mCdmaEriIconMode = in.readInt(); mIsEmergencyOnly = in.readInt() != 0; - mIsDataRoamingFromRegistration = in.readInt() != 0; mIsUsingCarrierAggregation = in.readInt() != 0; mLteEarfcnRsrpBoost = in.readInt(); mNetworkRegistrationStates = new ArrayList<>(); @@ -400,8 +400,6 @@ public class ServiceState implements Parcelable { public void writeToParcel(Parcel out, int flags) { out.writeInt(mVoiceRegState); out.writeInt(mDataRegState); - out.writeInt(mVoiceRoamingType); - out.writeInt(mDataRoamingType); out.writeString(mVoiceOperatorAlphaLong); out.writeString(mVoiceOperatorAlphaShort); out.writeString(mVoiceOperatorNumeric); @@ -419,7 +417,6 @@ public class ServiceState implements Parcelable { out.writeInt(mCdmaEriIconIndex); out.writeInt(mCdmaEriIconMode); out.writeInt(mIsEmergencyOnly ? 1 : 0); - out.writeInt(mIsDataRoamingFromRegistration ? 1 : 0); out.writeInt(mIsUsingCarrierAggregation ? 1 : 0); out.writeInt(mLteEarfcnRsrpBoost); out.writeList(mNetworkRegistrationStates); @@ -537,17 +534,21 @@ public class ServiceState implements Parcelable { */ @UnsupportedAppUsage public boolean getVoiceRoaming() { - return mVoiceRoamingType != ROAMING_TYPE_NOT_ROAMING; + return getVoiceRoamingType() != ROAMING_TYPE_NOT_ROAMING; } - /** * Get current voice network roaming type * @return roaming type * @hide */ @UnsupportedAppUsage - public int getVoiceRoamingType() { - return mVoiceRoamingType; + public @RoamingType int getVoiceRoamingType() { + final NetworkRegistrationState regState = getNetworkRegistrationState( + NetworkRegistrationState.DOMAIN_CS, AccessNetworkConstants.TransportType.WWAN); + if (regState != null) { + return regState.getRoamingType(); + } + return ROAMING_TYPE_NOT_ROAMING; } /** @@ -557,19 +558,7 @@ public class ServiceState implements Parcelable { */ @UnsupportedAppUsage public boolean getDataRoaming() { - return mDataRoamingType != ROAMING_TYPE_NOT_ROAMING; - } - - /** - * Set whether data network registration state is roaming - * - * This should only be set to the roaming value received - * once the data registration phase has completed. - * @hide - */ - @UnsupportedAppUsage - public void setDataRoamingFromRegistration(boolean dataRoaming) { - mIsDataRoamingFromRegistration = dataRoaming; + return getDataRoamingType() != ROAMING_TYPE_NOT_ROAMING; } /** @@ -578,7 +567,12 @@ public class ServiceState implements Parcelable { * @hide */ public boolean getDataRoamingFromRegistration() { - return mIsDataRoamingFromRegistration; + final NetworkRegistrationState regState = getNetworkRegistrationState( + NetworkRegistrationState.DOMAIN_PS, AccessNetworkConstants.TransportType.WWAN); + if (regState != null) { + return (regState.getRegState() == NetworkRegistrationState.REG_STATE_ROAMING); + } + return false; } /** @@ -587,8 +581,13 @@ public class ServiceState implements Parcelable { * @hide */ @UnsupportedAppUsage - public int getDataRoamingType() { - return mDataRoamingType; + public @RoamingType int getDataRoamingType() { + final NetworkRegistrationState regState = getNetworkRegistrationState( + NetworkRegistrationState.DOMAIN_PS, AccessNetworkConstants.TransportType.WWAN); + if (regState != null) { + return regState.getRoamingType(); + } + return ROAMING_TYPE_NOT_ROAMING; } /** @@ -761,8 +760,6 @@ public class ServiceState implements Parcelable { return Objects.hash( mVoiceRegState, mDataRegState, - mVoiceRoamingType, - mDataRoamingType, mChannelNumber, mCellBandwidths, mVoiceOperatorAlphaLong, @@ -782,7 +779,6 @@ public class ServiceState implements Parcelable { mCdmaEriIconIndex, mCdmaEriIconMode, mIsEmergencyOnly, - mIsDataRoamingFromRegistration, mIsUsingCarrierAggregation, mLteEarfcnRsrpBoost, mNetworkRegistrationStates); @@ -796,8 +792,6 @@ public class ServiceState implements Parcelable { return (mVoiceRegState == s.mVoiceRegState && mDataRegState == s.mDataRegState && mIsManualNetworkSelection == s.mIsManualNetworkSelection - && mVoiceRoamingType == s.mVoiceRoamingType - && mDataRoamingType == s.mDataRoamingType && mChannelNumber == s.mChannelNumber && Arrays.equals(mCellBandwidths, s.mCellBandwidths) && equalsHandlesNulls(mVoiceOperatorAlphaLong, s.mVoiceOperatorAlphaLong) @@ -815,7 +809,6 @@ public class ServiceState implements Parcelable { && equalsHandlesNulls(mCdmaDefaultRoamingIndicator, s.mCdmaDefaultRoamingIndicator) && mIsEmergencyOnly == s.mIsEmergencyOnly - && mIsDataRoamingFromRegistration == s.mIsDataRoamingFromRegistration && mIsUsingCarrierAggregation == s.mIsUsingCarrierAggregation) && (mNetworkRegistrationStates == null ? s.mNetworkRegistrationStates == null : s.mNetworkRegistrationStates != null && @@ -935,8 +928,6 @@ public class ServiceState implements Parcelable { .append(", mChannelNumber=").append(mChannelNumber) .append(", duplexMode()=").append(getDuplexMode()) .append(", mCellBandwidths=").append(Arrays.toString(mCellBandwidths)) - .append(", mVoiceRoamingType=").append(getRoamingLogString(mVoiceRoamingType)) - .append(", mDataRoamingType=").append(getRoamingLogString(mDataRoamingType)) .append(", mVoiceOperatorAlphaLong=").append(mVoiceOperatorAlphaLong) .append(", mVoiceOperatorAlphaShort=").append(mVoiceOperatorAlphaShort) .append(", mDataOperatorAlphaLong=").append(mDataOperatorAlphaLong) @@ -953,7 +944,6 @@ public class ServiceState implements Parcelable { .append(", mCdmaRoamingIndicator=").append(mCdmaRoamingIndicator) .append(", mCdmaDefaultRoamingIndicator=").append(mCdmaDefaultRoamingIndicator) .append(", mIsEmergencyOnly=").append(mIsEmergencyOnly) - .append(", mIsDataRoamingFromRegistration=").append(mIsDataRoamingFromRegistration) .append(", mIsUsingCarrierAggregation=").append(mIsUsingCarrierAggregation) .append(", mLteEarfcnRsrpBoost=").append(mLteEarfcnRsrpBoost) .append(", mNetworkRegistrationStates=").append(mNetworkRegistrationStates) @@ -964,8 +954,6 @@ public class ServiceState implements Parcelable { if (DBG) Rlog.d(LOG_TAG, "[ServiceState] setNullState=" + state); mVoiceRegState = state; mDataRegState = state; - mVoiceRoamingType = ROAMING_TYPE_NOT_ROAMING; - mDataRoamingType = ROAMING_TYPE_NOT_ROAMING; mChannelNumber = -1; mCellBandwidths = new int[0]; mVoiceOperatorAlphaLong = null; @@ -985,7 +973,6 @@ public class ServiceState implements Parcelable { mCdmaEriIconIndex = -1; mCdmaEriIconMode = -1; mIsEmergencyOnly = false; - mIsDataRoamingFromRegistration = false; mIsUsingCarrierAggregation = false; mLteEarfcnRsrpBoost = 0; mNetworkRegistrationStates = new ArrayList<>(); @@ -1031,32 +1018,50 @@ public class ServiceState implements Parcelable { } public void setRoaming(boolean roaming) { - mVoiceRoamingType = (roaming ? ROAMING_TYPE_UNKNOWN : ROAMING_TYPE_NOT_ROAMING); - mDataRoamingType = mVoiceRoamingType; + setVoiceRoaming(roaming); + setDataRoaming(roaming); } /** @hide */ @UnsupportedAppUsage public void setVoiceRoaming(boolean roaming) { - mVoiceRoamingType = (roaming ? ROAMING_TYPE_UNKNOWN : ROAMING_TYPE_NOT_ROAMING); + setVoiceRoamingType(roaming ? ROAMING_TYPE_UNKNOWN : ROAMING_TYPE_NOT_ROAMING); } /** @hide */ @UnsupportedAppUsage - public void setVoiceRoamingType(int type) { - mVoiceRoamingType = type; + public void setVoiceRoamingType(@RoamingType int type) { + NetworkRegistrationState regState = getNetworkRegistrationState( + NetworkRegistrationState.DOMAIN_CS, AccessNetworkConstants.TransportType.WWAN); + if (regState == null) { + regState = new NetworkRegistrationState( + NetworkRegistrationState.DOMAIN_CS, AccessNetworkConstants.TransportType.WWAN, + ServiceState.ROAMING_TYPE_NOT_ROAMING, TelephonyManager.NETWORK_TYPE_UNKNOWN, 0, + false, null, null); + addNetworkRegistrationState(regState); + } + regState.setRoamingType(type); } /** @hide */ @UnsupportedAppUsage public void setDataRoaming(boolean dataRoaming) { - mDataRoamingType = (dataRoaming ? ROAMING_TYPE_UNKNOWN : ROAMING_TYPE_NOT_ROAMING); + setDataRoamingType(dataRoaming ? ROAMING_TYPE_UNKNOWN : ROAMING_TYPE_NOT_ROAMING); } /** @hide */ @UnsupportedAppUsage - public void setDataRoamingType(int type) { - mDataRoamingType = type; + public void setDataRoamingType(@RoamingType int type) { + NetworkRegistrationState regState = getNetworkRegistrationState( + NetworkRegistrationState.DOMAIN_PS, AccessNetworkConstants.TransportType.WWAN); + if (regState == null) { + regState = new NetworkRegistrationState( + NetworkRegistrationState.DOMAIN_PS, AccessNetworkConstants.TransportType.WWAN, + ServiceState.ROAMING_TYPE_NOT_ROAMING, TelephonyManager.NETWORK_TYPE_UNKNOWN, 0, + false, null, null); + addNetworkRegistrationState(regState); + } + regState.setRoamingType(type); } /** @@ -1168,30 +1173,10 @@ public class ServiceState implements Parcelable { */ @UnsupportedAppUsage private void setFromNotifierBundle(Bundle m) { - mVoiceRegState = m.getInt("voiceRegState"); - mDataRegState = m.getInt("dataRegState"); - mVoiceRoamingType = m.getInt("voiceRoamingType"); - mDataRoamingType = m.getInt("dataRoamingType"); - mVoiceOperatorAlphaLong = m.getString("operator-alpha-long"); - mVoiceOperatorAlphaShort = m.getString("operator-alpha-short"); - mVoiceOperatorNumeric = m.getString("operator-numeric"); - mDataOperatorAlphaLong = m.getString("data-operator-alpha-long"); - mDataOperatorAlphaShort = m.getString("data-operator-alpha-short"); - mDataOperatorNumeric = m.getString("data-operator-numeric"); - mIsManualNetworkSelection = m.getBoolean("manual"); - mRilVoiceRadioTechnology = m.getInt("radioTechnology"); - mRilDataRadioTechnology = m.getInt("dataRadioTechnology"); - mCssIndicator = m.getBoolean("cssIndicator"); - mNetworkId = m.getInt("networkId"); - mSystemId = m.getInt("systemId"); - mCdmaRoamingIndicator = m.getInt("cdmaRoamingIndicator"); - mCdmaDefaultRoamingIndicator = m.getInt("cdmaDefaultRoamingIndicator"); - mIsEmergencyOnly = m.getBoolean("emergencyOnly"); - mIsDataRoamingFromRegistration = m.getBoolean("isDataRoamingFromRegistration"); - mIsUsingCarrierAggregation = m.getBoolean("isUsingCarrierAggregation"); - mLteEarfcnRsrpBoost = m.getInt("LteEarfcnRsrpBoost"); - mChannelNumber = m.getInt("ChannelNumber"); - mCellBandwidths = m.getIntArray("CellBandwidths"); + ServiceState ssFromBundle = m.getParcelable(Intent.EXTRA_SERVICE_STATE); + if (ssFromBundle != null) { + copyFrom(ssFromBundle); + } } /** @@ -1202,10 +1187,13 @@ public class ServiceState implements Parcelable { */ @UnsupportedAppUsage public void fillInNotifierBundle(Bundle m) { + m.putParcelable(Intent.EXTRA_SERVICE_STATE, this); + // serviceState already consists of below entries. + // for backward compatibility, we continue fill in below entries. m.putInt("voiceRegState", mVoiceRegState); m.putInt("dataRegState", mDataRegState); - m.putInt("voiceRoamingType", mVoiceRoamingType); - m.putInt("dataRoamingType", mDataRoamingType); + m.putInt("dataRoamingType", getDataRoamingType()); + m.putInt("voiceRoamingType", getVoiceRoamingType()); m.putString("operator-alpha-long", mVoiceOperatorAlphaLong); m.putString("operator-alpha-short", mVoiceOperatorAlphaShort); m.putString("operator-numeric", mVoiceOperatorNumeric); @@ -1221,7 +1209,7 @@ public class ServiceState implements Parcelable { m.putInt("cdmaRoamingIndicator", mCdmaRoamingIndicator); m.putInt("cdmaDefaultRoamingIndicator", mCdmaDefaultRoamingIndicator); m.putBoolean("emergencyOnly", mIsEmergencyOnly); - m.putBoolean("isDataRoamingFromRegistration", mIsDataRoamingFromRegistration); + m.putBoolean("isDataRoamingFromRegistration", getDataRoamingFromRegistration()); m.putBoolean("isUsingCarrierAggregation", mIsUsingCarrierAggregation); m.putInt("LteEarfcnRsrpBoost", mLteEarfcnRsrpBoost); m.putInt("ChannelNumber", mChannelNumber); diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index a2647077d51a..f85069385f3c 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -32,12 +32,14 @@ import android.annotation.SystemService; import android.annotation.UnsupportedAppUsage; import android.app.BroadcastOptions; import android.app.PendingIntent; +import android.app.job.JobService; import android.content.Context; import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.content.res.Resources; +import android.database.ContentObserver; import android.net.INetworkPolicyManager; import android.net.NetworkCapabilities; import android.net.Uri; @@ -116,6 +118,52 @@ public class SubscriptionManager { @UnsupportedAppUsage public static final Uri CONTENT_URI = Uri.parse("content://telephony/siminfo"); + + /** + * Generates a content {@link Uri} used to receive updates on simInfo change + * on the given subscriptionId + * @param subscriptionId the subscriptionId to receive updates on + * @return the Uri used to observe carrier identity changes + * @hide + */ + public static Uri getUriForSubscriptionId(int subscriptionId) { + return Uri.withAppendedPath(CONTENT_URI, String.valueOf(subscriptionId)); + } + + /** + * A content {@link Uri} used to receive updates on wfc enabled user setting. + * <p> + * Use this {@link Uri} with a {@link ContentObserver} to be notified of changes to the + * subscription wfc enabled {@link SubscriptionManager#WFC_IMS_ENABLED} + * while your app is running. You can also use a {@link JobService} to ensure your app + * is notified of changes to the {@link Uri} even when it is not running. + * Note, however, that using a {@link JobService} does not guarantee timely delivery of + * updates to the {@link Uri}. + * To be notified of changes to a specific subId, append subId to the URI + * {@link Uri#withAppendedPath(Uri, String)}. + * @hide + */ + @SystemApi + public static final Uri WFC_ENABLED_CONTENT_URI = Uri.withAppendedPath(CONTENT_URI, "wfc"); + + /** + * A content {@link Uri} used to receive updates on enhanced 4g user setting. + * <p> + * Use this {@link Uri} with a {@link ContentObserver} to be notified of changes to the + * subscription enhanced 4G enabled {@link SubscriptionManager#ENHANCED_4G_MODE_ENABLED} + * while your app is running. You can also use a {@link JobService} to ensure your app + * is notified of changes to the {@link Uri} even when it is not running. + * Note, however, that using a {@link JobService} does not guarantee timely delivery of + * updates to the {@link Uri}. + * To be notified of changes to a specific subId, append subId to the URI + * {@link Uri#withAppendedPath(Uri, String)}. + * @hide + */ + @SystemApi + public static final Uri ENHANCED_4G_ENABLED_CONTENT_URI = Uri.withAppendedPath( + CONTENT_URI, "enhanced_4g"); + + /** * TelephonyProvider unique key column name is the subscription id. * <P>Type: TEXT (String)</P> @@ -1603,7 +1651,7 @@ public class SubscriptionManager { * Check if the subscription ID is usable. * * A usable subscription ID has a valid value except some special values such as - * {@link DEFAULT_SUBSCRIPTION_ID}. It can be used for subscription functions. + * {@link #DEFAULT_SUBSCRIPTION_ID}. It can be used for subscription functions. * * @param subscriptionId the subscription ID * @return {@code true} if the subscription ID is usable; {@code false} otherwise. diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 1106a49d341f..fca14c823751 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -60,6 +60,7 @@ import android.telephony.ims.aidl.IImsMmTelFeature; import android.telephony.ims.aidl.IImsRcsFeature; import android.telephony.ims.aidl.IImsRegistration; import android.telephony.ims.stub.ImsRegistrationImplBase; +import android.text.TextUtils; import android.util.Log; import com.android.ims.internal.IImsServiceFeatureCallback; @@ -5402,7 +5403,7 @@ public class TelephonyManager { @UnsupportedAppUsage public static String getTelephonyProperty(String property, String defaultVal) { String propVal = SystemProperties.get(property); - return propVal == null ? defaultVal : propVal; + return TextUtils.isEmpty(propVal) ? defaultVal : propVal; } /** @hide */ diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java index 3a263504a2ec..cb8269efe443 100644 --- a/telephony/java/com/android/internal/telephony/RILConstants.java +++ b/telephony/java/com/android/internal/telephony/RILConstants.java @@ -420,6 +420,7 @@ public interface RILConstants { int RIL_REQUEST_SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING = 201; int RIL_REQUEST_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA = 202; int RIL_REQUEST_SET_LINK_CAPACITY_REPORTING_CRITERIA = 203; + int RIL_REQUEST_SET_PREFERRED_DATA_MODEM = 204; /* Responses begin */ int RIL_RESPONSE_ACKNOWLEDGEMENT = 800; diff --git a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java index dac7e04be07a..9730ebc57fcf 100644 --- a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java +++ b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java @@ -32,6 +32,7 @@ import android.os.UserHandle; import android.telephony.Rlog; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; +import android.util.Log; import com.android.internal.annotations.VisibleForTesting; @@ -43,6 +44,10 @@ public final class TelephonyPermissions { private static final boolean DBG = false; + // When set to true this flag will treat all apps that fail the device identifier check as + // though they are targeting pre-Q and return dummy data instead of throwing a SecurityException + private static final boolean RELAX_DEVICE_IDENTIFIER_CHECK = true; + private static final Supplier<ITelephony> TELEPHONY_SUPPLIER = () -> ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE)); @@ -275,31 +280,41 @@ public final class TelephonyPermissions { */ private static boolean reportAccessDeniedToReadIdentifiers(Context context, int subId, int pid, int uid, String callingPackage, String message) { - if (callingPackage != null) { - try { - // if the target SDK is pre-Q then check if the calling package would have - // previously had access to device identifiers. - ApplicationInfo callingPackageInfo = context.getPackageManager().getApplicationInfo( - callingPackage, 0); - if (callingPackageInfo != null - && callingPackageInfo.targetSdkVersion < Build.VERSION_CODES.Q) { - if (context.checkPermission(android.Manifest.permission.READ_PHONE_STATE, pid, - uid) == PackageManager.PERMISSION_GRANTED) { - return false; - } - if (SubscriptionManager.isValidSubscriptionId(subId) - && getCarrierPrivilegeStatus(TELEPHONY_SUPPLIER, subId, uid) - == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) { - return false; + // if the device identifier check is relaxed then just return false to return dummy data to + // the caller instead of throwing a SecurityException for apps targeting Q+. + if (RELAX_DEVICE_IDENTIFIER_CHECK) { + Log.wtf(LOG_TAG, + "reportAccessDeniedToReadIdentifiers:" + callingPackage + ":" + message); + return false; + } else { + if (callingPackage != null) { + try { + // if the target SDK is pre-Q then check if the calling package would have + // previously had access to device identifiers. + ApplicationInfo callingPackageInfo = + context.getPackageManager().getApplicationInfo( + callingPackage, 0); + if (callingPackageInfo != null + && callingPackageInfo.targetSdkVersion < Build.VERSION_CODES.Q) { + if (context.checkPermission(android.Manifest.permission.READ_PHONE_STATE, + pid, + uid) == PackageManager.PERMISSION_GRANTED) { + return false; + } + if (SubscriptionManager.isValidSubscriptionId(subId) + && getCarrierPrivilegeStatus(TELEPHONY_SUPPLIER, subId, uid) + == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) { + return false; + } } + } catch (PackageManager.NameNotFoundException e) { + // If the application info for the calling package could not be found then + // default to throwing the SecurityException. } - } catch (PackageManager.NameNotFoundException e) { - // If the application info for the calling package could not be found then default - // to throwing the SecurityException. } + throw new SecurityException(message + ": The user " + uid + " does not have the " + + "READ_PRIVILEGED_PHONE_STATE permission to access the device identifiers"); } - throw new SecurityException(message + ": The user " + uid + " does not have the " - + "READ_PRIVILEGED_PHONE_STATE permission to access the device identifiers"); } /** diff --git a/tests/NetworkSecurityConfigTest/Android.mk b/tests/NetworkSecurityConfigTest/Android.mk index c225e170c377..a6c21db16846 100644 --- a/tests/NetworkSecurityConfigTest/Android.mk +++ b/tests/NetworkSecurityConfigTest/Android.mk @@ -7,7 +7,6 @@ LOCAL_CERTIFICATE := platform LOCAL_JAVA_LIBRARIES := \ android.test.runner \ - conscrypt \ android.test.base \ LOCAL_STATIC_JAVA_LIBRARIES := junit diff --git a/tools/aapt2/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp index 954f1ed3fb72..3ea17552ea7c 100644 --- a/tools/aapt2/cmd/Convert.cpp +++ b/tools/aapt2/cmd/Convert.cpp @@ -46,7 +46,7 @@ class IApkSerializer { IApkSerializer(IAaptContext* context, const Source& source) : context_(context), source_(source) {} virtual bool SerializeXml(const xml::XmlResource* xml, const std::string& path, bool utf16, - IArchiveWriter* writer) = 0; + IArchiveWriter* writer, uint32_t compression_flags) = 0; virtual bool SerializeTable(ResourceTable* table, IArchiveWriter* writer) = 0; virtual bool SerializeFile(FileReference* file, IArchiveWriter* writer) = 0; @@ -59,7 +59,10 @@ class IApkSerializer { bool ConvertApk(IAaptContext* context, unique_ptr<LoadedApk> apk, IApkSerializer* serializer, IArchiveWriter* writer) { - if (!serializer->SerializeXml(apk->GetManifest(), kAndroidManifestPath, true /*utf16*/, writer)) { + io::IFile* manifest = apk->GetFileCollection()->FindFile(kAndroidManifestPath); + if (!serializer->SerializeXml(apk->GetManifest(), kAndroidManifestPath, true /*utf16*/, writer, + (manifest != nullptr && manifest->WasCompressed()) + ? ArchiveEntry::kCompress : 0u)) { context->GetDiagnostics()->Error(DiagMessage(apk->GetSource()) << "failed to serialize AndroidManifest.xml"); return false; @@ -133,7 +136,7 @@ class BinaryApkSerializer : public IApkSerializer { : IApkSerializer(context, source), tableFlattenerOptions_(options) {} bool SerializeXml(const xml::XmlResource* xml, const std::string& path, bool utf16, - IArchiveWriter* writer) override { + IArchiveWriter* writer, uint32_t compression_flags) override { BigBuffer buffer(4096); XmlFlattenerOptions options = {}; options.use_utf16 = utf16; @@ -144,8 +147,7 @@ class BinaryApkSerializer : public IApkSerializer { } io::BigBufferInputStream input_stream(&buffer); - return io::CopyInputStreamToArchive(context_, &input_stream, path, ArchiveEntry::kCompress, - writer); + return io::CopyInputStreamToArchive(context_, &input_stream, path, compression_flags, writer); } bool SerializeTable(ResourceTable* table, IArchiveWriter* writer) override { @@ -186,7 +188,8 @@ class BinaryApkSerializer : public IApkSerializer { return false; } - if (!SerializeXml(xml.get(), *file->path, false /*utf16*/, writer)) { + if (!SerializeXml(xml.get(), *file->path, false /*utf16*/, writer, + file->file->WasCompressed() ? ArchiveEntry::kCompress : 0u)) { context_->GetDiagnostics()->Error(DiagMessage(source_) << "failed to serialize to binary XML: " << *file->path); return false; @@ -216,10 +219,10 @@ class ProtoApkSerializer : public IApkSerializer { : IApkSerializer(context, source) {} bool SerializeXml(const xml::XmlResource* xml, const std::string& path, bool utf16, - IArchiveWriter* writer) override { + IArchiveWriter* writer, uint32_t compression_flags) override { pb::XmlNode pb_node; SerializeXmlResourceToPb(*xml, &pb_node); - return io::CopyProtoToArchive(context_, &pb_node, path, ArchiveEntry::kCompress, writer); + return io::CopyProtoToArchive(context_, &pb_node, path, compression_flags, writer); } bool SerializeTable(ResourceTable* table, IArchiveWriter* writer) override { @@ -246,7 +249,8 @@ class ProtoApkSerializer : public IApkSerializer { return false; } - if (!SerializeXml(xml.get(), *file->path, false /*utf16*/, writer)) { + if (!SerializeXml(xml.get(), *file->path, false /*utf16*/, writer, + file->file->WasCompressed() ? ArchiveEntry::kCompress : 0u)) { context_->GetDiagnostics()->Error(DiagMessage(source_) << "failed to serialize to proto XML: " << *file->path); return false; diff --git a/tools/stats_log_api_gen/Collation.cpp b/tools/stats_log_api_gen/Collation.cpp index 4245700ed90d..d1f42f8b398e 100644 --- a/tools/stats_log_api_gen/Collation.cpp +++ b/tools/stats_log_api_gen/Collation.cpp @@ -251,7 +251,7 @@ int collate_atom(const Descriptor *atom, AtomDecl *atomDecl, } atomDecl->fields.push_back(atField); - if (field->options().GetExtension(os::statsd::stateFieldOption).option() == + if (field->options().GetExtension(os::statsd::state_field_option).option() == os::statsd::StateField::PRIMARY) { if (javaType == JAVA_TYPE_UNKNOWN || javaType == JAVA_TYPE_ATTRIBUTION_CHAIN || @@ -261,7 +261,7 @@ int collate_atom(const Descriptor *atom, AtomDecl *atomDecl, atomDecl->primaryFields.push_back(it->first); } - if (field->options().GetExtension(os::statsd::stateFieldOption).option() == + if (field->options().GetExtension(os::statsd::state_field_option).option() == os::statsd::StateField::EXCLUSIVE) { if (javaType == JAVA_TYPE_UNKNOWN || javaType == JAVA_TYPE_ATTRIBUTION_CHAIN || diff --git a/tools/stats_log_api_gen/test.proto b/tools/stats_log_api_gen/test.proto index 264a865e3b39..f6359748a27e 100644 --- a/tools/stats_log_api_gen/test.proto +++ b/tools/stats_log_api_gen/test.proto @@ -127,33 +127,33 @@ message GoodStateAtoms { // The atom has only primary field but no exclusive state field. message BadStateAtom1 { optional int32 uid = 1 - [(android.os.statsd.stateFieldOption).option = PRIMARY]; + [(android.os.statsd.state_field_option).option = PRIMARY]; } // Only primative types can be annotated. message BadStateAtom2 { repeated android.os.statsd.AttributionNode attribution = 1 - [(android.os.statsd.stateFieldOption).option = PRIMARY]; + [(android.os.statsd.state_field_option).option = PRIMARY]; optional int32 state = 2 - [(android.os.statsd.stateFieldOption).option = EXCLUSIVE]; + [(android.os.statsd.state_field_option).option = EXCLUSIVE]; } // Having 2 exclusive state field in the atom means the atom is badly designed. // E.g., putting bluetooth state and wifi state in the same atom. message BadStateAtom3 { optional int32 uid = 1 - [(android.os.statsd.stateFieldOption).option = PRIMARY]; + [(android.os.statsd.state_field_option).option = PRIMARY]; optional int32 state = 2 - [(android.os.statsd.stateFieldOption).option = EXCLUSIVE]; + [(android.os.statsd.state_field_option).option = EXCLUSIVE]; optional int32 state2 = 3 - [(android.os.statsd.stateFieldOption).option = EXCLUSIVE]; + [(android.os.statsd.state_field_option).option = EXCLUSIVE]; } message GoodStateAtom1 { optional int32 uid = 1 - [(android.os.statsd.stateFieldOption).option = PRIMARY]; + [(android.os.statsd.state_field_option).option = PRIMARY]; optional int32 state = 2 - [(android.os.statsd.stateFieldOption).option = EXCLUSIVE]; + [(android.os.statsd.state_field_option).option = EXCLUSIVE]; } // Atoms can have exclusive state field, but no primary field. That means @@ -161,16 +161,16 @@ message GoodStateAtom1 { message GoodStateAtom2 { optional int32 uid = 1; optional int32 state = 2 - [(android.os.statsd.stateFieldOption).option = EXCLUSIVE]; + [(android.os.statsd.state_field_option).option = EXCLUSIVE]; } // We can have more than one primary fields. That means their combination is a // primary key. message GoodStateAtom3 { optional int32 uid = 1 - [(android.os.statsd.stateFieldOption).option = PRIMARY]; + [(android.os.statsd.state_field_option).option = PRIMARY]; optional int32 tid = 2 - [(android.os.statsd.stateFieldOption).option = PRIMARY]; + [(android.os.statsd.state_field_option).option = PRIMARY]; optional int32 state = 3 - [(android.os.statsd.stateFieldOption).option = EXCLUSIVE]; + [(android.os.statsd.state_field_option).option = EXCLUSIVE]; }
\ No newline at end of file |