diff options
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 (<append chunk 1><append chunk2>) AND (<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 (<append chunk 1><append chunk2>) AND (<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 (<append chunk 1><append chunk2>) AND (<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 (<append chunk 1><append chunk2>) AND (<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 (<append chunk 1><append chunk2>) AND (<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 (<append chunk 1><append chunk2>) AND (<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 (<append chunk 1><append chunk2>) AND (<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 (<append chunk 1><append chunk2>) AND (<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 <= 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 Binary files differnew file mode 100644 index 000000000000..e404f2b5de1f --- /dev/null +++ b/core/tests/coretests/assets/fonts_for_family_selection/ascii.ttc 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 Binary files differnew file mode 100644 index 000000000000..792b7d7a7563 --- /dev/null +++ b/core/tests/coretests/assets/fonts_for_family_selection/ascii_vf.ttf 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 Binary files differnew file mode 100644 index 000000000000..f220eb36b0d3 --- /dev/null +++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_a3em_weight100_upright.ttf 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 Binary files differnew file mode 100644 index 000000000000..b9ffb84a2fed --- /dev/null +++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_b3em_weight100_italic.ttf 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 Binary files differnew file mode 100644 index 000000000000..075b068370f1 --- /dev/null +++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_c3em_weight200_upright.ttf 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 Binary files differnew file mode 100644 index 000000000000..5b47f0d0069d --- /dev/null +++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_d3em_weight200_italic.ttf 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 Binary files differnew file mode 100644 index 000000000000..3e9133b2ee1a --- /dev/null +++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_e3em_weight300_upright.ttf 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 Binary files differnew file mode 100644 index 000000000000..3ba3febaf477 --- /dev/null +++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_f3em_weight300_italic.ttf 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 Binary files differnew file mode 100644 index 000000000000..b3175a108283 --- /dev/null +++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_g3em_weight400_upright.ttf 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 Binary files differnew file mode 100644 index 000000000000..099c4f146eee --- /dev/null +++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_h3em_weight400_italic.ttf 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 Binary files differnew file mode 100644 index 000000000000..b8edcb62f0b8 --- /dev/null +++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_i3em_weight500_upright.ttf 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 Binary files differnew file mode 100644 index 000000000000..6d7dde97c19f --- /dev/null +++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_j3em_weight500_italic.ttf 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 Binary files differnew file mode 100644 index 000000000000..eb6d7d1b9601 --- /dev/null +++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_k3em_weight600_upright.ttf 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 Binary files differnew file mode 100644 index 000000000000..f509e94dbfbd --- /dev/null +++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_l3em_weight600_italic.ttf 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 Binary files differnew file mode 100644 index 000000000000..062f2990fe0d --- /dev/null +++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_m3em_weight700_upright.ttf 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 Binary files differnew file mode 100644 index 000000000000..fdd02394e84f --- /dev/null +++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_n3em_weight700_italic.ttf 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 Binary files differnew file mode 100644 index 000000000000..993e7faa7ba8 --- /dev/null +++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_o3em_weight800_upright.ttf 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 Binary files differnew file mode 100644 index 000000000000..f0d54f07dfad --- /dev/null +++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_p3em_weight800_italic.ttf 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 Binary files differnew file mode 100644 index 000000000000..c776c783c7e7 --- /dev/null +++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_q3em_weight900_upright.ttf 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 Binary files differnew file mode 100644 index 000000000000..02f6246b384f --- /dev/null +++ b/core/tests/coretests/assets/fonts_for_family_selection/fonts/ascii_r3em_weight900_italic.ttf 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 = [ |