summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt8
-rw-r--r--cmds/statsd/Android.mk6
-rw-r--r--cmds/statsd/src/external/Perfetto.cpp2
-rw-r--r--cmds/statsd/src/external/Perfprofd.cpp13
-rw-r--r--cmds/statsd/src/perfetto/perfetto_config.proto60
l---------cmds/statsd/src/perfprofd/perfprofd_config.proto1
-rw-r--r--cmds/statsd/src/statsd_config.proto17
-rw-r--r--core/java/android/app/assist/AssistStructure.java4
-rw-r--r--core/java/android/database/sqlite/SQLiteQueryBuilder.java523
-rw-r--r--core/java/android/database/sqlite/SQLiteStatementBuilder.java1036
-rw-r--r--core/java/android/hardware/camera2/CameraCharacteristics.java24
-rw-r--r--core/java/android/hardware/camera2/CameraDevice.java20
-rw-r--r--core/java/android/hardware/display/DisplayManagerInternal.java10
-rw-r--r--core/java/android/text/Layout.java2
-rw-r--r--core/java/android/text/style/BulletSpan.java17
-rw-r--r--core/java/com/android/internal/annotations/GuardedBy.java4
-rw-r--r--core/java/com/android/internal/view/RotationPolicy.java6
-rw-r--r--core/java/com/android/server/BootReceiver.java4
-rw-r--r--core/proto/android/server/windowmanagerservice.proto36
-rw-r--r--core/res/res/values/styles_package_installer.xml1
-rw-r--r--core/tests/HdmiCec/Android.mk30
-rw-r--r--core/tests/HdmiCec/AndroidManifest.xml23
-rw-r--r--core/tests/HdmiCec/AndroidTest.xml28
-rw-r--r--core/tests/HdmiCec/HelloWorldTests_HalloWelt.config26
-rw-r--r--core/tests/HdmiCec/OWNERS3
-rw-r--r--core/tests/HdmiCec/src/android/test/example/helloworld/HelloWorldTest.java66
-rw-r--r--core/tests/coretests/AndroidManifest.xml8
-rw-r--r--core/tests/coretests/assets/fonts_for_family_selection/ascii.ttcbin0 -> 9048 bytes
-rw-r--r--core/tests/coretests/assets/fonts_for_family_selection/ascii_vf.ttfbin0 -> 10488 bytes
-rw-r--r--core/tests/coretests/assets/fonts_for_family_selection/ascii_vf.ttx1942
-rw-r--r--core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_a3em_weight100_upright.ttfbin0 -> 1824 bytes
-rw-r--r--core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_a3em_weight100_upright.ttx208
-rw-r--r--core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_b3em_weight100_italic.ttfbin0 -> 1824 bytes
-rw-r--r--core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_b3em_weight100_italic.ttx208
-rw-r--r--core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_c3em_weight200_upright.ttfbin0 -> 1824 bytes
-rw-r--r--core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_c3em_weight200_upright.ttx208
-rw-r--r--core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_d3em_weight200_italic.ttfbin0 -> 1824 bytes
-rw-r--r--core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_d3em_weight200_italic.ttx208
-rw-r--r--core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_e3em_weight300_upright.ttfbin0 -> 1824 bytes
-rw-r--r--core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_e3em_weight300_upright.ttx208
-rw-r--r--core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_f3em_weight300_italic.ttfbin0 -> 1824 bytes
-rw-r--r--core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_f3em_weight300_italic.ttx208
-rw-r--r--core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_g3em_weight400_upright.ttfbin0 -> 1824 bytes
-rw-r--r--core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_g3em_weight400_upright.ttx208
-rw-r--r--core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_h3em_weight400_italic.ttfbin0 -> 1824 bytes
-rw-r--r--core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_h3em_weight400_italic.ttx208
-rw-r--r--core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_i3em_weight500_upright.ttfbin0 -> 1824 bytes
-rw-r--r--core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_i3em_weight500_upright.ttx208
-rw-r--r--core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_j3em_weight500_italic.ttfbin0 -> 1824 bytes
-rw-r--r--core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_j3em_weight500_italic.ttx208
-rw-r--r--core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_k3em_weight600_upright.ttfbin0 -> 1824 bytes
-rw-r--r--core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_k3em_weight600_upright.ttx208
-rw-r--r--core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_l3em_weight600_italic.ttfbin0 -> 1824 bytes
-rw-r--r--core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_l3em_weight600_italic.ttx208
-rw-r--r--core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_m3em_weight700_upright.ttfbin0 -> 1824 bytes
-rw-r--r--core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_m3em_weight700_upright.ttx208
-rw-r--r--core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_n3em_weight700_italic.ttfbin0 -> 1824 bytes
-rw-r--r--core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_n3em_weight700_italic.ttx208
-rw-r--r--core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_names.txt18
-rw-r--r--core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_o3em_weight800_upright.ttfbin0 -> 1824 bytes
-rw-r--r--core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_o3em_weight800_upright.ttx208
-rw-r--r--core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_p3em_weight800_italic.ttfbin0 -> 1824 bytes
-rw-r--r--core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_p3em_weight800_italic.ttx208
-rw-r--r--core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_q3em_weight900_upright.ttfbin0 -> 1824 bytes
-rw-r--r--core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_q3em_weight900_upright.ttx208
-rw-r--r--core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_r3em_weight900_italic.ttfbin0 -> 1824 bytes
-rw-r--r--core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_r3em_weight900_italic.ttx208
-rw-r--r--core/tests/coretests/res/layout/empty_layout.xml26
-rw-r--r--core/tests/coretests/src/android/app/assist/AssistStructureTest.java269
-rw-r--r--core/tests/coretests/src/android/app/assist/EmptyLayoutActivity.java44
-rw-r--r--core/tests/coretests/src/android/graphics/FontFileUtilTest.java146
-rw-r--r--core/tests/coretests/src/android/graphics/FontTestUtil.java208
-rw-r--r--graphics/java/android/graphics/fonts/FontFileUtil.java127
-rw-r--r--media/java/android/media/AudioFormat.java14
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java67
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/location/InjectedSetting.java4
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/location/SettingsInjector.java (renamed from packages/SettingsLib/src/com/android/settingslib/location/BaseSettingsInjector.java)66
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/WifiSavedConfigUtils.java6
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java21
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java46
-rw-r--r--services/core/java/com/android/server/am/ActivityDisplay.java34
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java17
-rw-r--r--services/core/java/com/android/server/am/ActivityMetricsLogger.java2
-rw-r--r--services/core/java/com/android/server/am/ActivityRecord.java13
-rw-r--r--services/core/java/com/android/server/am/ActivityStack.java43
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java189
-rw-r--r--services/core/java/com/android/server/am/ActivityStarter.java40
-rw-r--r--services/core/java/com/android/server/am/ActivityTaskManagerService.java37
-rw-r--r--services/core/java/com/android/server/am/CompatModePackages.java2
-rw-r--r--services/core/java/com/android/server/am/KeyguardController.java4
-rw-r--r--services/core/java/com/android/server/am/TaskRecord.java2
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java18
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java17
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java3
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java140
-rw-r--r--services/core/java/com/android/server/policy/WindowManagerPolicy.java31
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityController.java5
-rw-r--r--services/core/java/com/android/server/wm/AppWindowToken.java11
-rw-r--r--services/core/java/com/android/server/wm/BlackFrame.java1
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java59
-rw-r--r--services/core/java/com/android/server/wm/DragDropController.java2
-rw-r--r--services/core/java/com/android/server/wm/DragState.java4
-rw-r--r--services/core/java/com/android/server/wm/InputMonitor.java2
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java30
-rw-r--r--services/core/java/com/android/server/wm/ScreenRotationAnimation.java20
-rw-r--r--services/core/java/com/android/server/wm/Task.java19
-rw-r--r--services/core/java/com/android/server/wm/TaskPositioner.java18
-rw-r--r--services/core/java/com/android/server/wm/WallpaperController.java4
-rw-r--r--services/core/java/com/android/server/wm/WindowFrames.java176
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java76
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java458
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java18
-rw-r--r--services/java/com/android/server/SystemServer.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java37
-rw-r--r--services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java22
-rw-r--r--services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java43
-rw-r--r--services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/am/TaskStackChangedListenerTest.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java162
-rw-r--r--services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java45
-rw-r--r--services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java120
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java66
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java103
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java6
-rw-r--r--tools/aapt2/java/ProguardRules.cpp7
-rw-r--r--tools/aapt2/java/ProguardRules_test.cpp8
-rwxr-xr-xtools/fonts/fontchain_linter.py7
131 files changed, 9220 insertions, 1662 deletions
diff --git a/api/current.txt b/api/current.txt
index a8874a9bb51a..e3db2566af48 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -12670,30 +12670,22 @@ package android.database.sqlite {
ctor public SQLiteQueryBuilder();
method public static void appendColumns(java.lang.StringBuilder, java.lang.String[]);
method public void appendWhere(java.lang.CharSequence);
- method public void appendWhere(java.lang.CharSequence, java.lang.String...);
method public void appendWhereEscapeString(java.lang.String);
- method public void appendWhereEscapeString(java.lang.String, java.lang.String...);
- method public void appendWhereExpression(java.lang.CharSequence);
- method public void appendWhereExpression(java.lang.CharSequence, java.lang.String...);
method public java.lang.String buildQuery(java.lang.String[], java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
method public deprecated java.lang.String buildQuery(java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String, java.lang.String);
method public static java.lang.String buildQueryString(boolean, java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
method public java.lang.String buildUnionQuery(java.lang.String[], java.lang.String, java.lang.String);
method public java.lang.String buildUnionSubQuery(java.lang.String, java.lang.String[], java.util.Set<java.lang.String>, int, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
method public deprecated java.lang.String buildUnionSubQuery(java.lang.String, java.lang.String[], java.util.Set<java.lang.String>, int, java.lang.String, java.lang.String, java.lang.String[], java.lang.String, java.lang.String);
- method public int delete(android.database.sqlite.SQLiteDatabase, java.lang.String, java.lang.String[]);
method public java.lang.String getTables();
method public android.database.Cursor query(android.database.sqlite.SQLiteDatabase, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String);
method public android.database.Cursor query(android.database.sqlite.SQLiteDatabase, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String, java.lang.String);
- method public android.database.Cursor query(android.database.sqlite.SQLiteDatabase, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal);
method public android.database.Cursor query(android.database.sqlite.SQLiteDatabase, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String, java.lang.String, android.os.CancellationSignal);
- method public android.database.Cursor query(android.database.sqlite.SQLiteDatabase, java.lang.String[], android.os.Bundle, android.os.CancellationSignal);
method public void setCursorFactory(android.database.sqlite.SQLiteDatabase.CursorFactory);
method public void setDistinct(boolean);
method public void setProjectionMap(java.util.Map<java.lang.String, java.lang.String>);
method public void setStrict(boolean);
method public void setTables(java.lang.String);
- method public int update(android.database.sqlite.SQLiteDatabase, android.content.ContentValues, java.lang.String, java.lang.String[]);
}
public class SQLiteReadOnlyDatabaseException extends android.database.sqlite.SQLiteException {
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index e182ad1b1a9a..49ea6d56d4fe 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -60,7 +60,6 @@ statsd_common_src := \
src/metrics/MetricsManager.cpp \
src/metrics/metrics_manager_util.cpp \
src/packages/UidMap.cpp \
- src/perfetto/perfetto_config.proto \
src/storage/StorageManager.cpp \
src/StatsLogProcessor.cpp \
src/StatsService.cpp \
@@ -73,8 +72,7 @@ statsd_common_src := \
# TODO(b/110563449): Once statsd is using a blueprint file, migrate to the proper filegroups.
statsd_common_src += \
- ../../../../system/extras/perfprofd/binder_interface/aidl/android/os/IPerfProfd.aidl \
- src/perfprofd/perfprofd_config.proto
+ ../../../../system/extras/perfprofd/binder_interface/aidl/android/os/IPerfProfd.aidl
statsd_common_c_includes := \
$(LOCAL_PATH)/src \
@@ -258,8 +256,6 @@ LOCAL_SRC_FILES := \
src/metrics_constants/metrics_constants.proto \
src/stats_log.proto \
src/statsd_config.proto \
- src/perfetto/perfetto_config.proto \
- src/perfprofd/perfprofd_config.proto \
src/atoms.proto
LOCAL_PROTOC_OPTIMIZE_TYPE := lite
diff --git a/cmds/statsd/src/external/Perfetto.cpp b/cmds/statsd/src/external/Perfetto.cpp
index 05544837b752..e44351b39769 100644
--- a/cmds/statsd/src/external/Perfetto.cpp
+++ b/cmds/statsd/src/external/Perfetto.cpp
@@ -113,7 +113,7 @@ bool CollectPerfettoTraceAndUploadToDropbox(const PerfettoDetails& config,
return false;
}
- std::string cfgProto = config.trace_config().SerializeAsString();
+ const std::string& cfgProto = config.trace_config();
size_t bytesWritten = fwrite(cfgProto.data(), 1, cfgProto.size(), writePipeStream);
fclose(writePipeStream);
if (bytesWritten != cfgProto.size() || cfgProto.size() == 0) {
diff --git a/cmds/statsd/src/external/Perfprofd.cpp b/cmds/statsd/src/external/Perfprofd.cpp
index ff237e8f6317..1678f104a07a 100644
--- a/cmds/statsd/src/external/Perfprofd.cpp
+++ b/cmds/statsd/src/external/Perfprofd.cpp
@@ -55,17 +55,8 @@ bool CollectPerfprofdTraceAndUploadToDropbox(const PerfprofdDetails& config,
return false;
}
- // Add protobufs can't be described in AIDL, we need to re-serialize
- // the config proto to send it.
- std::vector<uint8_t> proto_serialized;
- {
- const auto& config_proto = config.perfprofd_config();
- int size = config_proto.ByteSize();
- proto_serialized.resize(size);
- ::google::protobuf::uint8* target_ptr =
- reinterpret_cast<::google::protobuf::uint8*>(proto_serialized.data());
- config_proto.SerializeWithCachedSizesToArray(target_ptr);
- }
+ auto* data = reinterpret_cast<const uint8_t*>(config.perfprofd_config().data());
+ std::vector<uint8_t> proto_serialized(data, data + config.perfprofd_config().size());
// TODO: alert-id etc?
diff --git a/cmds/statsd/src/perfetto/perfetto_config.proto b/cmds/statsd/src/perfetto/perfetto_config.proto
deleted file mode 100644
index 56d12f8d81d4..000000000000
--- a/cmds/statsd/src/perfetto/perfetto_config.proto
+++ /dev/null
@@ -1,60 +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.
- */
-
-syntax = "proto2";
-
-package perfetto.protos;
-
-message DataSourceConfig {
- message FtraceConfig {
- repeated string event_names = 1;
- }
-
- optional string name = 1;
-
- optional uint32 target_buffer = 2;
-
- optional FtraceConfig ftrace_config = 100;
-}
-
-message TraceConfig {
- message BufferConfig {
- optional uint32 size_kb = 1;
-
- enum OptimizeFor {
- DEFAULT = 0;
- ONE_SHOT_READ = 1;
-
- }
- optional OptimizeFor optimize_for = 3;
-
- enum FillPolicy {
- UNSPECIFIED = 0;
- RING_BUFFER = 1;
- }
- optional FillPolicy fill_policy = 4;
- }
- repeated BufferConfig buffers = 1;
-
- message DataSource {
- optional protos.DataSourceConfig config = 1;
-
- repeated string producer_name_filter = 2;
- }
- repeated DataSource data_sources = 2;
-
- optional uint32 duration_ms = 3;
-}
diff --git a/cmds/statsd/src/perfprofd/perfprofd_config.proto b/cmds/statsd/src/perfprofd/perfprofd_config.proto
deleted file mode 120000
index c8be24707dfd..000000000000
--- a/cmds/statsd/src/perfprofd/perfprofd_config.proto
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../../system/extras/perfprofd/perfprofd_config.proto \ No newline at end of file
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index fabc5f94c405..26dfda3985d1 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -21,9 +21,6 @@ package android.os.statsd;
option java_package = "com.android.internal.os";
option java_outer_classname = "StatsdConfigProto";
-import "frameworks/base/cmds/statsd/src/perfetto/perfetto_config.proto";
-import "frameworks/base/cmds/statsd/src/perfprofd/perfprofd_config.proto";
-
enum Position {
POSITION_UNKNOWN = 0;
@@ -304,11 +301,21 @@ message IncidentdDetails {
}
message PerfettoDetails {
- optional perfetto.protos.TraceConfig trace_config = 1;
+ // The |trace_config| field is a proto-encoded message of type
+ // perfetto.protos.TraceConfig defined in
+ // //external/perfetto/protos/perfetto/config/. On device,
+ // statsd doesn't need to deserialize the message as it's just
+ // passed binary-encoded to the perfetto cmdline client.
+ optional bytes trace_config = 1;
}
message PerfprofdDetails {
- optional android.perfprofd.ProfilingConfig perfprofd_config = 1;
+ // The |perfprofd_config| field is a proto-encoded message of type
+ // android.perfprofd.ProfilingConfig defined in
+ // //system/extras/perfprofd/. On device, statsd doesn't need to
+ // deserialize the message as it's just passed binary-encoded to
+ // the perfprofd service.
+ optional bytes perfprofd_config = 1;
}
message BroadcastSubscriberDetails {
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index ef41b10640cb..2099e75d53d9 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -2167,13 +2167,13 @@ public class AssistStructure implements Parcelable {
if (autofillId == null) {
Log.i(TAG, prefix + " NO autofill ID");
} else {
- Log.i(TAG, prefix + "Autofill info: id= " + autofillId
+ Log.i(TAG, prefix + " Autofill info: id= " + autofillId
+ ", type=" + node.getAutofillType()
+ ", options=" + Arrays.toString(node.getAutofillOptions())
+ ", hints=" + Arrays.toString(node.getAutofillHints())
+ ", value=" + node.getAutofillValue()
+ ", sanitized=" + node.isSanitized()
- + ", importantFor=" + node.getImportantForAutofill());
+ + ", important=" + node.getImportantForAutofill());
}
final int NCHILDREN = node.getChildCount();
diff --git a/core/java/android/database/sqlite/SQLiteQueryBuilder.java b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
index b52c76154f56..c6c676f81758 100644
--- a/core/java/android/database/sqlite/SQLiteQueryBuilder.java
+++ b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
@@ -16,39 +16,17 @@
package android.database.sqlite;
-import static android.content.ContentResolver.QUERY_ARG_SQL_GROUP_BY;
-import static android.content.ContentResolver.QUERY_ARG_SQL_HAVING;
-import static android.content.ContentResolver.QUERY_ARG_SQL_LIMIT;
-import static android.content.ContentResolver.QUERY_ARG_SQL_SELECTION;
-import static android.content.ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS;
-import static android.content.ContentResolver.QUERY_ARG_SQL_SORT_ORDER;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.ContentResolver;
-import android.content.ContentValues;
import android.database.Cursor;
import android.database.DatabaseUtils;
-import android.os.Build;
-import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.OperationCanceledException;
import android.provider.BaseColumns;
import android.text.TextUtils;
-import android.util.ArrayMap;
import android.util.Log;
-import com.android.internal.util.ArrayUtils;
-
-import dalvik.system.VMRuntime;
-
-import libcore.util.EmptyArray;
-
-import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
-import java.util.Objects;
import java.util.Set;
import java.util.regex.Pattern;
@@ -56,7 +34,8 @@ import java.util.regex.Pattern;
* This is a convenience class that helps build SQL queries to be sent to
* {@link SQLiteDatabase} objects.
*/
-public class SQLiteQueryBuilder {
+public class SQLiteQueryBuilder
+{
private static final String TAG = "SQLiteQueryBuilder";
private static final Pattern sLimitPattern =
Pattern.compile("\\s*\\d+\\s*(,\\s*\\d+\\s*)?");
@@ -64,7 +43,6 @@ public class SQLiteQueryBuilder {
private Map<String, String> mProjectionMap = null;
private String mTables = "";
private StringBuilder mWhereClause = null; // lazily created
- private String[] mWhereArgs = EmptyArray.STRING;
private boolean mDistinct;
private SQLiteDatabase.CursorFactory mFactory;
private boolean mStrict;
@@ -104,131 +82,43 @@ public class SQLiteQueryBuilder {
mTables = inTables;
}
- /** {@hide} */
- public @Nullable String getWhere() {
- return (mWhereClause != null) ? mWhereClause.toString() : null;
- }
-
- /** {@hide} */
- public String[] getWhereArgs() {
- return mWhereArgs;
- }
-
- /**
- * Append a chunk to the {@code WHERE} clause of the query. All chunks
- * appended are surrounded by parenthesis and {@code AND}ed with the
- * selection passed to {@link #query}. The final {@code WHERE} clause looks
- * like:
- *
- * <pre>
- * WHERE (&lt;append chunk 1>&lt;append chunk2>) AND (&lt;query() selection parameter>)
- * </pre>
- *
- * @param inWhere the chunk of text to append to the {@code WHERE} clause.
- */
- public void appendWhere(@NonNull CharSequence inWhere) {
- appendWhere(inWhere, EmptyArray.STRING);
- }
-
/**
- * Append a chunk to the {@code WHERE} clause of the query. All chunks
- * appended are surrounded by parenthesis and {@code AND}ed with the
- * selection passed to {@link #query}. The final {@code WHERE} clause looks
- * like:
+ * Append a chunk to the WHERE clause of the query. All chunks appended are surrounded
+ * by parenthesis and ANDed with the selection passed to {@link #query}. The final
+ * WHERE clause looks like:
*
- * <pre>
* WHERE (&lt;append chunk 1>&lt;append chunk2>) AND (&lt;query() selection parameter>)
- * </pre>
*
- * @param inWhere the chunk of text to append to the {@code WHERE} clause.
- * @param inWhereArgs list of arguments to be bound to any '?' occurrences
- * in the where clause.
+ * @param inWhere the chunk of text to append to the WHERE clause.
*/
- public void appendWhere(@NonNull CharSequence inWhere, String... inWhereArgs) {
+ public void appendWhere(CharSequence inWhere) {
if (mWhereClause == null) {
mWhereClause = new StringBuilder(inWhere.length() + 16);
}
- mWhereClause.append(inWhere);
- mWhereArgs = ArrayUtils.concat(String.class, mWhereArgs, inWhereArgs);
- }
-
- /**
- * Append a standalone expression to the {@code WHERE} clause of this query.
- * <p>
- * This method differs from {@link #appendWhere(CharSequence)} in that it
- * automatically appends {@code AND} to any existing {@code WHERE} clause
- * already under construction before appending the given standalone
- * expression.
- *
- * @param inWhere the standalone expression to append to the {@code WHERE}
- * clause. It will be wrapped in parentheses when it's appended.
- */
- public void appendWhereExpression(@NonNull CharSequence inWhere) {
- appendWhereExpression(inWhere, EmptyArray.STRING);
- }
-
- /**
- * Append a standalone expression to the {@code WHERE} clause of this query.
- * <p>
- * This method differs from {@link #appendWhere(CharSequence)} in that it
- * automatically appends {@code AND} to any existing {@code WHERE} clause
- * already under construction before appending the given standalone
- * expression.
- *
- * @param inWhere the standalone expression to append to the {@code WHERE}
- * clause. It will be wrapped in parentheses when it's appended.
- * @param inWhereArgs list of arguments to be bound to any '?' occurrences
- * in the standalone expression.
- */
- public void appendWhereExpression(@NonNull CharSequence inWhere, String... inWhereArgs) {
- if (mWhereClause == null) {
- mWhereClause = new StringBuilder(inWhere.length() + 16);
- }
- if (mWhereClause.length() > 0) {
- mWhereClause.append(" AND ");
+ if (mWhereClause.length() == 0) {
+ mWhereClause.append('(');
}
- mWhereClause.append('(').append(inWhere).append(')');
- mWhereArgs = ArrayUtils.concat(String.class, mWhereArgs, inWhereArgs);
- }
-
- /**
- * Append a chunk to the {@code WHERE} clause of the query. All chunks
- * appended are surrounded by parenthesis and {@code AND}ed with the
- * selection passed to {@link #query}. The final {@code WHERE} clause looks
- * like this:
- *
- * <pre>
- * WHERE (&lt;append chunk 1>&lt;append chunk2>) AND (&lt;query() selection parameter>)
- * </pre>
- *
- * @param inWhere the chunk of text to append to the {@code WHERE} clause.
- * It will be escaped to avoid SQL injection attacks.
- */
- public void appendWhereEscapeString(@NonNull String inWhere) {
- appendWhereEscapeString(inWhere, EmptyArray.STRING);
+ mWhereClause.append(inWhere);
}
/**
- * Append a chunk to the {@code WHERE} clause of the query. All chunks
- * appended are surrounded by parenthesis and {@code AND}ed with the
- * selection passed to {@link #query}. The final {@code WHERE} clause looks
- * like this:
+ * Append a chunk to the WHERE clause of the query. All chunks appended are surrounded
+ * by parenthesis and ANDed with the selection passed to {@link #query}. The final
+ * WHERE clause looks like:
*
- * <pre>
* WHERE (&lt;append chunk 1>&lt;append chunk2>) AND (&lt;query() selection parameter>)
- * </pre>
*
- * @param inWhere the chunk of text to append to the {@code WHERE} clause.
- * It will be escaped to avoid SQL injection attacks.
- * @param inWhereArgs list of arguments to be bound to any '?' occurrences
- * in the where clause.
+ * @param inWhere the chunk of text to append to the WHERE clause. it will be escaped
+ * to avoid SQL injection attacks
*/
- public void appendWhereEscapeString(@NonNull String inWhere, String... inWhereArgs) {
+ public void appendWhereEscapeString(String inWhere) {
if (mWhereClause == null) {
mWhereClause = new StringBuilder(inWhere.length() + 16);
}
+ if (mWhereClause.length() == 0) {
+ mWhereClause.append('(');
+ }
DatabaseUtils.appendEscapedSQLString(mWhereClause, inWhere);
- mWhereArgs = ArrayUtils.concat(String.class, mWhereArgs, inWhereArgs);
}
/**
@@ -278,8 +168,8 @@ public class SQLiteQueryBuilder {
* </ul>
* By default, this value is false.
*/
- public void setStrict(boolean strict) {
- mStrict = strict;
+ public void setStrict(boolean flag) {
+ mStrict = flag;
}
/**
@@ -373,7 +263,7 @@ public class SQLiteQueryBuilder {
* information passed into this method.
*
* @param db the database to query on
- * @param projection A list of which columns to return. Passing
+ * @param projectionIn A list of which columns to return. Passing
* null will return all columns, which is discouraged to prevent
* reading data from storage that isn't going to be used.
* @param selection A filter declaring which rows to return,
@@ -398,14 +288,10 @@ public class SQLiteQueryBuilder {
* @see android.content.ContentResolver#query(android.net.Uri, String[],
* String, String[], String)
*/
- public @Nullable Cursor query(@NonNull SQLiteDatabase db,
- @Nullable String[] projection,
- @Nullable String selection,
- @Nullable String[] selectionArgs,
- @Nullable String groupBy,
- @Nullable String having,
- @Nullable String sortOrder) {
- return query(db, projection, selection, selectionArgs, groupBy, having, sortOrder,
+ public Cursor query(SQLiteDatabase db, String[] projectionIn,
+ String selection, String[] selectionArgs, String groupBy,
+ String having, String sortOrder) {
+ return query(db, projectionIn, selection, selectionArgs, groupBy, having, sortOrder,
null /* limit */, null /* cancellationSignal */);
}
@@ -414,7 +300,7 @@ public class SQLiteQueryBuilder {
* information passed into this method.
*
* @param db the database to query on
- * @param projection A list of which columns to return. Passing
+ * @param projectionIn A list of which columns to return. Passing
* null will return all columns, which is discouraged to prevent
* reading data from storage that isn't going to be used.
* @param selection A filter declaring which rows to return,
@@ -441,15 +327,10 @@ public class SQLiteQueryBuilder {
* @see android.content.ContentResolver#query(android.net.Uri, String[],
* String, String[], String)
*/
- public @Nullable Cursor query(@NonNull SQLiteDatabase db,
- @Nullable String[] projection,
- @Nullable String selection,
- @Nullable String[] selectionArgs,
- @Nullable String groupBy,
- @Nullable String having,
- @Nullable String sortOrder,
- @Nullable String limit) {
- return query(db, projection, selection, selectionArgs,
+ public Cursor query(SQLiteDatabase db, String[] projectionIn,
+ String selection, String[] selectionArgs, String groupBy,
+ String having, String sortOrder, String limit) {
+ return query(db, projectionIn, selection, selectionArgs,
groupBy, having, sortOrder, limit, null);
}
@@ -458,42 +339,7 @@ public class SQLiteQueryBuilder {
* information passed into this method.
*
* @param db the database to query on
- * @param projection A list of which columns to return. Passing
- * null will return all columns, which is discouraged to prevent
- * reading data from storage that isn't going to be used.
- * @param selection A filter declaring which rows to return,
- * formatted as an SQL WHERE clause (excluding the WHERE
- * itself). Passing null will return all rows for the given URL.
- * @param selectionArgs You may include ?s in selection, which
- * will be replaced by the values from selectionArgs, in order
- * that they appear in the selection. The values will be bound
- * as Strings.
- * @param sortOrder How to order the rows, formatted as an SQL
- * ORDER BY clause (excluding the ORDER BY itself). Passing null
- * will use the default sort order, which may be unordered.
- * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
- * If the operation is canceled, then {@link OperationCanceledException} will be thrown
- * when the query is executed.
- * @return a cursor over the result set
- * @see android.content.ContentResolver#query(android.net.Uri, String[],
- * String, String[], String)
- */
- public @Nullable Cursor query(@NonNull SQLiteDatabase db,
- @Nullable String[] projection,
- @Nullable String selection,
- @Nullable String[] selectionArgs,
- @Nullable String sortOrder,
- @Nullable CancellationSignal cancellationSignal) {
- return query(db, projection, selection, selectionArgs, null, null, sortOrder, null,
- cancellationSignal);
- }
-
- /**
- * Perform a query by combining all current settings and the
- * information passed into this method.
- *
- * @param db the database to query on
- * @param projection A list of which columns to return. Passing
+ * @param projectionIn A list of which columns to return. Passing
* null will return all columns, which is discouraged to prevent
* reading data from storage that isn't going to be used.
* @param selection A filter declaring which rows to return,
@@ -523,69 +369,14 @@ public class SQLiteQueryBuilder {
* @see android.content.ContentResolver#query(android.net.Uri, String[],
* String, String[], String)
*/
- public @Nullable Cursor query(@NonNull SQLiteDatabase db,
- @Nullable String[] projection,
- @Nullable String selection,
- @Nullable String[] selectionArgs,
- @Nullable String groupBy,
- @Nullable String having,
- @Nullable String sortOrder,
- @Nullable String limit,
- @Nullable CancellationSignal cancellationSignal) {
- final Bundle queryArgs = new Bundle();
- maybePutString(queryArgs, QUERY_ARG_SQL_SELECTION, selection);
- maybePutStringArray(queryArgs, QUERY_ARG_SQL_SELECTION_ARGS, selectionArgs);
- maybePutString(queryArgs, QUERY_ARG_SQL_GROUP_BY, groupBy);
- maybePutString(queryArgs, QUERY_ARG_SQL_HAVING, having);
- maybePutString(queryArgs, QUERY_ARG_SQL_SORT_ORDER, sortOrder);
- maybePutString(queryArgs, QUERY_ARG_SQL_LIMIT, limit);
- return query(db, projection, queryArgs, cancellationSignal);
- }
-
- /**
- * Perform a query by combining all current settings and the information
- * passed into this method.
- *
- * @param db the database to query on
- * @param projection A list of which columns to return. Passing null will
- * return all columns, which is discouraged to prevent reading
- * data from storage that isn't going to be used.
- * @param queryArgs A collection of arguments for the query, defined using
- * keys such as {@link ContentResolver#QUERY_ARG_SQL_SELECTION}
- * and {@link ContentResolver#QUERY_ARG_SQL_SELECTION_ARGS}.
- * @param cancellationSignal A signal to cancel the operation in progress,
- * or null if none. If the operation is canceled, then
- * {@link OperationCanceledException} will be thrown when the
- * query is executed.
- * @return a cursor over the result set
- */
- public Cursor query(@NonNull SQLiteDatabase db,
- @Nullable String[] projection,
- @Nullable Bundle queryArgs,
- @Nullable CancellationSignal cancellationSignal) {
- Objects.requireNonNull(db, "No database defined");
-
- if (VMRuntime.getRuntime().getTargetSdkVersion() >= Build.VERSION_CODES.Q) {
- Objects.requireNonNull(mTables, "No tables defined");
- } else if (mTables == null) {
+ public Cursor query(SQLiteDatabase db, String[] projectionIn,
+ String selection, String[] selectionArgs, String groupBy,
+ String having, String sortOrder, String limit, CancellationSignal cancellationSignal) {
+ if (mTables == null) {
return null;
}
- if (queryArgs == null) {
- queryArgs = Bundle.EMPTY;
- }
-
- // Final SQL that we will execute
- final String sql;
-
- final String unwrappedSql = buildQuery(projection,
- queryArgs.getString(QUERY_ARG_SQL_SELECTION),
- queryArgs.getString(QUERY_ARG_SQL_GROUP_BY),
- queryArgs.getString(QUERY_ARG_SQL_HAVING),
- queryArgs.getString(QUERY_ARG_SQL_SORT_ORDER),
- queryArgs.getString(QUERY_ARG_SQL_LIMIT));
-
- if (mStrict) {
+ if (mStrict && selection != null && selection.length() > 0) {
// Validate the user-supplied selection to detect syntactic anomalies
// in the selection string that could indicate a SQL injection attempt.
// The idea is to ensure that the selection clause is a valid SQL expression
@@ -593,136 +384,25 @@ public class SQLiteQueryBuilder {
// originally specified. An attacker cannot create an expression that
// would escape the SQL expression while maintaining balanced parentheses
// in both the wrapped and original forms.
-
- // NOTE: The ordering of the below operations is important; we must
- // execute the wrapped query to ensure the untrusted clause has been
- // fully isolated.
-
- // TODO: decode SORT ORDER and LIMIT clauses, since they can contain
- // "expr" inside that need to be validated
-
- final String wrappedSql = buildQuery(projection,
- wrap(queryArgs.getString(QUERY_ARG_SQL_SELECTION)),
- queryArgs.getString(QUERY_ARG_SQL_GROUP_BY),
- queryArgs.getString(QUERY_ARG_SQL_HAVING),
- queryArgs.getString(QUERY_ARG_SQL_SORT_ORDER),
- queryArgs.getString(QUERY_ARG_SQL_LIMIT));
-
- // Validate the unwrapped query
- db.validateSql(unwrappedSql, cancellationSignal);
-
- // Execute wrapped query for extra protection
- sql = wrappedSql;
- } else {
- // Execute unwrapped query
- sql = unwrappedSql;
+ String sqlForValidation = buildQuery(projectionIn, "(" + selection + ")", groupBy,
+ having, sortOrder, limit);
+ db.validateSql(sqlForValidation, cancellationSignal); // will throw if query is invalid
}
- final String[] sqlArgs = ArrayUtils.concat(String.class,
- queryArgs.getStringArray(QUERY_ARG_SQL_SELECTION_ARGS), mWhereArgs);
+ String sql = buildQuery(
+ projectionIn, selection, groupBy, having,
+ sortOrder, limit);
- if (Build.IS_DEBUGGABLE && Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, sql + " with args " + Arrays.toString(sqlArgs));
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Performing query: " + sql);
}
-
return db.rawQueryWithFactory(
- mFactory, sql, sqlArgs,
+ mFactory, sql, selectionArgs,
SQLiteDatabase.findEditTable(mTables),
cancellationSignal); // will throw if query is invalid
}
/**
- * Perform an update by combining all current settings and the
- * information passed into this method.
- *
- * @param db the database to update on
- * @param selection A filter declaring which rows to return,
- * formatted as an SQL WHERE clause (excluding the WHERE
- * itself). Passing null will return all rows for the given URL.
- * @param selectionArgs You may include ?s in selection, which
- * will be replaced by the values from selectionArgs, in order
- * that they appear in the selection. The values will be bound
- * as Strings.
- * @return the number of rows updated
- */
- public int update(@NonNull SQLiteDatabase db, @NonNull ContentValues values,
- @Nullable String selection, @Nullable String[] selectionArgs) {
- Objects.requireNonNull(mTables, "No tables defined");
- Objects.requireNonNull(db, "No database defined");
- Objects.requireNonNull(values, "No values defined");
-
- if (mStrict) {
- // Validate the user-supplied selection to detect syntactic anomalies
- // in the selection string that could indicate a SQL injection attempt.
- // The idea is to ensure that the selection clause is a valid SQL expression
- // by compiling it twice: once wrapped in parentheses and once as
- // originally specified. An attacker cannot create an expression that
- // would escape the SQL expression while maintaining balanced parentheses
- // in both the wrapped and original forms.
- final String sql = buildUpdate(values, wrap(selection));
- db.validateSql(sql, null); // will throw if query is invalid
- }
-
- final ArrayMap<String, Object> rawValues = values.getValues();
- final String[] updateArgs = new String[rawValues.size()];
- for (int i = 0; i < updateArgs.length; i++) {
- final Object arg = rawValues.valueAt(i);
- updateArgs[i] = (arg != null) ? arg.toString() : null;
- }
-
- final String sql = buildUpdate(values, selection);
- final String[] sqlArgs = ArrayUtils.concat(String.class, updateArgs,
- ArrayUtils.concat(String.class, selectionArgs, mWhereArgs));
-
- if (Build.IS_DEBUGGABLE && Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, sql + " with args " + Arrays.toString(sqlArgs));
- }
-
- return db.executeSql(sql, sqlArgs);
- }
-
- /**
- * Perform a delete by combining all current settings and the
- * information passed into this method.
- *
- * @param db the database to delete on
- * @param selection A filter declaring which rows to return,
- * formatted as an SQL WHERE clause (excluding the WHERE
- * itself). Passing null will return all rows for the given URL.
- * @param selectionArgs You may include ?s in selection, which
- * will be replaced by the values from selectionArgs, in order
- * that they appear in the selection. The values will be bound
- * as Strings.
- * @return the number of rows deleted
- */
- public int delete(@NonNull SQLiteDatabase db, @Nullable String selection,
- @Nullable String[] selectionArgs) {
- Objects.requireNonNull(mTables, "No tables defined");
- Objects.requireNonNull(db, "No database defined");
-
- if (mStrict) {
- // Validate the user-supplied selection to detect syntactic anomalies
- // in the selection string that could indicate a SQL injection attempt.
- // The idea is to ensure that the selection clause is a valid SQL expression
- // by compiling it twice: once wrapped in parentheses and once as
- // originally specified. An attacker cannot create an expression that
- // would escape the SQL expression while maintaining balanced parentheses
- // in both the wrapped and original forms.
- final String sql = buildDelete(wrap(selection));
- db.validateSql(sql, null); // will throw if query is invalid
- }
-
- final String sql = buildDelete(selection);
- final String[] sqlArgs = ArrayUtils.concat(String.class, selectionArgs, mWhereArgs);
-
- if (Build.IS_DEBUGGABLE && Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, sql + " with args " + Arrays.toString(sqlArgs));
- }
-
- return db.executeSql(sql, sqlArgs);
- }
-
- /**
* Construct a SELECT statement suitable for use in a group of
* SELECT statements that will be joined through UNION operators
* in buildUnionQuery.
@@ -754,10 +434,28 @@ public class SQLiteQueryBuilder {
String[] projectionIn, String selection, String groupBy,
String having, String sortOrder, String limit) {
String[] projection = computeProjection(projectionIn);
- String where = computeWhere(selection);
+
+ StringBuilder where = new StringBuilder();
+ boolean hasBaseWhereClause = mWhereClause != null && mWhereClause.length() > 0;
+
+ if (hasBaseWhereClause) {
+ where.append(mWhereClause.toString());
+ where.append(')');
+ }
+
+ // Tack on the user's selection, if present.
+ if (selection != null && selection.length() > 0) {
+ if (hasBaseWhereClause) {
+ where.append(" AND ");
+ }
+
+ where.append('(');
+ where.append(selection);
+ where.append(')');
+ }
return buildQueryString(
- mDistinct, mTables, projection, where,
+ mDistinct, mTables, projection, where.toString(),
groupBy, having, sortOrder, limit);
}
@@ -774,42 +472,6 @@ public class SQLiteQueryBuilder {
return buildQuery(projectionIn, selection, groupBy, having, sortOrder, limit);
}
- /** {@hide} */
- public String buildUpdate(ContentValues values, String selection) {
- if (values == null || values.isEmpty()) {
- throw new IllegalArgumentException("Empty values");
- }
-
- StringBuilder sql = new StringBuilder(120);
- sql.append("UPDATE ");
- sql.append(mTables);
- sql.append(" SET ");
-
- final ArrayMap<String, Object> rawValues = values.getValues();
- for (int i = 0; i < rawValues.size(); i++) {
- if (i > 0) {
- sql.append(',');
- }
- sql.append(rawValues.keyAt(i));
- sql.append("=?");
- }
-
- final String where = computeWhere(selection);
- appendClause(sql, " WHERE ", where);
- return sql.toString();
- }
-
- /** {@hide} */
- public String buildDelete(String selection) {
- StringBuilder sql = new StringBuilder(120);
- sql.append("DELETE FROM ");
- sql.append(mTables);
-
- final String where = computeWhere(selection);
- appendClause(sql, " WHERE ", where);
- return sql.toString();
- }
-
/**
* Construct a SELECT statement suitable for use in a group of
* SELECT statements that will be joined through UNION operators
@@ -934,7 +596,7 @@ public class SQLiteQueryBuilder {
return query.toString();
}
- private @Nullable String[] computeProjection(@Nullable String[] projectionIn) {
+ private String[] computeProjection(String[] projectionIn) {
if (projectionIn != null && projectionIn.length > 0) {
if (mProjectionMap != null) {
String[] projection = new String[projectionIn.length];
@@ -957,7 +619,7 @@ public class SQLiteQueryBuilder {
}
throw new IllegalArgumentException("Invalid column "
- + projectionIn[i] + " from tables " + mTables);
+ + projectionIn[i]);
}
return projection;
} else {
@@ -983,53 +645,4 @@ public class SQLiteQueryBuilder {
}
return null;
}
-
- private @NonNull String computeWhere(@Nullable String selection) {
- final boolean hasUser = selection != null && selection.length() > 0;
- final boolean hasInternal = mWhereClause != null && mWhereClause.length() > 0;
-
- if (hasUser || hasInternal) {
- final StringBuilder where = new StringBuilder();
- if (hasUser) {
- where.append('(').append(selection).append(')');
- }
- if (hasUser && hasInternal) {
- where.append(" AND ");
- }
- if (hasInternal) {
- where.append('(').append(mWhereClause.toString()).append(')');
- }
- return where.toString();
- } else {
- return null;
- }
- }
-
- /**
- * Wrap given argument in parenthesis, unless it's {@code null} or
- * {@code ()}, in which case return it verbatim.
- */
- private @Nullable String wrap(@Nullable String arg) {
- if (arg == null) {
- return null;
- } else if (arg.equals("")) {
- return arg;
- } else {
- return "(" + arg + ")";
- }
- }
-
- private static void maybePutString(@NonNull Bundle bundle, @NonNull String key,
- @Nullable String value) {
- if (value != null) {
- bundle.putString(key, value);
- }
- }
-
- private static void maybePutStringArray(@NonNull Bundle bundle, @NonNull String key,
- @Nullable String[] value) {
- if (value != null) {
- bundle.putStringArray(key, value);
- }
- }
}
diff --git a/core/java/android/database/sqlite/SQLiteStatementBuilder.java b/core/java/android/database/sqlite/SQLiteStatementBuilder.java
new file mode 100644
index 000000000000..e2efb2f8c39b
--- /dev/null
+++ b/core/java/android/database/sqlite/SQLiteStatementBuilder.java
@@ -0,0 +1,1036 @@
+/*
+ * Copyright (C) 2006 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.database.sqlite;
+
+import static android.content.ContentResolver.QUERY_ARG_SQL_GROUP_BY;
+import static android.content.ContentResolver.QUERY_ARG_SQL_HAVING;
+import static android.content.ContentResolver.QUERY_ARG_SQL_LIMIT;
+import static android.content.ContentResolver.QUERY_ARG_SQL_SELECTION;
+import static android.content.ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS;
+import static android.content.ContentResolver.QUERY_ARG_SQL_SORT_ORDER;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.DatabaseUtils;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.OperationCanceledException;
+import android.provider.BaseColumns;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.internal.util.ArrayUtils;
+
+import dalvik.system.VMRuntime;
+
+import libcore.util.EmptyArray;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+/**
+ * This is a convenience class that helps build SQL queries to be sent to
+ * {@link SQLiteDatabase} objects.
+ * @hide
+ */
+public class SQLiteStatementBuilder {
+ private static final String TAG = "SQLiteStatementBuilder";
+ private static final Pattern sLimitPattern =
+ Pattern.compile("\\s*\\d+\\s*(,\\s*\\d+\\s*)?");
+
+ private Map<String, String> mProjectionMap = null;
+ private String mTables = "";
+ private StringBuilder mWhereClause = null; // lazily created
+ private String[] mWhereArgs = EmptyArray.STRING;
+ private boolean mDistinct;
+ private SQLiteDatabase.CursorFactory mFactory;
+ private boolean mStrict;
+
+ public SQLiteStatementBuilder() {
+ mDistinct = false;
+ mFactory = null;
+ }
+
+ /**
+ * Mark the query as DISTINCT.
+ *
+ * @param distinct if true the query is DISTINCT, otherwise it isn't
+ */
+ public void setDistinct(boolean distinct) {
+ mDistinct = distinct;
+ }
+
+ /**
+ * Returns the list of tables being queried
+ *
+ * @return the list of tables being queried
+ */
+ public String getTables() {
+ return mTables;
+ }
+
+ /**
+ * Sets the list of tables to query. Multiple tables can be specified to perform a join.
+ * For example:
+ * setTables("foo, bar")
+ * setTables("foo LEFT OUTER JOIN bar ON (foo.id = bar.foo_id)")
+ *
+ * @param inTables the list of tables to query on
+ */
+ public void setTables(String inTables) {
+ mTables = inTables;
+ }
+
+ /** {@hide} */
+ public @Nullable String getWhere() {
+ return (mWhereClause != null) ? mWhereClause.toString() : null;
+ }
+
+ /** {@hide} */
+ public String[] getWhereArgs() {
+ return mWhereArgs;
+ }
+
+ /**
+ * Append a chunk to the {@code WHERE} clause of the query. All chunks
+ * appended are surrounded by parenthesis and {@code AND}ed with the
+ * selection passed to {@link #query}. The final {@code WHERE} clause looks
+ * like:
+ *
+ * <pre>
+ * WHERE (&lt;append chunk 1>&lt;append chunk2>) AND (&lt;query() selection parameter>)
+ * </pre>
+ *
+ * @param inWhere the chunk of text to append to the {@code WHERE} clause.
+ */
+ public void appendWhere(@NonNull CharSequence inWhere) {
+ appendWhere(inWhere, EmptyArray.STRING);
+ }
+
+ /**
+ * Append a chunk to the {@code WHERE} clause of the query. All chunks
+ * appended are surrounded by parenthesis and {@code AND}ed with the
+ * selection passed to {@link #query}. The final {@code WHERE} clause looks
+ * like:
+ *
+ * <pre>
+ * WHERE (&lt;append chunk 1>&lt;append chunk2>) AND (&lt;query() selection parameter>)
+ * </pre>
+ *
+ * @param inWhere the chunk of text to append to the {@code WHERE} clause.
+ * @param inWhereArgs list of arguments to be bound to any '?' occurrences
+ * in the where clause.
+ */
+ public void appendWhere(@NonNull CharSequence inWhere, String... inWhereArgs) {
+ if (mWhereClause == null) {
+ mWhereClause = new StringBuilder(inWhere.length() + 16);
+ }
+ mWhereClause.append(inWhere);
+ mWhereArgs = ArrayUtils.concat(String.class, mWhereArgs, inWhereArgs);
+ }
+
+ /**
+ * Append a standalone expression to the {@code WHERE} clause of this query.
+ * <p>
+ * This method differs from {@link #appendWhere(CharSequence)} in that it
+ * automatically appends {@code AND} to any existing {@code WHERE} clause
+ * already under construction before appending the given standalone
+ * expression.
+ *
+ * @param inWhere the standalone expression to append to the {@code WHERE}
+ * clause. It will be wrapped in parentheses when it's appended.
+ */
+ public void appendWhereExpression(@NonNull CharSequence inWhere) {
+ appendWhereExpression(inWhere, EmptyArray.STRING);
+ }
+
+ /**
+ * Append a standalone expression to the {@code WHERE} clause of this query.
+ * <p>
+ * This method differs from {@link #appendWhere(CharSequence)} in that it
+ * automatically appends {@code AND} to any existing {@code WHERE} clause
+ * already under construction before appending the given standalone
+ * expression.
+ *
+ * @param inWhere the standalone expression to append to the {@code WHERE}
+ * clause. It will be wrapped in parentheses when it's appended.
+ * @param inWhereArgs list of arguments to be bound to any '?' occurrences
+ * in the standalone expression.
+ */
+ public void appendWhereExpression(@NonNull CharSequence inWhere, String... inWhereArgs) {
+ if (mWhereClause == null) {
+ mWhereClause = new StringBuilder(inWhere.length() + 16);
+ }
+ if (mWhereClause.length() > 0) {
+ mWhereClause.append(" AND ");
+ }
+ mWhereClause.append('(').append(inWhere).append(')');
+ mWhereArgs = ArrayUtils.concat(String.class, mWhereArgs, inWhereArgs);
+ }
+
+ /**
+ * Append a chunk to the {@code WHERE} clause of the query. All chunks
+ * appended are surrounded by parenthesis and {@code AND}ed with the
+ * selection passed to {@link #query}. The final {@code WHERE} clause looks
+ * like this:
+ *
+ * <pre>
+ * WHERE (&lt;append chunk 1>&lt;append chunk2>) AND (&lt;query() selection parameter>)
+ * </pre>
+ *
+ * @param inWhere the chunk of text to append to the {@code WHERE} clause.
+ * It will be escaped to avoid SQL injection attacks.
+ */
+ public void appendWhereEscapeString(@NonNull String inWhere) {
+ appendWhereEscapeString(inWhere, EmptyArray.STRING);
+ }
+
+ /**
+ * Append a chunk to the {@code WHERE} clause of the query. All chunks
+ * appended are surrounded by parenthesis and {@code AND}ed with the
+ * selection passed to {@link #query}. The final {@code WHERE} clause looks
+ * like this:
+ *
+ * <pre>
+ * WHERE (&lt;append chunk 1>&lt;append chunk2>) AND (&lt;query() selection parameter>)
+ * </pre>
+ *
+ * @param inWhere the chunk of text to append to the {@code WHERE} clause.
+ * It will be escaped to avoid SQL injection attacks.
+ * @param inWhereArgs list of arguments to be bound to any '?' occurrences
+ * in the where clause.
+ */
+ public void appendWhereEscapeString(@NonNull String inWhere, String... inWhereArgs) {
+ if (mWhereClause == null) {
+ mWhereClause = new StringBuilder(inWhere.length() + 16);
+ }
+ DatabaseUtils.appendEscapedSQLString(mWhereClause, inWhere);
+ mWhereArgs = ArrayUtils.concat(String.class, mWhereArgs, inWhereArgs);
+ }
+
+ /**
+ * Sets the projection map for the query. The projection map maps
+ * from column names that the caller passes into query to database
+ * column names. This is useful for renaming columns as well as
+ * disambiguating column names when doing joins. For example you
+ * could map "name" to "people.name". If a projection map is set
+ * it must contain all column names the user may request, even if
+ * the key and value are the same.
+ *
+ * @param columnMap maps from the user column names to the database column names
+ */
+ public void setProjectionMap(Map<String, String> columnMap) {
+ mProjectionMap = columnMap;
+ }
+
+ /**
+ * Sets the cursor factory to be used for the query. You can use
+ * one factory for all queries on a database but it is normally
+ * easier to specify the factory when doing this query.
+ *
+ * @param factory the factory to use.
+ */
+ public void setCursorFactory(SQLiteDatabase.CursorFactory factory) {
+ mFactory = factory;
+ }
+
+ /**
+ * When set, the selection is verified against malicious arguments.
+ * When using this class to create a statement using
+ * {@link #buildQueryString(boolean, String, String[], String, String, String, String, String)},
+ * non-numeric limits will raise an exception. If a projection map is specified, fields
+ * not in that map will be ignored.
+ * If this class is used to execute the statement directly using
+ * {@link #query(SQLiteDatabase, String[], String, String[], String, String, String)}
+ * or
+ * {@link #query(SQLiteDatabase, String[], String, String[], String, String, String, String)},
+ * additionally also parenthesis escaping selection are caught.
+ *
+ * To summarize: To get maximum protection against malicious third party apps (for example
+ * content provider consumers), make sure to do the following:
+ * <ul>
+ * <li>Set this value to true</li>
+ * <li>Use a projection map</li>
+ * <li>Use one of the query overloads instead of getting the statement as a sql string</li>
+ * </ul>
+ * By default, this value is false.
+ */
+ public void setStrict(boolean strict) {
+ mStrict = strict;
+ }
+
+ /**
+ * Build an SQL query string from the given clauses.
+ *
+ * @param distinct true if you want each row to be unique, false otherwise.
+ * @param tables The table names to compile the query against.
+ * @param columns A list of which columns to return. Passing null will
+ * return all columns, which is discouraged to prevent reading
+ * data from storage that isn't going to be used.
+ * @param where A filter declaring which rows to return, formatted as an SQL
+ * WHERE clause (excluding the WHERE itself). Passing null will
+ * return all rows for the given URL.
+ * @param groupBy A filter declaring how to group rows, formatted as an SQL
+ * GROUP BY clause (excluding the GROUP BY itself). Passing null
+ * will cause the rows to not be grouped.
+ * @param having A filter declare which row groups to include in the cursor,
+ * if row grouping is being used, formatted as an SQL HAVING
+ * clause (excluding the HAVING itself). Passing null will cause
+ * all row groups to be included, and is required when row
+ * grouping is not being used.
+ * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
+ * (excluding the ORDER BY itself). Passing null will use the
+ * default sort order, which may be unordered.
+ * @param limit Limits the number of rows returned by the query,
+ * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
+ * @return the SQL query string
+ */
+ public static String buildQueryString(
+ boolean distinct, String tables, String[] columns, String where,
+ String groupBy, String having, String orderBy, String limit) {
+ if (TextUtils.isEmpty(groupBy) && !TextUtils.isEmpty(having)) {
+ throw new IllegalArgumentException(
+ "HAVING clauses are only permitted when using a groupBy clause");
+ }
+ if (!TextUtils.isEmpty(limit) && !sLimitPattern.matcher(limit).matches()) {
+ throw new IllegalArgumentException("invalid LIMIT clauses:" + limit);
+ }
+
+ StringBuilder query = new StringBuilder(120);
+
+ query.append("SELECT ");
+ if (distinct) {
+ query.append("DISTINCT ");
+ }
+ if (columns != null && columns.length != 0) {
+ appendColumns(query, columns);
+ } else {
+ query.append("* ");
+ }
+ query.append("FROM ");
+ query.append(tables);
+ appendClause(query, " WHERE ", where);
+ appendClause(query, " GROUP BY ", groupBy);
+ appendClause(query, " HAVING ", having);
+ appendClause(query, " ORDER BY ", orderBy);
+ appendClause(query, " LIMIT ", limit);
+
+ return query.toString();
+ }
+
+ private static void appendClause(StringBuilder s, String name, String clause) {
+ if (!TextUtils.isEmpty(clause)) {
+ s.append(name);
+ s.append(clause);
+ }
+ }
+
+ /**
+ * Add the names that are non-null in columns to s, separating
+ * them with commas.
+ */
+ public static void appendColumns(StringBuilder s, String[] columns) {
+ int n = columns.length;
+
+ for (int i = 0; i < n; i++) {
+ String column = columns[i];
+
+ if (column != null) {
+ if (i > 0) {
+ s.append(", ");
+ }
+ s.append(column);
+ }
+ }
+ s.append(' ');
+ }
+
+ /**
+ * Perform a query by combining all current settings and the
+ * information passed into this method.
+ *
+ * @param db the database to query on
+ * @param projection A list of which columns to return. Passing
+ * null will return all columns, which is discouraged to prevent
+ * reading data from storage that isn't going to be used.
+ * @param selection A filter declaring which rows to return,
+ * formatted as an SQL WHERE clause (excluding the WHERE
+ * itself). Passing null will return all rows for the given URL.
+ * @param selectionArgs You may include ?s in selection, which
+ * will be replaced by the values from selectionArgs, in order
+ * that they appear in the selection. The values will be bound
+ * as Strings.
+ * @param groupBy A filter declaring how to group rows, formatted
+ * as an SQL GROUP BY clause (excluding the GROUP BY
+ * itself). Passing null will cause the rows to not be grouped.
+ * @param having A filter declare which row groups to include in
+ * the cursor, if row grouping is being used, formatted as an
+ * SQL HAVING clause (excluding the HAVING itself). Passing
+ * null will cause all row groups to be included, and is
+ * required when row grouping is not being used.
+ * @param sortOrder How to order the rows, formatted as an SQL
+ * ORDER BY clause (excluding the ORDER BY itself). Passing null
+ * will use the default sort order, which may be unordered.
+ * @return a cursor over the result set
+ * @see android.content.ContentResolver#query(android.net.Uri, String[],
+ * String, String[], String)
+ */
+ public @Nullable Cursor query(@NonNull SQLiteDatabase db,
+ @Nullable String[] projection,
+ @Nullable String selection,
+ @Nullable String[] selectionArgs,
+ @Nullable String groupBy,
+ @Nullable String having,
+ @Nullable String sortOrder) {
+ return query(db, projection, selection, selectionArgs, groupBy, having, sortOrder,
+ null /* limit */, null /* cancellationSignal */);
+ }
+
+ /**
+ * Perform a query by combining all current settings and the
+ * information passed into this method.
+ *
+ * @param db the database to query on
+ * @param projection A list of which columns to return. Passing
+ * null will return all columns, which is discouraged to prevent
+ * reading data from storage that isn't going to be used.
+ * @param selection A filter declaring which rows to return,
+ * formatted as an SQL WHERE clause (excluding the WHERE
+ * itself). Passing null will return all rows for the given URL.
+ * @param selectionArgs You may include ?s in selection, which
+ * will be replaced by the values from selectionArgs, in order
+ * that they appear in the selection. The values will be bound
+ * as Strings.
+ * @param groupBy A filter declaring how to group rows, formatted
+ * as an SQL GROUP BY clause (excluding the GROUP BY
+ * itself). Passing null will cause the rows to not be grouped.
+ * @param having A filter declare which row groups to include in
+ * the cursor, if row grouping is being used, formatted as an
+ * SQL HAVING clause (excluding the HAVING itself). Passing
+ * null will cause all row groups to be included, and is
+ * required when row grouping is not being used.
+ * @param sortOrder How to order the rows, formatted as an SQL
+ * ORDER BY clause (excluding the ORDER BY itself). Passing null
+ * will use the default sort order, which may be unordered.
+ * @param limit Limits the number of rows returned by the query,
+ * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
+ * @return a cursor over the result set
+ * @see android.content.ContentResolver#query(android.net.Uri, String[],
+ * String, String[], String)
+ */
+ public @Nullable Cursor query(@NonNull SQLiteDatabase db,
+ @Nullable String[] projection,
+ @Nullable String selection,
+ @Nullable String[] selectionArgs,
+ @Nullable String groupBy,
+ @Nullable String having,
+ @Nullable String sortOrder,
+ @Nullable String limit) {
+ return query(db, projection, selection, selectionArgs,
+ groupBy, having, sortOrder, limit, null);
+ }
+
+ /**
+ * Perform a query by combining all current settings and the
+ * information passed into this method.
+ *
+ * @param db the database to query on
+ * @param projection A list of which columns to return. Passing
+ * null will return all columns, which is discouraged to prevent
+ * reading data from storage that isn't going to be used.
+ * @param selection A filter declaring which rows to return,
+ * formatted as an SQL WHERE clause (excluding the WHERE
+ * itself). Passing null will return all rows for the given URL.
+ * @param selectionArgs You may include ?s in selection, which
+ * will be replaced by the values from selectionArgs, in order
+ * that they appear in the selection. The values will be bound
+ * as Strings.
+ * @param sortOrder How to order the rows, formatted as an SQL
+ * ORDER BY clause (excluding the ORDER BY itself). Passing null
+ * will use the default sort order, which may be unordered.
+ * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
+ * If the operation is canceled, then {@link OperationCanceledException} will be thrown
+ * when the query is executed.
+ * @return a cursor over the result set
+ * @see android.content.ContentResolver#query(android.net.Uri, String[],
+ * String, String[], String)
+ */
+ public @Nullable Cursor query(@NonNull SQLiteDatabase db,
+ @Nullable String[] projection,
+ @Nullable String selection,
+ @Nullable String[] selectionArgs,
+ @Nullable String sortOrder,
+ @Nullable CancellationSignal cancellationSignal) {
+ return query(db, projection, selection, selectionArgs, null, null, sortOrder, null,
+ cancellationSignal);
+ }
+
+ /**
+ * Perform a query by combining all current settings and the
+ * information passed into this method.
+ *
+ * @param db the database to query on
+ * @param projection A list of which columns to return. Passing
+ * null will return all columns, which is discouraged to prevent
+ * reading data from storage that isn't going to be used.
+ * @param selection A filter declaring which rows to return,
+ * formatted as an SQL WHERE clause (excluding the WHERE
+ * itself). Passing null will return all rows for the given URL.
+ * @param selectionArgs You may include ?s in selection, which
+ * will be replaced by the values from selectionArgs, in order
+ * that they appear in the selection. The values will be bound
+ * as Strings.
+ * @param groupBy A filter declaring how to group rows, formatted
+ * as an SQL GROUP BY clause (excluding the GROUP BY
+ * itself). Passing null will cause the rows to not be grouped.
+ * @param having A filter declare which row groups to include in
+ * the cursor, if row grouping is being used, formatted as an
+ * SQL HAVING clause (excluding the HAVING itself). Passing
+ * null will cause all row groups to be included, and is
+ * required when row grouping is not being used.
+ * @param sortOrder How to order the rows, formatted as an SQL
+ * ORDER BY clause (excluding the ORDER BY itself). Passing null
+ * will use the default sort order, which may be unordered.
+ * @param limit Limits the number of rows returned by the query,
+ * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
+ * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
+ * If the operation is canceled, then {@link OperationCanceledException} will be thrown
+ * when the query is executed.
+ * @return a cursor over the result set
+ * @see android.content.ContentResolver#query(android.net.Uri, String[],
+ * String, String[], String)
+ */
+ public @Nullable Cursor query(@NonNull SQLiteDatabase db,
+ @Nullable String[] projection,
+ @Nullable String selection,
+ @Nullable String[] selectionArgs,
+ @Nullable String groupBy,
+ @Nullable String having,
+ @Nullable String sortOrder,
+ @Nullable String limit,
+ @Nullable CancellationSignal cancellationSignal) {
+ final Bundle queryArgs = new Bundle();
+ maybePutString(queryArgs, QUERY_ARG_SQL_SELECTION, selection);
+ maybePutStringArray(queryArgs, QUERY_ARG_SQL_SELECTION_ARGS, selectionArgs);
+ maybePutString(queryArgs, QUERY_ARG_SQL_GROUP_BY, groupBy);
+ maybePutString(queryArgs, QUERY_ARG_SQL_HAVING, having);
+ maybePutString(queryArgs, QUERY_ARG_SQL_SORT_ORDER, sortOrder);
+ maybePutString(queryArgs, QUERY_ARG_SQL_LIMIT, limit);
+ return query(db, projection, queryArgs, cancellationSignal);
+ }
+
+ /**
+ * Perform a query by combining all current settings and the information
+ * passed into this method.
+ *
+ * @param db the database to query on
+ * @param projection A list of which columns to return. Passing null will
+ * return all columns, which is discouraged to prevent reading
+ * data from storage that isn't going to be used.
+ * @param queryArgs A collection of arguments for the query, defined using
+ * keys such as {@link ContentResolver#QUERY_ARG_SQL_SELECTION}
+ * and {@link ContentResolver#QUERY_ARG_SQL_SELECTION_ARGS}.
+ * @param cancellationSignal A signal to cancel the operation in progress,
+ * or null if none. If the operation is canceled, then
+ * {@link OperationCanceledException} will be thrown when the
+ * query is executed.
+ * @return a cursor over the result set
+ */
+ public Cursor query(@NonNull SQLiteDatabase db,
+ @Nullable String[] projection,
+ @Nullable Bundle queryArgs,
+ @Nullable CancellationSignal cancellationSignal) {
+ Objects.requireNonNull(db, "No database defined");
+
+ if (VMRuntime.getRuntime().getTargetSdkVersion() >= Build.VERSION_CODES.Q) {
+ Objects.requireNonNull(mTables, "No tables defined");
+ } else if (mTables == null) {
+ return null;
+ }
+
+ if (queryArgs == null) {
+ queryArgs = Bundle.EMPTY;
+ }
+
+ // Final SQL that we will execute
+ final String sql;
+
+ final String unwrappedSql = buildQuery(projection,
+ queryArgs.getString(QUERY_ARG_SQL_SELECTION),
+ queryArgs.getString(QUERY_ARG_SQL_GROUP_BY),
+ queryArgs.getString(QUERY_ARG_SQL_HAVING),
+ queryArgs.getString(QUERY_ARG_SQL_SORT_ORDER),
+ queryArgs.getString(QUERY_ARG_SQL_LIMIT));
+
+ if (mStrict) {
+ // Validate the user-supplied selection to detect syntactic anomalies
+ // in the selection string that could indicate a SQL injection attempt.
+ // The idea is to ensure that the selection clause is a valid SQL expression
+ // by compiling it twice: once wrapped in parentheses and once as
+ // originally specified. An attacker cannot create an expression that
+ // would escape the SQL expression while maintaining balanced parentheses
+ // in both the wrapped and original forms.
+
+ // NOTE: The ordering of the below operations is important; we must
+ // execute the wrapped query to ensure the untrusted clause has been
+ // fully isolated.
+
+ // TODO: decode SORT ORDER and LIMIT clauses, since they can contain
+ // "expr" inside that need to be validated
+
+ final String wrappedSql = buildQuery(projection,
+ wrap(queryArgs.getString(QUERY_ARG_SQL_SELECTION)),
+ queryArgs.getString(QUERY_ARG_SQL_GROUP_BY),
+ queryArgs.getString(QUERY_ARG_SQL_HAVING),
+ queryArgs.getString(QUERY_ARG_SQL_SORT_ORDER),
+ queryArgs.getString(QUERY_ARG_SQL_LIMIT));
+
+ // Validate the unwrapped query
+ db.validateSql(unwrappedSql, cancellationSignal);
+
+ // Execute wrapped query for extra protection
+ sql = wrappedSql;
+ } else {
+ // Execute unwrapped query
+ sql = unwrappedSql;
+ }
+
+ final String[] sqlArgs = ArrayUtils.concat(String.class,
+ queryArgs.getStringArray(QUERY_ARG_SQL_SELECTION_ARGS), mWhereArgs);
+
+ if (Build.IS_DEBUGGABLE && Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, sql + " with args " + Arrays.toString(sqlArgs));
+ }
+
+ return db.rawQueryWithFactory(
+ mFactory, sql, sqlArgs,
+ SQLiteDatabase.findEditTable(mTables),
+ cancellationSignal); // will throw if query is invalid
+ }
+
+ /**
+ * Perform an update by combining all current settings and the
+ * information passed into this method.
+ *
+ * @param db the database to update on
+ * @param selection A filter declaring which rows to return,
+ * formatted as an SQL WHERE clause (excluding the WHERE
+ * itself). Passing null will return all rows for the given URL.
+ * @param selectionArgs You may include ?s in selection, which
+ * will be replaced by the values from selectionArgs, in order
+ * that they appear in the selection. The values will be bound
+ * as Strings.
+ * @return the number of rows updated
+ */
+ public int update(@NonNull SQLiteDatabase db, @NonNull ContentValues values,
+ @Nullable String selection, @Nullable String[] selectionArgs) {
+ Objects.requireNonNull(mTables, "No tables defined");
+ Objects.requireNonNull(db, "No database defined");
+ Objects.requireNonNull(values, "No values defined");
+
+ if (mStrict) {
+ // Validate the user-supplied selection to detect syntactic anomalies
+ // in the selection string that could indicate a SQL injection attempt.
+ // The idea is to ensure that the selection clause is a valid SQL expression
+ // by compiling it twice: once wrapped in parentheses and once as
+ // originally specified. An attacker cannot create an expression that
+ // would escape the SQL expression while maintaining balanced parentheses
+ // in both the wrapped and original forms.
+ final String sql = buildUpdate(values, wrap(selection));
+ db.validateSql(sql, null); // will throw if query is invalid
+ }
+
+ final ArrayMap<String, Object> rawValues = values.getValues();
+ final String[] updateArgs = new String[rawValues.size()];
+ for (int i = 0; i < updateArgs.length; i++) {
+ final Object arg = rawValues.valueAt(i);
+ updateArgs[i] = (arg != null) ? arg.toString() : null;
+ }
+
+ final String sql = buildUpdate(values, selection);
+ final String[] sqlArgs = ArrayUtils.concat(String.class, updateArgs,
+ ArrayUtils.concat(String.class, selectionArgs, mWhereArgs));
+
+ if (Build.IS_DEBUGGABLE && Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, sql + " with args " + Arrays.toString(sqlArgs));
+ }
+
+ return db.executeSql(sql, sqlArgs);
+ }
+
+ /**
+ * Perform a delete by combining all current settings and the
+ * information passed into this method.
+ *
+ * @param db the database to delete on
+ * @param selection A filter declaring which rows to return,
+ * formatted as an SQL WHERE clause (excluding the WHERE
+ * itself). Passing null will return all rows for the given URL.
+ * @param selectionArgs You may include ?s in selection, which
+ * will be replaced by the values from selectionArgs, in order
+ * that they appear in the selection. The values will be bound
+ * as Strings.
+ * @return the number of rows deleted
+ */
+ public int delete(@NonNull SQLiteDatabase db, @Nullable String selection,
+ @Nullable String[] selectionArgs) {
+ Objects.requireNonNull(mTables, "No tables defined");
+ Objects.requireNonNull(db, "No database defined");
+
+ if (mStrict) {
+ // Validate the user-supplied selection to detect syntactic anomalies
+ // in the selection string that could indicate a SQL injection attempt.
+ // The idea is to ensure that the selection clause is a valid SQL expression
+ // by compiling it twice: once wrapped in parentheses and once as
+ // originally specified. An attacker cannot create an expression that
+ // would escape the SQL expression while maintaining balanced parentheses
+ // in both the wrapped and original forms.
+ final String sql = buildDelete(wrap(selection));
+ db.validateSql(sql, null); // will throw if query is invalid
+ }
+
+ final String sql = buildDelete(selection);
+ final String[] sqlArgs = ArrayUtils.concat(String.class, selectionArgs, mWhereArgs);
+
+ if (Build.IS_DEBUGGABLE && Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, sql + " with args " + Arrays.toString(sqlArgs));
+ }
+
+ return db.executeSql(sql, sqlArgs);
+ }
+
+ /**
+ * Construct a SELECT statement suitable for use in a group of
+ * SELECT statements that will be joined through UNION operators
+ * in buildUnionQuery.
+ *
+ * @param projectionIn A list of which columns to return. Passing
+ * null will return all columns, which is discouraged to
+ * prevent reading data from storage that isn't going to be
+ * used.
+ * @param selection A filter declaring which rows to return,
+ * formatted as an SQL WHERE clause (excluding the WHERE
+ * itself). Passing null will return all rows for the given
+ * URL.
+ * @param groupBy A filter declaring how to group rows, formatted
+ * as an SQL GROUP BY clause (excluding the GROUP BY itself).
+ * Passing null will cause the rows to not be grouped.
+ * @param having A filter declare which row groups to include in
+ * the cursor, if row grouping is being used, formatted as an
+ * SQL HAVING clause (excluding the HAVING itself). Passing
+ * null will cause all row groups to be included, and is
+ * required when row grouping is not being used.
+ * @param sortOrder How to order the rows, formatted as an SQL
+ * ORDER BY clause (excluding the ORDER BY itself). Passing null
+ * will use the default sort order, which may be unordered.
+ * @param limit Limits the number of rows returned by the query,
+ * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
+ * @return the resulting SQL SELECT statement
+ */
+ public String buildQuery(
+ String[] projectionIn, String selection, String groupBy,
+ String having, String sortOrder, String limit) {
+ String[] projection = computeProjection(projectionIn);
+ String where = computeWhere(selection);
+
+ return buildQueryString(
+ mDistinct, mTables, projection, where,
+ groupBy, having, sortOrder, limit);
+ }
+
+ /**
+ * @deprecated This method's signature is misleading since no SQL parameter
+ * substitution is carried out. The selection arguments parameter does not get
+ * used at all. To avoid confusion, call
+ * {@link #buildQuery(String[], String, String, String, String, String)} instead.
+ */
+ @Deprecated
+ public String buildQuery(
+ String[] projectionIn, String selection, String[] selectionArgs,
+ String groupBy, String having, String sortOrder, String limit) {
+ return buildQuery(projectionIn, selection, groupBy, having, sortOrder, limit);
+ }
+
+ /** {@hide} */
+ public String buildUpdate(ContentValues values, String selection) {
+ if (values == null || values.isEmpty()) {
+ throw new IllegalArgumentException("Empty values");
+ }
+
+ StringBuilder sql = new StringBuilder(120);
+ sql.append("UPDATE ");
+ sql.append(mTables);
+ sql.append(" SET ");
+
+ final ArrayMap<String, Object> rawValues = values.getValues();
+ for (int i = 0; i < rawValues.size(); i++) {
+ if (i > 0) {
+ sql.append(',');
+ }
+ sql.append(rawValues.keyAt(i));
+ sql.append("=?");
+ }
+
+ final String where = computeWhere(selection);
+ appendClause(sql, " WHERE ", where);
+ return sql.toString();
+ }
+
+ /** {@hide} */
+ public String buildDelete(String selection) {
+ StringBuilder sql = new StringBuilder(120);
+ sql.append("DELETE FROM ");
+ sql.append(mTables);
+
+ final String where = computeWhere(selection);
+ appendClause(sql, " WHERE ", where);
+ return sql.toString();
+ }
+
+ /**
+ * Construct a SELECT statement suitable for use in a group of
+ * SELECT statements that will be joined through UNION operators
+ * in buildUnionQuery.
+ *
+ * @param typeDiscriminatorColumn the name of the result column
+ * whose cells will contain the name of the table from which
+ * each row was drawn.
+ * @param unionColumns the names of the columns to appear in the
+ * result. This may include columns that do not appear in the
+ * table this SELECT is querying (i.e. mTables), but that do
+ * appear in one of the other tables in the UNION query that we
+ * are constructing.
+ * @param columnsPresentInTable a Set of the names of the columns
+ * that appear in this table (i.e. in the table whose name is
+ * mTables). Since columns in unionColumns include columns that
+ * appear only in other tables, we use this array to distinguish
+ * which ones actually are present. Other columns will have
+ * NULL values for results from this subquery.
+ * @param computedColumnsOffset all columns in unionColumns before
+ * this index are included under the assumption that they're
+ * computed and therefore won't appear in columnsPresentInTable,
+ * e.g. "date * 1000 as normalized_date"
+ * @param typeDiscriminatorValue the value used for the
+ * type-discriminator column in this subquery
+ * @param selection A filter declaring which rows to return,
+ * formatted as an SQL WHERE clause (excluding the WHERE
+ * itself). Passing null will return all rows for the given
+ * URL.
+ * @param groupBy A filter declaring how to group rows, formatted
+ * as an SQL GROUP BY clause (excluding the GROUP BY itself).
+ * Passing null will cause the rows to not be grouped.
+ * @param having A filter declare which row groups to include in
+ * the cursor, if row grouping is being used, formatted as an
+ * SQL HAVING clause (excluding the HAVING itself). Passing
+ * null will cause all row groups to be included, and is
+ * required when row grouping is not being used.
+ * @return the resulting SQL SELECT statement
+ */
+ public String buildUnionSubQuery(
+ String typeDiscriminatorColumn,
+ String[] unionColumns,
+ Set<String> columnsPresentInTable,
+ int computedColumnsOffset,
+ String typeDiscriminatorValue,
+ String selection,
+ String groupBy,
+ String having) {
+ int unionColumnsCount = unionColumns.length;
+ String[] projectionIn = new String[unionColumnsCount];
+
+ for (int i = 0; i < unionColumnsCount; i++) {
+ String unionColumn = unionColumns[i];
+
+ if (unionColumn.equals(typeDiscriminatorColumn)) {
+ projectionIn[i] = "'" + typeDiscriminatorValue + "' AS "
+ + typeDiscriminatorColumn;
+ } else if (i <= computedColumnsOffset
+ || columnsPresentInTable.contains(unionColumn)) {
+ projectionIn[i] = unionColumn;
+ } else {
+ projectionIn[i] = "NULL AS " + unionColumn;
+ }
+ }
+ return buildQuery(
+ projectionIn, selection, groupBy, having,
+ null /* sortOrder */,
+ null /* limit */);
+ }
+
+ /**
+ * @deprecated This method's signature is misleading since no SQL parameter
+ * substitution is carried out. The selection arguments parameter does not get
+ * used at all. To avoid confusion, call
+ * {@link #buildUnionSubQuery}
+ * instead.
+ */
+ @Deprecated
+ public String buildUnionSubQuery(
+ String typeDiscriminatorColumn,
+ String[] unionColumns,
+ Set<String> columnsPresentInTable,
+ int computedColumnsOffset,
+ String typeDiscriminatorValue,
+ String selection,
+ String[] selectionArgs,
+ String groupBy,
+ String having) {
+ return buildUnionSubQuery(
+ typeDiscriminatorColumn, unionColumns, columnsPresentInTable,
+ computedColumnsOffset, typeDiscriminatorValue, selection,
+ groupBy, having);
+ }
+
+ /**
+ * Given a set of subqueries, all of which are SELECT statements,
+ * construct a query that returns the union of what those
+ * subqueries return.
+ * @param subQueries an array of SQL SELECT statements, all of
+ * which must have the same columns as the same positions in
+ * their results
+ * @param sortOrder How to order the rows, formatted as an SQL
+ * ORDER BY clause (excluding the ORDER BY itself). Passing
+ * null will use the default sort order, which may be unordered.
+ * @param limit The limit clause, which applies to the entire union result set
+ *
+ * @return the resulting SQL SELECT statement
+ */
+ public String buildUnionQuery(String[] subQueries, String sortOrder, String limit) {
+ StringBuilder query = new StringBuilder(128);
+ int subQueryCount = subQueries.length;
+ String unionOperator = mDistinct ? " UNION " : " UNION ALL ";
+
+ for (int i = 0; i < subQueryCount; i++) {
+ if (i > 0) {
+ query.append(unionOperator);
+ }
+ query.append(subQueries[i]);
+ }
+ appendClause(query, " ORDER BY ", sortOrder);
+ appendClause(query, " LIMIT ", limit);
+ return query.toString();
+ }
+
+ private @Nullable String[] computeProjection(@Nullable String[] projectionIn) {
+ if (projectionIn != null && projectionIn.length > 0) {
+ if (mProjectionMap != null) {
+ String[] projection = new String[projectionIn.length];
+ int length = projectionIn.length;
+
+ for (int i = 0; i < length; i++) {
+ String userColumn = projectionIn[i];
+ String column = mProjectionMap.get(userColumn);
+
+ if (column != null) {
+ projection[i] = column;
+ continue;
+ }
+
+ if (!mStrict &&
+ ( userColumn.contains(" AS ") || userColumn.contains(" as "))) {
+ /* A column alias already exist */
+ projection[i] = userColumn;
+ continue;
+ }
+
+ throw new IllegalArgumentException("Invalid column "
+ + projectionIn[i] + " from tables " + mTables);
+ }
+ return projection;
+ } else {
+ return projectionIn;
+ }
+ } else if (mProjectionMap != null) {
+ // Return all columns in projection map.
+ Set<Entry<String, String>> entrySet = mProjectionMap.entrySet();
+ String[] projection = new String[entrySet.size()];
+ Iterator<Entry<String, String>> entryIter = entrySet.iterator();
+ int i = 0;
+
+ while (entryIter.hasNext()) {
+ Entry<String, String> entry = entryIter.next();
+
+ // Don't include the _count column when people ask for no projection.
+ if (entry.getKey().equals(BaseColumns._COUNT)) {
+ continue;
+ }
+ projection[i++] = entry.getValue();
+ }
+ return projection;
+ }
+ return null;
+ }
+
+ private @NonNull String computeWhere(@Nullable String selection) {
+ final boolean hasUser = selection != null && selection.length() > 0;
+ final boolean hasInternal = mWhereClause != null && mWhereClause.length() > 0;
+
+ if (hasUser || hasInternal) {
+ final StringBuilder where = new StringBuilder();
+ if (hasUser) {
+ where.append('(').append(selection).append(')');
+ }
+ if (hasUser && hasInternal) {
+ where.append(" AND ");
+ }
+ if (hasInternal) {
+ where.append('(').append(mWhereClause.toString()).append(')');
+ }
+ return where.toString();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Wrap given argument in parenthesis, unless it's {@code null} or
+ * {@code ()}, in which case return it verbatim.
+ */
+ private @Nullable String wrap(@Nullable String arg) {
+ if (arg == null) {
+ return null;
+ } else if (arg.equals("")) {
+ return arg;
+ } else {
+ return "(" + arg + ")";
+ }
+ }
+
+ private static void maybePutString(@NonNull Bundle bundle, @NonNull String key,
+ @Nullable String value) {
+ if (value != null) {
+ bundle.putString(key, value);
+ }
+ }
+
+ private static void maybePutStringArray(@NonNull Bundle bundle, @NonNull String key,
+ @Nullable String[] value) {
+ if (value != null) {
+ bundle.putStringArray(key, value);
+ }
+ }
+}
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 32c6898e8c00..60e4ce29ab51 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -3136,12 +3136,26 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* the following code snippet can be used:</p>
* <pre><code>// Returns true if the device supports the required hardware level, or better.
* boolean isHardwareLevelSupported(CameraCharacteristics c, int requiredLevel) {
+ * final int[] sortedHwLevels = {
+ * CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY,
+ * CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL,
+ * CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+ * CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL,
+ * CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3
+ * };
* int deviceLevel = c.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
- * if (deviceLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
- * return requiredLevel == deviceLevel;
+ * if (requiredLevel == deviceLevel) {
+ * return true;
* }
- * // deviceLevel is not LEGACY, can use numerical sort
- * return requiredLevel &lt;= deviceLevel;
+ *
+ * for (int sortedlevel : sortedHwLevels) {
+ * if (sortedlevel == requiredLevel) {
+ * return true;
+ * } else if (sortedlevel == deviceLevel) {
+ * return false;
+ * }
+ * }
+ * return false; // Should never reach here
* }
* </code></pre>
* <p>At a high level, the levels are:</p>
@@ -3155,6 +3169,8 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* post-processing settings, and image capture at a high rate.</li>
* <li><code>LEVEL_3</code> devices additionally support YUV reprocessing and RAW image capture, along
* with additional output stream configurations.</li>
+ * <li><code>EXTERNAL</code> devices are similar to <code>LIMITED</code> devices with exceptions like some sensor or
+ * lens information not reorted or less stable framerates.</li>
* </ul>
* <p>See the individual level enums for full descriptions of the supported capabilities. The
* {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} entry describes the device's capabilities at a
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index f47d4640c09c..ce88697fa8db 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -44,19 +44,27 @@ import java.lang.annotation.RetentionPolicy;
* {@link android.Manifest.permission#CAMERA Camera} permission in its manifest
* in order to access camera devices.</p>
*
- * <p>A given camera device may provide support at one of two levels: limited or
- * full. If a device only supports the limited level, then Camera2 exposes a
- * feature set that is roughly equivalent to the older
+ * <p>A given camera device may provide support at one of several levels defined
+ * in {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL}.
+ * If a device supports {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY LEGACY} level,
+ * the camera device is running in backward compatibility mode and has minimum camera2 API support.
+ * If a device supports the {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED}
+ * level, then Camera2 exposes a feature set that is roughly equivalent to the older
* {@link android.hardware.Camera Camera} API, although with a cleaner and more
- * efficient interface. Devices that implement the full level of support
+ * efficient interface.
+ * If a device supports the {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL EXTERNAL}
+ * level, then the device is a removable camera that provides similar but slightly less features
+ * as the {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED} level.
+ * Devices that implement the {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_FULL FULL} or
+ * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_3 LEVEL3} level of support
* provide substantially improved capabilities over the older camera
- * API. Applications that target the limited level devices will run unchanged on
- * the full-level devices; if your application requires a full-level device for
+ * API. If your application requires a full-level device for
* proper operation, declare the "android.hardware.camera.level.full" feature in your
* manifest.</p>
*
* @see CameraManager#openCamera
* @see android.Manifest.permission#CAMERA
+ * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
*/
public abstract class CameraDevice implements AutoCloseable {
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 504f840af5bf..7840fd03d7ff 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -23,6 +23,7 @@ import android.util.IntArray;
import android.util.SparseArray;
import android.view.Display;
import android.view.DisplayInfo;
+import android.view.Surface;
import android.view.SurfaceControl;
/**
@@ -62,6 +63,15 @@ public abstract class DisplayManagerInternal {
public abstract boolean isProximitySensorAvailable();
/**
+ * Take a screenshot of the specified display into the provided {@link Surface}.
+ *
+ * @param displayId The display id to take the screenshot of.
+ * @param outSurface The {@link Surface} to take the screenshot into.
+ * @return True if the screenshot is taken.
+ */
+ public abstract boolean screenshot(int displayId, Surface outSurface);
+
+ /**
* Returns information about the specified logical display.
*
* @param displayId The logical display id.
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 2367d639f764..c6c1e8ae9ac9 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -1594,7 +1594,7 @@ public abstract class Layout {
}
float get(final int offset) {
- if (mHorizontals == null) {
+ if (mHorizontals == null || offset < 0 || offset >= mHorizontals.length) {
return getHorizontal(offset, mPrimary);
} else {
return mHorizontals[offset - mLineStartOffset];
diff --git a/core/java/android/text/style/BulletSpan.java b/core/java/android/text/style/BulletSpan.java
index 70175c8611e4..c0ac70e0477e 100644
--- a/core/java/android/text/style/BulletSpan.java
+++ b/core/java/android/text/style/BulletSpan.java
@@ -23,8 +23,6 @@ import android.annotation.Nullable;
import android.annotation.Px;
import android.graphics.Canvas;
import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.Path.Direction;
import android.os.Parcel;
import android.text.Layout;
import android.text.ParcelableSpan;
@@ -73,7 +71,6 @@ public class BulletSpan implements LeadingMarginSpan, ParcelableSpan {
private final int mGapWidth;
@Px
private final int mBulletRadius;
- private Path mBulletPath = null;
@ColorInt
private final int mColor;
private final boolean mWantColor;
@@ -224,19 +221,7 @@ public class BulletSpan implements LeadingMarginSpan, ParcelableSpan {
final float yPosition = (top + bottom) / 2f;
final float xPosition = x + dir * mBulletRadius;
- if (canvas.isHardwareAccelerated()) {
- if (mBulletPath == null) {
- mBulletPath = new Path();
- mBulletPath.addCircle(0.0f, 0.0f, mBulletRadius, Direction.CW);
- }
-
- canvas.save();
- canvas.translate(xPosition, yPosition);
- canvas.drawPath(mBulletPath, paint);
- canvas.restore();
- } else {
- canvas.drawCircle(xPosition, yPosition, mBulletRadius, paint);
- }
+ canvas.drawCircle(xPosition, yPosition, mBulletRadius, paint);
if (mWantColor) {
paint.setColor(oldcolor);
diff --git a/core/java/com/android/internal/annotations/GuardedBy.java b/core/java/com/android/internal/annotations/GuardedBy.java
index fc6194553f93..0e63214b4f1a 100644
--- a/core/java/com/android/internal/annotations/GuardedBy.java
+++ b/core/java/com/android/internal/annotations/GuardedBy.java
@@ -23,10 +23,10 @@ import java.lang.annotation.Target;
/**
* Annotation type used to mark a method or field that can only be accessed when
- * holding the referenced lock.
+ * holding the referenced locks.
*/
@Target({ ElementType.FIELD, ElementType.METHOD })
@Retention(RetentionPolicy.CLASS)
public @interface GuardedBy {
- String value();
+ String[] value();
}
diff --git a/core/java/com/android/internal/view/RotationPolicy.java b/core/java/com/android/internal/view/RotationPolicy.java
index e9472fa0f464..13425e5e9bec 100644
--- a/core/java/com/android/internal/view/RotationPolicy.java
+++ b/core/java/com/android/internal/view/RotationPolicy.java
@@ -77,7 +77,11 @@ public final class RotationPolicy {
final Point size = new Point();
final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
try {
- wm.getInitialDisplaySize(Display.DEFAULT_DISPLAY, size);
+ final Display display = context.getDisplay();
+ final int displayId = display != null
+ ? display.getDisplayId()
+ : Display.DEFAULT_DISPLAY;
+ wm.getInitialDisplaySize(displayId, size);
return size.x < size.y ?
Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE;
} catch (RemoteException e) {
diff --git a/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java
index 95534e264b80..621d5a6e5c55 100644
--- a/core/java/com/android/server/BootReceiver.java
+++ b/core/java/com/android/server/BootReceiver.java
@@ -258,13 +258,13 @@ public class BootReceiver extends BroadcastReceiver {
// Start watching for new tombstone files; will record them as they occur.
// This gets registered with the singleton file observer thread.
- sTombstoneObserver = new FileObserver(TOMBSTONE_DIR.getPath(), FileObserver.CLOSE_WRITE) {
+ sTombstoneObserver = new FileObserver(TOMBSTONE_DIR.getPath(), FileObserver.CREATE) {
@Override
public void onEvent(int event, String path) {
HashMap<String, Long> timestamps = readTimestamps();
try {
File file = new File(TOMBSTONE_DIR, path);
- if (file.isFile()) {
+ if (file.isFile() && file.getName().startsWith("tombstone_")) {
addFileToDropBox(db, timestamps, headers, file.getPath(), LOG_SIZE,
TAG_TOMBSTONE);
}
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 2de53b97312a..710fe3d6e3e9 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -288,10 +288,11 @@ message WindowStateProto {
optional int32 stack_id = 4;
optional .android.view.WindowLayoutParamsProto attributes = 5;
optional .android.graphics.RectProto given_content_insets = 6;
- optional .android.graphics.RectProto frame = 7;
- optional .android.graphics.RectProto containing_frame = 8;
- optional .android.graphics.RectProto parent_frame = 9;
- optional .android.graphics.RectProto content_frame = 10;
+ reserved 7 to 10;
+// optional .android.graphics.RectProto frame = 7;
+// optional .android.graphics.RectProto containing_frame = 8;
+// optional .android.graphics.RectProto parent_frame = 9;
+// optional .android.graphics.RectProto content_frame = 10;
optional .android.graphics.RectProto content_insets = 11;
optional .android.graphics.RectProto surface_insets = 12;
optional WindowStateAnimatorProto animator = 13;
@@ -304,11 +305,12 @@ message WindowStateProto {
optional int32 system_ui_visibility = 21;
optional bool has_surface = 22;
optional bool is_ready_for_display = 23;
- optional .android.graphics.RectProto display_frame = 24;
- optional .android.graphics.RectProto overscan_frame = 25;
- optional .android.graphics.RectProto visible_frame = 26;
- optional .android.graphics.RectProto decor_frame = 27;
- optional .android.graphics.RectProto outset_frame = 28;
+ reserved 24 to 28;
+// optional .android.graphics.RectProto display_frame = 24;
+// optional .android.graphics.RectProto overscan_frame = 25;
+// optional .android.graphics.RectProto visible_frame = 26;
+// optional .android.graphics.RectProto decor_frame = 27;
+// optional .android.graphics.RectProto outset_frame = 28;
optional .android.graphics.RectProto overscan_insets = 29;
optional .android.graphics.RectProto visible_insets = 30;
optional .android.graphics.RectProto stable_insets = 31;
@@ -321,6 +323,7 @@ message WindowStateProto {
optional bool is_visible = 38;
optional bool pending_forced_seamless_rotation = 39;
optional int64 finished_forced_seamless_rotation_frame = 40;
+ optional WindowFramesProto window_frames = 41;
}
message IdentifierProto {
@@ -382,3 +385,18 @@ message ConfigurationContainerProto {
optional .android.content.ConfigurationProto full_configuration = 2;
optional .android.content.ConfigurationProto merged_override_configuration = 3;
}
+
+/* represents WindowFrames */
+message WindowFramesProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional .android.graphics.RectProto containing_frame = 1;
+ optional .android.graphics.RectProto content_frame = 2;
+ optional .android.graphics.RectProto decor_frame = 3;
+ optional .android.graphics.RectProto display_frame = 4;
+ optional .android.graphics.RectProto frame = 5;
+ optional .android.graphics.RectProto outset_frame = 6;
+ optional .android.graphics.RectProto overscan_frame = 7;
+ optional .android.graphics.RectProto parent_frame = 8;
+ optional .android.graphics.RectProto visible_frame = 9;
+}
diff --git a/core/res/res/values/styles_package_installer.xml b/core/res/res/values/styles_package_installer.xml
index 8bfcc8dcde76..2ccdd8752443 100644
--- a/core/res/res/values/styles_package_installer.xml
+++ b/core/res/res/values/styles_package_installer.xml
@@ -21,6 +21,7 @@
<item name="background">?attr/windowBackground</item>
<item name="elevation">?attr/windowElevation</item>
<item name="layout_weight">@dimen/permissionGrantDialogWidth</item>
+ <item name="layout_width">320dp</item>
</style>
<style name="PermissionGrantTitleIcon">
diff --git a/core/tests/HdmiCec/Android.mk b/core/tests/HdmiCec/Android.mk
deleted file mode 100644
index 450068b18dcb..000000000000
--- a/core/tests/HdmiCec/Android.mk
+++ /dev/null
@@ -1,30 +0,0 @@
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-# Include all test java files
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_MODULE_TAGS := tests
-LOCAL_PACKAGE_NAME := HdmiCecTests
-
-LOCAL_SDK_VERSION := current
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
-LOCAL_CERTIFICATE := platform
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-include $(BUILD_PACKAGE)
diff --git a/core/tests/HdmiCec/AndroidManifest.xml b/core/tests/HdmiCec/AndroidManifest.xml
deleted file mode 100644
index 80129d0d9fba..000000000000
--- a/core/tests/HdmiCec/AndroidManifest.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.test.example.helloworld"
- android:sharedUserId="android.uid.system" >
- <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="21" />
- <application>
- <uses-library android:name="android.test.runner" />
- </application>
- <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
- android:targetPackage="android.test.example.helloworld"
- android:label="Hello World Test"/>
-</manifest>
diff --git a/core/tests/HdmiCec/AndroidTest.xml b/core/tests/HdmiCec/AndroidTest.xml
deleted file mode 100644
index 5af30fbd92ed..000000000000
--- a/core/tests/HdmiCec/AndroidTest.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<configuration description="Runs sample instrumentation test.">
- <option name="test-suite-tag" value="apct"/>
- <option name="test-suite-tag" value="apct-instrumentation"/>
- <target_preparer class="com.android.tradefed.targetprep.TestFilePushSetup"/>
- <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
- <option name="test-file-name" value="HelloWorldTests.apk"/>
- </target_preparer>
- <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"/>
- <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"/>
- <option name="test-suite-tag" value="apct"/>
- <option name="test-tag" value="SampleInstrumentationTest"/>
- <test class="com.android.tradefed.testtype.AndroidJUnitTest">
- <option name="package" value="android.test.example.helloworld"/>
- <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/>
- </test>
-</configuration>
diff --git a/core/tests/HdmiCec/HelloWorldTests_HalloWelt.config b/core/tests/HdmiCec/HelloWorldTests_HalloWelt.config
deleted file mode 100644
index 902699beafe1..000000000000
--- a/core/tests/HdmiCec/HelloWorldTests_HalloWelt.config
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<configuration description="Runs only the HalloWelt test.">
- <target_preparer class="com.android.tradefed.targetprep.TestFilePushSetup" />
- <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
- <option name="test-file-name" value="HelloWorldTests.apk" />
- </target_preparer>
- <option name="test-suite-tag" value="apct" />
- <option name="test-tag" value="SampleInstrumentationTest" />
- <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
- <option name="package" value="android.test.example.helloworld" />
- <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
- <option name="class" value="android.test.example.helloworld.HelloWorldTest" />
- <option name="method" value="testHalloWelt" />
- </test>
-</configuration>
diff --git a/core/tests/HdmiCec/OWNERS b/core/tests/HdmiCec/OWNERS
deleted file mode 100644
index cc016f1cb565..000000000000
--- a/core/tests/HdmiCec/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-amyjojo@google.com
-nchalko@google.com
-shubang@google.com \ No newline at end of file
diff --git a/core/tests/HdmiCec/src/android/test/example/helloworld/HelloWorldTest.java b/core/tests/HdmiCec/src/android/test/example/helloworld/HelloWorldTest.java
deleted file mode 100644
index 09321adfa2cb..000000000000
--- a/core/tests/HdmiCec/src/android/test/example/helloworld/HelloWorldTest.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.test.example.helloworld;
-
-import android.support.test.filters.SmallTest;
-import android.util.Log;
-import org.junit.After;
-import org.junit.AfterClass;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public class HelloWorldTest {
- private static final String TAG = HelloWorldTest.class.getSimpleName();
-
- @BeforeClass
- public static void beforeClass() {
- Log.d(TAG, "beforeClass()");
- }
-
- @AfterClass
- public static void afterClass() {
- Log.d(TAG, "afterClass()");
- }
-
- @Before
- public void before() {
- Log.d(TAG, "before()");
- }
-
- @After
- public void after() {
- Log.d(TAG, "after()");
- }
-
- @Test
- @SmallTest
- public void testHelloWorld() {
- Log.d(TAG, "testHelloWorld()");
- Assert.assertNotEquals("Hello", "world");
- }
-
- @Test
- @SmallTest
- public void testHalloWelt() {
- Log.d(TAG, "testHalloWelt()");
- Assert.assertNotEquals("Hallo", "Welt");
- }
-}
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 0f019e711954..2a906ae158de 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -1401,6 +1401,14 @@
<service android:name="android.content.CrossUserContentService"
android:exported="true" />
+ <activity android:name="android.app.assist.EmptyLayoutActivity"
+ android:label="My Title">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+ </intent-filter>
+ </activity>
+
</application>
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/ascii.ttc b/core/tests/coretests/assets/fonts_for_family_selection/ascii.ttc
new file mode 100644
index 000000000000..e404f2b5de1f
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/ascii.ttc
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/ascii_vf.ttf b/core/tests/coretests/assets/fonts_for_family_selection/ascii_vf.ttf
new file mode 100644
index 000000000000..792b7d7a7563
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/ascii_vf.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/ascii_vf.ttx b/core/tests/coretests/assets/fonts_for_family_selection/ascii_vf.ttx
new file mode 100644
index 000000000000..22b29410927c
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/ascii_vf.ttx
@@ -0,0 +1,1942 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="a"/>
+ <GlyphID id="2" name="b"/>
+ <GlyphID id="3" name="c"/>
+ <GlyphID id="4" name="d"/>
+ <GlyphID id="5" name="e"/>
+ <GlyphID id="6" name="f"/>
+ <GlyphID id="7" name="g"/>
+ <GlyphID id="8" name="h"/>
+ <GlyphID id="9" name="i"/>
+ <GlyphID id="10" name="j"/>
+ <GlyphID id="11" name="k"/>
+ <GlyphID id="12" name="l"/>
+ <GlyphID id="13" name="m"/>
+ <GlyphID id="14" name="n"/>
+ <GlyphID id="15" name="o"/>
+ <GlyphID id="16" name="p"/>
+ <GlyphID id="17" name="q"/>
+ <GlyphID id="18" name="r"/>
+ <GlyphID id="19" name="s"/>
+ <GlyphID id="20" name="t"/>
+ <GlyphID id="21" name="u"/>
+ <GlyphID id="22" name="v"/>
+ <GlyphID id="23" name="w"/>
+ <GlyphID id="24" name="x"/>
+ <GlyphID id="25" name="y"/>
+ <GlyphID id="26" name="z"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="50"/>
+ <created value="Thu Feb 22 10:04:28 2018"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="100"/>
+ <descent value="0"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="100"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000000"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="50" lsb="93"/>
+ <mtx name="a" width="50" lsb="0"/>
+ <mtx name="b" width="50" lsb="0"/>
+ <mtx name="c" width="50" lsb="0"/>
+ <mtx name="d" width="50" lsb="0"/>
+ <mtx name="e" width="50" lsb="0"/>
+ <mtx name="f" width="50" lsb="0"/>
+ <mtx name="g" width="50" lsb="0"/>
+ <mtx name="h" width="50" lsb="0"/>
+ <mtx name="i" width="50" lsb="0"/>
+ <mtx name="j" width="50" lsb="0"/>
+ <mtx name="k" width="50" lsb="0"/>
+ <mtx name="l" width="50" lsb="0"/>
+ <mtx name="m" width="50" lsb="0"/>
+ <mtx name="n" width="50" lsb="0"/>
+ <mtx name="o" width="50" lsb="0"/>
+ <mtx name="p" width="50" lsb="0"/>
+ <mtx name="q" width="50" lsb="0"/>
+ <mtx name="r" width="50" lsb="0"/>
+ <mtx name="s" width="50" lsb="0"/>
+ <mtx name="t" width="50" lsb="0"/>
+ <mtx name="u" width="50" lsb="0"/>
+ <mtx name="v" width="50" lsb="0"/>
+ <mtx name="w" width="50" lsb="0"/>
+ <mtx name="x" width="50" lsb="0"/>
+ <mtx name="y" width="50" lsb="0"/>
+ <mtx name="z" width="50" lsb="0"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="0" platEncID="3" language="0">
+ <map code="0x0061" name="a" /> <!-- a -->
+ <map code="0x0062" name="b" /> <!-- b -->
+ <map code="0x0063" name="c" /> <!-- c -->
+ <map code="0x0064" name="d" /> <!-- d -->
+ <map code="0x0065" name="e" /> <!-- e -->
+ <map code="0x0066" name="f" /> <!-- f -->
+ <map code="0x0067" name="g" /> <!-- g -->
+ <map code="0x0068" name="h" /> <!-- h -->
+ <map code="0x0069" name="i" /> <!-- i -->
+ <map code="0x006A" name="j" /> <!-- j -->
+ <map code="0x006B" name="k" /> <!-- k -->
+ <map code="0x006C" name="l" /> <!-- l -->
+ <map code="0x006D" name="m" /> <!-- m -->
+ <map code="0x006E" name="n" /> <!-- n -->
+ <map code="0x006F" name="o" /> <!-- o -->
+ <map code="0x0070" name="p" /> <!-- p -->
+ <map code="0x0071" name="q" /> <!-- q -->
+ <map code="0x0072" name="r" /> <!-- r -->
+ <map code="0x0073" name="s" /> <!-- s -->
+ <map code="0x0074" name="t" /> <!-- t -->
+ <map code="0x0075" name="u" /> <!-- u -->
+ <map code="0x0076" name="v" /> <!-- v -->
+ <map code="0x0077" name="w" /> <!-- w -->
+ <map code="0x0078" name="x" /> <!-- x -->
+ <map code="0x0079" name="y" /> <!-- y -->
+ <map code="0x007A" name="z" /> <!-- z -->
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="a" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="b" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="c" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="d" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="e" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="f" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="g" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="h" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="i" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="j" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="k" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="l" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="m" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="n" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="o" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="p" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="q" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="r" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="s" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="t" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="u" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="v" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="w" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="x" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="y" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ <TTGlyph name="z" xMin="0" yMin="0" xMax="50" yMax="50">
+ <contour>
+ <pt x="0" y="0" on="1"/>
+ <pt x="50" y="25" on="1"/>
+ <pt x="0" y="50" on="1"/>
+ </contour>
+ <instructions />
+ </TTGlyph>
+ </glyf>
+
+ <fvar>
+ <Axis>
+ <!-- Weight axis but no effects to the glyph. -->
+ <AxisTag>wght</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>400.0</DefaultValue>
+ <MaxValue>1000.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <!-- Italic axis but no effects to the glyph. -->
+ <AxisTag>ital</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Asca</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Ascb</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Ascc</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Ascd</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Asce</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Ascf</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Ascg</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Asch</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Asci</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Ascj</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Asck</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Ascl</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Ascm</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Ascn</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Asco</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Ascp</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Ascq</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Ascr</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Ascs</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Asct</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Ascu</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Ascv</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Ascw</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Ascx</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Ascy</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ <Axis>
+ <AxisTag>Ascz</AxisTag>
+ <Flags>0x0</Flags>
+ <MinValue>0.0</MinValue>
+ <DefaultValue>0.0</DefaultValue>
+ <MaxValue>1.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ </fvar>
+
+ <HVAR>
+ <Version value="0x00010000"/>
+ <VarStore Format="1">
+ <Format value="1" />
+ <VarRegionList>
+ <Region index="0">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="1">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="2">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="3">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="4">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="5">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="6">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="7">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="8">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="9">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="10">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="11">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="12">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="13">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="14">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="15">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="16">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="17">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="18">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="19">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="20">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="21">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="22">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="23">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="24">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="25">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ <Region index="26">
+ <VarRegionAxis index="0"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="1"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="2"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="3"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="4"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="5"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="6"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="7"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="8"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="9"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="10"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="11"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="12"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="13"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="14"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="15"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="16"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="17"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="18"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="19"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="20"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="21"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="22"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="23"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="24"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="25"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="26"><StartCoord value="0" /><PeakCoord value="0" /><EndCoord value="1" /></VarRegionAxis>
+ <VarRegionAxis index="27"><StartCoord value="1" /><PeakCoord value="1" /><EndCoord value="1" /></VarRegionAxis>
+ </Region>
+ </VarRegionList>
+ <VarData index="0">
+ <NumShorts value="0" />
+ <VarRegionIndex index="0" value="0" />
+ <VarRegionIndex index="1" value="1" />
+ <VarRegionIndex index="2" value="2" />
+ <VarRegionIndex index="3" value="3" />
+ <VarRegionIndex index="4" value="4" />
+ <VarRegionIndex index="5" value="5" />
+ <VarRegionIndex index="6" value="6" />
+ <VarRegionIndex index="7" value="7" />
+ <VarRegionIndex index="8" value="8" />
+ <VarRegionIndex index="9" value="9" />
+ <VarRegionIndex index="10" value="10" />
+ <VarRegionIndex index="11" value="11" />
+ <VarRegionIndex index="12" value="12" />
+ <VarRegionIndex index="13" value="13" />
+ <VarRegionIndex index="14" value="14" />
+ <VarRegionIndex index="15" value="15" />
+ <VarRegionIndex index="16" value="16" />
+ <VarRegionIndex index="17" value="17" />
+ <VarRegionIndex index="18" value="18" />
+ <VarRegionIndex index="19" value="19" />
+ <VarRegionIndex index="20" value="20" />
+ <VarRegionIndex index="21" value="21" />
+ <VarRegionIndex index="22" value="22" />
+ <VarRegionIndex index="23" value="23" />
+ <VarRegionIndex index="24" value="24" />
+ <VarRegionIndex index="25" value="25" />
+ <VarRegionIndex index="26" value="26" />
+ <Item index="0" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+ <Item index="1" value="[0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+ <Item index="2" value="[0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+ <Item index="3" value="[0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+ <Item index="4" value="[0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+ <Item index="5" value="[0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+ <Item index="6" value="[0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+ <Item index="7" value="[0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+ <Item index="8" value="[0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+ <Item index="9" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+ <Item index="10" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+ <Item index="11" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+ <Item index="12" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+ <Item index="13" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+ <Item index="14" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+ <Item index="15" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+ <Item index="16" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+ <Item index="17" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0]" />
+ <Item index="18" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0]" />
+ <Item index="19" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0]" />
+ <Item index="20" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0]" />
+ <Item index="21" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0]" />
+ <Item index="22" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0]" />
+ <Item index="23" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0]" />
+ <Item index="24" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0]" />
+ <Item index="25" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0]" />
+ <Item index="26" value="[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100]" />
+ </VarData>
+ </VarStore>
+ <AdvWidthMap>
+ <Map index="0" outer="0" inner="0" />
+ <Map index="1" outer="0" inner="1" />
+ <Map index="2" outer="0" inner="2" />
+ <Map index="3" outer="0" inner="3" />
+ <Map index="4" outer="0" inner="4" />
+ <Map index="5" outer="0" inner="5" />
+ <Map index="6" outer="0" inner="6" />
+ <Map index="7" outer="0" inner="7" />
+ <Map index="8" outer="0" inner="8" />
+ <Map index="9" outer="0" inner="9" />
+ <Map index="10" outer="0" inner="10" />
+ <Map index="11" outer="0" inner="11" />
+ <Map index="12" outer="0" inner="12" />
+ <Map index="13" outer="0" inner="13" />
+ <Map index="14" outer="0" inner="14" />
+ <Map index="15" outer="0" inner="15" />
+ <Map index="16" outer="0" inner="16" />
+ <Map index="17" outer="0" inner="17" />
+ <Map index="18" outer="0" inner="18" />
+ <Map index="19" outer="0" inner="19" />
+ <Map index="20" outer="0" inner="20" />
+ <Map index="21" outer="0" inner="21" />
+ <Map index="22" outer="0" inner="22" />
+ <Map index="23" outer="0" inner="23" />
+ <Map index="24" outer="0" inner="24" />
+ <Map index="25" outer="0" inner="25" />
+ <Map index="26" outer="0" inner="26" />
+ </AdvWidthMap>
+ </HVAR>
+
+ <gvar>
+ <version value="1" />
+ <reserved value="0" />
+ <glyphVariations glyph="a">
+ <tuple>
+ <coord axis="Asca" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="b">
+ <tuple>
+ <coord axis="Ascb" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="c">
+ <tuple>
+ <coord axis="Ascc" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="d">
+ <tuple>
+ <coord axis="Ascd" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="e">
+ <tuple>
+ <coord axis="Asce" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="f">
+ <tuple>
+ <coord axis="Ascf" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="g">
+ <tuple>
+ <coord axis="Ascg" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="h">
+ <tuple>
+ <coord axis="Asch" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="i">
+ <tuple>
+ <coord axis="Asci" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="j">
+ <tuple>
+ <coord axis="Ascj" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="k">
+ <tuple>
+ <coord axis="Asck" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="l">
+ <tuple>
+ <coord axis="Ascl" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="m">
+ <tuple>
+ <coord axis="Ascm" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="n">
+ <tuple>
+ <coord axis="Ascn" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="o">
+ <tuple>
+ <coord axis="Asco" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="p">
+ <tuple>
+ <coord axis="Ascp" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="q">
+ <tuple>
+ <coord axis="Ascq" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="r">
+ <tuple>
+ <coord axis="Ascr" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="s">
+ <tuple>
+ <coord axis="Ascs" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="t">
+ <tuple>
+ <coord axis="Asct" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="u">
+ <tuple>
+ <coord axis="Ascu" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="v">
+ <tuple>
+ <coord axis="Ascv" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="w">
+ <tuple>
+ <coord axis="Ascw" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="x">
+ <tuple>
+ <coord axis="Ascx" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="y">
+ <tuple>
+ <coord axis="Ascy" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ <glyphVariations glyph="z">
+ <tuple>
+ <coord axis="Ascz" value="1.0" />
+ <delta pt="0" x="0" y="0" />
+ <delta pt="1" x="100" y="0" />
+ <delta pt="2" x="0" y="0" />
+ <!-- deltas for phantom points -->
+ <delta pt="3" x="0" y="0" /> <!-- (left, 0) -->
+ <delta pt="4" x="100" y="0" /> <!-- (right, 0) -->
+ <delta pt="5" x="0" y="0" /> <!-- (0, top) -->
+ <delta pt="6" x="0" y="0" /> <!-- (0, bottom) -->
+ </tuple>
+ </glyphVariations>
+ </gvar>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2018 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ 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.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ <namerecord nameID="256" platformID="3" platEncID="1" langID="0x409">
+ 3 em signal
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_a3em_weight100_upright.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_a3em_weight100_upright.ttf
new file mode 100644
index 000000000000..f220eb36b0d3
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_a3em_weight100_upright.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_a3em_weight100_upright.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_a3em_weight100_upright.ttx
new file mode 100644
index 000000000000..ebedcb627a5c
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_a3em_weight100_upright.ttx
@@ -0,0 +1,208 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="1em"/>
+ <GlyphID id="2" name="3em"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Thu Feb 15 18:29:10 2018"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="100"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000000"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="1em" width="1000" lsb="93"/>
+ <mtx name="3em" width="3000" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="3em" /> <!-- a -->
+ <map code="0x0062" name="1em" /> <!-- b -->
+ <map code="0x0063" name="1em" /> <!-- c -->
+ <map code="0x0064" name="1em" /> <!-- d -->
+ <map code="0x0065" name="1em" /> <!-- e -->
+ <map code="0x0066" name="1em" /> <!-- f -->
+ <map code="0x0067" name="1em" /> <!-- g -->
+ <map code="0x0068" name="1em" /> <!-- h -->
+ <map code="0x0069" name="1em" /> <!-- i -->
+ <map code="0x006A" name="1em" /> <!-- j -->
+ <map code="0x006B" name="1em" /> <!-- k -->
+ <map code="0x006C" name="1em" /> <!-- l -->
+ <map code="0x006D" name="1em" /> <!-- m -->
+ <map code="0x006E" name="1em" /> <!-- n -->
+ <map code="0x006F" name="1em" /> <!-- o -->
+ <map code="0x0070" name="1em" /> <!-- p -->
+ <map code="0x0071" name="1em" /> <!-- q -->
+ <map code="0x0072" name="1em" /> <!-- r -->
+ <map code="0x0073" name="1em" /> <!-- s -->
+ <map code="0x0074" name="1em" /> <!-- t -->
+ <map code="0x0075" name="1em" /> <!-- u -->
+ <map code="0x0076" name="1em" /> <!-- v -->
+ <map code="0x0077" name="1em" /> <!-- w -->
+ <map code="0x0078" name="1em" /> <!-- x -->
+ <map code="0x0079" name="1em" /> <!-- y -->
+ <map code="0x007A" name="1em" /> <!-- z -->
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2018 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ 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.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_b3em_weight100_italic.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_b3em_weight100_italic.ttf
new file mode 100644
index 000000000000..b9ffb84a2fed
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_b3em_weight100_italic.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_b3em_weight100_italic.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_b3em_weight100_italic.ttx
new file mode 100644
index 000000000000..def6a29a3686
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_b3em_weight100_italic.ttx
@@ -0,0 +1,208 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="1em"/>
+ <GlyphID id="2" name="3em"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Thu Feb 15 18:29:11 2018"/>
+ <macStyle value="00000000 00000010"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="100"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 00000001"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="1em" width="1000" lsb="93"/>
+ <mtx name="3em" width="3000" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="1em" /> <!-- a -->
+ <map code="0x0062" name="3em" /> <!-- b -->
+ <map code="0x0063" name="1em" /> <!-- c -->
+ <map code="0x0064" name="1em" /> <!-- d -->
+ <map code="0x0065" name="1em" /> <!-- e -->
+ <map code="0x0066" name="1em" /> <!-- f -->
+ <map code="0x0067" name="1em" /> <!-- g -->
+ <map code="0x0068" name="1em" /> <!-- h -->
+ <map code="0x0069" name="1em" /> <!-- i -->
+ <map code="0x006A" name="1em" /> <!-- j -->
+ <map code="0x006B" name="1em" /> <!-- k -->
+ <map code="0x006C" name="1em" /> <!-- l -->
+ <map code="0x006D" name="1em" /> <!-- m -->
+ <map code="0x006E" name="1em" /> <!-- n -->
+ <map code="0x006F" name="1em" /> <!-- o -->
+ <map code="0x0070" name="1em" /> <!-- p -->
+ <map code="0x0071" name="1em" /> <!-- q -->
+ <map code="0x0072" name="1em" /> <!-- r -->
+ <map code="0x0073" name="1em" /> <!-- s -->
+ <map code="0x0074" name="1em" /> <!-- t -->
+ <map code="0x0075" name="1em" /> <!-- u -->
+ <map code="0x0076" name="1em" /> <!-- v -->
+ <map code="0x0077" name="1em" /> <!-- w -->
+ <map code="0x0078" name="1em" /> <!-- x -->
+ <map code="0x0079" name="1em" /> <!-- y -->
+ <map code="0x007A" name="1em" /> <!-- z -->
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2018 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ 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.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_c3em_weight200_upright.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_c3em_weight200_upright.ttf
new file mode 100644
index 000000000000..075b068370f1
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_c3em_weight200_upright.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_c3em_weight200_upright.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_c3em_weight200_upright.ttx
new file mode 100644
index 000000000000..d20118383d30
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_c3em_weight200_upright.ttx
@@ -0,0 +1,208 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="1em"/>
+ <GlyphID id="2" name="3em"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Thu Feb 15 18:29:11 2018"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="200"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000000"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="1em" width="1000" lsb="93"/>
+ <mtx name="3em" width="3000" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="1em" /> <!-- a -->
+ <map code="0x0062" name="1em" /> <!-- b -->
+ <map code="0x0063" name="3em" /> <!-- c -->
+ <map code="0x0064" name="1em" /> <!-- d -->
+ <map code="0x0065" name="1em" /> <!-- e -->
+ <map code="0x0066" name="1em" /> <!-- f -->
+ <map code="0x0067" name="1em" /> <!-- g -->
+ <map code="0x0068" name="1em" /> <!-- h -->
+ <map code="0x0069" name="1em" /> <!-- i -->
+ <map code="0x006A" name="1em" /> <!-- j -->
+ <map code="0x006B" name="1em" /> <!-- k -->
+ <map code="0x006C" name="1em" /> <!-- l -->
+ <map code="0x006D" name="1em" /> <!-- m -->
+ <map code="0x006E" name="1em" /> <!-- n -->
+ <map code="0x006F" name="1em" /> <!-- o -->
+ <map code="0x0070" name="1em" /> <!-- p -->
+ <map code="0x0071" name="1em" /> <!-- q -->
+ <map code="0x0072" name="1em" /> <!-- r -->
+ <map code="0x0073" name="1em" /> <!-- s -->
+ <map code="0x0074" name="1em" /> <!-- t -->
+ <map code="0x0075" name="1em" /> <!-- u -->
+ <map code="0x0076" name="1em" /> <!-- v -->
+ <map code="0x0077" name="1em" /> <!-- w -->
+ <map code="0x0078" name="1em" /> <!-- x -->
+ <map code="0x0079" name="1em" /> <!-- y -->
+ <map code="0x007A" name="1em" /> <!-- z -->
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2018 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ 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.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_d3em_weight200_italic.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_d3em_weight200_italic.ttf
new file mode 100644
index 000000000000..5b47f0d0069d
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_d3em_weight200_italic.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_d3em_weight200_italic.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_d3em_weight200_italic.ttx
new file mode 100644
index 000000000000..7c801a0a280e
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_d3em_weight200_italic.ttx
@@ -0,0 +1,208 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="1em"/>
+ <GlyphID id="2" name="3em"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Thu Feb 15 18:29:11 2018"/>
+ <macStyle value="00000000 00000010"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="200"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 00000001"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="1em" width="1000" lsb="93"/>
+ <mtx name="3em" width="3000" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="1em" /> <!-- a -->
+ <map code="0x0062" name="1em" /> <!-- b -->
+ <map code="0x0063" name="1em" /> <!-- c -->
+ <map code="0x0064" name="3em" /> <!-- d -->
+ <map code="0x0065" name="1em" /> <!-- e -->
+ <map code="0x0066" name="1em" /> <!-- f -->
+ <map code="0x0067" name="1em" /> <!-- g -->
+ <map code="0x0068" name="1em" /> <!-- h -->
+ <map code="0x0069" name="1em" /> <!-- i -->
+ <map code="0x006A" name="1em" /> <!-- j -->
+ <map code="0x006B" name="1em" /> <!-- k -->
+ <map code="0x006C" name="1em" /> <!-- l -->
+ <map code="0x006D" name="1em" /> <!-- m -->
+ <map code="0x006E" name="1em" /> <!-- n -->
+ <map code="0x006F" name="1em" /> <!-- o -->
+ <map code="0x0070" name="1em" /> <!-- p -->
+ <map code="0x0071" name="1em" /> <!-- q -->
+ <map code="0x0072" name="1em" /> <!-- r -->
+ <map code="0x0073" name="1em" /> <!-- s -->
+ <map code="0x0074" name="1em" /> <!-- t -->
+ <map code="0x0075" name="1em" /> <!-- u -->
+ <map code="0x0076" name="1em" /> <!-- v -->
+ <map code="0x0077" name="1em" /> <!-- w -->
+ <map code="0x0078" name="1em" /> <!-- x -->
+ <map code="0x0079" name="1em" /> <!-- y -->
+ <map code="0x007A" name="1em" /> <!-- z -->
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2018 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ 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.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_e3em_weight300_upright.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_e3em_weight300_upright.ttf
new file mode 100644
index 000000000000..3e9133b2ee1a
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_e3em_weight300_upright.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_e3em_weight300_upright.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_e3em_weight300_upright.ttx
new file mode 100644
index 000000000000..acb20066a2d6
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_e3em_weight300_upright.ttx
@@ -0,0 +1,208 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="1em"/>
+ <GlyphID id="2" name="3em"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Thu Feb 15 18:29:11 2018"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="300"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000000"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="1em" width="1000" lsb="93"/>
+ <mtx name="3em" width="3000" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="1em" /> <!-- a -->
+ <map code="0x0062" name="1em" /> <!-- b -->
+ <map code="0x0063" name="1em" /> <!-- c -->
+ <map code="0x0064" name="1em" /> <!-- d -->
+ <map code="0x0065" name="3em" /> <!-- e -->
+ <map code="0x0066" name="1em" /> <!-- f -->
+ <map code="0x0067" name="1em" /> <!-- g -->
+ <map code="0x0068" name="1em" /> <!-- h -->
+ <map code="0x0069" name="1em" /> <!-- i -->
+ <map code="0x006A" name="1em" /> <!-- j -->
+ <map code="0x006B" name="1em" /> <!-- k -->
+ <map code="0x006C" name="1em" /> <!-- l -->
+ <map code="0x006D" name="1em" /> <!-- m -->
+ <map code="0x006E" name="1em" /> <!-- n -->
+ <map code="0x006F" name="1em" /> <!-- o -->
+ <map code="0x0070" name="1em" /> <!-- p -->
+ <map code="0x0071" name="1em" /> <!-- q -->
+ <map code="0x0072" name="1em" /> <!-- r -->
+ <map code="0x0073" name="1em" /> <!-- s -->
+ <map code="0x0074" name="1em" /> <!-- t -->
+ <map code="0x0075" name="1em" /> <!-- u -->
+ <map code="0x0076" name="1em" /> <!-- v -->
+ <map code="0x0077" name="1em" /> <!-- w -->
+ <map code="0x0078" name="1em" /> <!-- x -->
+ <map code="0x0079" name="1em" /> <!-- y -->
+ <map code="0x007A" name="1em" /> <!-- z -->
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2018 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ 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.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_f3em_weight300_italic.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_f3em_weight300_italic.ttf
new file mode 100644
index 000000000000..3ba3febaf477
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_f3em_weight300_italic.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_f3em_weight300_italic.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_f3em_weight300_italic.ttx
new file mode 100644
index 000000000000..452ff66e599e
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_f3em_weight300_italic.ttx
@@ -0,0 +1,208 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="1em"/>
+ <GlyphID id="2" name="3em"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Thu Feb 15 18:29:11 2018"/>
+ <macStyle value="00000000 00000010"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="300"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 00000001"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="1em" width="1000" lsb="93"/>
+ <mtx name="3em" width="3000" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="1em" /> <!-- a -->
+ <map code="0x0062" name="1em" /> <!-- b -->
+ <map code="0x0063" name="1em" /> <!-- c -->
+ <map code="0x0064" name="1em" /> <!-- d -->
+ <map code="0x0065" name="1em" /> <!-- e -->
+ <map code="0x0066" name="3em" /> <!-- f -->
+ <map code="0x0067" name="1em" /> <!-- g -->
+ <map code="0x0068" name="1em" /> <!-- h -->
+ <map code="0x0069" name="1em" /> <!-- i -->
+ <map code="0x006A" name="1em" /> <!-- j -->
+ <map code="0x006B" name="1em" /> <!-- k -->
+ <map code="0x006C" name="1em" /> <!-- l -->
+ <map code="0x006D" name="1em" /> <!-- m -->
+ <map code="0x006E" name="1em" /> <!-- n -->
+ <map code="0x006F" name="1em" /> <!-- o -->
+ <map code="0x0070" name="1em" /> <!-- p -->
+ <map code="0x0071" name="1em" /> <!-- q -->
+ <map code="0x0072" name="1em" /> <!-- r -->
+ <map code="0x0073" name="1em" /> <!-- s -->
+ <map code="0x0074" name="1em" /> <!-- t -->
+ <map code="0x0075" name="1em" /> <!-- u -->
+ <map code="0x0076" name="1em" /> <!-- v -->
+ <map code="0x0077" name="1em" /> <!-- w -->
+ <map code="0x0078" name="1em" /> <!-- x -->
+ <map code="0x0079" name="1em" /> <!-- y -->
+ <map code="0x007A" name="1em" /> <!-- z -->
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2018 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ 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.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_g3em_weight400_upright.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_g3em_weight400_upright.ttf
new file mode 100644
index 000000000000..b3175a108283
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_g3em_weight400_upright.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_g3em_weight400_upright.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_g3em_weight400_upright.ttx
new file mode 100644
index 000000000000..34a638fcba17
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_g3em_weight400_upright.ttx
@@ -0,0 +1,208 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="1em"/>
+ <GlyphID id="2" name="3em"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Thu Feb 15 18:29:11 2018"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="400"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000000"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="1em" width="1000" lsb="93"/>
+ <mtx name="3em" width="3000" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="1em" /> <!-- a -->
+ <map code="0x0062" name="1em" /> <!-- b -->
+ <map code="0x0063" name="1em" /> <!-- c -->
+ <map code="0x0064" name="1em" /> <!-- d -->
+ <map code="0x0065" name="1em" /> <!-- e -->
+ <map code="0x0066" name="1em" /> <!-- f -->
+ <map code="0x0067" name="3em" /> <!-- g -->
+ <map code="0x0068" name="1em" /> <!-- h -->
+ <map code="0x0069" name="1em" /> <!-- i -->
+ <map code="0x006A" name="1em" /> <!-- j -->
+ <map code="0x006B" name="1em" /> <!-- k -->
+ <map code="0x006C" name="1em" /> <!-- l -->
+ <map code="0x006D" name="1em" /> <!-- m -->
+ <map code="0x006E" name="1em" /> <!-- n -->
+ <map code="0x006F" name="1em" /> <!-- o -->
+ <map code="0x0070" name="1em" /> <!-- p -->
+ <map code="0x0071" name="1em" /> <!-- q -->
+ <map code="0x0072" name="1em" /> <!-- r -->
+ <map code="0x0073" name="1em" /> <!-- s -->
+ <map code="0x0074" name="1em" /> <!-- t -->
+ <map code="0x0075" name="1em" /> <!-- u -->
+ <map code="0x0076" name="1em" /> <!-- v -->
+ <map code="0x0077" name="1em" /> <!-- w -->
+ <map code="0x0078" name="1em" /> <!-- x -->
+ <map code="0x0079" name="1em" /> <!-- y -->
+ <map code="0x007A" name="1em" /> <!-- z -->
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2018 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ 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.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_h3em_weight400_italic.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_h3em_weight400_italic.ttf
new file mode 100644
index 000000000000..099c4f146eee
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_h3em_weight400_italic.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_h3em_weight400_italic.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_h3em_weight400_italic.ttx
new file mode 100644
index 000000000000..b18af663c3be
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_h3em_weight400_italic.ttx
@@ -0,0 +1,208 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="1em"/>
+ <GlyphID id="2" name="3em"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Thu Feb 15 18:29:12 2018"/>
+ <macStyle value="00000000 00000010"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="400"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 00000001"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="1em" width="1000" lsb="93"/>
+ <mtx name="3em" width="3000" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="1em" /> <!-- a -->
+ <map code="0x0062" name="1em" /> <!-- b -->
+ <map code="0x0063" name="1em" /> <!-- c -->
+ <map code="0x0064" name="1em" /> <!-- d -->
+ <map code="0x0065" name="1em" /> <!-- e -->
+ <map code="0x0066" name="1em" /> <!-- f -->
+ <map code="0x0067" name="1em" /> <!-- g -->
+ <map code="0x0068" name="3em" /> <!-- h -->
+ <map code="0x0069" name="1em" /> <!-- i -->
+ <map code="0x006A" name="1em" /> <!-- j -->
+ <map code="0x006B" name="1em" /> <!-- k -->
+ <map code="0x006C" name="1em" /> <!-- l -->
+ <map code="0x006D" name="1em" /> <!-- m -->
+ <map code="0x006E" name="1em" /> <!-- n -->
+ <map code="0x006F" name="1em" /> <!-- o -->
+ <map code="0x0070" name="1em" /> <!-- p -->
+ <map code="0x0071" name="1em" /> <!-- q -->
+ <map code="0x0072" name="1em" /> <!-- r -->
+ <map code="0x0073" name="1em" /> <!-- s -->
+ <map code="0x0074" name="1em" /> <!-- t -->
+ <map code="0x0075" name="1em" /> <!-- u -->
+ <map code="0x0076" name="1em" /> <!-- v -->
+ <map code="0x0077" name="1em" /> <!-- w -->
+ <map code="0x0078" name="1em" /> <!-- x -->
+ <map code="0x0079" name="1em" /> <!-- y -->
+ <map code="0x007A" name="1em" /> <!-- z -->
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2018 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ 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.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_i3em_weight500_upright.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_i3em_weight500_upright.ttf
new file mode 100644
index 000000000000..b8edcb62f0b8
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_i3em_weight500_upright.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_i3em_weight500_upright.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_i3em_weight500_upright.ttx
new file mode 100644
index 000000000000..6daf8daa723b
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_i3em_weight500_upright.ttx
@@ -0,0 +1,208 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="1em"/>
+ <GlyphID id="2" name="3em"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Thu Feb 15 18:29:12 2018"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="500"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000000"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="1em" width="1000" lsb="93"/>
+ <mtx name="3em" width="3000" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="1em" /> <!-- a -->
+ <map code="0x0062" name="1em" /> <!-- b -->
+ <map code="0x0063" name="1em" /> <!-- c -->
+ <map code="0x0064" name="1em" /> <!-- d -->
+ <map code="0x0065" name="1em" /> <!-- e -->
+ <map code="0x0066" name="1em" /> <!-- f -->
+ <map code="0x0067" name="1em" /> <!-- g -->
+ <map code="0x0068" name="1em" /> <!-- h -->
+ <map code="0x0069" name="3em" /> <!-- i -->
+ <map code="0x006A" name="1em" /> <!-- j -->
+ <map code="0x006B" name="1em" /> <!-- k -->
+ <map code="0x006C" name="1em" /> <!-- l -->
+ <map code="0x006D" name="1em" /> <!-- m -->
+ <map code="0x006E" name="1em" /> <!-- n -->
+ <map code="0x006F" name="1em" /> <!-- o -->
+ <map code="0x0070" name="1em" /> <!-- p -->
+ <map code="0x0071" name="1em" /> <!-- q -->
+ <map code="0x0072" name="1em" /> <!-- r -->
+ <map code="0x0073" name="1em" /> <!-- s -->
+ <map code="0x0074" name="1em" /> <!-- t -->
+ <map code="0x0075" name="1em" /> <!-- u -->
+ <map code="0x0076" name="1em" /> <!-- v -->
+ <map code="0x0077" name="1em" /> <!-- w -->
+ <map code="0x0078" name="1em" /> <!-- x -->
+ <map code="0x0079" name="1em" /> <!-- y -->
+ <map code="0x007A" name="1em" /> <!-- z -->
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2018 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ 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.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_j3em_weight500_italic.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_j3em_weight500_italic.ttf
new file mode 100644
index 000000000000..6d7dde97c19f
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_j3em_weight500_italic.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_j3em_weight500_italic.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_j3em_weight500_italic.ttx
new file mode 100644
index 000000000000..c5843f07c572
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_j3em_weight500_italic.ttx
@@ -0,0 +1,208 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="1em"/>
+ <GlyphID id="2" name="3em"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Thu Feb 15 18:29:12 2018"/>
+ <macStyle value="00000000 00000010"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="500"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 00000001"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="1em" width="1000" lsb="93"/>
+ <mtx name="3em" width="3000" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="1em" /> <!-- a -->
+ <map code="0x0062" name="1em" /> <!-- b -->
+ <map code="0x0063" name="1em" /> <!-- c -->
+ <map code="0x0064" name="1em" /> <!-- d -->
+ <map code="0x0065" name="1em" /> <!-- e -->
+ <map code="0x0066" name="1em" /> <!-- f -->
+ <map code="0x0067" name="1em" /> <!-- g -->
+ <map code="0x0068" name="1em" /> <!-- h -->
+ <map code="0x0069" name="1em" /> <!-- i -->
+ <map code="0x006A" name="3em" /> <!-- j -->
+ <map code="0x006B" name="1em" /> <!-- k -->
+ <map code="0x006C" name="1em" /> <!-- l -->
+ <map code="0x006D" name="1em" /> <!-- m -->
+ <map code="0x006E" name="1em" /> <!-- n -->
+ <map code="0x006F" name="1em" /> <!-- o -->
+ <map code="0x0070" name="1em" /> <!-- p -->
+ <map code="0x0071" name="1em" /> <!-- q -->
+ <map code="0x0072" name="1em" /> <!-- r -->
+ <map code="0x0073" name="1em" /> <!-- s -->
+ <map code="0x0074" name="1em" /> <!-- t -->
+ <map code="0x0075" name="1em" /> <!-- u -->
+ <map code="0x0076" name="1em" /> <!-- v -->
+ <map code="0x0077" name="1em" /> <!-- w -->
+ <map code="0x0078" name="1em" /> <!-- x -->
+ <map code="0x0079" name="1em" /> <!-- y -->
+ <map code="0x007A" name="1em" /> <!-- z -->
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2018 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ 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.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_k3em_weight600_upright.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_k3em_weight600_upright.ttf
new file mode 100644
index 000000000000..eb6d7d1b9601
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_k3em_weight600_upright.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_k3em_weight600_upright.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_k3em_weight600_upright.ttx
new file mode 100644
index 000000000000..d46059fb9306
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_k3em_weight600_upright.ttx
@@ -0,0 +1,208 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="1em"/>
+ <GlyphID id="2" name="3em"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Thu Feb 15 18:29:12 2018"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="600"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000000"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="1em" width="1000" lsb="93"/>
+ <mtx name="3em" width="3000" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="1em" /> <!-- a -->
+ <map code="0x0062" name="1em" /> <!-- b -->
+ <map code="0x0063" name="1em" /> <!-- c -->
+ <map code="0x0064" name="1em" /> <!-- d -->
+ <map code="0x0065" name="1em" /> <!-- e -->
+ <map code="0x0066" name="1em" /> <!-- f -->
+ <map code="0x0067" name="1em" /> <!-- g -->
+ <map code="0x0068" name="1em" /> <!-- h -->
+ <map code="0x0069" name="1em" /> <!-- i -->
+ <map code="0x006A" name="1em" /> <!-- j -->
+ <map code="0x006B" name="3em" /> <!-- k -->
+ <map code="0x006C" name="1em" /> <!-- l -->
+ <map code="0x006D" name="1em" /> <!-- m -->
+ <map code="0x006E" name="1em" /> <!-- n -->
+ <map code="0x006F" name="1em" /> <!-- o -->
+ <map code="0x0070" name="1em" /> <!-- p -->
+ <map code="0x0071" name="1em" /> <!-- q -->
+ <map code="0x0072" name="1em" /> <!-- r -->
+ <map code="0x0073" name="1em" /> <!-- s -->
+ <map code="0x0074" name="1em" /> <!-- t -->
+ <map code="0x0075" name="1em" /> <!-- u -->
+ <map code="0x0076" name="1em" /> <!-- v -->
+ <map code="0x0077" name="1em" /> <!-- w -->
+ <map code="0x0078" name="1em" /> <!-- x -->
+ <map code="0x0079" name="1em" /> <!-- y -->
+ <map code="0x007A" name="1em" /> <!-- z -->
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2018 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ 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.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_l3em_weight600_italic.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_l3em_weight600_italic.ttf
new file mode 100644
index 000000000000..f509e94dbfbd
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_l3em_weight600_italic.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_l3em_weight600_italic.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_l3em_weight600_italic.ttx
new file mode 100644
index 000000000000..03073d33d203
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_l3em_weight600_italic.ttx
@@ -0,0 +1,208 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="1em"/>
+ <GlyphID id="2" name="3em"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Thu Feb 15 18:29:12 2018"/>
+ <macStyle value="00000000 00000010"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="600"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 00000001"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="1em" width="1000" lsb="93"/>
+ <mtx name="3em" width="3000" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="1em" /> <!-- a -->
+ <map code="0x0062" name="1em" /> <!-- b -->
+ <map code="0x0063" name="1em" /> <!-- c -->
+ <map code="0x0064" name="1em" /> <!-- d -->
+ <map code="0x0065" name="1em" /> <!-- e -->
+ <map code="0x0066" name="1em" /> <!-- f -->
+ <map code="0x0067" name="1em" /> <!-- g -->
+ <map code="0x0068" name="1em" /> <!-- h -->
+ <map code="0x0069" name="1em" /> <!-- i -->
+ <map code="0x006A" name="1em" /> <!-- j -->
+ <map code="0x006B" name="1em" /> <!-- k -->
+ <map code="0x006C" name="3em" /> <!-- l -->
+ <map code="0x006D" name="1em" /> <!-- m -->
+ <map code="0x006E" name="1em" /> <!-- n -->
+ <map code="0x006F" name="1em" /> <!-- o -->
+ <map code="0x0070" name="1em" /> <!-- p -->
+ <map code="0x0071" name="1em" /> <!-- q -->
+ <map code="0x0072" name="1em" /> <!-- r -->
+ <map code="0x0073" name="1em" /> <!-- s -->
+ <map code="0x0074" name="1em" /> <!-- t -->
+ <map code="0x0075" name="1em" /> <!-- u -->
+ <map code="0x0076" name="1em" /> <!-- v -->
+ <map code="0x0077" name="1em" /> <!-- w -->
+ <map code="0x0078" name="1em" /> <!-- x -->
+ <map code="0x0079" name="1em" /> <!-- y -->
+ <map code="0x007A" name="1em" /> <!-- z -->
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2018 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ 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.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_m3em_weight700_upright.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_m3em_weight700_upright.ttf
new file mode 100644
index 000000000000..062f2990fe0d
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_m3em_weight700_upright.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_m3em_weight700_upright.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_m3em_weight700_upright.ttx
new file mode 100644
index 000000000000..ca24fdeef0cc
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_m3em_weight700_upright.ttx
@@ -0,0 +1,208 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="1em"/>
+ <GlyphID id="2" name="3em"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Thu Feb 15 18:29:12 2018"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="700"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000000"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="1em" width="1000" lsb="93"/>
+ <mtx name="3em" width="3000" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="1em" /> <!-- a -->
+ <map code="0x0062" name="1em" /> <!-- b -->
+ <map code="0x0063" name="1em" /> <!-- c -->
+ <map code="0x0064" name="1em" /> <!-- d -->
+ <map code="0x0065" name="1em" /> <!-- e -->
+ <map code="0x0066" name="1em" /> <!-- f -->
+ <map code="0x0067" name="1em" /> <!-- g -->
+ <map code="0x0068" name="1em" /> <!-- h -->
+ <map code="0x0069" name="1em" /> <!-- i -->
+ <map code="0x006A" name="1em" /> <!-- j -->
+ <map code="0x006B" name="1em" /> <!-- k -->
+ <map code="0x006C" name="1em" /> <!-- l -->
+ <map code="0x006D" name="3em" /> <!-- m -->
+ <map code="0x006E" name="1em" /> <!-- n -->
+ <map code="0x006F" name="1em" /> <!-- o -->
+ <map code="0x0070" name="1em" /> <!-- p -->
+ <map code="0x0071" name="1em" /> <!-- q -->
+ <map code="0x0072" name="1em" /> <!-- r -->
+ <map code="0x0073" name="1em" /> <!-- s -->
+ <map code="0x0074" name="1em" /> <!-- t -->
+ <map code="0x0075" name="1em" /> <!-- u -->
+ <map code="0x0076" name="1em" /> <!-- v -->
+ <map code="0x0077" name="1em" /> <!-- w -->
+ <map code="0x0078" name="1em" /> <!-- x -->
+ <map code="0x0079" name="1em" /> <!-- y -->
+ <map code="0x007A" name="1em" /> <!-- z -->
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2018 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ 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.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_n3em_weight700_italic.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_n3em_weight700_italic.ttf
new file mode 100644
index 000000000000..fdd02394e84f
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_n3em_weight700_italic.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_n3em_weight700_italic.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_n3em_weight700_italic.ttx
new file mode 100644
index 000000000000..468d591b47ab
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_n3em_weight700_italic.ttx
@@ -0,0 +1,208 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="1em"/>
+ <GlyphID id="2" name="3em"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Thu Feb 15 18:29:13 2018"/>
+ <macStyle value="00000000 00000010"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="700"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 00000001"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="1em" width="1000" lsb="93"/>
+ <mtx name="3em" width="3000" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="1em" /> <!-- a -->
+ <map code="0x0062" name="1em" /> <!-- b -->
+ <map code="0x0063" name="1em" /> <!-- c -->
+ <map code="0x0064" name="1em" /> <!-- d -->
+ <map code="0x0065" name="1em" /> <!-- e -->
+ <map code="0x0066" name="1em" /> <!-- f -->
+ <map code="0x0067" name="1em" /> <!-- g -->
+ <map code="0x0068" name="1em" /> <!-- h -->
+ <map code="0x0069" name="1em" /> <!-- i -->
+ <map code="0x006A" name="1em" /> <!-- j -->
+ <map code="0x006B" name="1em" /> <!-- k -->
+ <map code="0x006C" name="1em" /> <!-- l -->
+ <map code="0x006D" name="1em" /> <!-- m -->
+ <map code="0x006E" name="3em" /> <!-- n -->
+ <map code="0x006F" name="1em" /> <!-- o -->
+ <map code="0x0070" name="1em" /> <!-- p -->
+ <map code="0x0071" name="1em" /> <!-- q -->
+ <map code="0x0072" name="1em" /> <!-- r -->
+ <map code="0x0073" name="1em" /> <!-- s -->
+ <map code="0x0074" name="1em" /> <!-- t -->
+ <map code="0x0075" name="1em" /> <!-- u -->
+ <map code="0x0076" name="1em" /> <!-- v -->
+ <map code="0x0077" name="1em" /> <!-- w -->
+ <map code="0x0078" name="1em" /> <!-- x -->
+ <map code="0x0079" name="1em" /> <!-- y -->
+ <map code="0x007A" name="1em" /> <!-- z -->
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2018 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ 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.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_names.txt b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_names.txt
new file mode 100644
index 000000000000..986d712c1b4b
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_names.txt
@@ -0,0 +1,18 @@
+ascii_a3em_weight100_upright.ttf
+ascii_b3em_weight100_italic.ttf
+ascii_c3em_weight200_upright.ttf
+ascii_d3em_weight200_italic.ttf
+ascii_e3em_weight300_upright.ttf
+ascii_f3em_weight300_italic.ttf
+ascii_g3em_weight400_upright.ttf
+ascii_h3em_weight400_italic.ttf
+ascii_i3em_weight500_upright.ttf
+ascii_j3em_weight500_italic.ttf
+ascii_k3em_weight600_upright.ttf
+ascii_l3em_weight600_italic.ttf
+ascii_m3em_weight700_upright.ttf
+ascii_n3em_weight700_italic.ttf
+ascii_o3em_weight800_upright.ttf
+ascii_p3em_weight800_italic.ttf
+ascii_q3em_weight900_upright.ttf
+ascii_r3em_weight900_italic.ttf
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_o3em_weight800_upright.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_o3em_weight800_upright.ttf
new file mode 100644
index 000000000000..993e7faa7ba8
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_o3em_weight800_upright.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_o3em_weight800_upright.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_o3em_weight800_upright.ttx
new file mode 100644
index 000000000000..b8c712ff9039
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_o3em_weight800_upright.ttx
@@ -0,0 +1,208 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="1em"/>
+ <GlyphID id="2" name="3em"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Thu Feb 15 18:29:13 2018"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="800"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000000"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="1em" width="1000" lsb="93"/>
+ <mtx name="3em" width="3000" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="1em" /> <!-- a -->
+ <map code="0x0062" name="1em" /> <!-- b -->
+ <map code="0x0063" name="1em" /> <!-- c -->
+ <map code="0x0064" name="1em" /> <!-- d -->
+ <map code="0x0065" name="1em" /> <!-- e -->
+ <map code="0x0066" name="1em" /> <!-- f -->
+ <map code="0x0067" name="1em" /> <!-- g -->
+ <map code="0x0068" name="1em" /> <!-- h -->
+ <map code="0x0069" name="1em" /> <!-- i -->
+ <map code="0x006A" name="1em" /> <!-- j -->
+ <map code="0x006B" name="1em" /> <!-- k -->
+ <map code="0x006C" name="1em" /> <!-- l -->
+ <map code="0x006D" name="1em" /> <!-- m -->
+ <map code="0x006E" name="1em" /> <!-- n -->
+ <map code="0x006F" name="3em" /> <!-- o -->
+ <map code="0x0070" name="1em" /> <!-- p -->
+ <map code="0x0071" name="1em" /> <!-- q -->
+ <map code="0x0072" name="1em" /> <!-- r -->
+ <map code="0x0073" name="1em" /> <!-- s -->
+ <map code="0x0074" name="1em" /> <!-- t -->
+ <map code="0x0075" name="1em" /> <!-- u -->
+ <map code="0x0076" name="1em" /> <!-- v -->
+ <map code="0x0077" name="1em" /> <!-- w -->
+ <map code="0x0078" name="1em" /> <!-- x -->
+ <map code="0x0079" name="1em" /> <!-- y -->
+ <map code="0x007A" name="1em" /> <!-- z -->
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2018 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ 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.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_p3em_weight800_italic.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_p3em_weight800_italic.ttf
new file mode 100644
index 000000000000..f0d54f07dfad
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_p3em_weight800_italic.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_p3em_weight800_italic.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_p3em_weight800_italic.ttx
new file mode 100644
index 000000000000..5d8ee18beb4a
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_p3em_weight800_italic.ttx
@@ -0,0 +1,208 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="1em"/>
+ <GlyphID id="2" name="3em"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Thu Feb 15 18:29:13 2018"/>
+ <macStyle value="00000000 00000010"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="800"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 00000001"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="1em" width="1000" lsb="93"/>
+ <mtx name="3em" width="3000" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="1em" /> <!-- a -->
+ <map code="0x0062" name="1em" /> <!-- b -->
+ <map code="0x0063" name="1em" /> <!-- c -->
+ <map code="0x0064" name="1em" /> <!-- d -->
+ <map code="0x0065" name="1em" /> <!-- e -->
+ <map code="0x0066" name="1em" /> <!-- f -->
+ <map code="0x0067" name="1em" /> <!-- g -->
+ <map code="0x0068" name="1em" /> <!-- h -->
+ <map code="0x0069" name="1em" /> <!-- i -->
+ <map code="0x006A" name="1em" /> <!-- j -->
+ <map code="0x006B" name="1em" /> <!-- k -->
+ <map code="0x006C" name="1em" /> <!-- l -->
+ <map code="0x006D" name="1em" /> <!-- m -->
+ <map code="0x006E" name="1em" /> <!-- n -->
+ <map code="0x006F" name="1em" /> <!-- o -->
+ <map code="0x0070" name="3em" /> <!-- p -->
+ <map code="0x0071" name="1em" /> <!-- q -->
+ <map code="0x0072" name="1em" /> <!-- r -->
+ <map code="0x0073" name="1em" /> <!-- s -->
+ <map code="0x0074" name="1em" /> <!-- t -->
+ <map code="0x0075" name="1em" /> <!-- u -->
+ <map code="0x0076" name="1em" /> <!-- v -->
+ <map code="0x0077" name="1em" /> <!-- w -->
+ <map code="0x0078" name="1em" /> <!-- x -->
+ <map code="0x0079" name="1em" /> <!-- y -->
+ <map code="0x007A" name="1em" /> <!-- z -->
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2018 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ 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.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_q3em_weight900_upright.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_q3em_weight900_upright.ttf
new file mode 100644
index 000000000000..c776c783c7e7
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_q3em_weight900_upright.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_q3em_weight900_upright.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_q3em_weight900_upright.ttx
new file mode 100644
index 000000000000..169fd738b66b
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_q3em_weight900_upright.ttx
@@ -0,0 +1,208 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="1em"/>
+ <GlyphID id="2" name="3em"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Thu Feb 15 18:29:13 2018"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="900"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000000"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="1em" width="1000" lsb="93"/>
+ <mtx name="3em" width="3000" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="1em" /> <!-- a -->
+ <map code="0x0062" name="1em" /> <!-- b -->
+ <map code="0x0063" name="1em" /> <!-- c -->
+ <map code="0x0064" name="1em" /> <!-- d -->
+ <map code="0x0065" name="1em" /> <!-- e -->
+ <map code="0x0066" name="1em" /> <!-- f -->
+ <map code="0x0067" name="1em" /> <!-- g -->
+ <map code="0x0068" name="1em" /> <!-- h -->
+ <map code="0x0069" name="1em" /> <!-- i -->
+ <map code="0x006A" name="1em" /> <!-- j -->
+ <map code="0x006B" name="1em" /> <!-- k -->
+ <map code="0x006C" name="1em" /> <!-- l -->
+ <map code="0x006D" name="1em" /> <!-- m -->
+ <map code="0x006E" name="1em" /> <!-- n -->
+ <map code="0x006F" name="1em" /> <!-- o -->
+ <map code="0x0070" name="1em" /> <!-- p -->
+ <map code="0x0071" name="3em" /> <!-- q -->
+ <map code="0x0072" name="1em" /> <!-- r -->
+ <map code="0x0073" name="1em" /> <!-- s -->
+ <map code="0x0074" name="1em" /> <!-- t -->
+ <map code="0x0075" name="1em" /> <!-- u -->
+ <map code="0x0076" name="1em" /> <!-- v -->
+ <map code="0x0077" name="1em" /> <!-- w -->
+ <map code="0x0078" name="1em" /> <!-- x -->
+ <map code="0x0079" name="1em" /> <!-- y -->
+ <map code="0x007A" name="1em" /> <!-- z -->
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2018 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ 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.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_r3em_weight900_italic.ttf b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_r3em_weight900_italic.ttf
new file mode 100644
index 000000000000..02f6246b384f
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_r3em_weight900_italic.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_r3em_weight900_italic.ttx b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_r3em_weight900_italic.ttx
new file mode 100644
index 000000000000..131d7b115ab1
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_r3em_weight900_italic.ttx
@@ -0,0 +1,208 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="1em"/>
+ <GlyphID id="2" name="3em"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Thu Feb 15 18:29:13 2018"/>
+ <macStyle value="00000000 00000010"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="900"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 00000001"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="1em" width="1000" lsb="93"/>
+ <mtx name="3em" width="3000" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="1em" /> <!-- a -->
+ <map code="0x0062" name="1em" /> <!-- b -->
+ <map code="0x0063" name="1em" /> <!-- c -->
+ <map code="0x0064" name="1em" /> <!-- d -->
+ <map code="0x0065" name="1em" /> <!-- e -->
+ <map code="0x0066" name="1em" /> <!-- f -->
+ <map code="0x0067" name="1em" /> <!-- g -->
+ <map code="0x0068" name="1em" /> <!-- h -->
+ <map code="0x0069" name="1em" /> <!-- i -->
+ <map code="0x006A" name="1em" /> <!-- j -->
+ <map code="0x006B" name="1em" /> <!-- k -->
+ <map code="0x006C" name="1em" /> <!-- l -->
+ <map code="0x006D" name="1em" /> <!-- m -->
+ <map code="0x006E" name="1em" /> <!-- n -->
+ <map code="0x006F" name="1em" /> <!-- o -->
+ <map code="0x0070" name="1em" /> <!-- p -->
+ <map code="0x0071" name="1em" /> <!-- q -->
+ <map code="0x0072" name="3em" /> <!-- r -->
+ <map code="0x0073" name="1em" /> <!-- s -->
+ <map code="0x0074" name="1em" /> <!-- t -->
+ <map code="0x0075" name="1em" /> <!-- u -->
+ <map code="0x0076" name="1em" /> <!-- v -->
+ <map code="0x0077" name="1em" /> <!-- w -->
+ <map code="0x0078" name="1em" /> <!-- x -->
+ <map code="0x0079" name="1em" /> <!-- y -->
+ <map code="0x007A" name="1em" /> <!-- z -->
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2018 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ 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.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/core/tests/coretests/res/layout/empty_layout.xml b/core/tests/coretests/res/layout/empty_layout.xml
new file mode 100644
index 000000000000..c208b958f093
--- /dev/null
+++ b/core/tests/coretests/res/layout/empty_layout.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/parent"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:orientation="vertical">
+</LinearLayout>
diff --git a/core/tests/coretests/src/android/app/assist/AssistStructureTest.java b/core/tests/coretests/src/android/app/assist/AssistStructureTest.java
new file mode 100644
index 000000000000..b6a37bc655b3
--- /dev/null
+++ b/core/tests/coretests/src/android/app/assist/AssistStructureTest.java
@@ -0,0 +1,269 @@
+/*
+ * 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.app.assist;
+
+import static android.view.View.IMPORTANT_FOR_AUTOFILL_AUTO;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.assist.AssistStructure.ViewNode;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Parcel;
+import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
+import android.widget.EditText;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Unit test for {@link AssistStructure}.
+ *
+ * <p>To run it: {@code atest app.assist.AssistStructureTest}
+ *
+ * <p>TODO: right now this test is focused in the parcelization of a big object, due to an
+ * upcoming refactoring on the Autofill parcelization that does not use
+ * {@link AssistStructure#ensureData()} to load the structure (which in turn requires calls from
+ * system server to the app). Ideally it should be emprove to:
+ *
+ * <ol>
+ * <li>Add tests for Assist (to make sure autofill properties are not parcelized).
+ * <li>Assert all properties and make sure just the relevant properties (for Autofill or Assist)
+ * are parcelized.
+ * <li>Add tests for Autofill requests with the {@code FLAG_MANUAL_REQUEST} flag.
+ * </ol>
+ */
+@RunWith(AndroidJUnit4.class)
+public class AssistStructureTest {
+
+ private static final String TAG = "AssistStructureTest";
+
+ private static final boolean FOR_AUTOFILL = true;
+ private static final int NO_FLAGS = 0;
+
+ private static final int BIG_VIEW_SIZE = 10_000_000;
+ private static final char BIG_VIEW_CHAR = '6';
+ private static final String BIG_STRING = repeat(BIG_VIEW_CHAR, BIG_VIEW_SIZE);
+ // Cannot be much big because it could hang test due to blocking GC
+ private static final int NUMBER_SMALL_VIEWS = 10_000;
+
+ private EmptyLayoutActivity mActivity;
+
+ private final ActivityTestRule<EmptyLayoutActivity> mActivityTestRule =
+ new ActivityTestRule<>(EmptyLayoutActivity.class);
+
+ private final Context mContext = InstrumentationRegistry.getTargetContext();
+
+ @Before
+ public void setup() {
+ mActivity = mActivityTestRule.launchActivity(null);
+ }
+
+ @Test
+ public void testParcelizationForAutofill_manySmallViews() {
+ Log.d(TAG, "Adding " + NUMBER_SMALL_VIEWS + " small views");
+
+ for (int i = 1; i <= NUMBER_SMALL_VIEWS; i++) {
+ mActivity.addView(newSmallView());
+ }
+
+ waitUntilViewsAreLaidOff();
+
+ AssistStructure structure = new AssistStructure(mActivity, FOR_AUTOFILL, NO_FLAGS);
+
+ // Check properties on "original" structure
+ assertStructureWithManySmallViews(structure);
+
+ // Check properties on "cloned" structure
+ AssistStructure clone = cloneThroughParcel(structure);
+ assertStructureWithManySmallViews(clone);
+ }
+
+ private void assertStructureWithManySmallViews(AssistStructure structure) {
+ int i = 0;
+ try {
+ assertPackageName(structure);
+
+ assertThat(structure.getWindowNodeCount()).isEqualTo(1);
+
+ ViewNode rootView = structure.getWindowNodeAt(0).getRootViewNode();
+ assertThat(rootView.getClassName()).isEqualTo(FrameLayout.class.getName());
+ assertThat(rootView.getChildCount()).isEqualTo(2); // title and parent
+ assertThat(rootView.getAutofillId()).isNotNull();
+ assertThat(rootView.getImportantForAutofill()).isEqualTo(IMPORTANT_FOR_AUTOFILL_AUTO);
+
+ // Title
+ ViewNode title = rootView.getChildAt(0);
+ assertThat(title.getClassName()).isEqualTo(TextView.class.getName());
+ assertThat(title.getChildCount()).isEqualTo(0);
+ assertThat(title.getText().toString()).isEqualTo("My Title");
+ assertThat(title.getAutofillId()).isNotNull();
+
+ // Parent
+ ViewNode parent = rootView.getChildAt(1);
+ assertThat(parent.getClassName()).isEqualTo(LinearLayout.class.getName());
+ assertThat(parent.getChildCount()).isEqualTo(NUMBER_SMALL_VIEWS);
+ assertThat(parent.getIdEntry()).isEqualTo("parent");
+ assertThat(parent.getAutofillId()).isNotNull();
+
+ // Children
+ for (i = 0; i < NUMBER_SMALL_VIEWS; i++) {
+ ViewNode smallView = parent.getChildAt(i);
+ assertSmallView(smallView);
+ }
+ } catch (RuntimeException | Error e) {
+ Log.e(TAG, "dumping structure because of error at index #" + i + ": " + e);
+ structure.dump(true);
+ throw e;
+ }
+ }
+
+ @Test
+ public void testParcelizationForAutofill_oneBigView() {
+ Log.d(TAG, "Adding view with " + BIG_VIEW_SIZE + " chars");
+
+ mActivity.addView(newBigView());
+ waitUntilViewsAreLaidOff();
+
+ AssistStructure structure = new AssistStructure(mActivity, FOR_AUTOFILL, NO_FLAGS);
+
+ // Check properties on "original" structure
+ assertStructureWithOneBigView(structure);
+
+ // Check properties on "cloned" structure
+ AssistStructure clone = cloneThroughParcel(structure);
+ assertStructureWithOneBigView(clone);
+ }
+
+ private void assertStructureWithOneBigView(AssistStructure structure) {
+ try {
+ assertPackageName(structure);
+
+ assertThat(structure.getWindowNodeCount()).isEqualTo(1);
+
+ ViewNode rootView = structure.getWindowNodeAt(0).getRootViewNode();
+ assertThat(rootView.getClassName()).isEqualTo(FrameLayout.class.getName());
+ assertThat(rootView.getChildCount()).isEqualTo(2); // title and parent
+ assertThat(rootView.getAutofillId()).isNotNull();
+ assertThat(rootView.getImportantForAutofill()).isEqualTo(IMPORTANT_FOR_AUTOFILL_AUTO);
+
+ // Title
+ ViewNode title = rootView.getChildAt(0);
+ assertThat(title.getClassName()).isEqualTo(TextView.class.getName());
+ assertThat(title.getChildCount()).isEqualTo(0);
+ assertThat(title.getText().toString()).isEqualTo("My Title");
+ assertThat(title.getAutofillId()).isNotNull();
+
+ // Parent
+ ViewNode parent = rootView.getChildAt(1);
+ assertThat(parent.getClassName()).isEqualTo(LinearLayout.class.getName());
+ assertThat(parent.getChildCount()).isEqualTo(1);
+ assertThat(parent.getIdEntry()).isEqualTo("parent");
+ assertThat(parent.getAutofillId()).isNotNull();
+
+ // Children
+ ViewNode bigView = parent.getChildAt(0);
+ assertBigView(bigView);
+ } catch (RuntimeException | Error e) {
+ Log.e(TAG, "dumping structure because of error: " + e);
+ structure.dump(true);
+ throw e;
+ }
+ }
+
+ private EditText newSmallView() {
+ EditText view = new EditText(mContext);
+ view.setText("I AM GROOT");
+ return view;
+ }
+
+ private void assertSmallView(ViewNode view) {
+ assertThat(view.getClassName()).isEqualTo(EditText.class.getName());
+ assertThat(view.getChildCount()).isEqualTo(0);
+ assertThat(view.getIdEntry()).isNull();
+ assertThat(view.getAutofillId()).isNotNull();
+ assertThat(view.getText().toString()).isEqualTo("I AM GROOT");
+ }
+
+ private EditText newBigView() {
+ EditText view = new EditText(mContext);
+ view.setText("Big Hint in Little View");
+ view.setAutofillHints(BIG_STRING);
+ return view;
+ }
+
+ private void assertBigView(ViewNode view) {
+ assertThat(view.getClassName()).isEqualTo(EditText.class.getName());
+ assertThat(view.getChildCount()).isEqualTo(0);
+ assertThat(view.getIdEntry()).isNull();
+ assertThat(view.getAutofillId()).isNotNull();
+ assertThat(view.getText().toString()).isEqualTo("Big Hint in Little View");
+
+ String[] hints = view.getAutofillHints();
+ assertThat(hints.length).isEqualTo(1);
+ String hint = hints[0];
+ // Cannot assert the whole string because it takes too long and crashes the test by ANR
+ assertThat(hint.length()).isEqualTo(BIG_VIEW_SIZE);
+ assertThat(hint.charAt(0)).isEqualTo(BIG_VIEW_CHAR);
+ assertThat(hint.charAt(BIG_VIEW_SIZE - 1)).isEqualTo(BIG_VIEW_CHAR);
+ }
+
+ private void assertPackageName(AssistStructure structure) {
+ assertThat(structure.getActivityComponent()).isEqualTo(
+ new ComponentName("com.android.frameworks.coretests",
+ "android.app.assist.EmptyLayoutActivity"));
+ }
+
+ private AssistStructure cloneThroughParcel(AssistStructure structure) {
+ Parcel parcel = Parcel.obtain();
+
+ try {
+ // Write to parcel
+ parcel.setDataPosition(0); // Sanity / paranoid check
+ structure.writeToParcel(parcel, NO_FLAGS);
+
+ // Read from parcel
+ parcel.setDataPosition(0);
+ AssistStructure clone = AssistStructure.CREATOR.createFromParcel(parcel);
+ assertThat(clone).isNotNull();
+ return clone;
+ } finally {
+ parcel.recycle();
+ }
+ }
+
+ private void waitUntilViewsAreLaidOff() {
+ // TODO: use a more robust mechanism than just sleeping
+ SystemClock.sleep(3000);
+ }
+
+ // TODO: use some common helper
+ private static String repeat(char c, int size) {
+ StringBuilder builder = new StringBuilder(size);
+ for (int i = 1; i <= size; i++) {
+ builder.append(c);
+ }
+ return builder.toString();
+ }
+}
diff --git a/core/tests/coretests/src/android/app/assist/EmptyLayoutActivity.java b/core/tests/coretests/src/android/app/assist/EmptyLayoutActivity.java
new file mode 100644
index 000000000000..f4b6bed1cac8
--- /dev/null
+++ b/core/tests/coretests/src/android/app/assist/EmptyLayoutActivity.java
@@ -0,0 +1,44 @@
+/*
+ * 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.app.assist;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.WindowManager.LayoutParams;
+import android.widget.LinearLayout;
+
+import com.android.frameworks.coretests.R;
+
+public class EmptyLayoutActivity extends Activity {
+
+ private LinearLayout mParent;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.empty_layout);
+
+ mParent = findViewById(R.id.parent);
+ }
+
+ public void addView(View view) {
+ view.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,
+ LayoutParams.WRAP_CONTENT));
+ runOnUiThread(() -> mParent.addView(view));
+ }
+}
diff --git a/core/tests/coretests/src/android/graphics/FontFileUtilTest.java b/core/tests/coretests/src/android/graphics/FontFileUtilTest.java
new file mode 100644
index 000000000000..76267b23a0ca
--- /dev/null
+++ b/core/tests/coretests/src/android/graphics/FontFileUtilTest.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright 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.graphics;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.Context;
+import android.content.res.AssetManager;
+import android.graphics.fonts.FontFileUtil;
+import android.graphics.fonts.FontVariationAxis;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.util.Log;
+import android.util.Pair;
+
+import org.junit.Test;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+
+@SmallTest
+public class FontFileUtilTest {
+ private static final String TAG = "FontFileUtilTest";
+ private static final String CACHE_FILE_PREFIX = ".font";
+
+ private static File getTempFile() {
+ Context ctx = InstrumentationRegistry.getTargetContext();
+ final String prefix = CACHE_FILE_PREFIX;
+ for (int i = 0; i < 100; ++i) {
+ final File file = new File(ctx.getCacheDir(), prefix + i);
+ try {
+ if (file.createNewFile()) {
+ return file;
+ }
+ } catch (IOException e) {
+ // ignore. Try next file.
+ }
+ }
+ return null;
+ }
+
+ private static ByteBuffer mmap(AssetManager am, String path) {
+ File file = getTempFile();
+ try (InputStream is = am.open(path)) {
+ if (!copyToFile(file, is)) {
+ return null;
+ }
+ return mmap(file);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to open assets");
+ return null;
+ } finally {
+ file.delete();
+ }
+ }
+
+ private static ByteBuffer mmap(File file) {
+ try (FileInputStream fis = new FileInputStream(file)) {
+ FileChannel channel = fis.getChannel();
+ return channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ private static boolean copyToFile(File file, InputStream is) {
+ try (FileOutputStream os = new FileOutputStream(file, false)) {
+ byte[] buffer = new byte[1024];
+ int readLen;
+ while ((readLen = is.read(buffer)) != -1) {
+ os.write(buffer, 0, readLen);
+ }
+ return true;
+ } catch (IOException e) {
+ Log.e(TAG, "Error copying resource contents to temp file: " + e.getMessage());
+ return false;
+ }
+ }
+
+ @Test
+ public void testRegularFonts() throws IOException {
+ AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
+ for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
+ int weight = style.first.intValue();
+ boolean italic = style.second.booleanValue();
+ String path = FontTestUtil.getFontPathFromStyle(weight, italic);
+
+ ByteBuffer buffer = mmap(am, path);
+ int packed = FontFileUtil.analyzeStyle(buffer, 0, null);
+ assertEquals(path, weight, FontFileUtil.unpackWeight(packed));
+ assertEquals(path, italic, FontFileUtil.unpackItalic(packed));
+ }
+ }
+
+ @Test
+ public void testTtcFont() throws IOException {
+ AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
+ String path = FontTestUtil.getTtcFontFileInAsset();
+ ByteBuffer buffer = mmap(am, path);
+ for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
+ int weight = style.first.intValue();
+ boolean italic = style.second.booleanValue();
+ int ttcIndex = FontTestUtil.getTtcIndexFromStyle(weight, italic);
+
+ int packed = FontFileUtil.analyzeStyle(buffer, ttcIndex, null);
+ assertEquals(path + "#" + ttcIndex, weight, FontFileUtil.unpackWeight(packed));
+ assertEquals(path + "#" + ttcIndex, italic, FontFileUtil.unpackItalic(packed));
+ }
+ }
+
+ @Test
+ public void testVariationFont() throws IOException {
+ AssetManager am = InstrumentationRegistry.getTargetContext().getAssets();
+ String path = FontTestUtil.getVFFontInAsset();
+ ByteBuffer buffer = mmap(am, path);
+ for (Pair<Integer, Boolean> style : FontTestUtil.getAllStyles()) {
+ int weight = style.first.intValue();
+ boolean italic = style.second.booleanValue();
+ String axes = FontTestUtil.getVarSettingsFromStyle(weight, italic);
+
+ int packed = FontFileUtil.analyzeStyle(buffer, 0,
+ FontVariationAxis.fromFontVariationSettings(axes));
+ assertEquals(path + "#" + axes, weight, FontFileUtil.unpackWeight(packed));
+ assertEquals(path + "#" + axes, italic, FontFileUtil.unpackItalic(packed));
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/graphics/FontTestUtil.java b/core/tests/coretests/src/android/graphics/FontTestUtil.java
new file mode 100644
index 000000000000..2becb4b18ac0
--- /dev/null
+++ b/core/tests/coretests/src/android/graphics/FontTestUtil.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright 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.graphics;
+
+import android.util.Pair;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Provides a utility for testing fonts
+ *
+ * For the purpose of testing font selection of families or fallbacks, this class provies following
+ * regular font files.
+ *
+ * - ascii_a3em_weight100_upright.ttf
+ * 'a' has 3em width and others have 1em width. The metadata has weight=100, non-italic value.
+ * - ascii_b3em_weight100_italic.ttf
+ * 'b' has 3em width and others have 1em width. The metadata has weight=100, italic value.
+ * - ascii_c3em_weight200_upright.ttf
+ * 'c' has 3em width and others have 1em width. The metadata has weight=200, non-italic value.
+ * - ascii_d3em_weight200_italic.ttf
+ * 'd' has 3em width and others have 1em width. The metadata has weight=200, italic value.
+ * - ascii_e3em_weight300_upright.ttf
+ * 'e' has 3em width and others have 1em width. The metadata has weight=300, non-italic value.
+ * - ascii_f3em_weight300_italic.ttf
+ * 'f' has 3em width and others have 1em width. The metadata has weight=300, italic value.
+ * - ascii_g3em_weight400_upright.ttf
+ * 'g' has 3em width and others have 1em width. The metadata has weight=400, non-italic value.
+ * - ascii_h3em_weight400_italic.ttf
+ * 'h' has 3em width and others have 1em width. The metadata has weight=400, italic value.
+ * - ascii_i3em_weight500_upright.ttf
+ * 'i' has 3em width and others have 1em width. The metadata has weight=500, non-italic value.
+ * - ascii_j3em_weight500_italic.ttf
+ * 'j' has 3em width and others have 1em width. The metadata has weight=500, italic value.
+ * - ascii_k3em_weight600_upright.ttf
+ * 'k' has 3em width and others have 1em width. The metadata has weight=600, non-italic value.
+ * - ascii_l3em_weight600_italic.ttf
+ * 'l' has 3em width and others have 1em width. The metadata has weight=600, italic value.
+ * - ascii_m3em_weight700_upright.ttf
+ * 'm' has 3em width and others have 1em width. The metadata has weight=700, non-italic value.
+ * - ascii_n3em_weight700_italic.ttf
+ * 'n' has 3em width and others have 1em width. The metadata has weight=700, italic value.
+ * - ascii_o3em_weight800_upright.ttf
+ * 'o' has 3em width and others have 1em width. The metadata has weight=800, non-italic value.
+ * - ascii_p3em_weight800_italic.ttf
+ * 'p' has 3em width and others have 1em width. The metadata has weight=800, italic value.
+ * - ascii_q3em_weight900_upright.ttf
+ * 'q' has 3em width and others have 1em width. The metadata has weight=900, non-italic value.
+ * - ascii_r3em_weight900_italic.ttf
+ * 'r' has 3em width and others have 1em width. The metadata has weight=900, italic value.
+ *
+ * In addition to above font files, this class provides a font collection file and a variable font
+ * file.
+ * - ascii.ttc
+ * The collection of above 18 fonts with above order.
+ * - ascii_vf.ttf
+ * This font supports a-z characters and all characters has 1em width. This font supports 'wght',
+ * 'ital' axes but no effect for the glyph width. This font also supports 'Asc[a-z]' 26 axes which
+ * makes glyph width 3em. For example, 'Asca 1.0' makes a glyph width of 'a' 3em, 'Ascb 1.0' makes
+ * a glyph width of 'b' 3em. With these axes, above font can be replicated like
+ * - 'Asca' 1.0, 'wght' 100.0' is equivalent with ascii_a3em_width100_upright.ttf
+ * - 'Ascb' 1.0, 'wght' 100.0, 'ital' 1.0' is equivalent with ascii_b3em_width100_italic.ttf
+ */
+public class FontTestUtil {
+ private static final String FAMILY_SELECTION_FONT_PATH_IN_ASSET = "fonts_for_family_selection";
+ private static final List<Pair<Integer, Boolean>> sStyleList;
+ private static final Map<Pair<Integer, Boolean>, String> sFontMap;
+ private static final Map<Pair<Integer, Boolean>, Integer> sTtcMap;
+ private static final Map<Pair<Integer, Boolean>, String> sVariationSettingsMap;
+ private static final String[] sFontList = { // Same order of ascii.ttc
+ FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_a3em_weight100_upright.ttf",
+ FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_b3em_weight100_italic.ttf",
+ FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_c3em_weight200_upright.ttf",
+ FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_d3em_weight200_italic.ttf",
+ FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_e3em_weight300_upright.ttf",
+ FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_f3em_weight300_italic.ttf",
+ FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_g3em_weight400_upright.ttf",
+ FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_h3em_weight400_italic.ttf",
+ FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_i3em_weight500_upright.ttf",
+ FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_j3em_weight500_italic.ttf",
+ FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_k3em_weight600_upright.ttf",
+ FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_l3em_weight600_italic.ttf",
+ FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_m3em_weight700_upright.ttf",
+ FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_n3em_weight700_italic.ttf",
+ FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_o3em_weight800_upright.ttf",
+ FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_p3em_weight800_italic.ttf",
+ FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_q3em_weight900_upright.ttf",
+ FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/fonts/ascii_r3em_weight900_italic.ttf",
+ };
+
+ private static final String[] FONT_VARIATION_SETTING_LIST = {
+ "'Asca' 1.0, 'wght' 100.0",
+ "'Ascb' 1.0, 'wght' 100.0, 'ital' 1.0",
+ "'Ascc' 1.0, 'wght' 200.0",
+ "'Ascd' 1.0, 'wght' 200.0, 'ital' 1.0",
+ "'Asce' 1.0, 'wght' 300.0",
+ "'Ascf' 1.0, 'wght' 300.0, 'ital' 1.0",
+ "'Ascg' 1.0, 'wght' 400.0",
+ "'Asch' 1.0, 'wght' 400.0, 'ital' 1.0",
+ "'Asci' 1.0, 'wght' 500.0",
+ "'Ascj' 1.0, 'wght' 500.0, 'ital' 1.0",
+ "'Asck' 1.0, 'wght' 600.0",
+ "'Ascl' 1.0, 'wght' 600.0, 'ital' 1.0",
+ "'Ascm' 1.0, 'wght' 700.0",
+ "'Ascn' 1.0, 'wght' 700.0, 'ital' 1.0",
+ "'Asco' 1.0, 'wght' 800.0",
+ "'Ascp' 1.0, 'wght' 800.0, 'ital' 1.0",
+ "'Ascq' 1.0, 'wght' 900.0",
+ "'Ascr' 1.0, 'wght' 900.0, 'ital' 1.0",
+ };
+
+ static {
+ // Style list with the same order of sFontList.
+ ArrayList<Pair<Integer, Boolean>> styles = new ArrayList<>();
+ styles.add(new Pair<>(100, false));
+ styles.add(new Pair<>(100, true));
+ styles.add(new Pair<>(200, false));
+ styles.add(new Pair<>(200, true));
+ styles.add(new Pair<>(300, false));
+ styles.add(new Pair<>(300, true));
+ styles.add(new Pair<>(400, false));
+ styles.add(new Pair<>(400, true));
+ styles.add(new Pair<>(500, false));
+ styles.add(new Pair<>(500, true));
+ styles.add(new Pair<>(600, false));
+ styles.add(new Pair<>(600, true));
+ styles.add(new Pair<>(700, false));
+ styles.add(new Pair<>(700, true));
+ styles.add(new Pair<>(800, false));
+ styles.add(new Pair<>(800, true));
+ styles.add(new Pair<>(900, false));
+ styles.add(new Pair<>(900, true));
+ sStyleList = Collections.unmodifiableList(styles);
+
+ HashMap<Pair<Integer, Boolean>, String> map = new HashMap<>();
+ HashMap<Pair<Integer, Boolean>, Integer> ttcMap = new HashMap<>();
+ HashMap<Pair<Integer, Boolean>, String> variationMap = new HashMap<>();
+ HashMap<Character, Pair<Integer, Boolean>> reverseMap = new HashMap<>();
+ for (int i = 0; i < sFontList.length; ++i) {
+ map.put(sStyleList.get(i), sFontList[i]);
+ ttcMap.put(sStyleList.get(i), i);
+ variationMap.put(sStyleList.get(i), FONT_VARIATION_SETTING_LIST[i]);
+ }
+ sFontMap = Collections.unmodifiableMap(map);
+ sTtcMap = Collections.unmodifiableMap(ttcMap);
+ sVariationSettingsMap = Collections.unmodifiableMap(variationMap);
+ }
+
+ /**
+ * Returns a path to the font collection file in asset directory.
+ */
+ public static String getTtcFontFileInAsset() {
+ return FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/ascii.ttc";
+ }
+
+ /**
+ * Returns a path to the variable font file in asset directory.
+ */
+ public static String getVFFontInAsset() {
+ return FAMILY_SELECTION_FONT_PATH_IN_ASSET + "/ascii_vf.ttf";
+ }
+
+ /**
+ * Returns a ttc index of the specified style.
+ */
+ public static int getTtcIndexFromStyle(int weight, boolean italic) {
+ return sTtcMap.get(new Pair<>(weight, italic)).intValue();
+ }
+
+ /**
+ * Returns a variation settings string of the specified style.
+ */
+ public static String getVarSettingsFromStyle(int weight, boolean italic) {
+ return sVariationSettingsMap.get(new Pair<>(weight, italic));
+ }
+
+ /**
+ * Returns a font path from the specified style.
+ */
+ public static String getFontPathFromStyle(int weight, boolean italic) {
+ return sFontMap.get(new Pair<>(weight, italic));
+ }
+
+ /**
+ * Returns all supported styles.
+ */
+ public static List<Pair<Integer, Boolean>> getAllStyles() {
+ return sStyleList;
+ }
+}
diff --git a/graphics/java/android/graphics/fonts/FontFileUtil.java b/graphics/java/android/graphics/fonts/FontFileUtil.java
new file mode 100644
index 000000000000..d15f581f918c
--- /dev/null
+++ b/graphics/java/android/graphics/fonts/FontFileUtil.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 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.graphics.fonts;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * Provides a utility for font file operations.
+ * @hide
+ */
+public class FontFileUtil {
+
+ private FontFileUtil() {} // Do not instanciate
+
+ /**
+ * Unpack the weight value from packed integer.
+ */
+ public static int unpackWeight(int packed) {
+ return packed & 0xFFFF;
+ }
+
+ /**
+ * Unpack the italic value from packed integer.
+ */
+ public static boolean unpackItalic(int packed) {
+ return (packed & 0x10000) != 0;
+ }
+
+ private static int pack(@IntRange(from = 0, to = 1000) int weight, boolean italic) {
+ return weight | (italic ? 0x10000 : 0);
+ }
+
+ private static final int SFNT_VERSION_1 = 0x00010000;
+ private static final int SFNT_VERSION_OTTO = 0x4F54544F;
+ private static final int TTC_TAG = 0x74746366;
+ private static final int OS2_TABLE_TAG = 0x4F532F32;
+
+ /**
+ * Analyze the font file returns packed style info
+ */
+ public static final int analyzeStyle(@NonNull ByteBuffer buffer,
+ @IntRange(from = 0) int ttcIndex, @Nullable FontVariationAxis[] varSettings)
+ throws IOException {
+ int weight = -1;
+ int italic = -1;
+ if (varSettings != null) {
+ for (FontVariationAxis axis :varSettings) {
+ if ("wght".equals(axis.getTag())) {
+ weight = (int) axis.getStyleValue();
+ } else if ("ital".equals(axis.getTag())) {
+ italic = (axis.getStyleValue() == 1.0f) ? 1 : 0;
+ }
+ }
+ }
+
+ if (weight != -1 && italic != -1) {
+ // Both weight/italic style are specifeid by variation settings.
+ // No need to look into OS/2 table.
+ // TODO: Good to look HVAR table to check if this font supports wght/ital axes.
+ return pack(weight, italic == 1);
+ }
+
+ ByteOrder originalOrder = buffer.order();
+ buffer.order(ByteOrder.BIG_ENDIAN);
+ try {
+ int fontFileOffset = 0;
+ int magicNumber = buffer.getInt(0);
+ if (magicNumber == TTC_TAG) {
+ // TTC file.
+ if (ttcIndex >= buffer.getInt(8 /* offset to number of fonts in TTC */)) {
+ throw new IOException("Font index out of bounds");
+ }
+ fontFileOffset = buffer.getInt(
+ 12 /* offset to array of offsets of font files */ + 4 * ttcIndex);
+ }
+ int sfntVersion = buffer.getInt(fontFileOffset);
+
+ if (sfntVersion != SFNT_VERSION_1 && sfntVersion != SFNT_VERSION_OTTO) {
+ throw new IOException("Unknown font file format");
+ }
+
+ int numTables = buffer.getShort(fontFileOffset + 4 /* offset to number of tables */);
+ int os2TableOffset = -1;
+ for (int i = 0; i < numTables; ++i) {
+ int tableOffset = fontFileOffset + 12 /* size of offset table */
+ + i * 16 /* size of table record */;
+ if (buffer.getInt(tableOffset) == OS2_TABLE_TAG) {
+ os2TableOffset = buffer.getInt(tableOffset + 8 /* offset to the table */);
+ break;
+ }
+ }
+
+ if (os2TableOffset == -1) {
+ // Couldn't find OS/2 table. use regular style
+ return pack(400, false);
+ }
+
+ int weightFromOS2 = buffer.getShort(os2TableOffset + 4 /* offset to weight class */);
+ boolean italicFromOS2 =
+ (buffer.getShort(os2TableOffset + 62 /* offset to fsSelection */) & 1) != 0;
+ return pack(weight == -1 ? weightFromOS2 : weight,
+ italic == -1 ? italicFromOS2 : italic == 1);
+ } finally {
+ buffer.order(originalOrder);
+ }
+ }
+}
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index e245425a4897..1030d9de89dc 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -1083,6 +1083,7 @@ public final class AudioFormat implements Parcelable {
ENCODING_AAC_LC,
ENCODING_DOLBY_TRUEHD,
ENCODING_E_AC3_JOC,
+ ENCODING_AC4,
};
/** @hide */
@@ -1093,7 +1094,8 @@ public final class AudioFormat implements Parcelable {
ENCODING_DTS_HD,
ENCODING_AAC_LC,
ENCODING_DOLBY_TRUEHD,
- ENCODING_E_AC3_JOC }
+ ENCODING_E_AC3_JOC,
+ ENCODING_AC4 }
)
@Retention(RetentionPolicy.SOURCE)
public @interface SurroundSoundEncoding {}
@@ -1105,14 +1107,14 @@ public final class AudioFormat implements Parcelable {
* It is just a default to use if an international name is not available.
*
* @param audioFormat a surround format
- * @return short default name for the format, eg. “AC3” for ENCODING_AC3.
+ * @return short default name for the format.
*/
public static String toDisplayName(@SurroundSoundEncoding int audioFormat) {
switch (audioFormat) {
case ENCODING_AC3:
- return "Dolby Digital (AC3)";
+ return "Dolby Digital";
case ENCODING_E_AC3:
- return "Dolby Digital Plus (E_AC3)";
+ return "Dolby Digital Plus";
case ENCODING_DTS:
return "DTS";
case ENCODING_DTS_HD:
@@ -1122,7 +1124,9 @@ public final class AudioFormat implements Parcelable {
case ENCODING_DOLBY_TRUEHD:
return "Dolby TrueHD";
case ENCODING_E_AC3_JOC:
- return "Dolby Atmos";
+ return "Dolby Atmos in Dolby Digital Plus";
+ case ENCODING_AC4:
+ return "Dolby AC-4";
default:
return "Unknown surround sound format";
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java
index d9605367dc56..2bec1d740af2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java
@@ -125,7 +125,7 @@ public class CategoryManager {
}
mCategoryByKeyMap.clear();
mCategories = TileUtils.getCategories(context, mTileByComponentCache,
- false /* categoryDefinedInManifest */, mExtraAction, settingPkg);
+ mExtraAction, settingPkg);
for (DashboardCategory category : mCategories) {
mCategoryByKeyMap.put(category.key, category);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
index 96ed0cdb8ab5..76f24bca8d83 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
@@ -35,7 +35,6 @@ import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
-import androidx.annotation.VisibleForTesting;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -198,36 +197,12 @@ public class TileUtils {
public static final String META_DATA_KEY_PROFILE = "com.android.settings.profile";
/**
- * Build a list of DashboardCategory. Each category must be defined in manifest.
- * eg: .Settings$DeviceSettings
- * @deprecated
- */
- @Deprecated
- public static List<DashboardCategory> getCategories(Context context,
- Map<Pair<String, String>, Tile> cache) {
- return getCategories(context, cache, true /*categoryDefinedInManifest*/);
- }
-
- /**
* Build a list of DashboardCategory.
- * @param categoryDefinedInManifest If true, an dummy activity must exists in manifest to
- * represent this category (eg: .Settings$DeviceSettings)
- */
- public static List<DashboardCategory> getCategories(Context context,
- Map<Pair<String, String>, Tile> cache, boolean categoryDefinedInManifest) {
- return getCategories(context, cache, categoryDefinedInManifest, null, SETTING_PKG);
- }
-
- /**
- * Build a list of DashboardCategory.
- * @param categoryDefinedInManifest If true, an dummy activity must exists in manifest to
- * represent this category (eg: .Settings$DeviceSettings)
* @param extraAction additional intent filter action to be usetileutild to build the dashboard
* categories
*/
public static List<DashboardCategory> getCategories(Context context,
- Map<Pair<String, String>, Tile> cache, boolean categoryDefinedInManifest,
- String extraAction, String settingPkg) {
+ Map<Pair<String, String>, Tile> cache, String extraAction, String settingPkg) {
final long startTime = System.currentTimeMillis();
boolean setup = Global.getInt(context.getContentResolver(), Global.DEVICE_PROVISIONED, 0)
!= 0;
@@ -247,14 +222,12 @@ public class TileUtils {
if (setup) {
getTilesForAction(context, user, EXTRA_SETTINGS_ACTION, cache, null, tiles, false,
settingPkg);
- if (!categoryDefinedInManifest) {
getTilesForAction(context, user, IA_SETTINGS_ACTION, cache, null, tiles, false,
settingPkg);
if (extraAction != null) {
getTilesForAction(context, user, extraAction, cache, null, tiles, false,
settingPkg);
}
- }
}
}
@@ -262,7 +235,9 @@ public class TileUtils {
for (Tile tile : tiles) {
DashboardCategory category = categoryMap.get(tile.category);
if (category == null) {
- category = createCategory(context, tile.category, categoryDefinedInManifest);
+ category = new DashboardCategory();
+ category.key = tile.category;
+
if (category == null) {
Log.w(LOG_TAG, "Couldn't find category " + tile.category);
continue;
@@ -281,40 +256,6 @@ public class TileUtils {
return categories;
}
- /**
- * Create a new DashboardCategory from key.
- *
- * @param context Context to query intent
- * @param categoryKey The category key
- * @param categoryDefinedInManifest If true, an dummy activity must exists in manifest to
- * represent this category (eg: .Settings$DeviceSettings)
- */
- private static DashboardCategory createCategory(Context context, String categoryKey,
- boolean categoryDefinedInManifest) {
- DashboardCategory category = new DashboardCategory();
- category.key = categoryKey;
- if (!categoryDefinedInManifest) {
- return category;
- }
- PackageManager pm = context.getPackageManager();
- List<ResolveInfo> results = pm.queryIntentActivities(new Intent(categoryKey), 0);
- if (results.size() == 0) {
- return null;
- }
- for (ResolveInfo resolved : results) {
- if (!resolved.system) {
- // Do not allow any app to add to settings, only system ones.
- continue;
- }
- category.title = resolved.activityInfo.loadLabel(pm);
- category.priority = SETTING_PKG.equals(
- resolved.activityInfo.applicationInfo.packageName) ? resolved.priority : 0;
- if (DEBUG) Log.d(LOG_TAG, "Adding category " + category.title);
- }
-
- return category;
- }
-
private static void getTilesForAction(Context context,
UserHandle user, String action, Map<Pair<String, String>, Tile> addedCache,
String defaultCategory, ArrayList<Tile> outTiles, boolean requireSettings,
diff --git a/packages/SettingsLib/src/com/android/settingslib/location/InjectedSetting.java b/packages/SettingsLib/src/com/android/settingslib/location/InjectedSetting.java
index 7a5c3be5e419..1805f1a90d10 100644
--- a/packages/SettingsLib/src/com/android/settingslib/location/InjectedSetting.java
+++ b/packages/SettingsLib/src/com/android/settingslib/location/InjectedSetting.java
@@ -178,8 +178,8 @@ public class InjectedSetting {
public InjectedSetting build() {
if (mPackageName == null || mClassName == null || TextUtils.isEmpty(mTitle)
|| TextUtils.isEmpty(mSettingsActivity)) {
- if (Log.isLoggable(BaseSettingsInjector.TAG, Log.WARN)) {
- Log.w(BaseSettingsInjector.TAG, "Illegal setting specification: package="
+ if (Log.isLoggable(SettingsInjector.TAG, Log.WARN)) {
+ Log.w(SettingsInjector.TAG, "Illegal setting specification: package="
+ mPackageName + ", class=" + mClassName
+ ", title=" + mTitle + ", settingsActivity=" + mSettingsActivity);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/location/BaseSettingsInjector.java b/packages/SettingsLib/src/com/android/settingslib/location/SettingsInjector.java
index f2d730f5e807..780fcbab9822 100644
--- a/packages/SettingsLib/src/com/android/settingslib/location/BaseSettingsInjector.java
+++ b/packages/SettingsLib/src/com/android/settingslib/location/SettingsInjector.java
@@ -20,12 +20,14 @@ import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
+import android.graphics.drawable.Drawable;
import android.location.SettingInjectorService;
import android.os.Bundle;
import android.os.Handler;
@@ -36,6 +38,7 @@ import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.AttributeSet;
+import android.util.IconDrawableFactory;
import android.util.Log;
import android.util.Xml;
@@ -62,7 +65,7 @@ import java.util.Set;
* android.content.pm.RegisteredServicesCache#parseServiceAttributes(android.content.res.Resources,
* String, android.util.AttributeSet)} into an interface, which didn't seem worth it.
*/
-public class BaseSettingsInjector {
+public class SettingsInjector {
static final String TAG = "SettingsInjector";
/**
@@ -96,7 +99,7 @@ public class BaseSettingsInjector {
private final Handler mHandler;
- public BaseSettingsInjector(Context context) {
+ public SettingsInjector(Context context) {
mContext = context;
mSettings = new HashSet<Setting>();
mHandler = new StatusLoadingHandler();
@@ -145,6 +148,65 @@ public class BaseSettingsInjector {
}
/**
+ * Adds the InjectedSetting information to a Preference object
+ */
+ private void populatePreference(Preference preference, InjectedSetting setting) {
+ final PackageManager pm = mContext.getPackageManager();
+ Drawable appIcon = null;
+ try {
+ final PackageItemInfo itemInfo = new PackageItemInfo();
+ itemInfo.icon = setting.iconId;
+ itemInfo.packageName = setting.packageName;
+ final ApplicationInfo appInfo = pm.getApplicationInfo(setting.packageName,
+ PackageManager.GET_META_DATA);
+ appIcon = IconDrawableFactory.newInstance(mContext)
+ .getBadgedIcon(itemInfo, appInfo, setting.mUserHandle.getIdentifier());
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Can't get ApplicationInfo for " + setting.packageName, e);
+ }
+ preference.setTitle(setting.title);
+ preference.setSummary(null);
+ preference.setIcon(appIcon);
+ preference.setOnPreferenceClickListener(new ServiceSettingClickedListener(setting));
+ }
+
+ /**
+ * Gets a list of preferences that other apps have injected.
+ *
+ * @param profileId Identifier of the user/profile to obtain the injected settings for or
+ * UserHandle.USER_CURRENT for all profiles associated with current user.
+ */
+ public List<Preference> getInjectedSettings(Context prefContext, final int profileId) {
+ final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ final List<UserHandle> profiles = um.getUserProfiles();
+ ArrayList<Preference> prefs = new ArrayList<>();
+ for (UserHandle userHandle : profiles) {
+ if (profileId == UserHandle.USER_CURRENT || profileId == userHandle.getIdentifier()) {
+ Iterable<InjectedSetting> settings = getSettings(userHandle);
+ for (InjectedSetting setting : settings) {
+ Preference preference = createPreference(prefContext, setting);
+ populatePreference(preference, setting);
+ prefs.add(preference);
+ mSettings.add(new Setting(setting, preference));
+ }
+ }
+ }
+
+ reloadStatusMessages();
+
+ return prefs;
+ }
+
+ /**
+ * Creates an injected Preference
+ *
+ * @return the created Preference
+ */
+ protected Preference createPreference(Context prefContext, InjectedSetting setting) {
+ return new Preference(prefContext);
+ }
+
+ /**
* Returns the settings parsed from the attributes of the
* {@link SettingInjectorService#META_DATA_NAME} tag, or null.
*
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiSavedConfigUtils.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiSavedConfigUtils.java
index 159b2a1047d9..19e38081fcad 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiSavedConfigUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiSavedConfigUtils.java
@@ -55,8 +55,10 @@ public class WifiSavedConfigUtils {
try {
List<PasspointConfiguration> savedPasspointConfigs =
wifiManager.getPasspointConfigurations();
- for (PasspointConfiguration config : savedPasspointConfigs) {
- savedConfigs.add(new AccessPoint(context, config));
+ if (savedPasspointConfigs != null) {
+ for (PasspointConfiguration config : savedPasspointConfigs) {
+ savedConfigs.add(new AccessPoint(context, config));
+ }
}
} catch (UnsupportedOperationException e) {
// Passpoint not supported.
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
index 6e66805795b0..9df43185fd1f 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
@@ -17,6 +17,7 @@
package com.android.settingslib.drawer;
import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
@@ -51,13 +52,14 @@ import android.util.ArrayMap;
import android.util.Pair;
import android.widget.RemoteViews;
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
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 org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
@@ -66,7 +68,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
-@RunWith(RobolectricTestRunner.class)
+@RunWith(SettingsLibRobolectricTestRunner.class)
@Config(shadows = TileUtilsTest.TileUtilsShadowRemoteViews.class)
public class TileUtilsTest {
@@ -176,13 +178,12 @@ public class TileUtilsTest {
.thenReturn(info);
List<DashboardCategory> categoryList = TileUtils.getCategories(
- mContext, cache, false /* categoryDefinedInManifest */, testAction,
- TileUtils.SETTING_PKG);
+ mContext, cache, testAction, TileUtils.SETTING_PKG);
assertThat(categoryList.get(0).getTile(0).category).isEqualTo(testCategory);
}
@Test
- public void getCategories_withPackageName() throws Exception {
+ public void getCategories_withPackageName() {
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
Map<Pair<String, String>, Tile> cache = new ArrayMap<>();
Global.putInt(mContext.getContentResolver(), Global.DEVICE_PROVISIONED, 1);
@@ -192,9 +193,7 @@ public class TileUtilsTest {
userHandleList.add(new UserHandle(ActivityManager.getCurrentUser()));
when(mUserManager.getUserProfiles()).thenReturn(userHandleList);
- TileUtils.getCategories(
- mContext, cache, false /* categoryDefinedInManifest */, null /* action */,
- TileUtils.SETTING_PKG);
+ TileUtils.getCategories(mContext, cache, null /* action */, TileUtils.SETTING_PKG);
verify(mPackageManager, atLeastOnce()).queryIntentActivitiesAsUser(
intentCaptor.capture(), anyInt(), anyInt());
@@ -203,7 +202,7 @@ public class TileUtilsTest {
}
@Test
- public void getTilesForIntent_shouldReadMetadataTitleAsString() throws RemoteException {
+ public void getTilesForIntent_shouldReadMetadataTitleAsString() {
Intent intent = new Intent();
Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
List<Tile> outTiles = new ArrayList<>();
@@ -224,7 +223,7 @@ public class TileUtilsTest {
}
@Test
- public void getTilesForIntent_shouldReadMetadataTitleFromResource() throws RemoteException {
+ public void getTilesForIntent_shouldReadMetadataTitleFromResource() {
Intent intent = new Intent();
Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
List<Tile> outTiles = new ArrayList<>();
@@ -339,7 +338,7 @@ public class TileUtilsTest {
}
@Test
- public void getTilesForIntent_shouldProcessUriContentForSystemApp() throws RemoteException {
+ public void getTilesForIntent_shouldProcessUriContentForSystemApp() {
Intent intent = new Intent();
Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
List<Tile> outTiles = new ArrayList<>();
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 376d16bf1476..2da0818ebc95 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -109,6 +109,7 @@ import com.android.internal.util.DumpUtils;
import com.android.internal.util.IntPair;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
+import com.android.server.SystemService;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
@@ -260,6 +261,25 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
return getUserStateLocked(mCurrentUserId);
}
+ public static final class Lifecycle extends SystemService {
+ private final AccessibilityManagerService mService;
+
+ public Lifecycle(Context context) {
+ super(context);
+ mService = new AccessibilityManagerService(context);
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(Context.ACCESSIBILITY_SERVICE, mService);
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ mService.onBootPhase(phase);
+ }
+ }
+
/**
* Creates a new instance.
*
@@ -296,6 +316,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
return mFingerprintGestureDispatcher;
}
+ private void onBootPhase(int phase) {
+ if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
+ if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS)) {
+ mAppWidgetService = LocalServices.getService(AppWidgetManagerInternal.class);
+ }
+ }
+ }
+
private UserState getUserState(int userId) {
synchronized (mLock) {
return getUserStateLocked(userId);
@@ -2684,16 +2712,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
- private AppWidgetManagerInternal getAppWidgetManager() {
- synchronized (mLock) {
- if (mAppWidgetService == null
- && mPackageManager.hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS)) {
- mAppWidgetService = LocalServices.getService(AppWidgetManagerInternal.class);
- }
- return mAppWidgetService;
- }
- }
-
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out,
FileDescriptor err, String[] args, ShellCallback callback,
@@ -3022,8 +3040,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
return packageName.toString();
}
// Appwidget hosts get to pass packages for widgets they host
- final AppWidgetManagerInternal appWidgetManager = getAppWidgetManager();
- if (appWidgetManager != null && ArrayUtils.contains(appWidgetManager
+ if (mAppWidgetService != null && ArrayUtils.contains(mAppWidgetService
.getHostedWidgetPackages(resolvedUid), packageNameStr)) {
return packageName.toString();
}
@@ -3051,9 +3068,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
// IMPORTANT: The target package is already vetted to be in the target UID
String[] uidPackages = new String[]{targetPackage};
// Appwidget hosts get to pass packages for widgets they host
- final AppWidgetManagerInternal appWidgetManager = getAppWidgetManager();
- if (appWidgetManager != null) {
- final ArraySet<String> widgetPackages = appWidgetManager
+ if (mAppWidgetService != null) {
+ final ArraySet<String> widgetPackages = mAppWidgetService
.getHostedWidgetPackages(targetUid);
if (widgetPackages != null && !widgetPackages.isEmpty()) {
final String[] validPackages = new String[uidPackages.length
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index 5f9f4ebcddbc..552ed160ce17 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -339,6 +339,38 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack>
this, stackId, mSupervisor, windowingMode, activityType, onTop);
}
+ ActivityStack getFocusedStack() {
+ for (int i = mStacks.size() - 1; i >= 0; --i) {
+ final ActivityStack stack = mStacks.get(i);
+ if (stack.isFocusable() && stack.shouldBeVisible(null /* starting */)) {
+ return stack;
+ }
+ }
+
+ return null;
+ }
+
+ ActivityRecord getResumedActivity() {
+ final ActivityStack focusedStack = getFocusedStack();
+ if (focusedStack == null) {
+ return null;
+ }
+ // TODO(b/111541062): Move this into ActivityStack#getResumedActivity()
+ // Check if the focused stack has the resumed activity
+ ActivityRecord resumedActivity = focusedStack.getResumedActivity();
+ if (resumedActivity == null || resumedActivity.app == null) {
+ // If there is no registered resumed activity in the stack or it is not running -
+ // try to use previously resumed one.
+ resumedActivity = focusedStack.mPausingActivity;
+ if (resumedActivity == null || resumedActivity.app == null) {
+ // If previously resumed activity doesn't work either - find the topmost running
+ // activity that can be focused.
+ resumedActivity = focusedStack.topRunningActivityLocked(true /* focusableOnly */);
+ }
+ }
+ return resumedActivity;
+ }
+
/**
* Removes stacks in the input windowing modes from the system if they are of activity type
* ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
@@ -585,7 +617,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack>
/**
* Get the topmost stack on the display. It may be different from focused stack, because
- * focus may be on another display.
+ * some stacks are not focusable (e.g. PiP).
*/
ActivityStack getTopStack() {
return mStacks.isEmpty() ? null : mStacks.get(mStacks.size() - 1);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index f02e8f6f7beb..e1ebbece0fdd 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -91,6 +91,7 @@ import static android.provider.Settings.Global.DEBUG_APP;
import static android.provider.Settings.Global.NETWORK_ACCESS_TIMEOUT_MS;
import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
+import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.internal.util.XmlUtils.readBooleanAttribute;
import static com.android.internal.util.XmlUtils.readIntAttribute;
import static com.android.internal.util.XmlUtils.readLongAttribute;
@@ -12825,8 +12826,8 @@ public class ActivityManagerService extends IActivityManager.Stub
boolean needSep = printedAnything;
boolean printed = ActivityStackSupervisor.printThisActivity(pw,
- mStackSupervisor.getResumedActivityLocked(),
- dumpPackage, needSep, " ResumedActivity: ");
+ mStackSupervisor.getTopResumedActivity(), dumpPackage, needSep,
+ " ResumedActivity: ");
if (printed) {
printedAnything = true;
needSep = false;
@@ -13230,7 +13231,8 @@ public class ActivityManagerService extends IActivityManager.Stub
}
if (dumpAll) {
if (dumpPackage == null) {
- pw.println(" mConfigWillChange: " + mActivityTaskManager.getFocusedStack().mConfigWillChange);
+ pw.println(" mConfigWillChange: "
+ + mActivityTaskManager.getTopDisplayFocusedStack().mConfigWillChange);
}
if (mCompatModePackages.getPackages().size() > 0) {
boolean printed = false;
@@ -13601,7 +13603,7 @@ public class ActivityManagerService extends IActivityManager.Stub
if (dumpPackage == null) {
mUserController.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.USER_CONTROLLER);
getGlobalConfiguration().writeToProto(proto, ActivityManagerServiceDumpProcessesProto.GLOBAL_CONFIGURATION);
- proto.write(ActivityManagerServiceDumpProcessesProto.CONFIG_WILL_CHANGE, mActivityTaskManager.getFocusedStack().mConfigWillChange);
+ proto.write(ActivityManagerServiceDumpProcessesProto.CONFIG_WILL_CHANGE, mActivityTaskManager.getTopDisplayFocusedStack().mConfigWillChange);
}
if (mActivityTaskManager.mHomeProcess != null && (dumpPackage == null
@@ -20642,9 +20644,10 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
- private final ActivityRecord resumedAppLocked() {
- final ActivityRecord act =
- mStackSupervisor != null ? mStackSupervisor.getResumedActivityLocked() : null;
+ // TODO(b/111541062): This method is only used for updating OOM adjustments. We need to update
+ // the logic there and in mBatteryStatsService to make them aware of multiple resumed activities
+ private ActivityRecord resumedAppLocked() {
+ final ActivityRecord act = mStackSupervisor.getTopResumedActivity();
String pkg;
int uid;
if (act != null) {
diff --git a/services/core/java/com/android/server/am/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
index 62f8c7285e6e..d3e3af386add 100644
--- a/services/core/java/com/android/server/am/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
@@ -184,7 +184,7 @@ class ActivityMetricsLogger {
mLastLogTimeSecs = now;
mWindowState = WINDOW_STATE_INVALID;
- ActivityStack stack = mSupervisor.getFocusedStack();
+ ActivityStack stack = mSupervisor.getTopDisplayFocusedStack();
if (stack == null) {
return;
}
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 70f638df6f33..b17aadad6423 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -1792,7 +1792,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
// considers the resumed activity, as normal means will bring the activity from STOPPED
// to RESUMED. Adding PAUSING in this scenario will lead to double lifecycles.
if (!isState(STOPPED, STOPPING) || getStack().mTranslucentActivityWaiting != null
- || mStackSupervisor.getResumedActivityLocked() == this) {
+ || isResumedActivityOnDisplay()) {
return false;
}
@@ -2162,7 +2162,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
// another activity to start or has stopped, then the key dispatching
// timeout should not be caused by this.
if (mStackSupervisor.mActivitiesWaitingForVisibleActivity.contains(this) || stopped) {
- final ActivityStack stack = mStackSupervisor.getFocusedStack();
+ final ActivityStack stack = mStackSupervisor.getTopDisplayFocusedStack();
// Try to use the one which is closest to top.
ActivityRecord r = stack.getResumedActivity();
if (r == null) {
@@ -3003,6 +3003,15 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
return mStackSupervisor.topRunningActivityLocked() == this;
}
+ /**
+ * @return {@code true} if this is the resumed activity on its current display, {@code false}
+ * otherwise.
+ */
+ boolean isResumedActivityOnDisplay() {
+ final ActivityDisplay display = getDisplay();
+ return display != null && this == display.getResumedActivity();
+ }
+
void registerRemoteAnimations(RemoteAnimationDefinition definition) {
mWindowContainerController.registerRemoteAnimations(definition);
}
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index bebaede0d482..d08784fa8576 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -847,7 +847,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
}
}
- private ActivityRecord topRunningActivityLocked(boolean focusableOnly) {
+ ActivityRecord topRunningActivityLocked(boolean focusableOnly) {
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
ActivityRecord r = mTaskHistory.get(taskNdx).topRunningActivityLocked();
if (r != null && (!focusableOnly || r.isFocusable())) {
@@ -1052,12 +1052,14 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
if (!isActivityTypeHome() && returnsToHomeStack()) {
// Make sure the home stack is behind this stack since that is where we should return to
// when this stack is no longer visible.
+ // TODO(b/111541062): Move home stack on the current display
mStackSupervisor.moveHomeStackToFront(reason + " returnToHome");
}
display.positionChildAtTop(this);
mStackSupervisor.setFocusStackUnchecked(reason, this);
if (task != null) {
+ // This also moves the entire hierarchy branch to top, including parents
insertTaskAtTop(task, null);
return;
}
@@ -1083,6 +1085,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
getDisplay().positionChildAtBottom(this);
mStackSupervisor.setFocusStackUnchecked(reason, getDisplay().getTopStack());
if (task != null) {
+ // TODO(b/111541062): We probably don't want to change display z-order to bottom just
+ // because one of its stacks moved to bottom.
insertTaskAtBottom(task);
return;
}
@@ -1611,7 +1615,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
}
if (resumeNext) {
- final ActivityStack topStack = mStackSupervisor.getFocusedStack();
+ final ActivityStack topStack = mStackSupervisor.getTopDisplayFocusedStack();
if (!topStack.shouldSleepOrShutDownActivities()) {
mStackSupervisor.resumeFocusedStackTopActivityLocked(topStack, prev, null);
} else {
@@ -1734,7 +1738,17 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
}
boolean isTopStackOnDisplay() {
- return getDisplay().isTopStack(this);
+ final ActivityDisplay display = getDisplay();
+ return display != null && display.isTopStack(this);
+ }
+
+ /**
+ * @return {@code true} if this is the focused stack on its current display, {@code false}
+ * otherwise.
+ */
+ boolean isFocusedStackOnDisplay() {
+ final ActivityDisplay display = getDisplay();
+ return display != null && this == display.getFocusedStack();
}
boolean isTopActivityVisible() {
@@ -1751,9 +1765,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
if (!isAttached() || mForceHidden) {
return false;
}
- if (mStackSupervisor.isFocusedStack(this)) {
- return true;
- }
final ActivityRecord top = topRunningActivityLocked();
if (top == null && isInStackLocked(starting) == null && !isTopStackOnDisplay()) {
@@ -1883,7 +1894,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
boolean aboveTop = top != null;
final boolean stackShouldBeVisible = shouldBeVisible(starting);
boolean behindFullscreenActivity = !stackShouldBeVisible;
- boolean resumeNextActivity = mStackSupervisor.isFocusedStack(this)
+ boolean resumeNextActivity = mStackSupervisor.isTopDisplayFocusedStack(this)
&& (isInStackLocked(starting) == null);
final boolean isTopNotPinnedStack =
isAttached() && getDisplay().isTopNotPinnedStack(this);
@@ -2435,7 +2446,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
boolean lastResumedCanPip = false;
ActivityRecord lastResumed = null;
- final ActivityStack lastFocusedStack = mStackSupervisor.getLastStack();
+ final ActivityStack lastFocusedStack = mStackSupervisor.getTopDisplayLastFocusedStack();
if (lastFocusedStack != null && lastFocusedStack != this) {
// So, why aren't we using prev here??? See the param comment on the method. prev doesn't
// represent the last resumed activity. However, the last focus stack does if it isn't null.
@@ -2596,7 +2607,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
mStackSupervisor.mNoAnimActivities.clear();
- ActivityStack lastStack = mStackSupervisor.getLastStack();
+ ActivityStack lastStack = mStackSupervisor.getTopDisplayLastFocusedStack();
if (next.attachedToProcess()) {
if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resume running: " + next
+ " stopped=" + next.stopped + " visible=" + next.visible);
@@ -2646,7 +2657,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
// the screen based on the new activity order.
boolean notUpdated = true;
- if (mStackSupervisor.isFocusedStack(this)) {
+ if (mStackSupervisor.isTopDisplayFocusedStack(this)) {
// We have special rotation behavior when here is some active activity that
// requests specific orientation or Keyguard is locked. Make sure all activity
// visibilities are set correctly as well as the transition is updated if needed
@@ -2784,7 +2795,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
// stack is not covering the entire screen or is on a secondary display (with no home
// stack).
return mStackSupervisor.resumeFocusedStackTopActivityLocked(
- mStackSupervisor.getFocusedStack(), prev, null);
+ mStackSupervisor.getTopDisplayFocusedStack(), prev, null);
}
// Let's just start up the Launcher...
@@ -3397,7 +3408,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
}
private void adjustFocusedActivityStack(ActivityRecord r, String reason) {
- if (!mStackSupervisor.isFocusedStack(this) ||
+ if (!mStackSupervisor.isTopDisplayFocusedStack(this) ||
((mResumedActivity != r) && (mResumedActivity != null))) {
return;
}
@@ -3845,7 +3856,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
r.setState(FINISHING, "finishCurrentActivityLocked");
final boolean finishingActivityInNonFocusedStack
- = r.getStack() != mStackSupervisor.getFocusedStack()
+ = r.getStack() != mStackSupervisor.getTopDisplayFocusedStack()
&& prevState == PAUSED && mode == FINISH_AFTER_VISIBLE;
if (mode == FINISH_IMMEDIATELY
@@ -4954,7 +4965,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
*/
void getRunningTasks(List<TaskRecord> tasksOut, @ActivityType int ignoreActivityType,
@WindowingMode int ignoreWindowingMode, int callingUid, boolean allowed) {
- boolean focusedStack = mStackSupervisor.getFocusedStack() == this;
+ boolean focusedStack = mStackSupervisor.getTopDisplayFocusedStack() == this;
boolean topTask = true;
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = mTaskHistory.get(taskNdx);
@@ -5161,7 +5172,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
// We only need to adjust focused stack if this stack is in focus and we are not in the
// process of moving the task to the top of the stack that will be focused.
if (isOnHomeDisplay() && mode != REMOVE_TASK_MODE_MOVING_TO_TOP
- && mStackSupervisor.isFocusedStack(this)) {
+ && mStackSupervisor.isTopDisplayFocusedStack(this)) {
String myReason = reason + " leftTaskHistoryEmpty";
if (!inMultiWindowMode() || !adjustFocusToNextFocusableStack(myReason)) {
mStackSupervisor.moveHomeStackToFront(myReason);
@@ -5365,7 +5376,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
// Do not sleep activities in this stack if we're marked as focused and the keyguard
// is in the process of going away.
- if (mStackSupervisor.getFocusedStack() == this
+ if (mStackSupervisor.getTopDisplayFocusedStack() == this
&& mStackSupervisor.getKeyguardController().isKeyguardGoingAway()) {
return false;
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 4b9500510cdd..4e39033d83d8 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -337,9 +337,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
* Display.DEFAULT_DISPLAY. */
ActivityStack mHomeStack;
- /** The stack currently receiving input or launching the next activity. */
- ActivityStack mFocusedStack;
-
/** If this is the same as mFocusedStack then the activity on the top of the focused stack has
* been resumed. If stacks are changing position this will hold the old stack until the new
* stack becomes resumed after which it will be set to mFocusedStack. */
@@ -683,12 +680,62 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
calculateDefaultMinimalSizeOfResizeableTasks(activityDisplay);
}
- mHomeStack = mFocusedStack = mLastFocusedStack = getDefaultDisplay().getOrCreateStack(
+ final ActivityDisplay defaultDisplay = getDefaultDisplay();
+ mHomeStack = mLastFocusedStack = defaultDisplay.getOrCreateStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
}
- ActivityStack getFocusedStack() {
- return mFocusedStack;
+ ActivityStack getTopDisplayFocusedStack() {
+ mWindowManager.getDisplaysInFocusOrder(mTmpOrderedDisplayIds);
+
+ for (int i = mTmpOrderedDisplayIds.size() - 1; i >= 0; --i) {
+ final int displayId = mTmpOrderedDisplayIds.get(i);
+ final ActivityDisplay display = mActivityDisplays.get(displayId);
+
+ // If WindowManagerService has encountered the display before we have, ignore as there
+ // will be no stacks present and therefore no activities.
+ if (display == null) {
+ continue;
+ }
+ final ActivityStack focusedStack = display.getFocusedStack();
+ if (focusedStack != null) {
+ return focusedStack;
+ }
+ }
+ return null;
+ }
+
+ ActivityRecord getTopResumedActivity() {
+ if (mWindowManager == null) {
+ return null;
+ }
+
+ final ActivityStack focusedStack = getTopDisplayFocusedStack();
+ if (focusedStack == null) {
+ return null;
+ }
+ final ActivityRecord resumedActivity = focusedStack.getResumedActivity();
+ if (resumedActivity != null && resumedActivity.app != null) {
+ return resumedActivity;
+ }
+ // The top focused stack might not have a resumed activity yet - look on all displays in
+ // focus order.
+ mWindowManager.getDisplaysInFocusOrder(mTmpOrderedDisplayIds);
+ for (int i = mTmpOrderedDisplayIds.size() - 1; i >= 0; --i) {
+ final int displayId = mTmpOrderedDisplayIds.get(i);
+ final ActivityDisplay display = mActivityDisplays.get(displayId);
+
+ // If WindowManagerService has encountered the display before we have, ignore as there
+ // will be no stacks present and therefore no activities.
+ if (display == null) {
+ continue;
+ }
+ final ActivityRecord resumedActivityOnDisplay = display.getResumedActivity();
+ if (resumedActivityOnDisplay != null) {
+ return resumedActivityOnDisplay;
+ }
+ }
+ return null;
}
boolean isFocusable(ConfigurationContainer container, boolean alwaysFocusable) {
@@ -699,12 +746,12 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
return container.getWindowConfiguration().canReceiveKeys() || alwaysFocusable;
}
- ActivityStack getLastStack() {
+ ActivityStack getTopDisplayLastFocusedStack() {
return mLastFocusedStack;
}
- boolean isFocusedStack(ActivityStack stack) {
- return stack != null && stack == mFocusedStack;
+ boolean isTopDisplayFocusedStack(ActivityStack stack) {
+ return stack != null && stack == getTopDisplayFocusedStack();
}
/** NOTE: Should only be called from {@link ActivityStack#moveToFront} */
@@ -719,12 +766,12 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
}
}
- if (focusCandidate != mFocusedStack) {
- mLastFocusedStack = mFocusedStack;
- mFocusedStack = focusCandidate;
-
+ final ActivityStack currentFocusedStack = getTopDisplayFocusedStack();
+ if (currentFocusedStack != focusCandidate) {
+ mLastFocusedStack = currentFocusedStack;
+ // TODO(b/111541062): Update event log to include focus movements on all displays
EventLogTags.writeAmFocusedStack(
- mCurrentUser, mFocusedStack == null ? -1 : mFocusedStack.getStackId(),
+ mCurrentUser, focusCandidate == null ? -1 : focusCandidate.getStackId(),
mLastFocusedStack == null ? -1 : mLastFocusedStack.getStackId(), reason);
}
@@ -962,21 +1009,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
return candidateTaskId;
}
- ActivityRecord getResumedActivityLocked() {
- ActivityStack stack = mFocusedStack;
- if (stack == null) {
- return null;
- }
- ActivityRecord resumedActivity = stack.getResumedActivity();
- if (resumedActivity == null || resumedActivity.app == null) {
- resumedActivity = stack.mPausingActivity;
- if (resumedActivity == null || resumedActivity.app == null) {
- resumedActivity = stack.topRunningActivityLocked();
- }
- }
- return resumedActivity;
- }
-
boolean attachApplicationLocked(ProcessRecord app) throws RemoteException {
final String processName = app.processName;
boolean didSomething = false;
@@ -984,7 +1016,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getChildAt(stackNdx);
- if (!isFocusedStack(stack)) {
+ if (!isTopDisplayFocusedStack(stack)) {
continue;
}
stack.getAllRunningVisibleActivitiesLocked(mTmpActivityList);
@@ -1019,7 +1051,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getChildAt(stackNdx);
- if (!isFocusedStack(stack) || stack.numActivities() == 0) {
+ if (!isTopDisplayFocusedStack(stack) || stack.numActivities() == 0) {
continue;
}
final ActivityRecord resumedActivity = stack.getResumedActivity();
@@ -1040,7 +1072,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getChildAt(stackNdx);
- if (isFocusedStack(stack)) {
+ if (isTopDisplayFocusedStack(stack)) {
final ActivityRecord r = stack.getResumedActivity();
if (r != null && !r.isState(RESUMED)) {
return false;
@@ -1049,10 +1081,11 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
}
}
// TODO: Not sure if this should check if all Paused are complete too.
+ final ActivityStack focusedStack = getTopDisplayFocusedStack();
if (DEBUG_STACK) Slog.d(TAG_STACK,
- "allResumedActivitiesComplete: mLastFocusedStack changing from=" +
- mLastFocusedStack + " to=" + mFocusedStack);
- mLastFocusedStack = mFocusedStack;
+ "allResumedActivitiesComplete: mLastFocusedStack changing from="
+ + mLastFocusedStack + " to=" + focusedStack);
+ mLastFocusedStack = focusedStack;
return true;
}
@@ -1088,7 +1121,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getChildAt(stackNdx);
- if (!isFocusedStack(stack) && stack.getResumedActivity() != null) {
+ if (!isTopDisplayFocusedStack(stack) && stack.getResumedActivity() != null) {
if (DEBUG_STATES) Slog.d(TAG_STATES, "pauseBackStacks: stack=" + stack +
" mResumedActivity=" + stack.getResumedActivity());
someActivityPaused |= stack.startPausingLocked(userLeaving, false, resuming,
@@ -1236,7 +1269,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
* @return The top running activity. {@code null} if none is available.
*/
ActivityRecord topRunningActivityLocked(boolean considerKeyguardState) {
- final ActivityStack focusedStack = mFocusedStack;
+ final ActivityStack focusedStack = getTopDisplayFocusedStack();
ActivityRecord r = focusedStack.topRunningActivityLocked();
if (r != null && isValidTopRunningActivity(r, considerKeyguardState)) {
return r;
@@ -1618,7 +1651,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
// launching the initial activity (that is, home), so that it can have
// a chance to initialize itself while in the background, making the
// switch back to it faster and look better.
- if (isFocusedStack(stack)) {
+ if (isTopDisplayFocusedStack(stack)) {
mService.getActivityStartController().startSetupActivity();
}
@@ -1725,12 +1758,27 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
boolean sendHint = forceSend;
if (!sendHint) {
- // If not forced, send power hint when the activity's process is different than the
- // current resumed activity.
- final ActivityRecord resumedActivity = getResumedActivityLocked();
- sendHint = resumedActivity == null
- || resumedActivity.app == null
- || !resumedActivity.app.equals(targetActivity.app);
+ // Send power hint if we don't know what we're launching yet
+ sendHint = targetActivity == null || targetActivity.app == null;
+ }
+
+ if (!sendHint) { // targetActivity != null
+ // Send power hint when the activity's process is different than the current resumed
+ // activity on all displays, or if there are no resumed activities in the system.
+ boolean noResumedActivities = true;
+ boolean allFocusedProcessesDiffer = true;
+ for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
+ final ActivityDisplay activityDisplay = mActivityDisplays.valueAt(displayNdx);
+ final ActivityRecord resumedActivity = activityDisplay.getResumedActivity();
+ final WindowProcessController resumedActivityProcess =
+ resumedActivity == null ? null : resumedActivity.app;
+
+ noResumedActivities &= resumedActivityProcess == null;
+ if (resumedActivityProcess != null) {
+ allFocusedProcessesDiffer &= !resumedActivityProcess.equals(targetActivity.app);
+ }
+ }
+ sendHint = noResumedActivities || allFocusedProcessesDiffer;
}
if (sendHint && mService.mAm.mLocalPowerManager != null) {
@@ -2049,7 +2097,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
r.idle = true;
//Slog.i(TAG, "IDLE: mBooted=" + mBooted + ", fromTimeout=" + fromTimeout);
- if (isFocusedStack(r.getStack()) || fromTimeout) {
+ if (isTopDisplayFocusedStack(r.getStack()) || fromTimeout) {
booting = checkFinishBootingLocked();
}
}
@@ -2197,7 +2245,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getChildAt(stackNdx);
- if (isFocusedStack(stack)) {
+ if (isTopDisplayFocusedStack(stack)) {
final ActivityRecord resumedActivity = stack.getResumedActivity();
if (resumedActivity != null) {
fgApp = resumedActivity.app;
@@ -2230,16 +2278,17 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
return false;
}
- if (targetStack != null && isFocusedStack(targetStack)) {
+ if (targetStack != null && isTopDisplayFocusedStack(targetStack)) {
return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
}
- final ActivityRecord r = mFocusedStack.topRunningActivityLocked();
+ final ActivityStack focusedStack = getTopDisplayFocusedStack();
+ final ActivityRecord r = focusedStack.topRunningActivityLocked();
if (r == null || !r.isState(RESUMED)) {
- mFocusedStack.resumeTopActivityUncheckedLocked(null, null);
+ focusedStack.resumeTopActivityUncheckedLocked(null, null);
} else if (r.isState(RESUMED)) {
// Kick off any lingering app transitions form the MoveTaskToFront operation.
- mFocusedStack.executeAppTransition(targetOptions);
+ focusedStack.executeAppTransition(targetOptions);
}
return false;
@@ -2263,7 +2312,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
*/
TaskRecord finishTopCrashedActivitiesLocked(WindowProcessController app, String reason) {
TaskRecord finishedTask = null;
- ActivityStack focusedStack = getFocusedStack();
+ ActivityStack focusedStack = getTopDisplayFocusedStack();
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
// It is possible that request to finish activity might also remove its task and stack,
@@ -3405,7 +3454,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
return false;
}
- if (stack == mFocusedStack && stack.topRunningActivityLocked() == r) {
+ if (r == getTopResumedActivity()) {
if (DEBUG_FOCUS) Slog.d(TAG_FOCUS,
"moveActivityStackToFront: already on top, r=" + r);
return false;
@@ -3565,8 +3614,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
stack.goToSleepIfPossible(false /* shuttingDown */);
} else {
stack.awakeFromSleepingLocked();
- if (isFocusedStack(stack) && !getKeyguardController().isKeyguardOrAodShowing(
- display.mDisplayId)) {
+ if (isTopDisplayFocusedStack(stack) && !getKeyguardController()
+ .isKeyguardOrAodShowing(display.mDisplayId)) {
// If the keyguard is unlocked - resume immediately.
// It is possible that the display will not be awake at the time we
// process the keyguard going away, which can happen before the sleep token
@@ -3645,7 +3694,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
mStoppingActivities.remove(r);
final ActivityStack stack = r.getStack();
- if (isFocusedStack(stack)) {
+ if (isTopDisplayFocusedStack(stack)) {
mService.mAm.updateUsageStats(r, true);
}
if (allResumedActivitiesComplete()) {
@@ -3793,11 +3842,11 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
}
boolean switchUserLocked(int userId, UserState uss) {
- final int focusStackId = mFocusedStack.getStackId();
+ final int focusStackId = getTopDisplayFocusedStack().getStackId();
// We dismiss the docked stack whenever we switch users.
final ActivityStack dockedStack = getDefaultDisplay().getSplitScreenPrimaryStack();
if (dockedStack != null) {
- moveTasksToFullscreenStackLocked(dockedStack, mFocusedStack == dockedStack);
+ moveTasksToFullscreenStackLocked(dockedStack, dockedStack.isFocusedStackOnDisplay());
}
// Also dismiss the pinned stack whenever we switch users. Removing the pinned stack will
// also cause all tasks to be moved to the fullscreen stack at a position that is
@@ -3919,7 +3968,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
final ActivityStack stack = display.getChildAt(stackNdx);
final ActivityRecord r = stack.topRunningActivityLocked();
final ActivityState state = r == null ? DESTROYED : r.getState();
- if (isFocusedStack(stack)) {
+ if (isTopDisplayFocusedStack(stack)) {
if (r == null) Slog.e(TAG,
"validateTop...: null top activity, stack=" + stack);
else {
@@ -3953,7 +4002,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
}
public void dump(PrintWriter pw, String prefix) {
- pw.print(prefix); pw.print("mFocusedStack=" + mFocusedStack);
+ pw.print(prefix); pw.print("mFocusedStack=" + getTopDisplayFocusedStack());
pw.print(" mLastFocusedStack="); pw.println(mLastFocusedStack);
pw.print(prefix);
pw.println("mCurTaskIdForUser=" + mCurTaskIdForUser);
@@ -3979,13 +4028,15 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
final long token = proto.start(fieldId);
super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */);
for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
- ActivityDisplay activityDisplay = mActivityDisplays.valueAt(displayNdx);
+ final ActivityDisplay activityDisplay = mActivityDisplays.valueAt(displayNdx);
activityDisplay.writeToProto(proto, DISPLAYS);
}
getKeyguardController().writeToProto(proto, KEYGUARD_CONTROLLER);
- if (mFocusedStack != null) {
- proto.write(FOCUSED_STACK_ID, mFocusedStack.mStackId);
- ActivityRecord focusedActivity = getResumedActivityLocked();
+ // TODO(b/111541062): Update tests to look for resumed activities on all displays
+ final ActivityStack focusedStack = getTopDisplayFocusedStack();
+ if (focusedStack != null) {
+ proto.write(FOCUSED_STACK_ID, focusedStack.mStackId);
+ final ActivityRecord focusedActivity = focusedStack.getDisplay().getResumedActivity();
if (focusedActivity != null) {
focusedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY);
}
@@ -4019,7 +4070,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
ArrayList<ActivityRecord> getDumpActivitiesLocked(String name, boolean dumpVisibleStacksOnly,
boolean dumpFocusedStackOnly) {
if (dumpFocusedStackOnly) {
- return mFocusedStack.getDumpActivitiesLocked(name);
+ return getTopDisplayFocusedStack().getDumpActivitiesLocked(name);
} else {
ArrayList<ActivityRecord> activities = new ArrayList<>();
int numDisplays = mActivityDisplays.size();
@@ -4101,6 +4152,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
}
needSep = printed;
}
+ printThisActivity(pw, activityDisplay.getResumedActivity(), dumpPackage, needSep,
+ " ResumedActivity:");
}
printed |= dumpHistoryList(fd, pw, mFinishingActivities, " ", "Fin", false, !dumpAll,
@@ -4586,9 +4639,12 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
}
void setDockedStackMinimized(boolean minimized) {
+ // Get currently focused stack before setting mIsDockMinimized. We do this because if
+ // split-screen is active, primary stack will not be focusable (see #isFocusable) while
+ // still occluding other stacks. This will cause getTopDisplayFocusedStack() to return null.
+ final ActivityStack current = getTopDisplayFocusedStack();
mIsDockMinimized = minimized;
if (mIsDockMinimized) {
- final ActivityStack current = getFocusedStack();
if (current.inSplitScreenPrimaryWindowingMode()) {
// The primary split-screen stack can't be focused while it is minimize, so move
// focus to something else.
@@ -4852,6 +4908,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
*/
List<IBinder> getTopVisibleActivities() {
final ArrayList<IBinder> topActivityTokens = new ArrayList<>();
+ final ActivityStack topFocusedStack = getTopDisplayFocusedStack();
// Traverse all displays.
for (int i = mActivityDisplays.size() - 1; i >= 0; i--) {
final ActivityDisplay display = mActivityDisplays.valueAt(i);
@@ -4862,7 +4919,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
if (stack.shouldBeVisible(null /* starting */)) {
final ActivityRecord top = stack.getTopActivity();
if (top != null) {
- if (stack == mFocusedStack) {
+ if (stack == topFocusedStack) {
topActivityTokens.add(0, top.appToken);
} else {
topActivityTokens.add(top.appToken);
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index d59a651a7a33..0572ca936f64 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -802,10 +802,10 @@ class ActivityStarter {
null /*profilerInfo*/);
if (DEBUG_PERMISSIONS_REVIEW) {
+ final ActivityStack focusedStack = mSupervisor.getTopDisplayFocusedStack();
Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true,
true, false) + "} from uid " + callingUid + " on display "
- + (mSupervisor.mFocusedStack == null
- ? DEFAULT_DISPLAY : mSupervisor.mFocusedStack.mDisplayId));
+ + (focusedStack == null ? DEFAULT_DISPLAY : focusedStack.mDisplayId));
}
}
}
@@ -839,7 +839,7 @@ class ActivityStarter {
r.appTimeTracker = sourceRecord.appTimeTracker;
}
- final ActivityStack stack = mSupervisor.mFocusedStack;
+ final ActivityStack stack = mSupervisor.getTopDisplayFocusedStack();
// If we are starting an activity that is not from the same uid as the currently resumed
// one, check whether app switches are allowed.
@@ -1013,7 +1013,7 @@ class ActivityStarter {
ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);
synchronized (mService.mGlobalLock) {
- final ActivityStack stack = mSupervisor.mFocusedStack;
+ final ActivityStack stack = mSupervisor.getTopDisplayFocusedStack();
stack.mConfigWillChange = globalConfig != null
&& mService.getGlobalConfiguration().diff(globalConfig) != 0;
if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
@@ -1354,7 +1354,7 @@ class ActivityStarter {
// If the activity being launched is the same as the one currently at the top, then
// we need to check if it should only be launched once.
- final ActivityStack topStack = mSupervisor.mFocusedStack;
+ final ActivityStack topStack = mSupervisor.getTopDisplayFocusedStack();
final ActivityRecord topFocused = topStack.getTopActivity();
final ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(mNotTop);
final boolean dontStart = top != null && mStartActivity.resultTo == null
@@ -1447,7 +1447,8 @@ class ActivityStarter {
// will not update the focused stack. If starting the new activity now allows the
// task stack to be focusable, then ensure that we now update the focused stack
// accordingly.
- if (mTargetStack.isFocusable() && !mSupervisor.isFocusedStack(mTargetStack)) {
+ if (mTargetStack.isFocusable()
+ && !mSupervisor.isTopDisplayFocusedStack(mTargetStack)) {
mTargetStack.moveToFront("startActivityUnchecked");
}
mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity,
@@ -1609,8 +1610,8 @@ class ActivityStarter {
if ((startFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
ActivityRecord checkedCaller = sourceRecord;
if (checkedCaller == null) {
- checkedCaller = mSupervisor.mFocusedStack.topRunningNonDelayedActivityLocked(
- mNotTop);
+ checkedCaller = mSupervisor.getTopDisplayFocusedStack()
+ .topRunningNonDelayedActivityLocked(mNotTop);
}
if (!checkedCaller.realActivity.equals(r.realActivity)) {
// Caller is not the same as launcher, so always needed.
@@ -1843,7 +1844,7 @@ class ActivityStarter {
// except... well, with SINGLE_TASK_LAUNCH it's not entirely clear. We'd like to have
// the same behavior as if a new instance was being started, which means not bringing it
// to the front if the caller is not itself in the front.
- final ActivityStack focusStack = mSupervisor.getFocusedStack();
+ final ActivityStack focusStack = mSupervisor.getTopDisplayFocusedStack();
ActivityRecord curTop = (focusStack == null)
? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop);
@@ -2304,23 +2305,23 @@ class ActivityStarter {
}
final ActivityStack currentStack = task != null ? task.getStack() : null;
+ final ActivityStack focusedStack = mSupervisor.getTopDisplayFocusedStack();
if (currentStack != null) {
- if (mSupervisor.mFocusedStack != currentStack) {
+ if (focusedStack != currentStack) {
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
"computeStackFocus: Setting " + "focused stack to r=" + r
+ " task=" + task);
} else {
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
- "computeStackFocus: Focused stack already="
- + mSupervisor.mFocusedStack);
+ "computeStackFocus: Focused stack already=" + focusedStack);
}
return currentStack;
}
if (canLaunchIntoFocusedStack(r, newTask)) {
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
- "computeStackFocus: Have a focused stack=" + mSupervisor.mFocusedStack);
- return mSupervisor.mFocusedStack;
+ "computeStackFocus: Have a focused stack=" + focusedStack);
+ return focusedStack;
}
if (mPreferredDisplayId != DEFAULT_DISPLAY) {
@@ -2356,7 +2357,7 @@ class ActivityStarter {
/** Check if provided activity record can launch in currently focused stack. */
// TODO: This method can probably be consolidated into getLaunchStack() below.
private boolean canLaunchIntoFocusedStack(ActivityRecord r, boolean newTask) {
- final ActivityStack focusedStack = mSupervisor.mFocusedStack;
+ final ActivityStack focusedStack = mSupervisor.getTopDisplayFocusedStack();
final boolean canUseFocusedStack;
if (focusedStack.isActivityTypeAssistant()) {
canUseFocusedStack = r.isActivityTypeAssistant();
@@ -2406,18 +2407,19 @@ class ActivityStarter {
}
// Otherwise handle adjacent launch.
+ final ActivityStack focusedStack = mSupervisor.getTopDisplayFocusedStack();
// The parent activity doesn't want to launch the activity on top of itself, but
// instead tries to put it onto other side in side-by-side mode.
- final ActivityStack parentStack = task != null ? task.getStack(): mSupervisor.mFocusedStack;
+ final ActivityStack parentStack = task != null ? task.getStack(): focusedStack;
- if (parentStack != mSupervisor.mFocusedStack) {
+ if (parentStack != focusedStack) {
// If task's parent stack is not focused - use it during adjacent launch.
return parentStack;
} else {
- if (mSupervisor.mFocusedStack != null && task == mSupervisor.mFocusedStack.topTask()) {
+ if (focusedStack != null && task == focusedStack.topTask()) {
// If task is already on top of focused stack - use it. We don't want to move the
// existing focused task to adjacent stack, just deliver new intent in this case.
- return mSupervisor.mFocusedStack;
+ return focusedStack;
}
if (parentStack != null && parentStack.inSplitScreenPrimaryWindowingMode()) {
diff --git a/services/core/java/com/android/server/am/ActivityTaskManagerService.java b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
index 3ed2875c7377..10e0182e62eb 100644
--- a/services/core/java/com/android/server/am/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
@@ -766,7 +766,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
synchronized (mGlobalLock) {
// If this is coming from the currently resumed activity, it is
// effectively saying that app switches are allowed at this point.
- final ActivityStack stack = getFocusedStack();
+ final ActivityStack stack = getTopDisplayFocusedStack();
if (stack.mResumedActivity != null &&
stack.mResumedActivity.info.applicationInfo.uid == Binder.getCallingUid()) {
mAppSwitchesAllowedTime = 0;
@@ -1371,7 +1371,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
r.immersive = immersive;
// update associated state if we're frontmost
- if (r == mStackSupervisor.getResumedActivityLocked()) {
+ if (r.isResumedActivityOnDisplay()) {
if (DEBUG_IMMERSIVE) Slog.d(TAG_IMMERSIVE, "Frontmost changed immersion: "+ r);
applyUpdateLockStateLocked(r);
}
@@ -1412,7 +1412,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
public boolean isTopActivityImmersive() {
enforceNotIsolatedCaller("isTopActivityImmersive");
synchronized (mGlobalLock) {
- final ActivityRecord r = getFocusedStack().topRunningActivityLocked();
+ final ActivityRecord r = getTopDisplayFocusedStack().topRunningActivityLocked();
return (r != null) ? r.immersive : false;
}
}
@@ -1443,7 +1443,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
enforceNotIsolatedCaller("getFrontActivityScreenCompatMode");
ApplicationInfo ai;
synchronized (mGlobalLock) {
- final ActivityRecord r = getFocusedStack().topRunningActivityLocked();
+ final ActivityRecord r = getTopDisplayFocusedStack().topRunningActivityLocked();
if (r == null) {
return ActivityManager.COMPAT_MODE_UNKNOWN;
}
@@ -1459,7 +1459,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
"setFrontActivityScreenCompatMode");
ApplicationInfo ai;
synchronized (mGlobalLock) {
- final ActivityRecord r = getFocusedStack().topRunningActivityLocked();
+ final ActivityRecord r = getTopDisplayFocusedStack().topRunningActivityLocked();
if (r == null) {
Slog.w(TAG, "setFrontActivityScreenCompatMode failed: no top activity");
return;
@@ -1583,7 +1583,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
- ActivityStack focusedStack = getFocusedStack();
+ ActivityStack focusedStack = getTopDisplayFocusedStack();
if (focusedStack != null) {
return mStackSupervisor.getStackInfo(focusedStack.mStackId);
}
@@ -1828,7 +1828,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
synchronized (mGlobalLock) {
final long origId = Binder.clearCallingIdentity();
try {
- getFocusedStack().unhandledBackLocked();
+ getTopDisplayFocusedStack().unhandledBackLocked();
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -2280,7 +2280,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
return;
}
- final ActivityStack stack = mStackSupervisor.getFocusedStack();
+ final ActivityStack stack = mStackSupervisor.getTopDisplayFocusedStack();
if (stack == null || task != stack.topTask()) {
throw new IllegalArgumentException("Invalid task, not in foreground");
}
@@ -2948,7 +2948,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
"enqueueAssistContext()");
synchronized (mGlobalLock) {
- ActivityRecord activity = getFocusedStack().getTopActivity();
+ ActivityRecord activity = getTopDisplayFocusedStack().getTopActivity();
if (activity == null) {
Slog.w(TAG, "getAssistContextExtras failed: no top activity");
return null;
@@ -3076,7 +3076,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
public boolean isAssistDataAllowedOnCurrentActivity() {
int userId;
synchronized (mGlobalLock) {
- final ActivityStack focusedStack = getFocusedStack();
+ final ActivityStack focusedStack = getTopDisplayFocusedStack();
if (focusedStack == null || focusedStack.isActivityTypeAssistant()) {
return false;
}
@@ -3096,7 +3096,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
try {
synchronized (mGlobalLock) {
ActivityRecord caller = ActivityRecord.forTokenLocked(token);
- ActivityRecord top = getFocusedStack().getTopActivity();
+ ActivityRecord top = getTopDisplayFocusedStack().getTopActivity();
if (top != caller) {
Slog.w(TAG, "showAssistFromActivity failed: caller " + caller
+ " is not current top " + top);
@@ -3314,7 +3314,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
// Caller wants the current split-screen primary stack to be the top stack after
// it goes fullscreen, so move it to the front.
stack.moveToFront("dismissSplitScreenMode");
- } else if (mStackSupervisor.isFocusedStack(stack)) {
+ } else if (mStackSupervisor.isTopDisplayFocusedStack(stack)) {
// In this case the current split-screen primary stack shouldn't be the top
// stack after it goes fullscreen, but it current has focus, so we move the
// focus to the top-most split-screen secondary stack next to it.
@@ -3703,7 +3703,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
r.requestedVrComponent = (enabled) ? packageName : null;
// Update associated state if this activity is currently focused
- if (r == mStackSupervisor.getResumedActivityLocked()) {
+ if (r.isResumedActivityOnDisplay()) {
applyUpdateVrModeLocked(r);
}
return 0;
@@ -3717,7 +3717,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
public void startLocalVoiceInteraction(IBinder callingActivity, Bundle options) {
Slog.i(TAG, "Activity tried to startLocalVoiceInteraction");
synchronized (mGlobalLock) {
- ActivityRecord activity = getFocusedStack().getTopActivity();
+ ActivityRecord activity = getTopDisplayFocusedStack().getTopActivity();
if (ActivityRecord.forTokenLocked(callingActivity) != activity) {
throw new SecurityException("Only focused activity can call startVoiceInteraction");
}
@@ -4130,8 +4130,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
});
}
- ActivityStack getFocusedStack() {
- return mStackSupervisor.getFocusedStack();
+ ActivityStack getTopDisplayFocusedStack() {
+ return mStackSupervisor.getTopDisplayFocusedStack();
}
/** Pokes the task persister. */
@@ -4720,8 +4720,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
mH.post(mAmInternal::updateOomAdj);
}
+ // TODO(b/111541062): Update app time tracking to make it aware of multiple resumed activities
private void startTimeTrackingFocusedActivityLocked() {
- final ActivityRecord resumedActivity = mStackSupervisor.getResumedActivityLocked();
+ final ActivityRecord resumedActivity = mStackSupervisor.getTopResumedActivity();
if (!mSleeping && mCurAppTimeTracker != null && resumedActivity != null) {
mCurAppTimeTracker.start(resumedActivity.packageName);
}
@@ -4768,7 +4769,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
/** Applies latest configuration and/or visibility updates if needed. */
private boolean ensureConfigAndVisibilityAfterUpdate(ActivityRecord starting, int changes) {
boolean kept = true;
- final ActivityStack mainStack = mStackSupervisor.getFocusedStack();
+ final ActivityStack mainStack = mStackSupervisor.getTopDisplayFocusedStack();
// mainStack is null during startup.
if (mainStack != null) {
if (changes != 0 && starting == null) {
diff --git a/services/core/java/com/android/server/am/CompatModePackages.java b/services/core/java/com/android/server/am/CompatModePackages.java
index 38254b881f46..77efbfc0b387 100644
--- a/services/core/java/com/android/server/am/CompatModePackages.java
+++ b/services/core/java/com/android/server/am/CompatModePackages.java
@@ -317,7 +317,7 @@ public final class CompatModePackages {
scheduleWrite();
- final ActivityStack stack = mService.mActivityTaskManager.getFocusedStack();
+ final ActivityStack stack = mService.mActivityTaskManager.getTopDisplayFocusedStack();
ActivityRecord starting = stack.restartPackage(packageName);
// Tell all processes that loaded this package about the change.
diff --git a/services/core/java/com/android/server/am/KeyguardController.java b/services/core/java/com/android/server/am/KeyguardController.java
index efde70de536b..e345b4d61cc1 100644
--- a/services/core/java/com/android/server/am/KeyguardController.java
+++ b/services/core/java/com/android/server/am/KeyguardController.java
@@ -272,7 +272,7 @@ class KeyguardController {
// Only the top activity of the focused stack on the default display may control
// occluded state.
if (display.mDisplayId == DEFAULT_DISPLAY
- && mStackSupervisor.isFocusedStack(stack)) {
+ && mStackSupervisor.isTopDisplayFocusedStack(stack)) {
// A dismissing activity occludes Keyguard in the insecure case for legacy
// reasons.
@@ -381,7 +381,7 @@ class KeyguardController {
return;
}
mStackSupervisor.moveTasksToFullscreenStackLocked(stack,
- mStackSupervisor.mFocusedStack == stack);
+ stack.isFocusedStackOnDisplay());
}
}
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 05869bbc3d53..11684afca5c4 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -657,7 +657,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
boolean kept = true;
try {
final ActivityRecord r = topRunningActivityLocked();
- final boolean wasFocused = r != null && supervisor.isFocusedStack(sourceStack)
+ final boolean wasFocused = r != null && supervisor.isTopDisplayFocusedStack(sourceStack)
&& (topRunningActivityLocked() == r);
final boolean wasResumed = r != null && sourceStack.getResumedActivity() == r;
final boolean wasPaused = r != null && sourceStack.mPausingActivity == r;
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 0a77269f63fc..75b3556f9206 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2165,6 +2165,24 @@ public final class DisplayManagerService extends SystemService {
}
@Override
+ public boolean screenshot(int displayId, Surface outSurface) {
+ synchronized (mSyncRoot) {
+ final LogicalDisplay display = mLogicalDisplays.get(displayId);
+ if (display != null) {
+ final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
+ if (device != null) {
+ final IBinder token = device.getDisplayTokenLocked();
+ if (token != null) {
+ SurfaceControl.screenshot(token, outSurface);
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
public DisplayInfo getDisplayInfo(int displayId) {
return getDisplayInfoInternal(displayId, Process.myUid());
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 846adcca2d5a..14807e9841f7 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -2079,6 +2079,11 @@ public class HdmiControlService extends SystemService {
return mWakeUpMessageReceived;
}
+ @VisibleForTesting
+ boolean isStandbyMessageReceived() {
+ return mStandbyMessageReceived;
+ }
+
@ServiceThreadOnly
private void onWakeUp() {
assertRunOnServiceThread();
@@ -2098,17 +2103,23 @@ public class HdmiControlService extends SystemService {
}
@ServiceThreadOnly
- private void onStandby(final int standbyAction) {
+ @VisibleForTesting
+ protected void onStandby(final int standbyAction) {
assertRunOnServiceThread();
mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY;
invokeVendorCommandListenersOnControlStateChanged(false,
HdmiControlManager.CONTROL_STATE_CHANGED_REASON_STANDBY);
- if (!canGoToStandby()) {
+
+ final List<HdmiCecLocalDevice> devices = getAllLocalDevices();
+
+ if (!isStandbyMessageReceived() && !canGoToStandby()) {
mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY;
+ for (HdmiCecLocalDevice device : devices) {
+ device.onStandby(mStandbyMessageReceived, standbyAction);
+ }
return;
}
- final List<HdmiCecLocalDevice> devices = getAllLocalDevices();
disableDevices(new PendingActionClearedCallback() {
@Override
public void onCleared(HdmiCecLocalDevice device) {
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
index 085d17d406dc..07f23ce2231a 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
@@ -228,6 +228,9 @@ class LockSettingsShellCommand extends ShellCommand {
mLockPatternUtils.reportFailedPasswordAttempt(mCurrentUserId);
}
getOutPrintWriter().println("Old password '" + mOld + "' didn't match");
+ } else {
+ // Resets the counter for failed password attempts to 0.
+ mLockPatternUtils.reportSuccessfulPasswordAttempt(mCurrentUserId);
}
return result;
} catch (RequestThrottledException e) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index e6195b47a586..cb652242f629 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -288,6 +288,7 @@ import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal.SleepToken;
import com.android.server.wm.AppTransition;
import com.android.server.wm.DisplayFrames;
+import com.android.server.wm.WindowFrames;
import com.android.server.wm.WindowManagerInternal;
import com.android.server.wm.WindowManagerInternal.AppTransitionListener;
import com.android.server.wm.utils.InsetUtils;
@@ -631,8 +632,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
int mPointerLocationMode = 0; // guarded by mLock
- // The last window we were told about in focusChanged.
+ // The windows we were told about in focusChanged.
WindowState mFocusedWindow;
+ WindowState mLastFocusedWindow;
+
IApplicationToken mFocusedApp;
PointerLocationView mPointerLocationView;
@@ -663,15 +666,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
InputConsumer mInputConsumer = null;
- static final Rect mTmpParentFrame = new Rect();
- static final Rect mTmpDisplayFrame = new Rect();
- static final Rect mTmpOverscanFrame = new Rect();
- static final Rect mTmpContentFrame = new Rect();
- static final Rect mTmpVisibleFrame = new Rect();
- static final Rect mTmpDecorFrame = new Rect();
- static final Rect mTmpStableFrame = new Rect();
- static final Rect mTmpNavigationFrame = new Rect();
- static final Rect mTmpOutsetFrame = new Rect();
+ private final WindowFrames mWindowFrames = new WindowFrames();
private static final Rect mTmpDisplayCutoutSafeExceptMaybeBarsRect = new Rect();
private static final Rect mTmpRect = new Rect();
@@ -3334,6 +3329,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mNavigationBar = null;
mNavigationBarController.setWindow(null);
}
+ if (mLastFocusedWindow == win) {
+ mLastFocusedWindow = null;
+ }
mScreenDecorWindows.remove(win);
}
@@ -4638,18 +4636,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mDockLayer = 0x10000000;
mStatusBarLayer = -1;
- // start with the current dock rect, which will be (0,0,displayWidth,displayHeight)
- final Rect pf = mTmpParentFrame;
- final Rect df = mTmpDisplayFrame;
- final Rect of = mTmpOverscanFrame;
- final Rect vf = mTmpVisibleFrame;
- final Rect dcf = mTmpDecorFrame;
- vf.set(displayFrames.mDock);
- of.set(displayFrames.mDock);
- df.set(displayFrames.mDock);
- pf.set(displayFrames.mDock);
- dcf.setEmpty(); // Decor frame N/A for system bars.
-
if (displayFrames.mDisplayId == DEFAULT_DISPLAY) {
// For purposes of putting out fake window up to steal focus, we will
// drive nav being hidden only by whether it is requested.
@@ -4690,16 +4676,15 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// be hidden (because of the screen aspect ratio), then take that into account.
navVisible |= !canHideNavigationBar();
- boolean updateSysUiVisibility = layoutNavigationBar(displayFrames, uiMode, dcf,
- navVisible, navTranslucent, navAllowedHidden, statusBarExpandedNotKeyguard);
+ boolean updateSysUiVisibility = layoutNavigationBar(displayFrames, uiMode, navVisible,
+ navTranslucent, navAllowedHidden, statusBarExpandedNotKeyguard);
if (DEBUG_LAYOUT) Slog.i(TAG, "mDock rect:" + displayFrames.mDock);
- updateSysUiVisibility |= layoutStatusBar(
- displayFrames, pf, df, of, vf, dcf, sysui, isKeyguardShowing);
+ updateSysUiVisibility |= layoutStatusBar(displayFrames, sysui, isKeyguardShowing);
if (updateSysUiVisibility) {
updateSystemUiVisibilityLw();
}
}
- layoutScreenDecorWindows(displayFrames, pf, df, dcf);
+ layoutScreenDecorWindows(displayFrames);
if (displayFrames.mDisplayCutoutSafe.top > displayFrames.mUnrestricted.top) {
// Make sure that the zone we're avoiding for the cutout is at least as tall as the
@@ -4710,11 +4695,18 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
- private void layoutScreenDecorWindows(DisplayFrames displayFrames, Rect pf, Rect df, Rect dcf) {
+ private void layoutScreenDecorWindows(DisplayFrames displayFrames) {
if (mScreenDecorWindows.isEmpty()) {
return;
}
+ mTmpRect.setEmpty();
+ mWindowFrames.setFrames(displayFrames.mDock /* parentFrame */,
+ displayFrames.mDock /* displayFrame */, displayFrames.mDock /* overscanFrame */,
+ displayFrames.mDock /* contentFrame */, displayFrames.mDock /* visibleFrame */,
+ mTmpRect /* decorFrame */, displayFrames.mDock /* stableFrame */,
+ displayFrames.mDock /* outsetFrame */);
+
final int displayId = displayFrames.mDisplayId;
final Rect dockFrame = displayFrames.mDock;
final int displayHeight = displayFrames.mDisplayHeight;
@@ -4727,9 +4719,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
continue;
}
- w.computeFrameLw(pf /* parentFrame */, df /* displayFrame */, df /* overlayFrame */,
- df /* contentFrame */, df /* visibleFrame */, dcf /* decorFrame */,
- df /* stableFrame */, df /* outsetFrame */, displayFrames.mDisplayCutout,
+ w.computeFrameLw(mWindowFrames, displayFrames.mDisplayCutout,
false /* parentFrameWasClippedByDisplayCutout */);
final Rect frame = w.getFrameLw();
@@ -4775,25 +4765,25 @@ public class PhoneWindowManager implements WindowManagerPolicy {
displayFrames.mRestrictedOverscan.set(dockFrame);
}
- private boolean layoutStatusBar(DisplayFrames displayFrames, Rect pf, Rect df, Rect of, Rect vf,
- Rect dcf, int sysui, boolean isKeyguardShowing) {
+ private boolean layoutStatusBar(DisplayFrames displayFrames, int sysui,
+ boolean isKeyguardShowing) {
// decide where the status bar goes ahead of time
if (mStatusBar == null) {
return false;
}
// apply any navigation bar insets
- of.set(displayFrames.mUnrestricted);
- df.set(displayFrames.mUnrestricted);
- pf.set(displayFrames.mUnrestricted);
- vf.set(displayFrames.mStable);
+ mTmpRect.setEmpty();
+ mWindowFrames.setFrames(displayFrames.mUnrestricted /* parentFrame */,
+ displayFrames.mUnrestricted /* displayFrame */,
+ displayFrames.mStable /* overscanFrame */, displayFrames.mStable /* contentFrame */,
+ displayFrames.mStable /* visibleFrame */, mTmpRect /* decorFrame */,
+ displayFrames.mStable /* stableFrame */, displayFrames.mStable /* outsetFrame */);
mStatusBarLayer = mStatusBar.getSurfaceLayer();
// Let the status bar determine its size.
- mStatusBar.computeFrameLw(pf /* parentFrame */, df /* displayFrame */,
- vf /* overlayFrame */, vf /* contentFrame */, vf /* visibleFrame */,
- dcf /* decorFrame */, vf /* stableFrame */, vf /* outsetFrame */,
- displayFrames.mDisplayCutout, false /* parentFrameWasClippedByDisplayCutout */);
+ mStatusBar.computeFrameLw(mWindowFrames, displayFrames.mDisplayCutout,
+ false /* parentFrameWasClippedByDisplayCutout */);
// For layout, the status bar is always at the top with our fixed height.
displayFrames.mStable.top = displayFrames.mUnrestricted.top
@@ -4840,12 +4830,14 @@ public class PhoneWindowManager implements WindowManagerPolicy {
return mStatusBarController.checkHiddenLw();
}
- private boolean layoutNavigationBar(DisplayFrames displayFrames, int uiMode, Rect dcf,
- boolean navVisible, boolean navTranslucent, boolean navAllowedHidden,
+ private boolean layoutNavigationBar(DisplayFrames displayFrames, int uiMode, boolean navVisible,
+ boolean navTranslucent, boolean navAllowedHidden,
boolean statusBarExpandedNotKeyguard) {
if (mNavigationBar == null) {
return false;
}
+
+ final Rect navigationFrame = mWindowFrames.mParentFrame;
boolean transientNavBarShowing = mNavigationBarController.isTransientShowing();
// Force the navigation bar to its appropriate place and size. We need to do this directly,
// instead of relying on it to bubble up from the nav bar, because this needs to change
@@ -4864,7 +4856,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// It's a system nav bar or a portrait screen; nav bar goes on bottom.
final int top = cutoutSafeUnrestricted.bottom
- getNavigationBarHeight(rotation, uiMode);
- mTmpNavigationFrame.set(0, top, displayWidth, displayFrames.mUnrestricted.bottom);
+ navigationFrame.set(0, top, displayWidth, displayFrames.mUnrestricted.bottom);
displayFrames.mStable.bottom = displayFrames.mStableFullscreen.bottom = top;
if (transientNavBarShowing) {
mNavigationBarController.setBarShowingLw(true);
@@ -4887,7 +4879,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// Landscape screen; nav bar goes to the right.
final int left = cutoutSafeUnrestricted.right
- getNavigationBarWidth(rotation, uiMode);
- mTmpNavigationFrame.set(left, 0, displayFrames.mUnrestricted.right, displayHeight);
+ navigationFrame.set(left, 0, displayFrames.mUnrestricted.right, displayHeight);
displayFrames.mStable.right = displayFrames.mStableFullscreen.right = left;
if (transientNavBarShowing) {
mNavigationBarController.setBarShowingLw(true);
@@ -4910,7 +4902,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// Seascape screen; nav bar goes to the left.
final int right = cutoutSafeUnrestricted.left
+ getNavigationBarWidth(rotation, uiMode);
- mTmpNavigationFrame.set(displayFrames.mUnrestricted.left, 0, right, displayHeight);
+ navigationFrame.set(displayFrames.mUnrestricted.left, 0, right, displayHeight);
displayFrames.mStable.left = displayFrames.mStableFullscreen.left = right;
if (transientNavBarShowing) {
mNavigationBarController.setBarShowingLw(true);
@@ -4938,13 +4930,19 @@ public class PhoneWindowManager implements WindowManagerPolicy {
displayFrames.mContent.set(dockFrame);
mStatusBarLayer = mNavigationBar.getSurfaceLayer();
// And compute the final frame.
- mNavigationBar.computeFrameLw(mTmpNavigationFrame, mTmpNavigationFrame,
- mTmpNavigationFrame, displayFrames.mDisplayCutoutSafe, mTmpNavigationFrame, dcf,
- mTmpNavigationFrame, displayFrames.mDisplayCutoutSafe,
- displayFrames.mDisplayCutout, false /* parentFrameWasClippedByDisplayCutout */);
+ mTmpRect.setEmpty();
+ mWindowFrames.setFrames(navigationFrame /* parentFrame */,
+ navigationFrame /* displayFrame */, navigationFrame /* overscanFrame */,
+ displayFrames.mDisplayCutoutSafe /* contentFrame */,
+ navigationFrame /* visibleFrame */, mTmpRect /* decorFrame */,
+ navigationFrame /* stableFrame */,
+ displayFrames.mDisplayCutoutSafe /* outsetFrame */);
+
+ mNavigationBar.computeFrameLw(mWindowFrames, displayFrames.mDisplayCutout,
+ false /* parentFrameWasClippedByDisplayCutout */);
mNavigationBarController.setContentFrame(mNavigationBar.getContentFrameLw());
- if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + mTmpNavigationFrame);
+ if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + navigationFrame);
return mNavigationBarController.checkHiddenLw();
}
@@ -5072,15 +5070,15 @@ public class PhoneWindowManager implements WindowManagerPolicy {
final int requestedSysUiFl = PolicyControl.getSystemUiVisibility(null, attrs);
final int sysUiFl = requestedSysUiFl | getImpliedSysUiFlagsForLayout(attrs);
- final Rect pf = mTmpParentFrame;
- final Rect df = mTmpDisplayFrame;
- final Rect of = mTmpOverscanFrame;
- final Rect cf = mTmpContentFrame;
- final Rect vf = mTmpVisibleFrame;
- final Rect dcf = mTmpDecorFrame;
- final Rect sf = mTmpStableFrame;
- Rect osf = null;
+ final Rect pf = mWindowFrames.mParentFrame;
+ final Rect df = mWindowFrames.mDisplayFrame;
+ final Rect of = mWindowFrames.mOverscanFrame;
+ final Rect cf = mWindowFrames.mContentFrame;
+ final Rect vf = mWindowFrames.mVisibleFrame;
+ final Rect dcf = mWindowFrames.mDecorFrame;
+ final Rect sf = mWindowFrames.mStableFrame;
dcf.setEmpty();
+ mWindowFrames.mOutsetFrame.setEmpty();
final boolean hasNavBar = (isDefaultDisplay && mHasNavigationBar
&& mNavigationBar != null && mNavigationBar.isVisibleLw());
@@ -5479,7 +5477,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// apply the outsets to floating dialogs, because they wouldn't make sense there.
final boolean useOutsets = shouldUseOutsets(attrs, fl);
if (isDefaultDisplay && useOutsets) {
- osf = mTmpOutsetFrame;
+ final Rect osf = mWindowFrames.mOutsetFrame;
osf.set(cf.left, cf.top, cf.right, cf.bottom);
int outset = ScreenShapeHelper.getWindowOutsetBottomPx(mContext.getResources());
if (outset > 0) {
@@ -5507,9 +5505,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
+ " cf=" + cf.toShortString() + " vf=" + vf.toShortString()
+ " dcf=" + dcf.toShortString()
+ " sf=" + sf.toShortString()
- + " osf=" + (osf == null ? "null" : osf.toShortString()));
+ + " osf=" + mWindowFrames.mOutsetFrame.toShortString());
- win.computeFrameLw(pf, df, of, cf, vf, dcf, sf, osf, displayFrames.mDisplayCutout,
+ win.computeFrameLw(mWindowFrames, displayFrames.mDisplayCutout,
parentFrameWasClippedByDisplayCutout);
// Dock windows carve out the bottom of the screen, so normal windows
// can't appear underneath them.
@@ -5879,7 +5877,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
@Override
public int focusChangedLw(WindowState lastFocus, WindowState newFocus) {
mFocusedWindow = newFocus;
- if ((updateSystemUiVisibilityLw()&SYSTEM_UI_CHANGING_LAYOUT) != 0) {
+ mLastFocusedWindow = lastFocus;
+ if ((updateSystemUiVisibilityLw() & SYSTEM_UI_CHANGING_LAYOUT) != 0) {
// If the navigation bar has been hidden or shown, we need to do another
// layout pass to update that window.
return FINISH_LAYOUT_REDO_LAYOUT;
@@ -8105,10 +8104,19 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (winCandidate == null) {
return 0;
}
+
+ // The immersive mode confirmation should never affect the system bar visibility, otherwise
+ // it will unhide the navigation bar and hide itself.
if (winCandidate.getAttrs().token == mImmersiveModeConfirmation.getWindowToken()) {
- // The immersive mode confirmation should never affect the system bar visibility,
- // otherwise it will unhide the navigation bar and hide itself.
- winCandidate = isStatusBarKeyguard() ? mStatusBar : mTopFullscreenOpaqueWindowState;
+
+ // The immersive mode confirmation took the focus from mLastFocusedWindow which was
+ // controlling the system ui visibility. So if mLastFocusedWindow can still receive
+ // keys, we let it keep controlling the visibility.
+ final boolean lastFocusCanReceiveKeys =
+ (mLastFocusedWindow != null && mLastFocusedWindow.canReceiveKeys());
+ winCandidate = isStatusBarKeyguard() ? mStatusBar
+ : lastFocusCanReceiveKeys ? mLastFocusedWindow
+ : mTopFullscreenOpaqueWindowState;
if (winCandidate == null) {
return 0;
}
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 1ebbe3ac36fa..d63433db7310 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -93,6 +93,7 @@ import android.view.animation.Animation;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.policy.IShortcutService;
import com.android.server.wm.DisplayFrames;
+import com.android.server.wm.WindowFrames;
import com.android.server.wm.utils.WmDisplayCutout;
import java.io.PrintWriter;
@@ -197,34 +198,13 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
* getFrame() if so desired. Must be called with the window manager
* lock held.
*
- * @param parentFrame The frame of the parent container this window
- * is in, used for computing its basic position.
- * @param displayFrame The frame of the overall display in which this
- * window can appear, used for constraining the overall dimensions
- * of the window.
- * @param overlayFrame The frame within the display that is inside
- * of the overlay region.
- * @param contentFrame The frame within the display in which we would
- * like active content to appear. This will cause windows behind to
- * be resized to match the given content frame.
- * @param visibleFrame The frame within the display that the window
- * is actually visible, used for computing its visible insets to be
- * given to windows behind.
- * This can be used as a hint for scrolling (avoiding resizing)
- * the window to make certain that parts of its content
- * are visible.
- * @param decorFrame The decor frame specified by policy specific to this window,
- * to use for proper cropping during animation.
- * @param stableFrame The frame around which stable system decoration is positioned.
- * @param outsetFrame The frame that includes areas that aren't part of the surface but we
- * want to treat them as such.
+ * @param windowFrames Container for all the window frames that affect how the window is
+ * laid out.
* @param displayCutout the display cutout
* @param parentFrameWasClippedByDisplayCutout true if the parent frame would have been
* different if there was no display cutout.
*/
- public void computeFrameLw(Rect parentFrame, Rect displayFrame,
- Rect overlayFrame, Rect contentFrame, Rect visibleFrame, Rect decorFrame,
- Rect stableFrame, @Nullable Rect outsetFrame, WmDisplayCutout displayCutout,
+ public void computeFrameLw(WindowFrames windowFrames, WmDisplayCutout displayCutout,
boolean parentFrameWasClippedByDisplayCutout);
/**
@@ -491,6 +471,9 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
*/
boolean canAcquireSleepToken();
+ /** @return true if this window desires key events. */
+ boolean canReceiveKeys();
+
/**
* Writes {@link com.android.server.wm.IdentifierProto} to stream.
*/
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index a6ec3cff1b2c..f0898c0228b2 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -549,7 +549,8 @@ final class AccessibilityController {
touchableRegion.getBounds(touchableFrame);
RectF windowFrame = mTempRectF;
windowFrame.set(touchableFrame);
- windowFrame.offset(-windowState.mFrame.left, -windowState.mFrame.top);
+ windowFrame.offset(-windowState.getFrameLw().left,
+ -windowState.getFrameLw().top);
matrix.mapRect(windowFrame);
Region windowBounds = mTempRegion2;
windowBounds.set((int) windowFrame.left, (int) windowFrame.top,
@@ -1222,7 +1223,7 @@ final class AccessibilityController {
// Move to origin as all transforms are captured by the matrix.
RectF windowFrame = mTempRectF;
windowFrame.set(touchableFrame);
- windowFrame.offset(-windowState.mFrame.left, -windowState.mFrame.top);
+ windowFrame.offset(-windowState.getFrameLw().left, -windowState.getFrameLw().top);
// Map the frame to get what appears on the screen.
Matrix matrix = mTempMatrix;
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index fa6079c51906..d9ddf9f9b3a4 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -1523,7 +1523,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
if (mLetterbox == null) {
mLetterbox = new Letterbox(() -> makeChildSurface(null));
}
- mLetterbox.layout(getParent().getBounds(), w.mFrame);
+ mLetterbox.layout(getParent().getBounds(), w.getFrameLw());
} else if (mLetterbox != null) {
mLetterbox.hide();
}
@@ -1808,7 +1808,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
// won't exactly match the final freeform window frame (e.g. when overlapping with
// the status bar). In that case we need to use the final frame.
if (freeform) {
- frame.set(win.mFrame);
+ frame.set(win.getFrameLw());
} else if (win.isLetterboxedAppWindow()) {
frame.set(getTask().getBounds());
} else if (win.isDockedResizing()) {
@@ -1816,7 +1816,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
// animation target (which will be different than the task bounds)
frame.set(getTask().getParent().getBounds());
} else {
- frame.set(win.mContainingFrame);
+ frame.set(win.getContainingFrame());
}
surfaceInsets = win.getAttrs().surfaceInsets;
// XXX(b/72757033): These are insets relative to the window frame, but we're really
@@ -2022,7 +2022,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
if (win == null) {
return;
}
- final Rect frame = win.mFrame;
+ final Rect frame = win.getFrameLw();
final int thumbnailDrawableRes = getTask().mUserId == mService.mCurrentUserId
? R.drawable.ic_account_circle
: R.drawable.ic_corp_badge;
@@ -2034,7 +2034,8 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
}
mThumbnail = new AppWindowThumbnail(getPendingTransaction(), this, thumbnail);
final Animation animation =
- mService.mAppTransition.createCrossProfileAppsThumbnailAnimationLocked(win.mFrame);
+ mService.mAppTransition.createCrossProfileAppsThumbnailAnimationLocked(
+ win.getFrameLw());
mThumbnail.startAnimation(getPendingTransaction(), animation, new Point(frame.left,
frame.top));
}
diff --git a/services/core/java/com/android/server/wm/BlackFrame.java b/services/core/java/com/android/server/wm/BlackFrame.java
index 1977e126a8a0..fff1fa4dde66 100644
--- a/services/core/java/com/android/server/wm/BlackFrame.java
+++ b/services/core/java/com/android/server/wm/BlackFrame.java
@@ -56,6 +56,7 @@ public class BlackFrame {
.setParent(null) // TODO: Work-around for b/69259549
.build();
+ transaction.setLayerStack(surface, dc.getDisplayId());
transaction.setAlpha(surface, 1);
transaction.setLayer(surface, layer);
transaction.show(surface);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 0ca8e1aae193..8a1c4a4e88e9 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -303,6 +303,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
// Accessed directly by all users.
private boolean mLayoutNeeded;
int pendingLayoutChanges;
+ int mDeferredRotationPauseCount;
// TODO(multi-display): remove some of the usages.
boolean isDefaultDisplay;
/**
@@ -578,9 +579,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
w.mAppToken.layoutLetterbox(w);
}
- if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + w.mFrame
- + " mContainingFrame=" + w.mContainingFrame
- + " mDisplayFrame=" + w.mDisplayFrame);
+ if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + w.getFrameLw()
+ + " mContainingFrame=" + w.getContainingFrame()
+ + " mDisplayFrame=" + w.getDisplayFrameLw());
}
}
};
@@ -606,9 +607,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
w.prelayout();
mService.mPolicy.layoutWindowLw(w, w.getParentWindow(), mDisplayFrames);
w.mLayoutSeq = mLayoutSeq;
- if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + w.mFrame
- + " mContainingFrame=" + w.mContainingFrame
- + " mDisplayFrame=" + w.mDisplayFrame);
+ if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + w.getFrameLw()
+ + " mContainingFrame=" + w.getContainingFrame()
+ + " mDisplayFrame=" + w.getDisplayFrameLw());
}
} else if (w.mAttrs.type == TYPE_DREAM) {
// Don't layout windows behind a dream, so that if it does stuff like hide the
@@ -942,6 +943,36 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
/**
+ * Temporarily pauses rotation changes until resumed.
+ *
+ * This can be used to prevent rotation changes from occurring while the user is
+ * performing certain operations, such as drag and drop.
+ *
+ * This call nests and must be matched by an equal number of calls to
+ * {@link #resumeRotationLocked}.
+ */
+ void pauseRotationLocked() {
+ mDeferredRotationPauseCount++;
+ }
+
+ /**
+ * Resumes normal rotation changes after being paused.
+ */
+ void resumeRotationLocked() {
+ if (mDeferredRotationPauseCount <= 0) {
+ return;
+ }
+
+ mDeferredRotationPauseCount--;
+ if (mDeferredRotationPauseCount == 0) {
+ final boolean changed = updateRotationUnchecked();
+ if (changed) {
+ mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, mDisplayId).sendToTarget();
+ }
+ }
+ }
+
+ /**
* Update rotation of the display.
*
* @return {@code true} if the rotation has been changed. In this case YOU MUST CALL
@@ -964,7 +995,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
boolean updateRotationUnchecked(boolean forceUpdate) {
ScreenRotationAnimation screenRotationAnimation;
if (!forceUpdate) {
- if (mService.mDeferredRotationPauseCount > 0) {
+ if (mDeferredRotationPauseCount > 0) {
// Rotation updates have been paused temporarily. Defer the update until
// updates have been resumed.
if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Deferring rotation, rotation is paused.");
@@ -1065,9 +1096,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE;
- mService.mH.removeMessages(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT);
- mService.mH.sendEmptyMessageDelayed(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT,
- WINDOW_FREEZE_TIMEOUT_DURATION);
+ mService.mH.sendNewMessageDelayed(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT,
+ this, WINDOW_FREEZE_TIMEOUT_DURATION);
setLayoutNeeded();
final int[] anim = new int[2];
@@ -1124,9 +1154,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}, true /* traverseTopToBottom */);
if (rotateSeamlessly) {
- mService.mH.removeMessages(WindowManagerService.H.SEAMLESS_ROTATION_TIMEOUT);
- mService.mH.sendEmptyMessageDelayed(WindowManagerService.H.SEAMLESS_ROTATION_TIMEOUT,
- SEAMLESS_ROTATION_TIMEOUT_DURATION);
+ mService.mH.sendNewMessageDelayed(WindowManagerService.H.SEAMLESS_ROTATION_TIMEOUT,
+ this, SEAMLESS_ROTATION_TIMEOUT_DURATION);
}
for (int i = mService.mRotationWatchers.size() - 1; i >= 0; i--) {
@@ -2282,6 +2311,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
pw.println();
pw.print(prefix); pw.print("mLayoutSeq="); pw.println(mLayoutSeq);
+ pw.print(prefix);
+ pw.print("mDeferredRotationPauseCount="); pw.println(mDeferredRotationPauseCount);
pw.println();
pw.println(prefix + "Application tokens in top down Z order:");
@@ -2876,7 +2907,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mWallpaperController.adjustWallpaperWindows(this);
}
- if (isDefaultDisplay && (pendingLayoutChanges & FINISH_LAYOUT_REDO_CONFIG) != 0) {
+ if ((pendingLayoutChanges & FINISH_LAYOUT_REDO_CONFIG) != 0) {
if (DEBUG_LAYOUT) Slog.v(TAG, "Computing new config from layout");
if (mService.updateOrientationFromAppTokensLocked(mDisplayId)) {
setLayoutNeeded();
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index fc370d930cf3..f42e97908724 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -151,6 +151,7 @@ class DragDropController {
mDragState.mUid = callerUid;
mDragState.mOriginalAlpha = alpha;
mDragState.mToken = dragToken;
+ mDragState.mDisplayContent = displayContent;
final Display display = displayContent.getDisplay();
if (!mCallback.get().registerInputChannel(
@@ -160,7 +161,6 @@ class DragDropController {
return null;
}
- mDragState.mDisplayContent = displayContent;
mDragState.mData = data;
mDragState.broadcastDragStartedLocked(touchX, touchY);
mDragState.overridePointerIconLocked(touchSource);
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 1ac9b88749a7..d4046e94c530 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -255,7 +255,7 @@ class DragState {
if (DEBUG_ORIENTATION) {
Slog.d(TAG_WM, "Pausing rotation during drag");
}
- mService.pauseRotationLocked();
+ mDisplayContent.pauseRotationLocked();
}
void tearDown() {
@@ -274,7 +274,7 @@ class DragState {
if (DEBUG_ORIENTATION) {
Slog.d(TAG_WM, "Resuming rotation after drag");
}
- mService.resumeRotationLocked();
+ mDisplayContent.resumeRotationLocked();
}
}
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index a626663c2e67..8ab46518ed4d 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -326,7 +326,7 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks {
inputWindowHandle.ownerUid = child.mSession.mUid;
inputWindowHandle.inputFeatures = child.mAttrs.inputFeatures;
- final Rect frame = child.mFrame;
+ final Rect frame = child.getFrameLw();
inputWindowHandle.frameLeft = frame.left;
inputWindowHandle.frameTop = frame.top;
inputWindowHandle.frameRight = frame.right;
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index a709c55bbd30..2be400136f01 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -746,22 +746,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> {
if (mUpdateRotation) {
if (DEBUG_ORIENTATION) Slog.d(TAG, "Performing post-rotate rotation");
- // TODO(multi-display): Update rotation for different displays separately.
- final int displayId = defaultDisplay.getDisplayId();
- if (defaultDisplay.updateRotationUnchecked()) {
- mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, displayId).sendToTarget();
- } else {
- mUpdateRotation = false;
- }
- // Update rotation of VR virtual display separately. Currently this is the only kind of
- // secondary display that can be rotated because of the single-display limitations in
- // PhoneWindowManager.
- final DisplayContent vrDisplay = mService.mVr2dDisplayId != INVALID_DISPLAY
- ? getDisplayContent(mService.mVr2dDisplayId) : null;
- if (vrDisplay != null && vrDisplay.updateRotationUnchecked()) {
- mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, mService.mVr2dDisplayId)
- .sendToTarget();
- }
+ mUpdateRotation = updateRotationUnchecked();
}
if (mService.mWaitingForDrawnCallback != null ||
@@ -958,6 +943,19 @@ class RootWindowContainer extends WindowContainer<DisplayContent> {
return displayHasContent;
}
+ boolean updateRotationUnchecked() {
+ boolean changed = false;
+ for (int i = mChildren.size() - 1; i >= 0; i--) {
+ final DisplayContent displayContent = mChildren.get(i);
+ if (displayContent.updateRotationUnchecked()) {
+ changed = true;
+ mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, displayContent.getDisplayId())
+ .sendToTarget();
+ }
+ }
+ return changed;
+ }
+
boolean copyAnimToLayoutParams() {
boolean doRequest = false;
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index fa8a5c66aeea..2f189a6bcdb9 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -268,24 +268,18 @@ class ScreenRotationAnimation {
.setSecure(isSecure)
.build();
- // capture a screenshot into the surface we just created
- // TODO(multidisplay): we should use the proper display
- final int displayId = SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN;
- final IBinder displayHandle = SurfaceControl.getBuiltInDisplay(displayId);
- // This null check below is to guard a race condition where WMS didn't have a chance to
- // respond to display disconnection before handling rotation , that surfaceflinger may
- // return a null handle here because it doesn't think that display is valid anymore.
- if (displayHandle != null) {
- Surface sur = new Surface();
- sur.copyFrom(mSurfaceControl);
- SurfaceControl.screenshot(displayHandle, sur);
+ // Capture a screenshot into the surface we just created.
+ final int displayId = display.getDisplayId();
+ final Surface surface = new Surface();
+ surface.copyFrom(mSurfaceControl);
+ if (mService.mDisplayManagerInternal.screenshot(displayId, surface)) {
t.setLayer(mSurfaceControl, SCREEN_FREEZE_LAYER_SCREENSHOT);
t.setAlpha(mSurfaceControl, 0);
t.show(mSurfaceControl);
- sur.destroy();
} else {
- Slog.w(TAG, "Built-in display " + displayId + " is null.");
+ Slog.w(TAG, "Unable to take screenshot of display " + displayId);
}
+ surface.destroy();
} catch (OutOfResourcesException e) {
Slog.w(TAG, "Unable to allocate freeze surface", e);
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 2da77a15b956..71f34c98ee1f 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -409,7 +409,7 @@ class Task extends WindowContainer<AppWindowToken> {
* @param out Rect containing the max visible bounds.
* @return true if the task has some visible app windows; false otherwise.
*/
- boolean getMaxVisibleBounds(Rect out) {
+ private boolean getMaxVisibleBounds(Rect out) {
boolean foundTop = false;
for (int i = mChildren.size() - 1; i >= 0; i--) {
final AppWindowToken token = mChildren.get(i);
@@ -422,22 +422,11 @@ class Task extends WindowContainer<AppWindowToken> {
continue;
}
if (!foundTop) {
- out.set(win.mVisibleFrame);
foundTop = true;
- continue;
- }
- if (win.mVisibleFrame.left < out.left) {
- out.left = win.mVisibleFrame.left;
- }
- if (win.mVisibleFrame.top < out.top) {
- out.top = win.mVisibleFrame.top;
- }
- if (win.mVisibleFrame.right > out.right) {
- out.right = win.mVisibleFrame.right;
- }
- if (win.mVisibleFrame.bottom > out.bottom) {
- out.bottom = win.mVisibleFrame.bottom;
+ out.setEmpty();
}
+
+ win.getMaxVisibleBounds(out);
}
return foundTop;
}
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 30f46a05cb72..bd7e61cc34e5 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -97,7 +97,7 @@ class TaskPositioner {
private final WindowManagerService mService;
private final IActivityTaskManager mActivityManager;
private WindowPositionerEventReceiver mInputEventReceiver;
- private Display mDisplay;
+ private DisplayContent mDisplayContent;
private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
private Rect mTmpRect = new Rect();
private int mSideMargin;
@@ -250,8 +250,8 @@ class TaskPositioner {
return;
}
- mDisplay = display;
- mDisplay.getMetrics(mDisplayMetrics);
+ mDisplayContent = displayContent;
+ display.getMetrics(mDisplayMetrics);
final InputChannel[] channels = InputChannel.openInputChannelPair(TAG);
mServerChannel = channels[0];
mClientChannel = channels[1];
@@ -267,7 +267,7 @@ class TaskPositioner {
WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null, null,
- mDisplay.getDisplayId());
+ display.getDisplayId());
mDragWindowHandle.name = TAG;
mDragWindowHandle.inputChannel = mServerChannel;
mDragWindowHandle.layer = mService.getDragLayerLocked();
@@ -292,7 +292,7 @@ class TaskPositioner {
mDragWindowHandle.frameLeft = 0;
mDragWindowHandle.frameTop = 0;
final Point p = new Point();
- mDisplay.getRealSize(p);
+ display.getRealSize(p);
mDragWindowHandle.frameRight = p.x;
mDragWindowHandle.frameBottom = p.y;
@@ -300,12 +300,12 @@ class TaskPositioner {
if (DEBUG_ORIENTATION) {
Slog.d(TAG, "Pausing rotation during re-position");
}
- mService.pauseRotationLocked();
+ mDisplayContent.pauseRotationLocked();
mSideMargin = dipToPixel(SIDE_MARGIN_DIP, mDisplayMetrics);
mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, mDisplayMetrics);
mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, mDisplayMetrics);
- mDisplay.getRealSize(mMaxVisibleSize);
+ display.getRealSize(mMaxVisibleSize);
mDragEnded = false;
}
@@ -331,14 +331,14 @@ class TaskPositioner {
mDragWindowHandle = null;
mDragApplicationHandle = null;
- mDisplay = null;
mDragEnded = true;
// Resume rotations after a drag.
if (DEBUG_ORIENTATION) {
Slog.d(TAG, "Resuming rotation after re-position");
}
- mService.resumeRotationLocked();
+ mDisplayContent.resumeRotationLocked();
+ mDisplayContent = null;
}
void startDrag(WindowState win, boolean resize, boolean preserveOrientation, float startX,
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index c63da77d4a47..3d349ce34d6b 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -295,7 +295,7 @@ class WallpaperController {
float defaultWallpaperX = wallpaperWin.isRtl() ? 1f : 0f;
float wpx = mLastWallpaperX >= 0 ? mLastWallpaperX : defaultWallpaperX;
float wpxs = mLastWallpaperXStep >= 0 ? mLastWallpaperXStep : -1.0f;
- int availw = wallpaperWin.mFrame.right - wallpaperWin.mFrame.left - dw;
+ int availw = wallpaperWin.getFrameLw().right - wallpaperWin.getFrameLw().left - dw;
int offset = availw > 0 ? -(int)(availw * wpx + .5f) : 0;
if (mLastWallpaperDisplayOffsetX != Integer.MIN_VALUE) {
offset += mLastWallpaperDisplayOffsetX;
@@ -310,7 +310,7 @@ class WallpaperController {
float wpy = mLastWallpaperY >= 0 ? mLastWallpaperY : 0.5f;
float wpys = mLastWallpaperYStep >= 0 ? mLastWallpaperYStep : -1.0f;
- int availh = wallpaperWin.mFrame.bottom - wallpaperWin.mFrame.top - dh;
+ int availh = wallpaperWin.getFrameLw().bottom - wallpaperWin.getFrameLw().top - dh;
offset = availh > 0 ? -(int)(availh * wpy + .5f) : 0;
if (mLastWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
offset += mLastWallpaperDisplayOffsetY;
diff --git a/services/core/java/com/android/server/wm/WindowFrames.java b/services/core/java/com/android/server/wm/WindowFrames.java
new file mode 100644
index 000000000000..25317bb69768
--- /dev/null
+++ b/services/core/java/com/android/server/wm/WindowFrames.java
@@ -0,0 +1,176 @@
+/*
+ * 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.server.wm;
+
+import static com.android.server.wm.WindowFramesProto.CONTAINING_FRAME;
+import static com.android.server.wm.WindowFramesProto.CONTENT_FRAME;
+import static com.android.server.wm.WindowFramesProto.DECOR_FRAME;
+import static com.android.server.wm.WindowFramesProto.DISPLAY_FRAME;
+import static com.android.server.wm.WindowFramesProto.FRAME;
+import static com.android.server.wm.WindowFramesProto.OUTSET_FRAME;
+import static com.android.server.wm.WindowFramesProto.OVERSCAN_FRAME;
+import static com.android.server.wm.WindowFramesProto.PARENT_FRAME;
+import static com.android.server.wm.WindowFramesProto.VISIBLE_FRAME;
+
+import android.annotation.NonNull;
+import android.graphics.Rect;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.PrintWriter;
+
+/**
+ * Container class for all the window frames that affect how windows are laid out.
+ *
+ * TODO(b/111611553): Investigate which frames are still needed and which are duplicates
+ */
+public class WindowFrames {
+
+ /**
+ * In most cases, this is the area of the entire screen.
+ *
+ * TODO(b/111611553): The name is unclear and most likely should be swapped with
+ * {@link #mDisplayFrame}
+ * TODO(b/111611553): In some cases, it also includes top insets, like for IME. Determine
+ * whether this is still necessary to do.
+ */
+ public final Rect mParentFrame = new Rect();
+
+ /**
+ * The entire screen area of the {@link TaskStack} this window is in. Usually equal to the
+ * screen area of the device.
+ *
+ * TODO(b/111611553): The name is unclear and most likely should be swapped with
+ * {@link #mParentFrame}
+ */
+ public final Rect mDisplayFrame = new Rect();
+
+ /**
+ * The region of the display frame that the display type supports displaying content on. This
+ * is mostly a special case for TV where some displays don’t have the entire display usable.
+ * {@link android.view.WindowManager.LayoutParams#FLAG_LAYOUT_IN_OVERSCAN} flag can be used to
+ * allow window display contents to extend into the overscan region.
+ */
+ public final Rect mOverscanFrame = new Rect();
+
+ /**
+ * Legacy stuff. Generally equal to the content frame expect when the IME for older apps
+ * displays hint text.
+ */
+ public final Rect mVisibleFrame = new Rect();
+
+ /**
+ * The area not occupied by the status and navigation bars. So, if both status and navigation
+ * bars are visible, the decor frame is equal to the stable frame.
+ */
+ public final Rect mDecorFrame = new Rect();
+
+ /**
+ * Equal to the decor frame if the IME (e.g. keyboard) is not present. Equal to the decor frame
+ * minus the area occupied by the IME if the IME is present.
+ */
+ public final Rect mContentFrame = new Rect();
+
+ /**
+ * The display frame minus the stable insets. This value is always constant regardless of if
+ * the status bar or navigation bar is visible.
+ */
+ public final Rect mStableFrame = new Rect();
+
+ /**
+ * Frame that includes dead area outside of the surface but where we want to pretend that it's
+ * possible to draw.
+ */
+ final public Rect mOutsetFrame = new Rect();
+
+ /**
+ * Similar to {@link #mDisplayFrame}
+ *
+ * TODO: Why is this different than mDisplayFrame
+ */
+ final Rect mContainingFrame = new Rect();
+
+ /**
+ * "Real" frame that the application sees, in display coordinate space.
+ */
+ final Rect mFrame = new Rect();
+
+ /**
+ * The last real frame that was reported to the client.
+ */
+ final Rect mLastFrame = new Rect();
+
+ public WindowFrames() {
+ }
+
+ public WindowFrames(Rect parentFrame, Rect displayFrame, Rect overscanFrame, Rect contentFrame,
+ Rect visibleFrame, Rect decorFrame, Rect stableFrame, Rect outsetFrame) {
+ setFrames(parentFrame, displayFrame, overscanFrame, contentFrame, visibleFrame, decorFrame,
+ stableFrame, outsetFrame);
+ }
+
+ public void setFrames(Rect parentFrame, Rect displayFrame, Rect overscanFrame,
+ Rect contentFrame, Rect visibleFrame, Rect decorFrame, Rect stableFrame,
+ Rect outsetFrame) {
+ mParentFrame.set(parentFrame);
+ mDisplayFrame.set(displayFrame);
+ mOverscanFrame.set(overscanFrame);
+ mContentFrame.set(contentFrame);
+ mVisibleFrame.set(visibleFrame);
+ mDecorFrame.set(decorFrame);
+ mStableFrame.set(stableFrame);
+ mOutsetFrame.set(outsetFrame);
+ }
+
+ public void writeToProto(@NonNull ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ mParentFrame.writeToProto(proto, PARENT_FRAME);
+ mContentFrame.writeToProto(proto, CONTENT_FRAME);
+ mDisplayFrame.writeToProto(proto, DISPLAY_FRAME);
+ mOverscanFrame.writeToProto(proto, OVERSCAN_FRAME);
+ mVisibleFrame.writeToProto(proto, VISIBLE_FRAME);
+ mDecorFrame.writeToProto(proto, DECOR_FRAME);
+ mOutsetFrame.writeToProto(proto, OUTSET_FRAME);
+ mContainingFrame.writeToProto(proto, CONTAINING_FRAME);
+ mFrame.writeToProto(proto, FRAME);
+ proto.end(token);
+ }
+
+ public void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix); pw.print("Frames: containing=");
+ mContainingFrame.printShortString(pw);
+ pw.print(" parent="); mParentFrame.printShortString(pw);
+ pw.println();
+ pw.print(prefix); pw.print(" display=");
+ mDisplayFrame.printShortString(pw);
+ pw.print(" overscan="); mOverscanFrame.printShortString(pw);
+ pw.println();
+ pw.print(prefix); pw.print(" content=");
+ mContentFrame.printShortString(pw);
+ pw.print(" visible="); mVisibleFrame.printShortString(pw);
+ pw.println();
+ pw.print(prefix); pw.print(" decor=");
+ mDecorFrame.printShortString(pw);
+ pw.println();
+ pw.print(prefix); pw.print(" outset=");
+ mOutsetFrame.printShortString(pw);
+ pw.println();
+ pw.print(prefix); pw.print("mFrame="); mFrame.printShortString(pw);
+ pw.print(" last="); mLastFrame.printShortString(pw);
+ pw.println();
+ }
+
+}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 736aec74bebe..16ab45655d7f 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -583,7 +583,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
ArrayList<RotationWatcher> mRotationWatchers = new ArrayList<>();
- int mDeferredRotationPauseCount;
final WallpaperVisibilityListeners mWallpaperVisibilityListeners =
new WallpaperVisibilityListeners();
@@ -1596,7 +1595,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
// We use the visible frame, because we want the animation to morph the window from what
// was visible to the user to the final destination of the new window.
- Rect frame = replacedWindow.mVisibleFrame;
+ Rect frame = replacedWindow.getVisibleFrameLw();
// We treat this as if this activity was opening, so we can trigger the app transition
// animation and piggy-back on existing transition animation infrastructure.
mOpeningApps.add(atoken);
@@ -1832,7 +1831,7 @@ public class WindowManagerService extends IWindowManager.Stub
outDisplayFrame.setEmpty();
return;
}
- outDisplayFrame.set(win.mDisplayFrame);
+ outDisplayFrame.set(win.getDisplayFrameLw());
}
}
@@ -2169,7 +2168,7 @@ public class WindowManagerService extends IWindowManager.Stub
outStableInsets.set(win.mStableInsets);
outCutout.set(win.mDisplayCutout.getDisplayCutout());
outOutsets.set(win.mOutsets);
- outBackdropFrame.set(win.getBackdropFrame(win.mFrame));
+ outBackdropFrame.set(win.getBackdropFrame(win.getFrameLw()));
if (localLOGV) Slog.v(
TAG_WM, "Relayout given client " + client.asBinder()
+ ", requestedWidth=" + requestedWidth
@@ -3823,37 +3822,6 @@ public class WindowManagerService extends IWindowManager.Stub
updateRotationUnchecked(alwaysSendConfiguration, forceRelayout);
}
- /**
- * Temporarily pauses rotation changes until resumed.
- *
- * This can be used to prevent rotation changes from occurring while the user is
- * performing certain operations, such as drag and drop.
- *
- * This call nests and must be matched by an equal number of calls to
- * {@link #resumeRotationLocked}.
- */
- void pauseRotationLocked() {
- mDeferredRotationPauseCount += 1;
- }
-
- /**
- * Resumes normal rotation changes after being paused.
- */
- void resumeRotationLocked() {
- if (mDeferredRotationPauseCount > 0) {
- mDeferredRotationPauseCount -= 1;
- if (mDeferredRotationPauseCount == 0) {
- // TODO(multi-display): Update rotation for different displays separately.
- final DisplayContent displayContent = getDefaultDisplayContentLocked();
- final boolean changed = displayContent.updateRotationUnchecked();
- if (changed) {
- mH.obtainMessage(H.SEND_NEW_CONFIGURATION, displayContent.getDisplayId())
- .sendToTarget();
- }
- }
- }
- }
-
private void updateRotationUnchecked(boolean alwaysSendConfiguration, boolean forceRelayout) {
if(DEBUG_ORIENTATION) Slog.v(TAG_WM, "updateRotationUnchecked:"
+ " alwaysSendConfiguration=" + alwaysSendConfiguration
@@ -3907,6 +3875,15 @@ public class WindowManagerService extends IWindowManager.Stub
@Override
public int watchRotation(IRotationWatcher watcher, int displayId) {
+ final DisplayContent displayContent;
+ synchronized (mWindowMap) {
+ displayContent = mRoot.getDisplayContent(displayId);
+ }
+ if (displayContent == null) {
+ throw new IllegalArgumentException("Trying to register rotation event "
+ + "for invalid display: " + displayId);
+ }
+
final IBinder watcherBinder = watcher.asBinder();
IBinder.DeathRecipient dr = new IBinder.DeathRecipient() {
@Override
@@ -3934,7 +3911,7 @@ public class WindowManagerService extends IWindowManager.Stub
// Client died, no cleanup needed.
}
- return getDefaultDisplayRotation();
+ return displayContent.getRotation();
}
}
@@ -4719,9 +4696,9 @@ public class WindowManagerService extends IWindowManager.Stub
} break;
case WINDOW_FREEZE_TIMEOUT: {
- // TODO(multidisplay): Can non-default displays rotate?
+ final DisplayContent displayContent = (DisplayContent) msg.obj;
synchronized (mWindowMap) {
- getDefaultDisplayContentLocked().onWindowFreezeTimeout();
+ displayContent.onWindowFreezeTimeout();
}
break;
}
@@ -5034,11 +5011,9 @@ public class WindowManagerService extends IWindowManager.Stub
}
break;
case SEAMLESS_ROTATION_TIMEOUT: {
- // Rotation only supported on primary display.
- // TODO(multi-display)
- synchronized(mWindowMap) {
- final DisplayContent dc = getDefaultDisplayContentLocked();
- dc.onSeamlessRotationTimeout();
+ final DisplayContent displayContent = (DisplayContent) msg.obj;
+ synchronized (mWindowMap) {
+ displayContent.onSeamlessRotationTimeout();
}
}
break;
@@ -5078,6 +5053,12 @@ public class WindowManagerService extends IWindowManager.Stub
Slog.v(TAG_WM, "handleMessage: exit");
}
}
+
+ /** Remove the previous messages with the same 'what' and 'obj' then send the new one. */
+ void sendNewMessageDelayed(int what, Object obj, long delayMillis) {
+ removeMessages(what, obj);
+ sendMessageDelayed(obtainMessage(what, obj), delayMillis);
+ }
}
void destroyPreservedSurfaceLocked() {
@@ -5541,8 +5522,7 @@ public class WindowManagerService extends IWindowManager.Stub
mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE;
// XXX should probably keep timeout from
// when we first froze the display.
- mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT);
- mH.sendEmptyMessageDelayed(H.WINDOW_FREEZE_TIMEOUT,
+ mH.sendNewMessageDelayed(H.WINDOW_FREEZE_TIMEOUT, w.getDisplayContent(),
WINDOW_FREEZE_TIMEOUT_DURATION);
}
}
@@ -5789,8 +5769,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
mLatencyTracker.onActionStart(ACTION_ROTATE_SCREEN);
- // TODO(multidisplay): rotation on non-default displays
- if (CUSTOM_SCREEN_ROTATION && displayContent.isDefaultDisplay) {
+ if (CUSTOM_SCREEN_ROTATION) {
mExitAnimId = exitAnim;
mEnterAnimId = enterAnim;
ScreenRotationAnimation screenRotationAnimation =
@@ -6475,7 +6454,6 @@ public class WindowManagerService extends IWindowManager.Stub
pw.print(defaultDisplayContent.getLastWindowForcedOrientation());
pw.print(" mLastOrientation=");
pw.println(defaultDisplayContent.getLastOrientation());
- pw.print(" mDeferredRotationPauseCount="); pw.println(mDeferredRotationPauseCount);
pw.print(" Animation settings: disabled="); pw.print(mAnimationsDisabled);
pw.print(" window="); pw.print(mWindowAnimationScaleSetting);
pw.print(" transition="); pw.print(mTransitionAnimationScaleSetting);
@@ -7274,7 +7252,7 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized (mWindowMap) {
WindowState windowState = mWindowMap.get(token);
if (windowState != null) {
- outBounds.set(windowState.mFrame);
+ outBounds.set(windowState.getFrameLw());
} else {
outBounds.setEmpty();
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index c8c4b588f014..2d44b1368fa9 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -119,15 +119,10 @@ import static com.android.server.wm.WindowStateProto.ANIMATING_EXIT;
import static com.android.server.wm.WindowStateProto.ANIMATOR;
import static com.android.server.wm.WindowStateProto.ATTRIBUTES;
import static com.android.server.wm.WindowStateProto.CHILD_WINDOWS;
-import static com.android.server.wm.WindowStateProto.CONTAINING_FRAME;
-import static com.android.server.wm.WindowStateProto.CONTENT_FRAME;
import static com.android.server.wm.WindowStateProto.CONTENT_INSETS;
import static com.android.server.wm.WindowStateProto.CUTOUT;
-import static com.android.server.wm.WindowStateProto.DECOR_FRAME;
import static com.android.server.wm.WindowStateProto.DESTROYING;
-import static com.android.server.wm.WindowStateProto.DISPLAY_FRAME;
import static com.android.server.wm.WindowStateProto.DISPLAY_ID;
-import static com.android.server.wm.WindowStateProto.FRAME;
import static com.android.server.wm.WindowStateProto.GIVEN_CONTENT_INSETS;
import static com.android.server.wm.WindowStateProto.HAS_SURFACE;
import static com.android.server.wm.WindowStateProto.IDENTIFIER;
@@ -135,10 +130,7 @@ import static com.android.server.wm.WindowStateProto.IS_ON_SCREEN;
import static com.android.server.wm.WindowStateProto.IS_READY_FOR_DISPLAY;
import static com.android.server.wm.WindowStateProto.IS_VISIBLE;
import static com.android.server.wm.WindowStateProto.OUTSETS;
-import static com.android.server.wm.WindowStateProto.OUTSET_FRAME;
-import static com.android.server.wm.WindowStateProto.OVERSCAN_FRAME;
import static com.android.server.wm.WindowStateProto.OVERSCAN_INSETS;
-import static com.android.server.wm.WindowStateProto.PARENT_FRAME;
import static com.android.server.wm.WindowStateProto.REMOVED;
import static com.android.server.wm.WindowStateProto.REMOVE_ON_EXIT;
import static com.android.server.wm.WindowStateProto.REQUESTED_HEIGHT;
@@ -149,9 +141,9 @@ import static com.android.server.wm.WindowStateProto.SURFACE_INSETS;
import static com.android.server.wm.WindowStateProto.SURFACE_POSITION;
import static com.android.server.wm.WindowStateProto.SYSTEM_UI_VISIBILITY;
import static com.android.server.wm.WindowStateProto.VIEW_VISIBILITY;
-import static com.android.server.wm.WindowStateProto.VISIBLE_FRAME;
import static com.android.server.wm.WindowStateProto.VISIBLE_INSETS;
import static com.android.server.wm.WindowStateProto.WINDOW_CONTAINER;
+import static com.android.server.wm.WindowStateProto.WINDOW_FRAMES;
import static com.android.server.wm.utils.CoordinateTransforms.transformRect;
import static com.android.server.wm.utils.CoordinateTransforms.transformToRotation;
@@ -401,50 +393,15 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
float mLastHScale=1, mLastVScale=1;
final Matrix mTmpMatrix = new Matrix();
- // "Real" frame that the application sees, in display coordinate space.
- final Rect mFrame = new Rect();
- final Rect mLastFrame = new Rect();
private boolean mFrameSizeChanged = false;
// Frame that is scaled to the application's coordinate space when in
// screen size compatibility mode.
final Rect mCompatFrame = new Rect();
- final Rect mContainingFrame = new Rect();
-
- final Rect mParentFrame = new Rect();
-
/** Whether the parent frame would have been different if there was no display cutout. */
private boolean mParentFrameWasClippedByDisplayCutout;
- // The entire screen area of the {@link TaskStack} this window is in. Usually equal to the
- // screen area of the device.
- final Rect mDisplayFrame = new Rect();
-
- // The region of the display frame that the display type supports displaying content on. This
- // is mostly a special case for TV where some displays don’t have the entire display usable.
- // {@link WindowManager.LayoutParams#FLAG_LAYOUT_IN_OVERSCAN} flag can be used to allow
- // window display contents to extend into the overscan region.
- private final Rect mOverscanFrame = new Rect();
-
- // The display frame minus the stable insets. This value is always constant regardless of if
- // the status bar or navigation bar is visible.
- private final Rect mStableFrame = new Rect();
-
- // The area not occupied by the status and navigation bars. So, if both status and navigation
- // bars are visible, the decor frame is equal to the stable frame.
- final Rect mDecorFrame = new Rect();
-
- // Equal to the decor frame if the IME (e.g. keyboard) is not present. Equal to the decor frame
- // minus the area occupied by the IME if the IME is present.
- private final Rect mContentFrame = new Rect();
-
- // Legacy stuff. Generally equal to the content frame expect when the IME for older apps
- // displays hint text.
- final Rect mVisibleFrame = new Rect();
-
- // Frame that includes dead area outside of the surface but where we want to pretend that it's
- // possible to draw.
- private final Rect mOutsetFrame = new Rect();
+ private final WindowFrames mWindowFrames = new WindowFrames();
/**
* Usually empty. Set to the task's tempInsetFrame. See
@@ -850,9 +807,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
@Override
- public void computeFrameLw(Rect parentFrame, Rect displayFrame, Rect overscanFrame,
- Rect contentFrame, Rect visibleFrame, Rect decorFrame, Rect stableFrame,
- Rect outsetFrame, WmDisplayCutout displayCutout,
+ public void computeFrameLw(WindowFrames windowFrames, WmDisplayCutout displayCutout,
boolean parentFrameWasClippedByDisplayCutout) {
if (mWillReplaceWindow && (mAnimatingExit || !mReplacingRemoveRequested)) {
// This window is being replaced and either already got information that it's being
@@ -892,36 +847,39 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
final int layoutYDiff;
if (inFullscreenContainer || layoutInParentFrame()) {
// We use the parent frame as the containing frame for fullscreen and child windows
- mContainingFrame.set(parentFrame);
- mDisplayFrame.set(displayFrame);
- layoutDisplayFrame = displayFrame;
- layoutContainingFrame = parentFrame;
+ mWindowFrames.mContainingFrame.set(windowFrames.mParentFrame);
+ mWindowFrames.mDisplayFrame.set(windowFrames.mDisplayFrame);
+ layoutDisplayFrame = windowFrames.mDisplayFrame;
+ layoutContainingFrame = windowFrames.mParentFrame;
layoutXDiff = 0;
layoutYDiff = 0;
} else {
- getBounds(mContainingFrame);
+ getBounds(mWindowFrames.mContainingFrame);
if (mAppToken != null && !mAppToken.mFrozenBounds.isEmpty()) {
// If the bounds are frozen, we still want to translate the window freely and only
// freeze the size.
Rect frozen = mAppToken.mFrozenBounds.peek();
- mContainingFrame.right = mContainingFrame.left + frozen.width();
- mContainingFrame.bottom = mContainingFrame.top + frozen.height();
+ mWindowFrames.mContainingFrame.right =
+ mWindowFrames.mContainingFrame.left + frozen.width();
+ mWindowFrames.mContainingFrame.bottom =
+ mWindowFrames.mContainingFrame.top + frozen.height();
}
final WindowState imeWin = mService.mInputMethodWindow;
// IME is up and obscuring this window. Adjust the window position so it is visible.
if (imeWin != null && imeWin.isVisibleNow() && isInputMethodTarget()) {
- if (inFreeformWindowingMode()
- && mContainingFrame.bottom > contentFrame.bottom) {
+ if (inFreeformWindowingMode() && mWindowFrames.mContainingFrame.bottom
+ > windowFrames.mContentFrame.bottom) {
// In freeform we want to move the top up directly.
// TODO: Investigate why this is contentFrame not parentFrame.
- mContainingFrame.top -= mContainingFrame.bottom - contentFrame.bottom;
- } else if (!inPinnedWindowingMode()
- && mContainingFrame.bottom > parentFrame.bottom) {
+ mWindowFrames.mContainingFrame.top -= mWindowFrames.mContainingFrame.bottom
+ - windowFrames.mContentFrame.bottom;
+ } else if (!inPinnedWindowingMode() && mWindowFrames.mContainingFrame.bottom
+ > windowFrames.mParentFrame.bottom) {
// But in docked we want to behave like fullscreen and behave as if the task
// were given smaller bounds for the purposes of layout. Skip adjustments for
// the pinned stack, they are handled separately in the PinnedStackController.
- mContainingFrame.bottom = parentFrame.bottom;
+ mWindowFrames.mContainingFrame.bottom = windowFrames.mParentFrame.bottom;
}
}
@@ -929,8 +887,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// In floating modes (e.g. freeform, pinned) we have only to set the rectangle
// if it wasn't set already. No need to intersect it with the (visible)
// "content frame" since it is allowed to be outside the visible desktop.
- if (mContainingFrame.isEmpty()) {
- mContainingFrame.set(contentFrame);
+ if (mWindowFrames.mContainingFrame.isEmpty()) {
+ mWindowFrames.mContainingFrame.set(windowFrames.mContentFrame);
}
}
@@ -940,31 +898,39 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// PIP edge case: When going from pinned to fullscreen, we apply a
// tempInsetFrame for the full task - but we're still at the start of the animation.
// To prevent a jump if there's a letterbox, restrict to the parent frame.
- mInsetFrame.intersectUnchecked(parentFrame);
- mContainingFrame.intersectUnchecked(parentFrame);
+ mInsetFrame.intersectUnchecked(windowFrames.mParentFrame);
+ mWindowFrames.mContainingFrame.intersectUnchecked(windowFrames.mParentFrame);
}
- mDisplayFrame.set(mContainingFrame);
- layoutXDiff = !mInsetFrame.isEmpty() ? mInsetFrame.left - mContainingFrame.left : 0;
- layoutYDiff = !mInsetFrame.isEmpty() ? mInsetFrame.top - mContainingFrame.top : 0;
- layoutContainingFrame = !mInsetFrame.isEmpty() ? mInsetFrame : mContainingFrame;
+ mWindowFrames.mDisplayFrame.set(mWindowFrames.mContainingFrame);
+ layoutXDiff =
+ !mInsetFrame.isEmpty() ? mInsetFrame.left - mWindowFrames.mContainingFrame.left
+ : 0;
+ layoutYDiff =
+ !mInsetFrame.isEmpty() ? mInsetFrame.top - mWindowFrames.mContainingFrame.top
+ : 0;
+ layoutContainingFrame =
+ !mInsetFrame.isEmpty() ? mInsetFrame : mWindowFrames.mContainingFrame;
mTmpRect.set(0, 0, dc.getDisplayInfo().logicalWidth, dc.getDisplayInfo().logicalHeight);
- subtractInsets(mDisplayFrame, layoutContainingFrame, displayFrame, mTmpRect);
+ subtractInsets(mWindowFrames.mDisplayFrame, layoutContainingFrame,
+ windowFrames.mDisplayFrame, mTmpRect);
if (!layoutInParentFrame()) {
- subtractInsets(mContainingFrame, layoutContainingFrame, parentFrame, mTmpRect);
- subtractInsets(mInsetFrame, layoutContainingFrame, parentFrame, mTmpRect);
+ subtractInsets(mWindowFrames.mContainingFrame, layoutContainingFrame,
+ windowFrames.mParentFrame, mTmpRect);
+ subtractInsets(mInsetFrame, layoutContainingFrame, windowFrames.mParentFrame,
+ mTmpRect);
}
- layoutDisplayFrame = displayFrame;
+ layoutDisplayFrame = windowFrames.mDisplayFrame;
layoutDisplayFrame.intersect(layoutContainingFrame);
}
- final int pw = mContainingFrame.width();
- final int ph = mContainingFrame.height();
+ final int pw = mWindowFrames.mContainingFrame.width();
+ final int ph = mWindowFrames.mContainingFrame.height();
- if (!mParentFrame.equals(parentFrame)) {
+ if (!mWindowFrames.mParentFrame.equals(windowFrames.mParentFrame)) {
//Slog.i(TAG_WM, "Window " + this + " content frame from " + mParentFrame
// + " to " + parentFrame);
- mParentFrame.set(parentFrame);
+ mWindowFrames.mParentFrame.set(windowFrames.mParentFrame);
mContentChanged = true;
}
if (mRequestedWidth != mLastRequestedWidth || mRequestedHeight != mLastRequestedHeight) {
@@ -973,100 +939,110 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mContentChanged = true;
}
- mOverscanFrame.set(overscanFrame);
- mContentFrame.set(contentFrame);
- mVisibleFrame.set(visibleFrame);
- mDecorFrame.set(decorFrame);
- mStableFrame.set(stableFrame);
- final boolean hasOutsets = outsetFrame != null;
+ mWindowFrames.mOverscanFrame.set(windowFrames.mOverscanFrame);
+ mWindowFrames.mContentFrame.set(windowFrames.mContentFrame);
+ mWindowFrames.mVisibleFrame.set(windowFrames.mVisibleFrame);
+ mWindowFrames.mDecorFrame.set(windowFrames.mDecorFrame);
+ mWindowFrames.mStableFrame.set(windowFrames.mStableFrame);
+ final boolean hasOutsets = !windowFrames.mOutsetFrame.isEmpty();
if (hasOutsets) {
- mOutsetFrame.set(outsetFrame);
+ mWindowFrames.mOutsetFrame.set(windowFrames.mOutsetFrame);
}
- final int fw = mFrame.width();
- final int fh = mFrame.height();
+ final int fw = mWindowFrames.mFrame.width();
+ final int fh = mWindowFrames.mFrame.height();
applyGravityAndUpdateFrame(layoutContainingFrame, layoutDisplayFrame);
// Calculate the outsets before the content frame gets shrinked to the window frame.
if (hasOutsets) {
- mOutsets.set(Math.max(mContentFrame.left - mOutsetFrame.left, 0),
- Math.max(mContentFrame.top - mOutsetFrame.top, 0),
- Math.max(mOutsetFrame.right - mContentFrame.right, 0),
- Math.max(mOutsetFrame.bottom - mContentFrame.bottom, 0));
+ mOutsets.set(
+ Math.max(mWindowFrames.mContentFrame.left - mWindowFrames.mOutsetFrame.left, 0),
+ Math.max(mWindowFrames.mContentFrame.top - mWindowFrames.mOutsetFrame.top, 0),
+ Math.max(mWindowFrames.mOutsetFrame.right - mWindowFrames.mContentFrame.right,
+ 0),
+ Math.max(mWindowFrames.mOutsetFrame.bottom - mWindowFrames.mContentFrame.bottom,
+ 0));
} else {
mOutsets.set(0, 0, 0, 0);
}
// Make sure the content and visible frames are inside of the
// final window frame.
- if (windowsAreFloating && !mFrame.isEmpty()) {
+ if (windowsAreFloating && !mWindowFrames.mFrame.isEmpty()) {
// For pinned workspace the frame isn't limited in any particular
// way since SystemUI controls the bounds. For freeform however
// we want to keep things inside the content frame.
- final Rect limitFrame = task.inPinnedWindowingMode() ? mFrame : mContentFrame;
+ final Rect limitFrame = task.inPinnedWindowingMode() ? mWindowFrames.mFrame
+ : mWindowFrames.mContentFrame;
// Keep the frame out of the blocked system area, limit it in size to the content area
// and make sure that there is always a minimum visible so that the user can drag it
// into a usable area..
- final int height = Math.min(mFrame.height(), limitFrame.height());
- final int width = Math.min(limitFrame.width(), mFrame.width());
+ final int height = Math.min(mWindowFrames.mFrame.height(), limitFrame.height());
+ final int width = Math.min(limitFrame.width(), mWindowFrames.mFrame.width());
final DisplayMetrics displayMetrics = getDisplayContent().getDisplayMetrics();
final int minVisibleHeight = Math.min(height, WindowManagerService.dipToPixel(
MINIMUM_VISIBLE_HEIGHT_IN_DP, displayMetrics));
final int minVisibleWidth = Math.min(width, WindowManagerService.dipToPixel(
MINIMUM_VISIBLE_WIDTH_IN_DP, displayMetrics));
final int top = Math.max(limitFrame.top,
- Math.min(mFrame.top, limitFrame.bottom - minVisibleHeight));
+ Math.min( mWindowFrames.mFrame.top, limitFrame.bottom - minVisibleHeight));
final int left = Math.max(limitFrame.left + minVisibleWidth - width,
- Math.min(mFrame.left, limitFrame.right - minVisibleWidth));
- mFrame.set(left, top, left + width, top + height);
- mContentFrame.set(mFrame);
- mVisibleFrame.set(mContentFrame);
- mStableFrame.set(mContentFrame);
+ Math.min( mWindowFrames.mFrame.left, limitFrame.right - minVisibleWidth));
+ mWindowFrames.mFrame.set(left, top, left + width, top + height);
+ mWindowFrames.mContentFrame.set( mWindowFrames.mFrame);
+ mWindowFrames.mVisibleFrame.set(mWindowFrames.mContentFrame);
+ mWindowFrames.mStableFrame.set(mWindowFrames.mContentFrame);
} else if (mAttrs.type == TYPE_DOCK_DIVIDER) {
- dc.getDockedDividerController().positionDockedStackedDivider(mFrame);
- mContentFrame.set(mFrame);
- if (!mFrame.equals(mLastFrame)) {
+ dc.getDockedDividerController().positionDockedStackedDivider(mWindowFrames.mFrame);
+ mWindowFrames.mContentFrame.set(mWindowFrames.mFrame);
+ if (!mWindowFrames.mFrame.equals(mWindowFrames.mLastFrame)) {
mMovedByResize = true;
}
} else {
- mContentFrame.set(Math.max(mContentFrame.left, mFrame.left),
- Math.max(mContentFrame.top, mFrame.top),
- Math.min(mContentFrame.right, mFrame.right),
- Math.min(mContentFrame.bottom, mFrame.bottom));
+ mWindowFrames.mContentFrame.set(
+ Math.max(mWindowFrames.mContentFrame.left, mWindowFrames.mFrame.left),
+ Math.max(mWindowFrames.mContentFrame.top, mWindowFrames.mFrame.top),
+ Math.min(mWindowFrames.mContentFrame.right, mWindowFrames.mFrame.right),
+ Math.min(mWindowFrames.mContentFrame.bottom, mWindowFrames.mFrame.bottom));
- mVisibleFrame.set(Math.max(mVisibleFrame.left, mFrame.left),
- Math.max(mVisibleFrame.top, mFrame.top),
- Math.min(mVisibleFrame.right, mFrame.right),
- Math.min(mVisibleFrame.bottom, mFrame.bottom));
+ mWindowFrames.mVisibleFrame.set(
+ Math.max(mWindowFrames.mVisibleFrame.left, mWindowFrames.mFrame.left),
+ Math.max(mWindowFrames.mVisibleFrame.top, mWindowFrames.mFrame.top),
+ Math.min(mWindowFrames.mVisibleFrame.right, mWindowFrames.mFrame.right),
+ Math.min(mWindowFrames.mVisibleFrame.bottom, mWindowFrames.mFrame.bottom));
- mStableFrame.set(Math.max(mStableFrame.left, mFrame.left),
- Math.max(mStableFrame.top, mFrame.top),
- Math.min(mStableFrame.right, mFrame.right),
- Math.min(mStableFrame.bottom, mFrame.bottom));
+ mWindowFrames.mStableFrame.set(
+ Math.max(mWindowFrames.mStableFrame.left, mWindowFrames.mFrame.left),
+ Math.max(mWindowFrames.mStableFrame.top, mWindowFrames.mFrame.top),
+ Math.min(mWindowFrames.mStableFrame.right, mWindowFrames.mFrame.right),
+ Math.min(mWindowFrames.mStableFrame.bottom, mWindowFrames.mFrame.bottom));
}
if (inFullscreenContainer && !windowsAreFloating) {
// Windows that are not fullscreen can be positioned outside of the display frame,
// but that is not a reason to provide them with overscan insets.
- mOverscanInsets.set(Math.max(mOverscanFrame.left - layoutContainingFrame.left, 0),
- Math.max(mOverscanFrame.top - layoutContainingFrame.top, 0),
- Math.max(layoutContainingFrame.right - mOverscanFrame.right, 0),
- Math.max(layoutContainingFrame.bottom - mOverscanFrame.bottom, 0));
+ mOverscanInsets.set(
+ Math.max(mWindowFrames.mOverscanFrame.left - layoutContainingFrame.left, 0),
+ Math.max(mWindowFrames.mOverscanFrame.top - layoutContainingFrame.top, 0),
+ Math.max(layoutContainingFrame.right - mWindowFrames.mOverscanFrame.right, 0),
+ Math.max(layoutContainingFrame.bottom - mWindowFrames.mOverscanFrame.bottom,
+ 0));
}
if (mAttrs.type == TYPE_DOCK_DIVIDER) {
// For the docked divider, we calculate the stable insets like a full-screen window
// so it can use it to calculate the snap positions.
- final WmDisplayCutout c = displayCutout.calculateRelativeTo(mDisplayFrame);
- mTmpRect.set(mDisplayFrame);
+ final WmDisplayCutout c = displayCutout.calculateRelativeTo(
+ mWindowFrames.mDisplayFrame);
+ mTmpRect.set(mWindowFrames.mDisplayFrame);
mTmpRect.inset(c.getDisplayCutout().getSafeInsets());
- mTmpRect.intersectUnchecked(mStableFrame);
+ mTmpRect.intersectUnchecked(mWindowFrames.mStableFrame);
- mStableInsets.set(Math.max(mTmpRect.left - mDisplayFrame.left, 0),
- Math.max(mTmpRect.top - mDisplayFrame.top, 0),
- Math.max(mDisplayFrame.right - mTmpRect.right, 0),
- Math.max(mDisplayFrame.bottom - mTmpRect.bottom, 0));
+ mStableInsets.set(Math.max(mTmpRect.left - mWindowFrames.mDisplayFrame.left, 0),
+ Math.max(mTmpRect.top - mWindowFrames.mDisplayFrame.top, 0),
+ Math.max(mWindowFrames.mDisplayFrame.right - mTmpRect.right, 0),
+ Math.max(mWindowFrames.mDisplayFrame.bottom - mTmpRect.bottom, 0));
// The divider doesn't care about insets in any case, so set it to empty so we don't
// trigger a relayout when moving it.
@@ -1078,41 +1054,44 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// Override right and/or bottom insets in case if the frame doesn't fit the screen in
// non-fullscreen mode.
boolean overrideRightInset = !windowsAreFloating && !inFullscreenContainer
- && mFrame.right > mTmpRect.right;
+ && mWindowFrames.mFrame.right > mTmpRect.right;
boolean overrideBottomInset = !windowsAreFloating && !inFullscreenContainer
- && mFrame.bottom > mTmpRect.bottom;
- mContentInsets.set(mContentFrame.left - mFrame.left,
- mContentFrame.top - mFrame.top,
- overrideRightInset ? mTmpRect.right - mContentFrame.right
- : mFrame.right - mContentFrame.right,
- overrideBottomInset ? mTmpRect.bottom - mContentFrame.bottom
- : mFrame.bottom - mContentFrame.bottom);
-
- mVisibleInsets.set(mVisibleFrame.left - mFrame.left,
- mVisibleFrame.top - mFrame.top,
- overrideRightInset ? mTmpRect.right - mVisibleFrame.right
- : mFrame.right - mVisibleFrame.right,
- overrideBottomInset ? mTmpRect.bottom - mVisibleFrame.bottom
- : mFrame.bottom - mVisibleFrame.bottom);
-
- mStableInsets.set(Math.max(mStableFrame.left - mFrame.left, 0),
- Math.max(mStableFrame.top - mFrame.top, 0),
- overrideRightInset ? Math.max(mTmpRect.right - mStableFrame.right, 0)
- : Math.max(mFrame.right - mStableFrame.right, 0),
- overrideBottomInset ? Math.max(mTmpRect.bottom - mStableFrame.bottom, 0)
- : Math.max(mFrame.bottom - mStableFrame.bottom, 0));
- }
-
- mDisplayCutout = displayCutout.calculateRelativeTo(mFrame);
+ && mWindowFrames.mFrame.bottom > mTmpRect.bottom;
+ mContentInsets.set(mWindowFrames.mContentFrame.left - mWindowFrames.mFrame.left,
+ mWindowFrames.mContentFrame.top - mWindowFrames.mFrame.top,
+ overrideRightInset ? mTmpRect.right - mWindowFrames.mContentFrame.right
+ : mWindowFrames.mFrame.right - mWindowFrames.mContentFrame.right,
+ overrideBottomInset ? mTmpRect.bottom - mWindowFrames.mContentFrame.bottom
+ : mWindowFrames.mFrame.bottom - mWindowFrames.mContentFrame.bottom);
+
+ mVisibleInsets.set(mWindowFrames.mVisibleFrame.left - mWindowFrames.mFrame.left,
+ mWindowFrames.mVisibleFrame.top - mWindowFrames.mFrame.top,
+ overrideRightInset ? mTmpRect.right - mWindowFrames.mVisibleFrame.right
+ : mWindowFrames.mFrame.right - mWindowFrames.mVisibleFrame.right,
+ overrideBottomInset ? mTmpRect.bottom - mWindowFrames.mVisibleFrame.bottom
+ : mWindowFrames.mFrame.bottom - mWindowFrames.mVisibleFrame.bottom);
+
+ mStableInsets.set(
+ Math.max(mWindowFrames.mStableFrame.left - mWindowFrames.mFrame.left, 0),
+ Math.max(mWindowFrames.mStableFrame.top - mWindowFrames.mFrame.top, 0),
+ overrideRightInset ? Math.max(mTmpRect.right - mWindowFrames.mStableFrame.right,
+ 0) : Math.max(
+ mWindowFrames.mFrame.right - mWindowFrames.mStableFrame.right, 0),
+ overrideBottomInset ? Math.max(
+ mTmpRect.bottom - mWindowFrames.mStableFrame.bottom, 0) : Math.max(
+ mWindowFrames.mFrame.bottom - mWindowFrames.mStableFrame.bottom, 0));
+ }
+
+ mDisplayCutout = displayCutout.calculateRelativeTo(mWindowFrames.mFrame);
// Offset the actual frame by the amount layout frame is off.
- mFrame.offset(-layoutXDiff, -layoutYDiff);
+ mWindowFrames.mFrame.offset(-layoutXDiff, -layoutYDiff);
mCompatFrame.offset(-layoutXDiff, -layoutYDiff);
- mContentFrame.offset(-layoutXDiff, -layoutYDiff);
- mVisibleFrame.offset(-layoutXDiff, -layoutYDiff);
- mStableFrame.offset(-layoutXDiff, -layoutYDiff);
+ mWindowFrames.mContentFrame.offset(-layoutXDiff, -layoutYDiff);
+ mWindowFrames.mVisibleFrame.offset(-layoutXDiff, -layoutYDiff);
+ mWindowFrames.mStableFrame.offset(-layoutXDiff, -layoutYDiff);
- mCompatFrame.set(mFrame);
+ mCompatFrame.set(mWindowFrames.mFrame);
if (mEnforceSizeCompat) {
// If there is a size compatibility scale being applied to the
// window, we need to apply this to its insets so that they are
@@ -1128,12 +1107,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mCompatFrame.scale(mInvGlobalScale);
}
- if (mIsWallpaper && (fw != mFrame.width() || fh != mFrame.height())) {
+ if (mIsWallpaper && (fw != mWindowFrames.mFrame.width()
+ || fh != mWindowFrames.mFrame.height())) {
final DisplayContent displayContent = getDisplayContent();
if (displayContent != null) {
final DisplayInfo displayInfo = displayContent.getDisplayInfo();
- getDisplayContent().mWallpaperController.updateWallpaperOffset(
- this, displayInfo.logicalWidth, displayInfo.logicalHeight, false);
+ getDisplayContent().mWallpaperController.updateWallpaperOffset(this,
+ displayInfo.logicalWidth, displayInfo.logicalHeight, false);
}
}
@@ -1141,7 +1121,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
"Resolving (mRequestedWidth="
+ mRequestedWidth + ", mRequestedheight="
+ mRequestedHeight + ") to" + " (pw=" + pw + ", ph=" + ph
- + "): frame=" + mFrame.toShortString()
+ + "): frame=" + mWindowFrames.mFrame.toShortString()
+ " ci=" + mContentInsets.toShortString()
+ " vi=" + mVisibleInsets.toShortString()
+ " si=" + mStableInsets.toShortString()
@@ -1162,31 +1142,43 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
@Override
public Rect getFrameLw() {
- return mFrame;
+ return mWindowFrames.mFrame;
}
@Override
public Rect getDisplayFrameLw() {
- return mDisplayFrame;
+ return mWindowFrames.mDisplayFrame;
}
@Override
public Rect getOverscanFrameLw() {
- return mOverscanFrame;
+ return mWindowFrames.mOverscanFrame;
}
@Override
public Rect getContentFrameLw() {
- return mContentFrame;
+ return mWindowFrames.mContentFrame;
}
@Override
public Rect getVisibleFrameLw() {
- return mVisibleFrame;
+ return mWindowFrames.mVisibleFrame;
}
Rect getStableFrameLw() {
- return mStableFrame;
+ return mWindowFrames.mStableFrame;
+ }
+
+ Rect getDecorFrame() {
+ return mWindowFrames.mDecorFrame;
+ }
+
+ Rect getParentFrame() {
+ return mWindowFrames.mParentFrame;
+ }
+
+ Rect getContainingFrame() {
+ return mWindowFrames.mContainingFrame;
}
@Override
@@ -1245,8 +1237,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mVisibleInsetsChanged |= !mLastVisibleInsets.equals(mVisibleInsets);
mStableInsetsChanged |= !mLastStableInsets.equals(mStableInsets);
mOutsetsChanged |= !mLastOutsets.equals(mOutsets);
- mFrameSizeChanged |= (mLastFrame.width() != mFrame.width()) ||
- (mLastFrame.height() != mFrame.height());
+ mFrameSizeChanged |= (mWindowFrames.mLastFrame.width() != mWindowFrames.mFrame.width()) ||
+ (mWindowFrames.mLastFrame.height() != mWindowFrames.mFrame.height());
mDisplayCutoutChanged |= !mLastDisplayCutout.equals(mDisplayCutout);
return mOverscanInsetsChanged || mContentInsetsChanged || mVisibleInsetsChanged
|| mOutsetsChanged || mFrameSizeChanged || mDisplayCutoutChanged;
@@ -1282,12 +1274,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
&& !isDragResizingChangeReported();
if (localLOGV) Slog.v(TAG_WM, "Resizing " + this + ": configChanged=" + configChanged
- + " dragResizingChanged=" + dragResizingChanged + " last=" + mLastFrame
- + " frame=" + mFrame);
+ + " dragResizingChanged=" + dragResizingChanged
+ + " last=" + mWindowFrames.mLastFrame + " frame=" + mWindowFrames.mFrame);
// We update mLastFrame always rather than in the conditional with the last inset
// variables, because mFrameSizeChanged only tracks the width and height changing.
- mLastFrame.set(mFrame);
+ mWindowFrames.mLastFrame.set(mWindowFrames.mFrame);
if (mContentInsetsChanged
|| mVisibleInsetsChanged
@@ -1442,13 +1434,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
}
- bounds.set(mVisibleFrame);
+ bounds.set(mWindowFrames.mVisibleFrame);
if (intersectWithStackBounds) {
bounds.intersect(mTmpRect);
}
if (bounds.isEmpty()) {
- bounds.set(mFrame);
+ bounds.set(mWindowFrames.mFrame);
if (intersectWithStackBounds) {
bounds.intersect(mTmpRect);
}
@@ -1805,8 +1797,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// Frame has moved, containing content frame has also moved, and we're not currently
// animating... let's do something.
- final int left = mFrame.left;
- final int top = mFrame.top;
+ final int left = mWindowFrames.mFrame.left;
+ final int top = mWindowFrames.mFrame.top;
final Task task = getTask();
final boolean adjustedForMinimizedDockOrIme = task != null
&& (task.mStack.isAdjustedForMinimizedDockedStack()
@@ -1840,7 +1832,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
private boolean hasMoved() {
return mHasSurface && (mContentChanged || mMovedByResize)
&& !mAnimatingExit
- && (mFrame.top != mLastFrame.top || mFrame.left != mLastFrame.left)
+ && (mWindowFrames.mFrame.top != mWindowFrames.mLastFrame.top
+ || mWindowFrames.mFrame.left != mWindowFrames.mLastFrame.left)
&& (!mIsChildWindow || !getParentWindow().hasMoved());
}
@@ -1854,8 +1847,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
boolean fillsDisplay() {
final DisplayInfo displayInfo = getDisplayInfo();
- return mFrame.left <= 0 && mFrame.top <= 0
- && mFrame.right >= displayInfo.appWidth && mFrame.bottom >= displayInfo.appHeight;
+ return mWindowFrames.mFrame.left <= 0 && mWindowFrames.mFrame.top <= 0
+ && mWindowFrames.mFrame.right >= displayInfo.appWidth
+ && mWindowFrames.mFrame.bottom >= displayInfo.appHeight;
}
/** Returns true if last applied config was not yet requested by client. */
@@ -2493,8 +2487,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return getWindowConfiguration().keepVisibleDeadAppWindowOnScreen();
}
- /** @return true if this window desires key events. */
- boolean canReceiveKeys() {
+ @Override
+ public boolean canReceiveKeys() {
return isVisibleOrAdding()
&& (mViewVisibility == View.VISIBLE) && !mRemoveOnExit
&& ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0)
@@ -2889,10 +2883,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// All window frames that are fullscreen extend above status bar, but some don't extend
// below navigation bar. Thus, check for display frame for top/left and stable frame for
// bottom right.
- if (win.mFrame.left <= win.mDisplayFrame.left
- && win.mFrame.top <= win.mDisplayFrame.top
- && win.mFrame.right >= win.mStableFrame.right
- && win.mFrame.bottom >= win.mStableFrame.bottom) {
+ if (win.getFrameLw().left <= win.getDisplayFrameLw().left
+ && win.getFrameLw().top <= win.getDisplayFrameLw().top
+ && win.getFrameLw().right >= win.getStableFrameLw().right
+ && win.getFrameLw().bottom >= win.getStableFrameLw().bottom) {
// Is a fullscreen window, like the clock alarm. Show to everyone.
return false;
}
@@ -2909,7 +2903,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
void getTouchableRegion(Region outRegion) {
- final Rect frame = mFrame;
+ final Rect frame = mWindowFrames.mFrame;
switch (mTouchableInsets) {
default:
case TOUCHABLE_INSETS_FRAME:
@@ -2994,7 +2988,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
if (DEBUG_ORIENTATION && mWinAnimator.mDrawState == DRAW_PENDING)
Slog.i(TAG, "Resizing " + this + " WITH DRAW PENDING");
- final Rect frame = mFrame;
+ final Rect frame = mWindowFrames.mFrame;
final Rect overscanInsets = mLastOverscanInsets;
final Rect contentInsets = mLastContentInsets;
final Rect visibleInsets = mLastVisibleInsets;
@@ -3159,7 +3153,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
*/
private boolean frameCoversEntireAppTokenBounds() {
mTmpRect.set(mAppToken.getBounds());
- mTmpRect.intersectUnchecked(mFrame);
+ mTmpRect.intersectUnchecked(mWindowFrames.mFrame);
return mAppToken.getBounds().equals(mTmpRect);
}
@@ -3261,10 +3255,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
proto.write(STACK_ID, getStackId());
mAttrs.writeToProto(proto, ATTRIBUTES);
mGivenContentInsets.writeToProto(proto, GIVEN_CONTENT_INSETS);
- mFrame.writeToProto(proto, FRAME);
- mContainingFrame.writeToProto(proto, CONTAINING_FRAME);
- mParentFrame.writeToProto(proto, PARENT_FRAME);
- mContentFrame.writeToProto(proto, CONTENT_FRAME);
+ mWindowFrames.writeToProto(proto, WINDOW_FRAMES);
mContentInsets.writeToProto(proto, CONTENT_INSETS);
mAttrs.surfaceInsets.writeToProto(proto, SURFACE_INSETS);
mSurfacePosition.writeToProto(proto, SURFACE_POSITION);
@@ -3279,11 +3270,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
proto.write(SYSTEM_UI_VISIBILITY, mSystemUiVisibility);
proto.write(HAS_SURFACE, mHasSurface);
proto.write(IS_READY_FOR_DISPLAY, isReadyForDisplay());
- mDisplayFrame.writeToProto(proto, DISPLAY_FRAME);
- mOverscanFrame.writeToProto(proto, OVERSCAN_FRAME);
- mVisibleFrame.writeToProto(proto, VISIBLE_FRAME);
- mDecorFrame.writeToProto(proto, DECOR_FRAME);
- mOutsetFrame.writeToProto(proto, OUTSET_FRAME);
mOverscanInsets.writeToProto(proto, OVERSCAN_INSETS);
mVisibleInsets.writeToProto(proto, VISIBLE_INSETS);
mStableInsets.writeToProto(proto, STABLE_INSETS);
@@ -3405,30 +3391,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
pw.print(prefix); pw.print("mHasSurface="); pw.print(mHasSurface);
pw.print(" isReadyForDisplay()="); pw.print(isReadyForDisplay());
pw.print(" mWindowRemovalAllowed="); pw.println(mWindowRemovalAllowed);
- if (dumpAll) {
- pw.print(prefix); pw.print("mFrame="); mFrame.printShortString(pw);
- pw.print(" last="); mLastFrame.printShortString(pw);
- pw.println();
- }
if (mEnforceSizeCompat) {
pw.print(prefix); pw.print("mCompatFrame="); mCompatFrame.printShortString(pw);
pw.println();
}
if (dumpAll) {
- pw.print(prefix); pw.print("Frames: containing=");
- mContainingFrame.printShortString(pw);
- pw.print(" parent="); mParentFrame.printShortString(pw);
- pw.println();
- pw.print(prefix); pw.print(" display="); mDisplayFrame.printShortString(pw);
- pw.print(" overscan="); mOverscanFrame.printShortString(pw);
- pw.println();
- pw.print(prefix); pw.print(" content="); mContentFrame.printShortString(pw);
- pw.print(" visible="); mVisibleFrame.printShortString(pw);
- pw.println();
- pw.print(prefix); pw.print(" decor="); mDecorFrame.printShortString(pw);
- pw.println();
- pw.print(prefix); pw.print(" outset="); mOutsetFrame.printShortString(pw);
- pw.println();
+ mWindowFrames.dump(pw, prefix);
pw.print(prefix); pw.print("Cur insets: overscan=");
mOverscanInsets.printShortString(pw);
pw.print(" content="); mContentInsets.printShortString(pw);
@@ -3609,16 +3577,16 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// Set mFrame
Gravity.apply(mAttrs.gravity, w, h, containingFrame,
(int) (x + mAttrs.horizontalMargin * pw),
- (int) (y + mAttrs.verticalMargin * ph), mFrame);
+ (int) (y + mAttrs.verticalMargin * ph), mWindowFrames.mFrame);
// Now make sure the window fits in the overall display frame.
if (fitToDisplay) {
- Gravity.applyDisplay(mAttrs.gravity, displayFrame, mFrame);
+ Gravity.applyDisplay(mAttrs.gravity, displayFrame, mWindowFrames.mFrame);
}
// We need to make sure we update the CompatFrame as it is used for
// cropping decisions, etc, on systems where we lack a decor layer.
- mCompatFrame.set(mFrame);
+ mCompatFrame.set(mWindowFrames.mFrame);
if (mEnforceSizeCompat) {
// See comparable block in computeFrameLw.
mCompatFrame.scale(mInvGlobalScale);
@@ -3731,7 +3699,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
float translateToWindowX(float x) {
- float winX = x - mFrame.left;
+ float winX = x - mWindowFrames.mFrame.left;
if (mEnforceSizeCompat) {
winX *= mGlobalScale;
}
@@ -3739,7 +3707,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
float translateToWindowY(float y) {
- float winY = y - mFrame.top;
+ float winY = y - mWindowFrames.mFrame.top;
if (mEnforceSizeCompat) {
winY *= mGlobalScale;
}
@@ -4303,7 +4271,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// The decor frame is used to specify the region not covered by the system
// decorations (nav bar, status bar). In case this is empty, for example with
// FLAG_TRANSLUCENT_NAVIGATION, we don't need to do any cropping.
- if (mDecorFrame.isEmpty()) {
+ if (mWindowFrames.mDecorFrame.isEmpty()) {
return true;
}
@@ -4348,12 +4316,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
* by system decorations.
*/
private void calculateSystemDecorRect(Rect systemDecorRect) {
- final Rect decorRect = mDecorFrame;
- final int width = mFrame.width();
- final int height = mFrame.height();
+ final Rect decorRect = mWindowFrames.mDecorFrame;
+ final int width = mWindowFrames.mFrame.width();
+ final int height = mWindowFrames.mFrame.height();
- final int left = mFrame.left;
- final int top = mFrame.top;
+ final int left = mWindowFrames.mFrame.left;
+ final int top = mWindowFrames.mFrame.top;
// Initialize the decor rect to the entire frame.
if (isDockedResizing()) {
@@ -4504,7 +4472,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
void startAnimation(Animation anim) {
final DisplayInfo displayInfo = getDisplayContent().getDisplayInfo();
- anim.initialize(mFrame.width(), mFrame.height(),
+ anim.initialize(mWindowFrames.mFrame.width(), mWindowFrames.mFrame.height(),
displayInfo.appWidth, displayInfo.appHeight);
anim.restrictDuration(MAX_ANIMATION_DURATION);
anim.scaleCurrentDuration(mService.getWindowAnimationScaleLocked());
@@ -4519,7 +4487,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
if (DEBUG_ANIM) Slog.v(TAG, "Setting move animation on " + this);
final Point oldPosition = new Point();
final Point newPosition = new Point();
- transformFrameToSurfacePosition(mLastFrame.left, mLastFrame.top, oldPosition);
+ transformFrameToSurfacePosition(mWindowFrames.mLastFrame.left, mWindowFrames.mLastFrame.top,
+ oldPosition);
transformFrameToSurfacePosition(left, top, newPosition);
final AnimationAdapter adapter = new LocalAnimationAdapter(
new MoveAnimationSpec(oldPosition.x, oldPosition.y, newPosition.x, newPosition.y),
@@ -4554,8 +4523,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
final WindowContainer parent = getParent();
if (isChildWindow()) {
final WindowState parentWindow = getParentWindow();
- x += parentWindow.mFrame.left - parentWindow.mAttrs.surfaceInsets.left;
- y += parentWindow.mFrame.top - parentWindow.mAttrs.surfaceInsets.top;
+ x += parentWindow.mWindowFrames.mFrame.left - parentWindow.mAttrs.surfaceInsets.left;
+ y += parentWindow.mWindowFrames.mFrame.top - parentWindow.mAttrs.surfaceInsets.top;
} else if (parent != null) {
final Rect parentBounds = parent.getBounds();
x += parentBounds.left;
@@ -4708,7 +4677,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return;
}
- transformFrameToSurfacePosition(mFrame.left, mFrame.top, mSurfacePosition);
+ transformFrameToSurfacePosition(mWindowFrames.mFrame.left, mWindowFrames.mFrame.top,
+ mSurfacePosition);
if (!mSurfaceAnimator.hasLeash() && !mLastSurfacePosition.equals(mSurfacePosition)) {
t.setPosition(mSurfaceControl, mSurfacePosition.x, mSurfacePosition.y);
@@ -4734,8 +4704,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// Since the parent was outset by its surface insets, we need to undo the outsetting
// with insetting by the same amount.
final WindowState parent = getParentWindow();
- outPoint.offset(-parent.mFrame.left + parent.mAttrs.surfaceInsets.left,
- -parent.mFrame.top + parent.mAttrs.surfaceInsets.top);
+ outPoint.offset(-parent.mWindowFrames.mFrame.left + parent.mAttrs.surfaceInsets.left,
+ -parent.mWindowFrames.mFrame.top + parent.mAttrs.surfaceInsets.top);
} else if (parentWindowContainer != null) {
final Rect parentBounds = parentWindowContainer.getBounds();
outPoint.offset(-parentBounds.left, -parentBounds.top);
@@ -4876,7 +4846,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// we recompute the coordinates of mFrame in the new orientation, so the surface can be
// properly placed.
transformToRotation(oldRotation, newRotation, getDisplayInfo(), transform);
- transformRect(transform, mFrame, null /* tmpRectF */);
+ transformRect(transform, mWindowFrames.mFrame, null /* tmpRectF */);
updateSurfacePosition(t);
mWinAnimator.seamlesslyRotate(t, oldRotation, newRotation);
@@ -4886,6 +4856,26 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
super.seamlesslyRotate(t, oldRotation, newRotation);
}
+ public void getMaxVisibleBounds(Rect out) {
+ if (out.isEmpty()) {
+ out.set(mWindowFrames.mVisibleFrame);
+ return;
+ }
+
+ if (mWindowFrames.mVisibleFrame.left < out.left) {
+ out.left = mWindowFrames.mVisibleFrame.left;
+ }
+ if (mWindowFrames.mVisibleFrame.top < out.top) {
+ out.top = mWindowFrames.mVisibleFrame.top;
+ }
+ if (mWindowFrames.mVisibleFrame.right > out.right) {
+ out.right = mWindowFrames.mVisibleFrame.right;
+ }
+ if (mWindowFrames.mVisibleFrame.bottom > out.bottom) {
+ out.bottom = mWindowFrames.mVisibleFrame.bottom;
+ }
+ }
+
private final class MoveAnimationSpec implements AnimationSpec {
private final long mDuration;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 14e0e13414a9..5ba1da80abfe 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -540,13 +540,13 @@ class WindowStateAnimator {
}
if (WindowManagerService.localLOGV) Slog.v(TAG, "Got surface: " + mSurfaceController
- + ", set left=" + w.mFrame.left + " top=" + w.mFrame.top
+ + ", set left=" + w.getFrameLw().left + " top=" + w.getFrameLw().top
+ ", animLayer=" + mAnimLayer);
if (SHOW_LIGHT_TRANSACTIONS) {
Slog.i(TAG, ">>> OPEN TRANSACTION createSurfaceLocked");
WindowManagerService.logSurface(w, "CREATE pos=("
- + w.mFrame.left + "," + w.mFrame.top + ") ("
+ + w.getFrameLw().left + "," + w.getFrameLw().top + ") ("
+ width + "x" + height + "), layer=" + mAnimLayer + " HIDE", false);
}
@@ -691,7 +691,7 @@ class WindowStateAnimator {
if (screenAnimation) {
// cache often used attributes locally
- final Rect frame = mWin.mFrame;
+ final Rect frame = mWin.getFrameLw();
final float tmpFloats[] = mService.mTmpFloats;
final Matrix tmpMatrix = mWin.mTmpMatrix;
@@ -811,7 +811,7 @@ class WindowStateAnimator {
w.calculatePolicyCrop(mSystemDecorRect);
if (DEBUG_WINDOW_CROP) Slog.d(TAG, "Applying decor to crop win=" + w + " mDecorFrame="
- + w.mDecorFrame + " mSystemDecorRect=" + mSystemDecorRect);
+ + w.getDecorFrame() + " mSystemDecorRect=" + mSystemDecorRect);
final Task task = w.getTask();
final boolean fullscreen = w.fillsDisplay() || (task != null && task.isFullscreen());
@@ -931,9 +931,9 @@ class WindowStateAnimator {
// Make sure that what we're animating to and from is actually the right size in case
// the window cannot take up the full screen.
- mTmpStackBounds.intersectUnchecked(w.mParentFrame);
- mTmpSourceBounds.intersectUnchecked(w.mParentFrame);
- mTmpAnimatingBounds.intersectUnchecked(w.mParentFrame);
+ mTmpStackBounds.intersectUnchecked(w.getParentFrame());
+ mTmpSourceBounds.intersectUnchecked(w.getParentFrame());
+ mTmpAnimatingBounds.intersectUnchecked(w.getParentFrame());
if (!mTmpSourceBounds.isEmpty()) {
// Get the final target stack bounds, if we are not animating, this is just the
@@ -1504,8 +1504,8 @@ class WindowStateAnimator {
// We rotated the screen, but have not received a new buffer with the correct size yet. In
// the mean time, we rotate the buffer we have to the new orientation.
final Matrix transform = mService.mTmpTransform;
- transformToRotation(oldRotation, newRotation, w.mFrame.width(), w.mFrame.height(),
- transform);
+ transformToRotation(oldRotation, newRotation, w.getFrameLw().width(),
+ w.getFrameLw().height(), transform);
transform.getValues(mService.mTmpFloats);
float DsDx = mService.mTmpFloats[Matrix.MSCALE_X];
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 252a1fdb7416..85de5810bbae 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -235,6 +235,8 @@ public final class SystemServer {
"com.android.server.timedetector.TimeDetectorService$Lifecycle";
private static final String TIME_ZONE_DETECTOR_SERVICE_CLASS =
"com.android.server.timezonedetector.TimeZoneDetectorService$Lifecycle";
+ private static final String ACCESSIBILITY_MANAGER_SERVICE_CLASS =
+ "com.android.server.accessibility.AccessibilityManagerService$Lifecycle";
private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
@@ -957,8 +959,7 @@ public final class SystemServer {
traceBeginAndSlog("StartAccessibilityManagerService");
try {
- ServiceManager.addService(Context.ACCESSIBILITY_SERVICE,
- new AccessibilityManagerService(context));
+ mSystemServiceManager.startService(ACCESSIBILITY_MANAGER_SERVICE_CLASS);
} catch (Throwable e) {
reportWtf("starting Accessibility Manager", e);
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index c70d1e18c871..01f53843eb8c 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -258,7 +258,7 @@ public class ActivityManagerServiceTest {
uidRec.hasInternetPermission = true;
mAms.mActiveUids.put(uid, uidRec);
- final ProcessRecord appRec = new ProcessRecord(null, mBatteryStatsImpl,
+ final ProcessRecord appRec = new ProcessRecord(mAms, mBatteryStatsImpl,
new ApplicationInfo(), TAG, uid);
appRec.thread = Mockito.mock(IApplicationThread.class);
mAms.mLruProcesses.add(appRec);
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
index dd3e5a8a33fe..6cfa317fe60c 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
@@ -43,6 +43,7 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -136,7 +137,8 @@ public class ActivityRecordTests extends ActivityTestsBase {
assertFalse(pauseFound.value);
// Clear focused stack
- mActivity.mStackSupervisor.mFocusedStack = null;
+ final ActivityDisplay display = mActivity.mStackSupervisor.getDefaultDisplay();
+ when(display.getFocusedStack()).thenReturn(null);
// In the unfocused stack, the activity should move to paused.
mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
index f92ca5f02d2b..3c4fe18a7820 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
@@ -84,12 +84,12 @@ public class ActivityStackSupervisorTests extends ActivityTestsBase {
}
/**
- * This test ensures that we do not try to restore a task based off an invalid task id. The
- * stack supervisor is a test version so there will be no tasks present. We should expect
- * {@code null} to be returned in this case.
+ * This test ensures that we do not try to restore a task based off an invalid task id. We
+ * should expect {@code null} to be returned in this case.
*/
@Test
public void testRestoringInvalidTask() throws Exception {
+ ((TestActivityDisplay) mSupervisor.getDefaultDisplay()).removeAllTasks();
TaskRecord task = mSupervisor.anyTaskForIdLocked(0 /*taskId*/,
MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, null, false /* onTop */);
assertNull(task);
@@ -109,7 +109,7 @@ public class ActivityStackSupervisorTests extends ActivityTestsBase {
.setStack(mFullscreenStack).build();
final TaskRecord secondTask = secondActivity.getTask();
- mSupervisor.setFocusStackUnchecked("testReplacingTaskInPinnedStack", mFullscreenStack);
+ mFullscreenStack.moveToFront("testReplacingTaskInPinnedStack");
// Ensure full screen stack has both tasks.
ensureStackPlacement(mFullscreenStack, firstTask, secondTask);
@@ -239,7 +239,7 @@ public class ActivityStackSupervisorTests extends ActivityTestsBase {
doReturn(displaySleeping).when(display).isSleeping();
doReturn(keyguardShowing).when(keyguard).isKeyguardOrAodShowing(anyInt());
- mSupervisor.mFocusedStack = isFocusedStack ? stack : null;
+ doReturn(isFocusedStack ? stack : null).when(display).getFocusedStack();
mSupervisor.applySleepTokensLocked(true);
verify(stack, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleepingLocked();
verify(stack, times(expectResumeTopActivity ? 1 : 0)).resumeTopActivityUncheckedLocked(
@@ -253,12 +253,11 @@ public class ActivityStackSupervisorTests extends ActivityTestsBase {
doAnswer((InvocationOnMock invocationOnMock) -> {
final SparseIntArray displayIds = invocationOnMock.<SparseIntArray>getArgument(0);
- displayIds.put(0, unknownDisplayId);
+ displayIds.put(0, 0);
+ displayIds.put(1, unknownDisplayId);
return null;
}).when(mSupervisor.mWindowManager).getDisplaysInFocusOrder(any());
- mSupervisor.mFocusedStack = mock(ActivityStack.class);
-
// Supervisor should skip over the non-existent display.
assertEquals(null, mSupervisor.topRunningActivityLocked());
}
@@ -330,8 +329,9 @@ public class ActivityStackSupervisorTests extends ActivityTestsBase {
@Test
public void testTopRunningActivity() throws Exception {
// Create stack to hold focus
- final ActivityStack emptyStack = mService.mStackSupervisor.getDefaultDisplay()
- .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final ActivityDisplay display = mService.mStackSupervisor.getDefaultDisplay();
+ final ActivityStack emptyStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_STANDARD, true /* onTop */);
final KeyguardController keyguard = mSupervisor.getKeyguardController();
final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack(
@@ -339,11 +339,9 @@ public class ActivityStackSupervisorTests extends ActivityTestsBase {
final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
.setStack(stack).build();
- mSupervisor.mFocusedStack = emptyStack;
-
doAnswer((InvocationOnMock invocationOnMock) -> {
final SparseIntArray displayIds = invocationOnMock.<SparseIntArray>getArgument(0);
- displayIds.put(0, mSupervisor.getDefaultDisplay().mDisplayId);
+ displayIds.put(0, display.mDisplayId);
return null;
}).when(mSupervisor.mWindowManager).getDisplaysInFocusOrder(any());
@@ -359,7 +357,8 @@ public class ActivityStackSupervisorTests extends ActivityTestsBase {
true /* considerKeyguardState */));
// Change focus to stack with activity.
- mSupervisor.mFocusedStack = stack;
+ stack.moveToFront("focusChangeToTestStack");
+ assertEquals(stack, display.getFocusedStack());
assertEquals(activity, mService.mStackSupervisor.topRunningActivityLocked());
assertEquals(null, mService.mStackSupervisor.topRunningActivityLocked(
true /* considerKeyguardState */));
@@ -377,10 +376,12 @@ public class ActivityStackSupervisorTests extends ActivityTestsBase {
true /* considerKeyguardState */));
// Change focus back to empty stack
- mSupervisor.mFocusedStack = emptyStack;
- // Ensure the show when locked activity is returned when not the focused stack
- assertEquals(showWhenLockedActivity, mService.mStackSupervisor.topRunningActivityLocked());
- assertEquals(showWhenLockedActivity, mService.mStackSupervisor.topRunningActivityLocked(
+ emptyStack.moveToFront("focusChangeToEmptyStack");
+ assertEquals(emptyStack, display.getFocusedStack());
+ // Looking for running activity only in top and focused stack, so nothing should be returned
+ // from empty stack.
+ assertEquals(null, mService.mStackSupervisor.topRunningActivityLocked());
+ assertEquals(null, mService.mStackSupervisor.topRunningActivityLocked(
true /* considerKeyguardState */));
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
index 6290751576b2..d51c99b2697c 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
@@ -167,7 +167,7 @@ public class ActivityStackTests extends ActivityTestsBase {
public void testStopActivityWhenActivityDestroyed() throws Exception {
final ActivityRecord r = new ActivityBuilder(mService).setTask(mTask).build();
r.info.flags |= ActivityInfo.FLAG_NO_HISTORY;
- mSupervisor.setFocusStackUnchecked("testStopActivityWithDestroy", mStack);
+ mStack.moveToFront("testStopActivityWithDestroy");
mStack.stopActivityLocked(r);
// Mostly testing to make sure there is a crash in the call part, so if we get here we are
// good-to-go!
@@ -546,9 +546,20 @@ public class ActivityStackTests extends ActivityTestsBase {
private <T extends ActivityStack> T createStackForShouldBeVisibleTest(
ActivityDisplay display, int windowingMode, int activityType, boolean onTop) {
- final T stack = display.createStack(windowingMode, activityType, onTop);
- final ActivityRecord r = new ActivityBuilder(mService).setUid(0).setStack(stack)
- .setCreateTask(true).build();
+ final T stack;
+ if (activityType == ACTIVITY_TYPE_HOME) {
+ // Home stack and activity are created in ActivityTestsBase#setupActivityManagerService
+ stack = mDefaultDisplay.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
+ if (onTop) {
+ mDefaultDisplay.positionChildAtTop(stack);
+ } else {
+ mDefaultDisplay.positionChildAtBottom(stack);
+ }
+ } else {
+ stack = display.createStack(windowingMode, activityType, onTop);
+ final ActivityRecord r = new ActivityBuilder(mService).setUid(0).setStack(stack)
+ .setCreateTask(true).build();
+ }
return stack;
}
@@ -654,14 +665,13 @@ public class ActivityStackTests extends ActivityTestsBase {
private void verifyShouldSleepActivities(boolean focusedStack,
boolean keyguardGoingAway, boolean displaySleeping, boolean expected) {
- mSupervisor.mFocusedStack = focusedStack ? mStack : null;
-
final ActivityDisplay display = mock(ActivityDisplay.class);
final KeyguardController keyguardController = mSupervisor.getKeyguardController();
doReturn(display).when(mSupervisor).getActivityDisplay(anyInt());
doReturn(keyguardGoingAway).when(keyguardController).isKeyguardGoingAway();
doReturn(displaySleeping).when(display).isSleeping();
+ doReturn(focusedStack ? mStack : null).when(mSupervisor).getTopDisplayFocusedStack();
assertEquals(expected, mStack.shouldSleepActivities());
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
index 267e689f0a91..19a3e4a17c20 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
@@ -55,6 +55,7 @@ import org.junit.Test;
import static android.content.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED;
import static com.android.server.am.ActivityManagerService.ANIMATE;
+import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -77,6 +78,7 @@ import com.android.server.am.LaunchParamsController.LaunchParamsModifier;
import com.android.server.am.TaskRecord.TaskRecordFactory;
import java.util.ArrayList;
+import java.util.List;
/**
* Tests for the {@link ActivityStarter} class.
@@ -312,9 +314,6 @@ public class ActivityStarterTests extends ActivityTestsBase {
.setCreateStack(false)
.build();
- // supervisor needs a focused stack.
- mService.mStackSupervisor.mFocusedStack = stack;
-
// use factory that only returns spy task.
final TaskRecordFactory factory = mock(TaskRecordFactory.class);
TaskRecord.setTaskRecordFactory(factory);
@@ -404,8 +403,8 @@ public class ActivityStarterTests extends ActivityTestsBase {
reusableActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
// Set focus back to primary.
- mService.mStackSupervisor.setFocusStackUnchecked("testSplitScreenDeliverToTop",
- focusActivity.getStack());
+ final ActivityStack focusStack = focusActivity.getStack();
+ focusStack.moveToFront("testSplitScreenDeliverToTop");
doReturn(reusableActivity).when(mService.mStackSupervisor).findTaskLocked(any(), anyInt());
@@ -453,6 +452,7 @@ public class ActivityStarterTests extends ActivityTestsBase {
@Test
public void testTaskModeViolation() {
final ActivityDisplay display = mService.mStackSupervisor.getDefaultDisplay();
+ ((TestActivityDisplay) display).removeAllTasks();
assertNoTasks(display);
final ActivityStarter starter = prepareStarter(0);
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index f2d3eb6acd47..8e887d12ee24 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -16,11 +16,16 @@
package com.android.server.am;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.view.Display.DEFAULT_DISPLAY;
+
+import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
+import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
+
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
@@ -55,6 +60,7 @@ import android.os.UserHandle;
import android.service.voice.IVoiceInteractionSession;
import android.support.test.InstrumentationRegistry;
import android.testing.DexmakerShareClassLoaderRule;
+import android.util.SparseIntArray;
import com.android.internal.app.IVoiceInteractor;
@@ -70,6 +76,8 @@ import org.junit.After;
import org.junit.Before;
import org.mockito.MockitoAnnotations;
+import java.util.List;
+
/**
* A base class to handle common operations in activity related unit tests.
@@ -119,9 +127,9 @@ public class ActivityTestsBase {
}
ActivityManagerService setupActivityManagerService(TestActivityTaskManagerService atm) {
+ AttributeCache.init(mContext);
final ActivityManagerService am = spy(new TestActivityManagerService(mContext, atm));
setupActivityManagerService(am, atm);
- AttributeCache.init(mContext);
return am;
}
@@ -135,6 +143,15 @@ public class ActivityTestsBase {
doNothing().when(am).grantEphemeralAccessLocked(anyInt(), any(), anyInt(), anyInt());
am.mWindowManager = prepareMockWindowManager();
atm.setWindowManager(am.mWindowManager);
+
+ // Put a home stack on the default display, so that we'll always have something focusable.
+ final TestActivityStackSupervisor supervisor =
+ (TestActivityStackSupervisor) atm.mStackSupervisor;
+ supervisor.mHomeStack = supervisor.mDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_HOME, ON_TOP);
+ final TaskRecord task = new TaskBuilder(atm.mStackSupervisor)
+ .setStack(supervisor.mHomeStack).build();
+ new ActivityBuilder(atm).setTask(task).build();
}
/**
@@ -327,7 +344,7 @@ public class ActivityTestsBase {
task.userId = mUserId;
if (mStack != null) {
- mSupervisor.setFocusStackUnchecked("test", mStack);
+ mStack.moveToFront("test");
mStack.addTask(task, true, "creating test task");
task.setStack(mStack);
task.setWindowContainerController();
@@ -464,13 +481,6 @@ public class ActivityTestsBase {
ActivityDisplay getDefaultDisplay() {
return mDisplay;
}
-
- // Just return the current front task. This is called internally so we cannot use spy to mock this out.
- @Override
- ActivityStack getNextFocusableStackLocked(ActivityStack currentFocus,
- boolean ignoreCurrent) {
- return mFocusedStack;
- }
}
protected static class TestActivityDisplay extends ActivityDisplay {
@@ -507,6 +517,15 @@ public class ActivityTestsBase {
protected DisplayWindowController createWindowContainerController() {
return mock(DisplayWindowController.class);
}
+
+ void removeAllTasks() {
+ for (int i = 0; i < getChildCount(); i++) {
+ final ActivityStack stack = getChildAt(i);
+ for (TaskRecord task : (List<TaskRecord>) stack.getAllTasks()) {
+ stack.removeTask(task, "removeAllTasks", REMOVE_TASK_MODE_DESTROYING);
+ }
+ }
+ }
}
private static WindowManagerService prepareMockWindowManager() {
@@ -520,6 +539,12 @@ public class ActivityTestsBase {
return null;
}).when(service).inSurfaceTransaction(any());
+ doAnswer((InvocationOnMock invocationOnMock) -> {
+ final SparseIntArray displayIds = invocationOnMock.<SparseIntArray>getArgument(0);
+ displayIds.put(0, 0);
+ return null;
+ }).when(service).getDisplaysInFocusOrder(any());
+
return service;
}
diff --git a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
index 3547b0ddcadf..37de79524ee3 100644
--- a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
@@ -119,7 +119,7 @@ public class RecentTasksTest extends ActivityTestsBase {
setupActivityManagerService(am, mService);
mRecentTasks = (TestRecentTasks) mService.getRecentTasks();
mRecentTasks.loadParametersFromResources(mContext.getResources());
- mHomeStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+ mHomeStack = mService.mStackSupervisor.getDefaultDisplay().getOrCreateStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
mStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
diff --git a/services/tests/servicestests/src/com/android/server/am/TaskStackChangedListenerTest.java b/services/tests/servicestests/src/com/android/server/am/TaskStackChangedListenerTest.java
index 8e87a5f2b689..5cd410e0e851 100644
--- a/services/tests/servicestests/src/com/android/server/am/TaskStackChangedListenerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/TaskStackChangedListenerTest.java
@@ -181,6 +181,7 @@ public class TaskStackChangedListenerTest {
final ActivityTaskChangeCallbacks activity =
(ActivityTaskChangeCallbacks) startTestActivity(ActivityTaskChangeCallbacks.class);
+ activity.setDetachedFromWindowLatch(onDetachedFromWindowLatch);
final int id = activity.getTaskId();
// Test for onTaskCreated.
@@ -207,6 +208,7 @@ public class TaskStackChangedListenerTest {
assertEquals(1, taskRemovedLatch.getCount());
waitForCallback(taskRemovedLatch);
assertEquals(id, params[0]);
+ waitForCallback(onDetachedFromWindowLatch);
assertTrue(activity.onDetachedFromWindowCalled);
}
@@ -288,11 +290,17 @@ public class TaskStackChangedListenerTest {
}
public static class ActivityTaskChangeCallbacks extends Activity {
- public boolean onDetachedFromWindowCalled = false;;
+ boolean onDetachedFromWindowCalled = false;
+ CountDownLatch onDetachedFromWindowCountDownLatch;
@Override
public void onDetachedFromWindow() {
onDetachedFromWindowCalled = true;
+ onDetachedFromWindowCountDownLatch.countDown();
+ }
+
+ void setDetachedFromWindowLatch(CountDownLatch countDownLatch) {
+ onDetachedFromWindowCountDownLatch = countDownLatch;
}
}
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
new file mode 100644
index 000000000000..90ad34982421
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -0,0 +1,162 @@
+/*
+ * 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.server.hdmi;
+
+import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM;
+import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_PLAYBACK;
+import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import android.os.Looper;
+import android.os.test.TestLooper;
+import android.support.test.filters.SmallTest;
+import java.util.ArrayList;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests for {@link HdmiControlService} class.
+ */
+@SmallTest
+@RunWith(JUnit4.class)
+public class HdmiControlServiceTest {
+
+ private class HdmiCecLocalDeviceMyDevice extends HdmiCecLocalDevice {
+
+ private boolean mCanGoToStandby;
+ private boolean mIsStandby;
+ private boolean mIsDisabled;
+
+ protected HdmiCecLocalDeviceMyDevice(HdmiControlService service, int deviceType) {
+ super(service, deviceType);
+ }
+
+ @Override
+ protected void onAddressAllocated(int logicalAddress, int reason) {
+
+ }
+
+ @Override
+ protected int getPreferredAddress() {
+ return 0;
+ }
+
+ @Override
+ protected void setPreferredAddress(int addr) {
+
+ }
+
+ @Override
+ protected boolean canGoToStandby() {
+ return mCanGoToStandby;
+ }
+
+ @Override
+ protected void disableDevice(boolean initiatedByCec,
+ final PendingActionClearedCallback originalCallback) {
+ mIsDisabled = true;
+ originalCallback.onCleared(this);
+ }
+
+ @Override
+ protected void onStandby(boolean initiatedByCec, int standbyAction) {
+ mIsStandby = true;
+ }
+
+ protected boolean isStandby() {
+ return mIsStandby;
+ }
+
+ protected boolean isDisabled() {
+ return mIsDisabled;
+ }
+
+ protected void setCanGoToStandby(boolean canGoToStandby) {
+ mCanGoToStandby = canGoToStandby;
+ }
+ }
+
+ private static final String TAG = "HdmiControlServiceTest";
+ private HdmiControlService mHdmiControlService;
+ private HdmiCecController mHdmiCecController;
+ private HdmiCecLocalDeviceMyDevice mMyAudioSystemDevice;
+ private HdmiCecLocalDeviceMyDevice mMyPlaybackDevice;
+ private FakeNativeWrapper mNativeWrapper;
+ private Looper mMyLooper;
+ private TestLooper mTestLooper = new TestLooper();
+ private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
+ private boolean mStandbyMessageReceived;
+
+ @Before
+ public void SetUp() {
+ mHdmiControlService = new HdmiControlService(null) {
+ @Override
+ boolean isStandbyMessageReceived() {
+ return mStandbyMessageReceived;
+ }
+ };
+ mMyLooper = mTestLooper.getLooper();
+
+ mMyAudioSystemDevice = new HdmiCecLocalDeviceMyDevice(
+ mHdmiControlService, DEVICE_AUDIO_SYSTEM);
+ mMyPlaybackDevice = new HdmiCecLocalDeviceMyDevice(
+ mHdmiControlService, DEVICE_PLAYBACK);
+ mMyAudioSystemDevice.init();
+ mMyPlaybackDevice.init();
+
+ mHdmiControlService.setIoLooper(mMyLooper);
+
+ mNativeWrapper = new FakeNativeWrapper();
+ mHdmiCecController = HdmiCecController.createWithNativeWrapper(
+ mHdmiControlService, mNativeWrapper);
+ mHdmiControlService.setCecController(mHdmiCecController);
+ mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
+ mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
+
+ mLocalDevices.add(mMyAudioSystemDevice);
+ mLocalDevices.add(mMyPlaybackDevice);
+ mHdmiControlService.initPortInfo();
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+
+ mTestLooper.dispatchAll();
+ }
+
+ @Test
+ public void onStandby_notByCec_cannotGoToStandby() {
+ mStandbyMessageReceived = false;
+ mMyPlaybackDevice.setCanGoToStandby(false);
+
+ mHdmiControlService.onStandby(HdmiControlService.STANDBY_SCREEN_OFF);
+ assertTrue(mMyPlaybackDevice.isStandby());
+ assertTrue(mMyAudioSystemDevice.isStandby());
+ assertFalse(mMyPlaybackDevice.isDisabled());
+ assertFalse(mMyAudioSystemDevice.isDisabled());
+ }
+
+ @Test
+ public void onStandby_byCec() {
+ mStandbyMessageReceived = true;
+
+ mHdmiControlService.onStandby(HdmiControlService.STANDBY_SCREEN_OFF);
+ assertTrue(mMyPlaybackDevice.isStandby());
+ assertTrue(mMyAudioSystemDevice.isStandby());
+ assertTrue(mMyPlaybackDevice.isDisabled());
+ assertTrue(mMyAudioSystemDevice.isDisabled());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java b/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
index 7487d4490d9a..eb6ed1924235 100644
--- a/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
+++ b/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
@@ -25,19 +25,12 @@ import android.view.DisplayCutout;
import android.view.IApplicationToken;
import android.view.WindowManager;
+import com.android.server.wm.WindowFrames;
import com.android.server.wm.utils.WmDisplayCutout;
public class FakeWindowState implements WindowManagerPolicy.WindowState {
- public final Rect parentFrame = new Rect();
- public final Rect displayFrame = new Rect();
- public final Rect overscanFrame = new Rect();
- public final Rect contentFrame = new Rect();
- public final Rect visibleFrame = new Rect();
- public final Rect decorFrame = new Rect();
- public final Rect stableFrame = new Rect();
- public Rect outsetFrame = new Rect();
-
+ public WindowFrames windowFrames;
public WmDisplayCutout displayCutout;
public WindowManager.LayoutParams attrs;
@@ -61,44 +54,43 @@ public class FakeWindowState implements WindowManagerPolicy.WindowState {
}
@Override
- public void computeFrameLw(Rect parentFrame, Rect displayFrame, Rect overlayFrame,
- Rect contentFrame, Rect visibleFrame, Rect decorFrame, Rect stableFrame,
- @Nullable Rect outsetFrame, WmDisplayCutout displayCutout,
+ public void computeFrameLw(WindowFrames windowFrames, WmDisplayCutout displayCutout,
boolean parentFrameWasClippedByDisplayCutout) {
- this.parentFrame.set(parentFrame);
- this.displayFrame.set(displayFrame);
- this.overscanFrame.set(overlayFrame);
- this.contentFrame.set(contentFrame);
- this.visibleFrame.set(visibleFrame);
- this.decorFrame.set(decorFrame);
- this.stableFrame.set(stableFrame);
- this.outsetFrame = outsetFrame == null ? null : new Rect(outsetFrame);
+ this.windowFrames = windowFrames;
this.displayCutout = displayCutout;
}
@Override
public Rect getFrameLw() {
- return parentFrame;
+ return windowFrames.mParentFrame;
}
@Override
public Rect getDisplayFrameLw() {
- return displayFrame;
+ return windowFrames.mDisplayFrame;
}
@Override
public Rect getOverscanFrameLw() {
- return overscanFrame;
+ return windowFrames.mOverscanFrame;
}
@Override
public Rect getContentFrameLw() {
- return contentFrame;
+ return windowFrames.mContentFrame;
}
@Override
public Rect getVisibleFrameLw() {
- return visibleFrame;
+ return windowFrames.mVisibleFrame;
+ }
+
+ public Rect getStableFrame() {
+ return windowFrames.mStableFrame;
+ }
+
+ public Rect getDecorFrame() {
+ return windowFrames.mDecorFrame;
}
@Override
@@ -255,6 +247,9 @@ public class FakeWindowState implements WindowManagerPolicy.WindowState {
}
@Override
+ public boolean canReceiveKeys() { return false; }
+
+ @Override
public void writeIdentifierToProto(ProtoOutputStream proto, long fieldId){
throw new UnsupportedOperationException("not implemented");
}
diff --git a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java
index cb94ec7caaca..cb9fab3fa87c 100644
--- a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java
+++ b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java
@@ -75,11 +75,11 @@ public class PhoneWindowManagerLayoutTest extends PhoneWindowManagerTestBase {
mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
- assertInsetByTopBottom(mAppWindow.parentFrame, 0, 0);
- assertInsetByTopBottom(mAppWindow.stableFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.contentFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.decorFrame, 0, 0);
- assertInsetBy(mAppWindow.displayFrame, 0, 0, 0, 0);
+ assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, 0);
+ assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0);
+ assertInsetBy(mAppWindow.getDisplayFrameLw(), 0, 0, 0, 0);
}
@Test
@@ -90,11 +90,11 @@ public class PhoneWindowManagerLayoutTest extends PhoneWindowManagerTestBase {
mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
- assertInsetByTopBottom(mAppWindow.parentFrame, 0, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.stableFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.contentFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.decorFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.displayFrame, 0, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getDecorFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), 0, NAV_BAR_HEIGHT);
}
@Test
@@ -106,11 +106,11 @@ public class PhoneWindowManagerLayoutTest extends PhoneWindowManagerTestBase {
mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
- assertInsetByTopBottom(mAppWindow.parentFrame, 0, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.stableFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.contentFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.decorFrame, 0, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.displayFrame, 0, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), 0, NAV_BAR_HEIGHT);
}
@Test
@@ -131,11 +131,11 @@ public class PhoneWindowManagerLayoutTest extends PhoneWindowManagerTestBase {
mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
- assertInsetByTopBottom(mAppWindow.parentFrame, 0, 0);
- assertInsetByTopBottom(mAppWindow.stableFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.contentFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.decorFrame, 0, 0);
- assertInsetByTopBottom(mAppWindow.displayFrame, 0, 0);
+ assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, 0);
+ assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0);
+ assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), 0, 0);
}
@Test
@@ -148,11 +148,11 @@ public class PhoneWindowManagerLayoutTest extends PhoneWindowManagerTestBase {
mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
- assertInsetByTopBottom(mAppWindow.parentFrame, STATUS_BAR_HEIGHT, 0);
- assertInsetByTopBottom(mAppWindow.stableFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.contentFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.decorFrame, 0, 0);
- assertInsetByTopBottom(mAppWindow.displayFrame, STATUS_BAR_HEIGHT, 0);
+ assertInsetByTopBottom(mAppWindow.getFrameLw(), STATUS_BAR_HEIGHT, 0);
+ assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0);
+ assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0);
}
@Test
@@ -165,11 +165,11 @@ public class PhoneWindowManagerLayoutTest extends PhoneWindowManagerTestBase {
mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
- assertInsetByTopBottom(mAppWindow.parentFrame, 0, 0);
- assertInsetByTopBottom(mAppWindow.stableFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.contentFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.decorFrame, 0, 0);
- assertInsetBy(mAppWindow.displayFrame, 0, 0, 0, 0);
+ assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, 0);
+ assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0);
+ assertInsetBy(mAppWindow.getDisplayFrameLw(), 0, 0, 0, 0);
}
@Test
@@ -182,11 +182,11 @@ public class PhoneWindowManagerLayoutTest extends PhoneWindowManagerTestBase {
mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
- assertInsetByTopBottom(mAppWindow.parentFrame, STATUS_BAR_HEIGHT, 0);
- assertInsetByTopBottom(mAppWindow.stableFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.contentFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.decorFrame, 0, 0);
- assertInsetByTopBottom(mAppWindow.displayFrame, STATUS_BAR_HEIGHT, 0);
+ assertInsetByTopBottom(mAppWindow.getFrameLw(), STATUS_BAR_HEIGHT, 0);
+ assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0);
+ assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0);
}
@Test
@@ -200,11 +200,11 @@ public class PhoneWindowManagerLayoutTest extends PhoneWindowManagerTestBase {
mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
- assertInsetByTopBottom(mAppWindow.parentFrame, 0, 0);
- assertInsetByTopBottom(mAppWindow.stableFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.contentFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.decorFrame, 0, 0);
- assertInsetByTopBottom(mAppWindow.displayFrame, 0, 0);
+ assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, 0);
+ assertInsetByTopBottom(mAppWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getDecorFrame(), 0, 0);
+ assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), 0, 0);
}
@@ -217,12 +217,12 @@ public class PhoneWindowManagerLayoutTest extends PhoneWindowManagerTestBase {
mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
- assertInsetBy(mAppWindow.parentFrame, DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
- assertInsetBy(mAppWindow.stableFrame, 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
- assertInsetBy(mAppWindow.contentFrame,
+ assertInsetBy(mAppWindow.getFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
+ assertInsetBy(mAppWindow.getStableFrame(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+ assertInsetBy(mAppWindow.getContentFrameLw(),
DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
- assertInsetBy(mAppWindow.decorFrame, 0, 0, 0, 0);
- assertInsetBy(mAppWindow.displayFrame, DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
+ assertInsetBy(mAppWindow.getDecorFrame(), 0, 0, 0, 0);
+ assertInsetBy(mAppWindow.getDisplayFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
}
@Test
@@ -234,12 +234,12 @@ public class PhoneWindowManagerLayoutTest extends PhoneWindowManagerTestBase {
mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
- assertInsetBy(mAppWindow.parentFrame, 0, 0, DISPLAY_CUTOUT_HEIGHT, 0);
- assertInsetBy(mAppWindow.stableFrame, NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, 0, 0);
- assertInsetBy(mAppWindow.contentFrame,
+ assertInsetBy(mAppWindow.getFrameLw(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0);
+ assertInsetBy(mAppWindow.getStableFrame(), NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, 0, 0);
+ assertInsetBy(mAppWindow.getContentFrameLw(),
NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, DISPLAY_CUTOUT_HEIGHT, 0);
- assertInsetBy(mAppWindow.decorFrame, 0, 0, 0, 0);
- assertInsetBy(mAppWindow.displayFrame, 0, 0, DISPLAY_CUTOUT_HEIGHT, 0);
+ assertInsetBy(mAppWindow.getDecorFrame(), 0, 0, 0, 0);
+ assertInsetBy(mAppWindow.getDisplayFrameLw(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0);
}
@Test
@@ -253,11 +253,11 @@ public class PhoneWindowManagerLayoutTest extends PhoneWindowManagerTestBase {
mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
- assertInsetBy(mAppWindow.parentFrame, DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
- assertInsetBy(mAppWindow.stableFrame, 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
- assertInsetBy(mAppWindow.contentFrame,
+ assertInsetBy(mAppWindow.getFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
+ assertInsetBy(mAppWindow.getStableFrame(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+ assertInsetBy(mAppWindow.getContentFrameLw(),
DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
- assertInsetBy(mAppWindow.decorFrame, 0, 0, 0, 0);
+ assertInsetBy(mAppWindow.getDecorFrame(), 0, 0, 0, 0);
}
@Test
@@ -273,8 +273,8 @@ public class PhoneWindowManagerLayoutTest extends PhoneWindowManagerTestBase {
mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
- assertInsetByTopBottom(mAppWindow.parentFrame, 0, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mAppWindow.displayFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getFrameLw(), 0, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mAppWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
}
@Test
@@ -289,11 +289,11 @@ public class PhoneWindowManagerLayoutTest extends PhoneWindowManagerTestBase {
mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
- assertInsetBy(mAppWindow.parentFrame, 0, 0, 0, 0);
- assertInsetBy(mAppWindow.stableFrame, 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
- assertInsetBy(mAppWindow.contentFrame,
+ assertInsetBy(mAppWindow.getFrameLw(), 0, 0, 0, 0);
+ assertInsetBy(mAppWindow.getStableFrame(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+ assertInsetBy(mAppWindow.getContentFrameLw(),
DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
- assertInsetBy(mAppWindow.decorFrame, 0, 0, 0, 0);
+ assertInsetBy(mAppWindow.getDecorFrame(), 0, 0, 0, 0);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
index cd8e650406d2..4e9894b66d3a 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
@@ -37,6 +37,8 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -351,34 +353,36 @@ public class DisplayContentTests extends WindowTestsBase {
*/
@Test
public void testMaxUiWidth() throws Exception {
+ // Prevent base display metrics for test from being updated to the value of real display.
+ final DisplayContent displayContent = createDisplayNoUpdateDisplayInfo();
final int baseWidth = 1440;
final int baseHeight = 2560;
final int baseDensity = 300;
- mDisplayContent.updateBaseDisplayMetrics(baseWidth, baseHeight, baseDensity);
+ displayContent.updateBaseDisplayMetrics(baseWidth, baseHeight, baseDensity);
final int maxWidth = 300;
final int resultingHeight = (maxWidth * baseHeight) / baseWidth;
final int resultingDensity = (maxWidth * baseDensity) / baseWidth;
- mDisplayContent.setMaxUiWidth(maxWidth);
- verifySizes(mDisplayContent, maxWidth, resultingHeight, resultingDensity);
+ displayContent.setMaxUiWidth(maxWidth);
+ verifySizes(displayContent, maxWidth, resultingHeight, resultingDensity);
// Assert setting values again does not change;
- mDisplayContent.updateBaseDisplayMetrics(baseWidth, baseHeight, baseDensity);
- verifySizes(mDisplayContent, maxWidth, resultingHeight, resultingDensity);
+ displayContent.updateBaseDisplayMetrics(baseWidth, baseHeight, baseDensity);
+ verifySizes(displayContent, maxWidth, resultingHeight, resultingDensity);
final int smallerWidth = 200;
final int smallerHeight = 400;
final int smallerDensity = 100;
// Specify smaller dimension, verify that it is honored
- mDisplayContent.updateBaseDisplayMetrics(smallerWidth, smallerHeight, smallerDensity);
- verifySizes(mDisplayContent, smallerWidth, smallerHeight, smallerDensity);
+ displayContent.updateBaseDisplayMetrics(smallerWidth, smallerHeight, smallerDensity);
+ verifySizes(displayContent, smallerWidth, smallerHeight, smallerDensity);
// Verify that setting the max width to a greater value than the base width has no effect
- mDisplayContent.setMaxUiWidth(maxWidth);
- verifySizes(mDisplayContent, smallerWidth, smallerHeight, smallerDensity);
+ displayContent.setMaxUiWidth(maxWidth);
+ verifySizes(displayContent, smallerWidth, smallerHeight, smallerDensity);
}
/**
@@ -476,22 +480,42 @@ public class DisplayContentTests extends WindowTestsBase {
@Test
public void testDisplayCutout_rot90() throws Exception {
synchronized (sWm.getWindowManagerLock()) {
- final DisplayContent dc = createNewDisplay();
- dc.mInitialDisplayWidth = 200;
- dc.mInitialDisplayHeight = 400;
- Rect r1 = new Rect(80, 0, 120, 10);
+ // Prevent mInitialDisplayCutout from being updated from real display (e.g. null
+ // if the device has no cutout).
+ final DisplayContent dc = createDisplayNoUpdateDisplayInfo();
+ // Rotation may use real display info to compute bound, so here also uses the
+ // same width and height.
+ final int displayWidth = dc.mInitialDisplayWidth;
+ final int displayHeight = dc.mInitialDisplayHeight;
+ final int cutoutWidth = 40;
+ final int cutoutHeight = 10;
+ final int left = (displayWidth - cutoutWidth) / 2;
+ final int top = 0;
+ final int right = (displayWidth + cutoutWidth) / 2;
+ final int bottom = cutoutHeight;
+
+ final Rect r1 = new Rect(left, top, right, bottom);
final DisplayCutout cutout = new WmDisplayCutout(
fromBoundingRect(r1.left, r1.top, r1.right, r1.bottom), null)
- .computeSafeInsets(200, 400).getDisplayCutout();
+ .computeSafeInsets(displayWidth, displayHeight).getDisplayCutout();
dc.mInitialDisplayCutout = cutout;
dc.setRotation(Surface.ROTATION_90);
dc.computeScreenConfiguration(new Configuration()); // recomputes dc.mDisplayInfo.
- final Rect r = new Rect(0, 80, 10, 120);
+ // ----o---------- -------------
+ // | | | | |
+ // | ------o | o---
+ // | | | |
+ // | | -> | |
+ // | | ---o
+ // | | |
+ // | | -------------
+ final Rect r = new Rect(top, left, bottom, right);
assertEquals(new WmDisplayCutout(
fromBoundingRect(r.left, r.top, r.right, r.bottom), null)
- .computeSafeInsets(400, 200).getDisplayCutout(), dc.getDisplayInfo().displayCutout);
+ .computeSafeInsets(displayHeight, displayWidth)
+ .getDisplayCutout(), dc.getDisplayInfo().displayCutout);
}
}
@@ -553,6 +577,16 @@ public class DisplayContentTests extends WindowTestsBase {
assertEquals(displayContent.mBaseDisplayDensity, expectedBaseDensity);
}
+ /**
+ * Create DisplayContent that does not update display base/initial values from device to keep
+ * the values set by test.
+ */
+ private DisplayContent createDisplayNoUpdateDisplayInfo() {
+ final DisplayContent displayContent = spy(createNewDisplay());
+ doNothing().when(displayContent).updateDisplayInfo();
+ return displayContent;
+ }
+
private void assertForAllWindowsOrder(List<WindowState> expectedWindowsBottomToTop) {
final LinkedList<WindowState> actualWindows = new LinkedList<>();
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
index 5a563320f9cb..6b410b68c45d 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
@@ -51,6 +51,7 @@ public class WindowFrameTests extends WindowTestsBase {
private WindowToken mWindowToken;
private final IWindow mIWindow = new TestIWindow();
+ private final Rect mEmptyRect = new Rect();
class WindowStateWithTask extends WindowState {
final Task mTask;
@@ -160,8 +161,9 @@ public class WindowFrameTests extends WindowTestsBase {
// When mFrame extends past cf, the content insets are
// the difference between mFrame and ContentFrame. Visible
// and stable frames work the same way.
- w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null, WmDisplayCutout.NO_CUTOUT, false);
- assertRect(w.mFrame,0, 0, 1000, 1000);
+ final WindowFrames windowFrames = new WindowFrames(pf, df, of, cf, vf, dcf, sf, mEmptyRect);
+ w.computeFrameLw(windowFrames, WmDisplayCutout.NO_CUTOUT, false);
+ assertRect(w.getFrameLw(),0, 0, 1000, 1000);
assertRect(w.mContentInsets, 0, topContentInset, 0, bottomContentInset);
assertRect(w.mVisibleInsets, 0, topVisibleInset, 0, bottomVisibleInset);
assertRect(w.mStableInsets, leftStableInset, 0, rightStableInset, 0);
@@ -169,19 +171,19 @@ public class WindowFrameTests extends WindowTestsBase {
assertTrue(cf.equals(w.getContentFrameLw()));
assertTrue(vf.equals(w.getVisibleFrameLw()));
assertTrue(sf.equals(w.getStableFrameLw()));
- // On the other hand mFrame doesn't extend past cf we won't get any insets
+ // On the other hand getFrame() doesn't extend past cf we won't get any insets
w.mAttrs.x = 100;
w.mAttrs.y = 100;
w.mAttrs.width = 100; w.mAttrs.height = 100; //have to clear MATCH_PARENT
w.mRequestedWidth = 100;
w.mRequestedHeight = 100;
- w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null, WmDisplayCutout.NO_CUTOUT, false);
- assertRect(w.mFrame, 100, 100, 200, 200);
+ w.computeFrameLw(windowFrames, WmDisplayCutout.NO_CUTOUT, false);
+ assertRect(w.getFrameLw(), 100, 100, 200, 200);
assertRect(w.mContentInsets, 0, 0, 0, 0);
// In this case the frames are shrunk to the window frame.
- assertTrue(w.mFrame.equals(w.getContentFrameLw()));
- assertTrue(w.mFrame.equals(w.getVisibleFrameLw()));
- assertTrue(w.mFrame.equals(w.getStableFrameLw()));
+ assertTrue(w.getFrameLw().equals(w.getContentFrameLw()));
+ assertTrue(w.getFrameLw().equals(w.getVisibleFrameLw()));
+ assertTrue(w.getFrameLw().equals(w.getStableFrameLw()));
}
@Test
@@ -196,25 +198,26 @@ public class WindowFrameTests extends WindowTestsBase {
// Here the window has FILL_PARENT, FILL_PARENT
// so we expect it to fill the entire available frame.
- w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
- assertRect(w.mFrame, 0, 0, 1000, 1000);
+ final WindowFrames windowFrames = new WindowFrames(pf, pf, pf, pf, pf, pf, pf, pf);
+ w.computeFrameLw(windowFrames, WmDisplayCutout.NO_CUTOUT, false);
+ assertRect(w.getFrameLw(), 0, 0, 1000, 1000);
// It can select various widths and heights within the bounds.
// Strangely the window attribute width is ignored for normal windows
// and we use mRequestedWidth/mRequestedHeight
w.mAttrs.width = 300;
w.mAttrs.height = 300;
- w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
+ w.computeFrameLw(windowFrames, WmDisplayCutout.NO_CUTOUT, false);
// Explicit width and height without requested width/height
// gets us nothing.
- assertRect(w.mFrame, 0, 0, 0, 0);
+ assertRect(w.getFrameLw(), 0, 0, 0, 0);
w.mRequestedWidth = 300;
w.mRequestedHeight = 300;
- w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
+ w.computeFrameLw(windowFrames, WmDisplayCutout.NO_CUTOUT, false);
// With requestedWidth/Height we can freely choose our size within the
// parent bounds.
- assertRect(w.mFrame, 0, 0, 300, 300);
+ assertRect(w.getFrameLw(), 0, 0, 300, 300);
// With FLAG_SCALED though, requestedWidth/height is used to control
// the unscaled surface size, and mAttrs.width/height becomes the
@@ -224,23 +227,23 @@ public class WindowFrameTests extends WindowTestsBase {
w.mRequestedWidth = -1;
w.mAttrs.width = 100;
w.mAttrs.height = 100;
- w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
- assertRect(w.mFrame, 0, 0, 100, 100);
+ w.computeFrameLw(windowFrames, WmDisplayCutout.NO_CUTOUT, false);
+ assertRect(w.getFrameLw(), 0, 0, 100, 100);
w.mAttrs.flags = 0;
// But sizes too large will be clipped to the containing frame
w.mRequestedWidth = 1200;
w.mRequestedHeight = 1200;
- w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
- assertRect(w.mFrame, 0, 0, 1000, 1000);
+ w.computeFrameLw(windowFrames, WmDisplayCutout.NO_CUTOUT, false);
+ assertRect(w.getFrameLw(), 0, 0, 1000, 1000);
// Before they are clipped though windows will be shifted
w.mAttrs.x = 300;
w.mAttrs.y = 300;
w.mRequestedWidth = 1000;
w.mRequestedHeight = 1000;
- w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
- assertRect(w.mFrame, 0, 0, 1000, 1000);
+ w.computeFrameLw(windowFrames, WmDisplayCutout.NO_CUTOUT, false);
+ assertRect(w.getFrameLw(), 0, 0, 1000, 1000);
// If there is room to move around in the parent frame the window will be shifted according
// to gravity.
@@ -249,17 +252,17 @@ public class WindowFrameTests extends WindowTestsBase {
w.mRequestedWidth = 300;
w.mRequestedHeight = 300;
w.mAttrs.gravity = Gravity.RIGHT | Gravity.TOP;
- w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
- assertRect(w.mFrame, 700, 0, 1000, 300);
+ w.computeFrameLw(windowFrames, WmDisplayCutout.NO_CUTOUT, false);
+ assertRect(w.getFrameLw(), 700, 0, 1000, 300);
w.mAttrs.gravity = Gravity.RIGHT | Gravity.BOTTOM;
- w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
- assertRect(w.mFrame, 700, 700, 1000, 1000);
+ w.computeFrameLw(windowFrames, WmDisplayCutout.NO_CUTOUT, false);
+ assertRect(w.getFrameLw(), 700, 700, 1000, 1000);
// Window specified x and y are interpreted as offsets in the opposite
// direction of gravity
w.mAttrs.x = 100;
w.mAttrs.y = 100;
- w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
- assertRect(w.mFrame, 600, 600, 900, 900);
+ w.computeFrameLw(windowFrames, WmDisplayCutout.NO_CUTOUT, false);
+ assertRect(w.getFrameLw(), 600, 600, 900, 900);
}
@Test
@@ -279,10 +282,11 @@ public class WindowFrameTests extends WindowTestsBase {
w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight);
- w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, null, WmDisplayCutout.NO_CUTOUT, false);
+ final WindowFrames windowFrames = new WindowFrames(pf, pf, pf, pf, pf, pf, pf, mEmptyRect);
+ w.computeFrameLw(windowFrames, WmDisplayCutout.NO_CUTOUT, false);
// For non fullscreen tasks the containing frame is based off the
// task bounds not the parent frame.
- assertRect(w.mFrame, taskLeft, taskTop, taskRight, taskBottom);
+ assertRect(w.getFrameLw(), taskLeft, taskTop, taskRight, taskBottom);
assertRect(w.getContentFrameLw(), taskLeft, taskTop, taskRight, taskBottom);
assertRect(w.mContentInsets, 0, 0, 0, 0);
@@ -291,8 +295,10 @@ public class WindowFrameTests extends WindowTestsBase {
final int cfRight = logicalWidth / 2;
final int cfBottom = logicalHeight / 2;
final Rect cf = new Rect(0, 0, cfRight, cfBottom);
- w.computeFrameLw(pf, pf, pf, cf, cf, pf, cf, null, WmDisplayCutout.NO_CUTOUT, false);
- assertRect(w.mFrame, taskLeft, taskTop, taskRight, taskBottom);
+
+ windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect);
+ w.computeFrameLw(windowFrames, WmDisplayCutout.NO_CUTOUT, false);
+ assertRect(w.getFrameLw(), taskLeft, taskTop, taskRight, taskBottom);
int contentInsetRight = taskRight - cfRight;
int contentInsetBottom = taskBottom - cfBottom;
assertRect(w.mContentInsets, 0, 0, contentInsetRight, contentInsetBottom);
@@ -308,8 +314,10 @@ public class WindowFrameTests extends WindowTestsBase {
final int insetRight = insetLeft + (taskRight - taskLeft);
final int insetBottom = insetTop + (taskBottom - taskTop);
task.mInsetBounds.set(insetLeft, insetTop, insetRight, insetBottom);
- w.computeFrameLw(pf, pf, pf, cf, cf, pf, cf, null, WmDisplayCutout.NO_CUTOUT, false);
- assertRect(w.mFrame, taskLeft, taskTop, taskRight, taskBottom);
+
+ windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect);
+ w.computeFrameLw(windowFrames, WmDisplayCutout.NO_CUTOUT, false);
+ assertRect(w.getFrameLw(), taskLeft, taskTop, taskRight, taskBottom);
contentInsetRight = insetRight - cfRight;
contentInsetBottom = insetBottom - cfBottom;
assertRect(w.mContentInsets, 0, 0, contentInsetRight, contentInsetBottom);
@@ -340,13 +348,14 @@ public class WindowFrameTests extends WindowTestsBase {
final Rect policyCrop = new Rect();
- w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null, WmDisplayCutout.NO_CUTOUT, false);
+ final WindowFrames windowFrames = new WindowFrames(pf, df, of, cf, vf, dcf, sf, mEmptyRect);
+ w.computeFrameLw(windowFrames, WmDisplayCutout.NO_CUTOUT, false);
w.calculatePolicyCrop(policyCrop);
assertRect(policyCrop, 0, cf.top, logicalWidth, cf.bottom);
- dcf.setEmpty();
+ windowFrames.mDecorFrame.setEmpty();
// Likewise with no decor frame we would get no crop
- w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null, WmDisplayCutout.NO_CUTOUT, false);
+ w.computeFrameLw(windowFrames, WmDisplayCutout.NO_CUTOUT, false);
w.calculatePolicyCrop(policyCrop);
assertRect(policyCrop, 0, 0, logicalWidth, logicalHeight);
@@ -354,12 +363,14 @@ public class WindowFrameTests extends WindowTestsBase {
// Normally it would be cropped to it's frame but in the case of docked resizing
// we need to account for the fact the windows surface will be made
// fullscreen and thus also make the crop fullscreen.
+
+ windowFrames.setFrames(pf, pf, pf, pf, pf, pf, pf, pf);
w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
w.mAttrs.width = logicalWidth / 2;
w.mAttrs.height = logicalHeight / 2;
w.mRequestedWidth = logicalWidth / 2;
w.mRequestedHeight = logicalHeight / 2;
- w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, WmDisplayCutout.NO_CUTOUT, false);
+ w.computeFrameLw(windowFrames, WmDisplayCutout.NO_CUTOUT, false);
w.calculatePolicyCrop(policyCrop);
// Normally the crop is shrunk from the decor frame
@@ -394,12 +405,11 @@ public class WindowFrameTests extends WindowTestsBase {
w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight);
- w.computeFrameLw(pf /* parentFrame */, pf /* displayFrame */, pf /* overscanFrame */,
- pf /* contentFrame */, pf /* visibleFrame */, pf /* decorFrame */,
- pf /* stableFrame */, null /* outsetFrame */, WmDisplayCutout.NO_CUTOUT, false);
+ final WindowFrames windowFrames = new WindowFrames(pf, pf, pf, pf, pf, pf, pf, mEmptyRect);
+ w.computeFrameLw(windowFrames, WmDisplayCutout.NO_CUTOUT, false);
// For non fullscreen tasks the containing frame is based off the
// task bounds not the parent frame.
- assertRect(w.mFrame, taskLeft, taskTop, taskRight, taskBottom);
+ assertRect(w.getFrameLw(), taskLeft, taskTop, taskRight, taskBottom);
assertRect(w.getContentFrameLw(), taskLeft, taskTop, taskRight, taskBottom);
assertRect(w.mContentInsets, 0, 0, 0, 0);
@@ -413,10 +423,9 @@ public class WindowFrameTests extends WindowTestsBase {
pf.set(0, 0, logicalWidth, logicalHeight);
task.mFullscreenForTest = true;
- w.computeFrameLw(pf /* parentFrame */, pf /* displayFrame */, pf /* overscanFrame */,
- cf /* contentFrame */, cf /* visibleFrame */, pf /* decorFrame */,
- cf /* stableFrame */, null /* outsetFrame */, WmDisplayCutout.NO_CUTOUT, false);
- assertEquals(cf, w.mFrame);
+ windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect);
+ w.computeFrameLw(windowFrames, WmDisplayCutout.NO_CUTOUT, false);
+ assertEquals(cf, w.getFrameLw());
assertEquals(cf, w.getContentFrameLw());
assertRect(w.mContentInsets, 0, 0, 0, 0);
}
@@ -433,7 +442,8 @@ public class WindowFrameTests extends WindowTestsBase {
final WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
fromBoundingRect(500, 0, 550, 50), pf.width(), pf.height());
- w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, cutout, false);
+ final WindowFrames windowFrames = new WindowFrames(pf, pf, pf, pf, pf, pf, pf, pf);
+ w.computeFrameLw(windowFrames, cutout, false);
assertEquals(w.mDisplayCutout.getDisplayCutout().getSafeInsetTop(), 50);
assertEquals(w.mDisplayCutout.getDisplayCutout().getSafeInsetBottom(), 0);
@@ -455,7 +465,8 @@ public class WindowFrameTests extends WindowTestsBase {
final WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
fromBoundingRect(500, 0, 550, 50), pf.width(), pf.height());
- w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, cutout, false);
+ final WindowFrames windowFrames = new WindowFrames(pf, pf, pf, pf, pf, pf, pf, pf);
+ w.computeFrameLw(windowFrames, cutout, false);
assertEquals(w.mDisplayCutout.getDisplayCutout().getSafeInsetTop(), 50);
assertEquals(w.mDisplayCutout.getDisplayCutout().getSafeInsetBottom(), 0);
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
index 9f113ad3137e..0ddba6ac401c 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
@@ -364,15 +364,15 @@ public class WindowStateTests extends WindowTestsBase {
app.mSurfaceControl = mock(SurfaceControl.class);
app.mWinAnimator.mSurfaceController = mock(WindowSurfaceController.class);
try {
- app.mFrame.set(10, 20, 60, 80);
+ app.getFrameLw().set(10, 20, 60, 80);
app.seamlesslyRotate(t, ROTATION_0, ROTATION_90);
assertTrue(app.mSeamlesslyRotated);
assertEquals(new Rect(20, mDisplayInfo.logicalWidth - 60,
- 80, mDisplayInfo.logicalWidth - 10), app.mFrame);
+ 80, mDisplayInfo.logicalWidth - 10), app.getFrameLw());
- verify(t).setPosition(app.mSurfaceControl, app.mFrame.left, app.mFrame.top);
+ verify(t).setPosition(app.mSurfaceControl, app.getFrameLw().left, app.getFrameLw().top);
verify(app.mWinAnimator.mSurfaceController).setPosition(t, 0, 50, false);
verify(app.mWinAnimator.mSurfaceController).setMatrix(t, 0, -1, 1, 0, false);
} finally {
diff --git a/tools/aapt2/java/ProguardRules.cpp b/tools/aapt2/java/ProguardRules.cpp
index d03cdb3d8518..92487f4e6854 100644
--- a/tools/aapt2/java/ProguardRules.cpp
+++ b/tools/aapt2/java/ProguardRules.cpp
@@ -259,6 +259,13 @@ class ManifestVisitor : public BaseVisitor {
AddClass(node->line_number, result.value());
}
}
+ attr = node->FindAttribute(xml::kSchemaAndroid, "appComponentFactory");
+ if (attr) {
+ Maybe<std::string> result = util::GetFullyQualifiedClassName(package_, attr->value);
+ if (result) {
+ AddClass(node->line_number, result.value());
+ }
+ }
if (main_dex_only_) {
xml::Attribute* default_process = node->FindAttribute(xml::kSchemaAndroid, "process");
if (default_process) {
diff --git a/tools/aapt2/java/ProguardRules_test.cpp b/tools/aapt2/java/ProguardRules_test.cpp
index b5e27e0cb952..aeb9eb6e91c4 100644
--- a/tools/aapt2/java/ProguardRules_test.cpp
+++ b/tools/aapt2/java/ProguardRules_test.cpp
@@ -37,7 +37,11 @@ std::string GetKeepSetString(const proguard::KeepSet& set) {
TEST(ProguardRulesTest, ManifestRuleDefaultConstructorOnly) {
std::unique_ptr<xml::XmlResource> manifest = test::BuildXmlDom(R"(
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
- <application android:backupAgent="com.foo.BarBackupAgent">
+ <application
+ android:appComponentFactory="com.foo.BarAppComponentFactory"
+ android:backupAgent="com.foo.BarBackupAgent"
+ android:name="com.foo.BarApplication"
+ >
<activity android:name="com.foo.BarActivity"/>
<service android:name="com.foo.BarService"/>
<receiver android:name="com.foo.BarReceiver"/>
@@ -51,7 +55,9 @@ TEST(ProguardRulesTest, ManifestRuleDefaultConstructorOnly) {
std::string actual = GetKeepSetString(set);
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarAppComponentFactory { <init>(); }"));
EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarBackupAgent { <init>(); }"));
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarApplication { <init>(); }"));
EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarActivity { <init>(); }"));
EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarService { <init>(); }"));
EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarReceiver { <init>(); }"));
diff --git a/tools/fonts/fontchain_linter.py b/tools/fonts/fontchain_linter.py
index a8411aa5c488..ce9becd40b1e 100755
--- a/tools/fonts/fontchain_linter.py
+++ b/tools/fonts/fontchain_linter.py
@@ -524,9 +524,12 @@ LEGACY_ANDROID_EMOJI = {
0xFE837: (ord('0'), COMBINING_KEYCAP),
}
+# This is used to define the emoji that should have the same glyph.
+# i.e. previously we had gender based Kiss (0x1F48F), which had the same glyph
+# with Kiss: Woman, Man (0x1F469, 0x200D, 0x2764, 0x200D, 0x1F48B, 0x200D, 0x1F468)
+# in that case a valid row would be:
+# (0x1F469, 0x200D, 0x2764, 0x200D, 0x1F48B, 0x200D, 0x1F468): 0x1F48F,
ZWJ_IDENTICALS = {
- # KISS
- (0x1F469, 0x200D, 0x2764, 0x200D, 0x1F48B, 0x200D, 0x1F468): 0x1F48F,
}
SAME_FLAG_MAPPINGS = [