diff options
1199 files changed, 36863 insertions, 39477 deletions
diff --git a/Android.bp b/Android.bp index 5abbf68f338f..c47b74717dcd 100644 --- a/Android.bp +++ b/Android.bp @@ -528,6 +528,7 @@ java_library { "android.hardware.vibrator-V1.3-java", "android.system.keystore2-java", "android.system.suspend.control.internal-java", + "cameraprotosnano", "devicepolicyprotosnano", "com.android.sysprop.apex", @@ -627,6 +628,7 @@ java_defaults { "av-types-aidl-java", "mediatranscoding_aidl_interface-java", "soundtrigger_middleware-aidl-java", + "modules-utils-os", ], } @@ -745,6 +747,7 @@ gensrcs { srcs: [ ":ipconnectivity-proto-src", + ":libstats_atom_enum_protos", "core/proto/**/*.proto", "libs/incident/**/*.proto", ], @@ -771,6 +774,7 @@ gensrcs { srcs: [ ":ipconnectivity-proto-src", + ":libstats_atom_enum_protos", "core/proto/**/*.proto", "libs/incident/**/*.proto", ], @@ -910,13 +914,18 @@ java_library_host { name: "platformprotos", srcs: [ ":ipconnectivity-proto-src", + ":libstats_atom_enum_protos", + ":libstats_internal_protos", "cmds/am/proto/instrumentation_data.proto", "cmds/statsd/src/**/*.proto", "core/proto/**/*.proto", "libs/incident/proto/**/*.proto", ], proto: { - include_dirs: ["external/protobuf/src"], + include_dirs: [ + "external/protobuf/src", + "frameworks/proto_logging/stats", + ], type: "full", }, // Protos have lots of MissingOverride and similar. @@ -941,6 +950,7 @@ java_library { sdk_version: "9", srcs: [ ":ipconnectivity-proto-src", + ":libstats_atom_enum_protos", "core/proto/**/*.proto", "libs/incident/proto/android/os/**/*.proto", ], @@ -956,6 +966,7 @@ java_library { srcs: [ ":ipconnectivity-proto-src", + ":libstats_atom_enum_protos", "core/proto/**/*.proto", "libs/incident/proto/android/os/**/*.proto", ], @@ -977,7 +988,9 @@ cc_defaults { proto: { export_proto_headers: true, - include_dirs: ["external/protobuf/src"], + include_dirs: [ + "external/protobuf/src", + ], }, cflags: [ @@ -988,6 +1001,7 @@ cc_defaults { srcs: [ ":ipconnectivity-proto-src", + ":libstats_atom_enum_protos", "core/proto/**/*.proto", ], } @@ -1268,7 +1282,6 @@ aidl_mapping { filegroup { name: "framework-telephony-common-shared-srcs", srcs: [ - "core/java/android/os/BasicShellCommandHandler.java", "core/java/android/os/RegistrantList.java", "core/java/android/os/Registrant.java", "core/java/android/util/IndentingPrintWriter.java", @@ -1351,7 +1364,6 @@ filegroup { name: "framework-wifi-service-shared-srcs", srcs: [ "core/java/android/net/InterfaceConfiguration.java", - "core/java/android/os/BasicShellCommandHandler.java", "core/java/android/util/BackupUtils.java", "core/java/android/util/Rational.java", "core/java/com/android/internal/util/FastXmlSerializer.java", diff --git a/StubLibraries.bp b/StubLibraries.bp index 49a42d7525a5..380839e5c06b 100644 --- a/StubLibraries.bp +++ b/StubLibraries.bp @@ -70,6 +70,7 @@ stubs_defaults { "android.hardware.cas-V1.2-java", "android.hardware.health-V1.0-java-constants", "android.hardware.radio-V1.5-java", + "android.hardware.radio-V1.6-java", "android.hardware.thermal-V1.0-java-constants", "android.hardware.thermal-V2.0-java", "android.hardware.tv.input-V1.0-java-constants", @@ -252,21 +253,7 @@ java_library_static { "framework-wifi.stubs", "private-stub-annotations-jar", ], - defaults: [ - "android_defaults_stubs_current", - "android_stubs_dists_default", - ], - dist: { - dir: "apistubs/android/system", - }, - dists: [ - { - // Legacy dist path - targets: ["sdk", "win_sdk"], - tag: ".jar", - dest: "android_system.jar", - }, - ], + defaults: ["android_defaults_stubs_current"], } java_library_static { @@ -285,7 +272,21 @@ java_library_static { "framework-wifi.stubs.system", "private-stub-annotations-jar", ], - defaults: ["android_defaults_stubs_current"], + defaults: [ + "android_defaults_stubs_current", + "android_stubs_dists_default", + ], + dist: { + dir: "apistubs/android/system", + }, + dists: [ + { + // Legacy dist path + targets: ["sdk", "win_sdk"], + tag: ".jar", + dest: "android_system.jar", + }, + ], } java_library_static { diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceSerializationPerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceSerializationPerfTest.java new file mode 100644 index 000000000000..547369055e95 --- /dev/null +++ b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceSerializationPerfTest.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.graphics.perftests; + +import android.graphics.Typeface; +import android.os.SharedMemory; +import android.perftests.utils.BenchmarkState; +import android.perftests.utils.PerfStatusReporter; + +import androidx.test.filters.LargeTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.Map; + +@LargeTest +@RunWith(AndroidJUnit4.class) +public class TypefaceSerializationPerfTest { + + @Rule + public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + @Test + public void testSerializeFontMap() throws Exception { + Map<String, Typeface> systemFontMap = Typeface.getSystemFontMap(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + + while (state.keepRunning()) { + Typeface.serializeFontMap(systemFontMap); + } + } + + @Test + public void testDeserializeFontMap() throws Exception { + SharedMemory memory = Typeface.serializeFontMap(Typeface.getSystemFontMap()); + ByteBuffer buffer = memory.mapReadOnly().order(ByteOrder.BIG_ENDIAN); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + + while (state.keepRunning()) { + buffer.position(0); + Typeface.deserializeFontMap(buffer); + } + } + + @Test + public void testSetSystemFontMap() throws Exception { + SharedMemory memory = null; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + + while (state.keepRunning()) { + state.pauseTiming(); + // Explicitly destroy lazy-loaded typefaces, so that we don't hit the mmap limit + // (max_map_count). + Typeface.destroySystemFontMap(); + Typeface.loadPreinstalledSystemFontMap(); + if (memory != null) { + memory.close(); + } + memory = Typeface.serializeFontMap(Typeface.getSystemFontMap()); + state.resumeTiming(); + Typeface.setSystemFontMap(memory); + } + } +} diff --git a/apex/appsearch/framework/api/current.txt b/apex/appsearch/framework/api/current.txt index d802177e249b..2be873cc8bca 100644 --- a/apex/appsearch/framework/api/current.txt +++ b/apex/appsearch/framework/api/current.txt @@ -1 +1,143 @@ // Signature format: 2.0 +package android.app.appsearch { + + public final class AppSearchSchema { + method @NonNull public java.util.List<android.app.appsearch.AppSearchSchema.PropertyConfig> getProperties(); + method @NonNull public String getSchemaType(); + } + + public static final class AppSearchSchema.Builder { + ctor public AppSearchSchema.Builder(@NonNull String); + method @NonNull public android.app.appsearch.AppSearchSchema.Builder addProperty(@NonNull android.app.appsearch.AppSearchSchema.PropertyConfig); + method @NonNull public android.app.appsearch.AppSearchSchema build(); + } + + public static final class AppSearchSchema.PropertyConfig { + method public int getCardinality(); + method public int getDataType(); + method public int getIndexingType(); + method @NonNull public String getName(); + method @Nullable public String getSchemaType(); + method public int getTokenizerType(); + field public static final int CARDINALITY_OPTIONAL = 2; // 0x2 + field public static final int CARDINALITY_REPEATED = 1; // 0x1 + field public static final int CARDINALITY_REQUIRED = 3; // 0x3 + field public static final int DATA_TYPE_BOOLEAN = 4; // 0x4 + field public static final int DATA_TYPE_BYTES = 5; // 0x5 + field public static final int DATA_TYPE_DOCUMENT = 6; // 0x6 + field public static final int DATA_TYPE_DOUBLE = 3; // 0x3 + field public static final int DATA_TYPE_INT64 = 2; // 0x2 + field public static final int DATA_TYPE_STRING = 1; // 0x1 + field public static final int INDEXING_TYPE_EXACT_TERMS = 1; // 0x1 + field public static final int INDEXING_TYPE_NONE = 0; // 0x0 + field public static final int INDEXING_TYPE_PREFIXES = 2; // 0x2 + field public static final int TOKENIZER_TYPE_NONE = 0; // 0x0 + field public static final int TOKENIZER_TYPE_PLAIN = 1; // 0x1 + } + + public static final class AppSearchSchema.PropertyConfig.Builder { + ctor public AppSearchSchema.PropertyConfig.Builder(@NonNull String); + method @NonNull public android.app.appsearch.AppSearchSchema.PropertyConfig build(); + method @NonNull public android.app.appsearch.AppSearchSchema.PropertyConfig.Builder setCardinality(int); + method @NonNull public android.app.appsearch.AppSearchSchema.PropertyConfig.Builder setDataType(int); + method @NonNull public android.app.appsearch.AppSearchSchema.PropertyConfig.Builder setIndexingType(int); + method @NonNull public android.app.appsearch.AppSearchSchema.PropertyConfig.Builder setSchemaType(@NonNull String); + method @NonNull public android.app.appsearch.AppSearchSchema.PropertyConfig.Builder setTokenizerType(int); + } + + public class GenericDocument { + ctor protected GenericDocument(@NonNull android.app.appsearch.GenericDocument); + method public long getCreationTimestampMillis(); + method public static int getMaxIndexedProperties(); + method @NonNull public String getNamespace(); + method public boolean getPropertyBoolean(@NonNull String); + method @Nullable public boolean[] getPropertyBooleanArray(@NonNull String); + method @Nullable public byte[] getPropertyBytes(@NonNull String); + method @Nullable public byte[][] getPropertyBytesArray(@NonNull String); + method @Nullable public android.app.appsearch.GenericDocument getPropertyDocument(@NonNull String); + method @Nullable public android.app.appsearch.GenericDocument[] getPropertyDocumentArray(@NonNull String); + method public double getPropertyDouble(@NonNull String); + method @Nullable public double[] getPropertyDoubleArray(@NonNull String); + method public long getPropertyLong(@NonNull String); + method @Nullable public long[] getPropertyLongArray(@NonNull String); + method @NonNull public java.util.Set<java.lang.String> getPropertyNames(); + method @Nullable public String getPropertyString(@NonNull String); + method @Nullable public String[] getPropertyStringArray(@NonNull String); + method @NonNull public String getSchemaType(); + method public int getScore(); + method public long getTtlMillis(); + method @NonNull public String getUri(); + field public static final String DEFAULT_NAMESPACE = ""; + } + + public static class GenericDocument.Builder<BuilderType extends android.app.appsearch.GenericDocument.Builder> { + ctor public GenericDocument.Builder(@NonNull String, @NonNull String); + method @NonNull public android.app.appsearch.GenericDocument build(); + method @NonNull public BuilderType setCreationTimestampMillis(long); + method @NonNull public BuilderType setNamespace(@NonNull String); + method @NonNull public BuilderType setPropertyBoolean(@NonNull String, @NonNull boolean...); + method @NonNull public BuilderType setPropertyBytes(@NonNull String, @NonNull byte[]...); + method @NonNull public BuilderType setPropertyDocument(@NonNull String, @NonNull android.app.appsearch.GenericDocument...); + method @NonNull public BuilderType setPropertyDouble(@NonNull String, @NonNull double...); + method @NonNull public BuilderType setPropertyLong(@NonNull String, @NonNull long...); + method @NonNull public BuilderType setPropertyString(@NonNull String, @NonNull java.lang.String...); + method @NonNull public BuilderType setScore(@IntRange(from=0, to=java.lang.Integer.MAX_VALUE) int); + method @NonNull public BuilderType setTtlMillis(long); + } + + public final class SearchResult { + method @NonNull public android.app.appsearch.GenericDocument getDocument(); + method @NonNull public java.util.List<android.app.appsearch.SearchResult.MatchInfo> getMatches(); + } + + public static final class SearchResult.MatchInfo { + method @NonNull public CharSequence getExactMatch(); + method @NonNull public android.app.appsearch.SearchResult.MatchRange getExactMatchPosition(); + method @NonNull public String getFullText(); + method @NonNull public String getPropertyPath(); + method @NonNull public CharSequence getSnippet(); + method @NonNull public android.app.appsearch.SearchResult.MatchRange getSnippetPosition(); + } + + public static final class SearchResult.MatchRange { + method public int getEnd(); + method public int getStart(); + } + + public final class SearchSpec { + method public int getMaxSnippetSize(); + method @NonNull public java.util.List<java.lang.String> getNamespaces(); + method public int getOrder(); + method public int getRankingStrategy(); + method public int getResultCountPerPage(); + method @NonNull public java.util.List<java.lang.String> getSchemaTypes(); + method public int getSnippetCount(); + method public int getSnippetCountPerProperty(); + method public int getTermMatch(); + field public static final int ORDER_ASCENDING = 1; // 0x1 + field public static final int ORDER_DESCENDING = 0; // 0x0 + field public static final int RANKING_STRATEGY_CREATION_TIMESTAMP = 2; // 0x2 + field public static final int RANKING_STRATEGY_DOCUMENT_SCORE = 1; // 0x1 + field public static final int RANKING_STRATEGY_NONE = 0; // 0x0 + field public static final int TERM_MATCH_EXACT_ONLY = 1; // 0x1 + field public static final int TERM_MATCH_PREFIX = 2; // 0x2 + } + + public static final class SearchSpec.Builder { + ctor public SearchSpec.Builder(); + method @NonNull public android.app.appsearch.SearchSpec.Builder addNamespace(@NonNull java.lang.String...); + method @NonNull public android.app.appsearch.SearchSpec.Builder addNamespace(@NonNull java.util.Collection<java.lang.String>); + method @NonNull public android.app.appsearch.SearchSpec.Builder addSchemaType(@NonNull java.lang.String...); + method @NonNull public android.app.appsearch.SearchSpec.Builder addSchemaType(@NonNull java.util.Collection<java.lang.String>); + method @NonNull public android.app.appsearch.SearchSpec build(); + method @NonNull public android.app.appsearch.SearchSpec.Builder setMaxSnippetSize(@IntRange(from=0, to=android.app.appsearch.SearchSpec.MAX_SNIPPET_SIZE_LIMIT) int); + method @NonNull public android.app.appsearch.SearchSpec.Builder setOrder(int); + method @NonNull public android.app.appsearch.SearchSpec.Builder setRankingStrategy(int); + method @NonNull public android.app.appsearch.SearchSpec.Builder setResultCountPerPage(@IntRange(from=0, to=android.app.appsearch.SearchSpec.MAX_NUM_PER_PAGE) int); + method @NonNull public android.app.appsearch.SearchSpec.Builder setSnippetCount(@IntRange(from=0, to=android.app.appsearch.SearchSpec.MAX_SNIPPET_COUNT) int); + method @NonNull public android.app.appsearch.SearchSpec.Builder setSnippetCountPerProperty(@IntRange(from=0, to=android.app.appsearch.SearchSpec.MAX_SNIPPET_PER_PROPERTY_COUNT) int); + method @NonNull public android.app.appsearch.SearchSpec.Builder setTermMatch(int); + } + +} + diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.aidl b/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.aidl new file mode 100644 index 000000000000..4686de8df268 --- /dev/null +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.aidl @@ -0,0 +1,19 @@ +/** + * Copyright 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.app.appsearch; + +/** {@hide} */ +parcelable AppSearchBatchResult;
\ No newline at end of file diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchEmail.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchEmail.java index beb9ad3d27ea..9ca363ed9008 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchEmail.java +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchEmail.java @@ -16,10 +16,8 @@ package android.app.appsearch; - import android.annotation.NonNull; import android.annotation.Nullable; - import android.app.appsearch.AppSearchSchema.PropertyConfig; /** @@ -29,9 +27,8 @@ import android.app.appsearch.AppSearchSchema.PropertyConfig; * * @hide */ - public class AppSearchEmail extends GenericDocument { - /** The name of the schema type for {@link AppSearchEmail} documents.*/ + /** The name of the schema type for {@link AppSearchEmail} documents. */ public static final String SCHEMA_TYPE = "builtin:Email"; private static final String KEY_FROM = "from"; @@ -41,54 +38,55 @@ public class AppSearchEmail extends GenericDocument { private static final String KEY_SUBJECT = "subject"; private static final String KEY_BODY = "body"; - public static final AppSearchSchema SCHEMA = new AppSearchSchema.Builder(SCHEMA_TYPE) - .addProperty(new PropertyConfig.Builder(KEY_FROM) - .setDataType(PropertyConfig.DATA_TYPE_STRING) - .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) - .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) - .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES) - .build() - - ).addProperty(new PropertyConfig.Builder(KEY_TO) - .setDataType(PropertyConfig.DATA_TYPE_STRING) - .setCardinality(PropertyConfig.CARDINALITY_REPEATED) - .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) - .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES) - .build() - - ).addProperty(new PropertyConfig.Builder(KEY_CC) - .setDataType(PropertyConfig.DATA_TYPE_STRING) - .setCardinality(PropertyConfig.CARDINALITY_REPEATED) - .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) - .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES) - .build() - - ).addProperty(new PropertyConfig.Builder(KEY_BCC) - .setDataType(PropertyConfig.DATA_TYPE_STRING) - .setCardinality(PropertyConfig.CARDINALITY_REPEATED) - .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) - .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES) - .build() - - ).addProperty(new PropertyConfig.Builder(KEY_SUBJECT) - .setDataType(PropertyConfig.DATA_TYPE_STRING) - .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) - .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) - .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES) - .build() - - ).addProperty(new PropertyConfig.Builder(KEY_BODY) - .setDataType(PropertyConfig.DATA_TYPE_STRING) - .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) - .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) - .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES) - .build() - - ).build(); + public static final AppSearchSchema SCHEMA = + new AppSearchSchema.Builder(SCHEMA_TYPE) + .addProperty( + new PropertyConfig.Builder(KEY_FROM) + .setDataType(PropertyConfig.DATA_TYPE_STRING) + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) + .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES) + .build()) + .addProperty( + new PropertyConfig.Builder(KEY_TO) + .setDataType(PropertyConfig.DATA_TYPE_STRING) + .setCardinality(PropertyConfig.CARDINALITY_REPEATED) + .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) + .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES) + .build()) + .addProperty( + new PropertyConfig.Builder(KEY_CC) + .setDataType(PropertyConfig.DATA_TYPE_STRING) + .setCardinality(PropertyConfig.CARDINALITY_REPEATED) + .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) + .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES) + .build()) + .addProperty( + new PropertyConfig.Builder(KEY_BCC) + .setDataType(PropertyConfig.DATA_TYPE_STRING) + .setCardinality(PropertyConfig.CARDINALITY_REPEATED) + .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) + .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES) + .build()) + .addProperty( + new PropertyConfig.Builder(KEY_SUBJECT) + .setDataType(PropertyConfig.DATA_TYPE_STRING) + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) + .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES) + .build()) + .addProperty( + new PropertyConfig.Builder(KEY_BODY) + .setDataType(PropertyConfig.DATA_TYPE_STRING) + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) + .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES) + .build()) + .build(); /** - * Creates a new {@link AppSearchEmail} from the contents of an existing - * {@link GenericDocument}. + * Creates a new {@link AppSearchEmail} from the contents of an existing {@link + * GenericDocument}. * * @param document The {@link GenericDocument} containing the email content. */ @@ -109,8 +107,8 @@ public class AppSearchEmail extends GenericDocument { /** * Gets the destination addresses of {@link AppSearchEmail}. * - * @return The destination addresses of {@link AppSearchEmail} or {@code null} if it's not - * been set yet. + * @return The destination addresses of {@link AppSearchEmail} or {@code null} if it's not been + * set yet. */ @Nullable public String[] getTo() { @@ -157,9 +155,7 @@ public class AppSearchEmail extends GenericDocument { return getPropertyString(KEY_BODY); } - /** - * The builder class for {@link AppSearchEmail}. - */ + /** The builder class for {@link AppSearchEmail}. */ public static class Builder extends GenericDocument.Builder<AppSearchEmail.Builder> { /** @@ -171,54 +167,42 @@ public class AppSearchEmail extends GenericDocument { super(uri, SCHEMA_TYPE); } - /** - * Sets the from address of {@link AppSearchEmail} - */ + /** Sets the from address of {@link AppSearchEmail} */ @NonNull public AppSearchEmail.Builder setFrom(@NonNull String from) { setPropertyString(KEY_FROM, from); return this; } - /** - * Sets the destination address of {@link AppSearchEmail} - */ + /** Sets the destination address of {@link AppSearchEmail} */ @NonNull public AppSearchEmail.Builder setTo(@NonNull String... to) { setPropertyString(KEY_TO, to); return this; } - /** - * Sets the CC list of {@link AppSearchEmail} - */ + /** Sets the CC list of {@link AppSearchEmail} */ @NonNull public AppSearchEmail.Builder setCc(@NonNull String... cc) { setPropertyString(KEY_CC, cc); return this; } - /** - * Sets the BCC list of {@link AppSearchEmail} - */ + /** Sets the BCC list of {@link AppSearchEmail} */ @NonNull public AppSearchEmail.Builder setBcc(@NonNull String... bcc) { setPropertyString(KEY_BCC, bcc); return this; } - /** - * Sets the subject of {@link AppSearchEmail} - */ + /** Sets the subject of {@link AppSearchEmail} */ @NonNull public AppSearchEmail.Builder setSubject(@NonNull String subject) { setPropertyString(KEY_SUBJECT, subject); return this; } - /** - * Sets the body of {@link AppSearchEmail} - */ + /** Sets the body of {@link AppSearchEmail} */ @NonNull public AppSearchEmail.Builder setBody(@NonNull String body) { setPropertyString(KEY_BODY, body); diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java index f2c9942edbb3..e57359fbf50e 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java @@ -15,10 +15,12 @@ */ package android.app.appsearch; +import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.SystemService; import android.content.Context; import android.os.Bundle; +import android.os.ParcelableException; import android.os.RemoteException; import com.android.internal.infra.AndroidFuture; @@ -27,7 +29,10 @@ import com.android.internal.util.Preconditions; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.function.Consumer; /** * This class provides access to the centralized AppSearch index maintained by the system. @@ -40,12 +45,99 @@ import java.util.concurrent.ExecutionException; // TODO(b/148046169): This class header needs a detailed example/tutorial. @SystemService(Context.APP_SEARCH_SERVICE) public class AppSearchManager { - private static final String DEFAULT_DATABASE = ""; + /** + * The default empty database name. + * @hide + */ + public static final String DEFAULT_DATABASE_NAME = ""; + private final IAppSearchManager mService; /** @hide */ public AppSearchManager(@NonNull IAppSearchManager service) { - mService = service; + mService = Objects.requireNonNull(service); + } + + /** Contains information about how to create the search session. */ + public static final class SearchContext { + final String mDatabaseName; + + SearchContext(@NonNull String databaseName) { + mDatabaseName = Objects.requireNonNull(databaseName); + } + + /** + * Returns the name of the database to create or open. + * + * <p>Databases with different names are fully separate with distinct types, namespaces, + * and data. + */ + @NonNull + public String getDatabaseName() { + return mDatabaseName; + } + + /** Builder for {@link SearchContext} objects. */ + public static final class Builder { + private String mDatabaseName = DEFAULT_DATABASE_NAME; + private boolean mBuilt = false; + + /** + * Sets the name of the database associated with {@link AppSearchSession}. + * + * <p>{@link AppSearchSession} will create or open a database under the given name. + * + * <p>Databases with different names are fully separate with distinct types, namespaces, + * and data. + * + * <p>Database name cannot contain {@code '/'}. + * + * <p>If not specified, defaults to {@link #DEFAULT_DATABASE_NAME}. + * @param databaseName The name of the database. + * @throws IllegalArgumentException if the databaseName contains {@code '/'}. + */ + @NonNull + public Builder setDatabaseName(@NonNull String databaseName) { + Preconditions.checkState(!mBuilt, "Builder has already been used"); + Objects.requireNonNull(databaseName); + if (databaseName.contains("/")) { + throw new IllegalArgumentException("Database name cannot contain '/'"); + } + mDatabaseName = databaseName; + return this; + } + + /** Builds a {@link SearchContext} instance. */ + @NonNull + public SearchContext build() { + Preconditions.checkState(!mBuilt, "Builder has already been used"); + mBuilt = true; + return new SearchContext(mDatabaseName); + } + } + } + + /** + * Creates a new {@link AppSearchSession}. + * + * <p>This process requires an AppSearch native indexing file system for each user. If it's not + * created for this user, the initialization process will create one under user's directory. + * + * @param searchContext The {@link SearchContext} contains all information to create a new + * {@link AppSearchSession} + * @param executor Executor on which to invoke the callback. + * @param callback The {@link AppSearchResult}<{@link AppSearchSession}> of + * performing this operation. Or a {@link AppSearchResult} with failure + * reason code and error information. + */ + public void createSearchSession( + @NonNull SearchContext searchContext, + @NonNull @CallbackExecutor Executor executor, + @NonNull Consumer<AppSearchResult<AppSearchSession>> callback) { + Objects.requireNonNull(searchContext); + Objects.requireNonNull(executor); + Objects.requireNonNull(callback); + AppSearchSession.createSearchSession(searchContext, mService, executor, callback); } /** @@ -99,7 +191,7 @@ public class AppSearchManager { * * @param request The schema update request. * @return the result of performing this operation. - * + * @deprecated use {@link AppSearchSession#setSchema} instead. * @hide */ @NonNull @@ -113,9 +205,14 @@ public class AppSearchManager { } AndroidFuture<AppSearchResult> future = new AndroidFuture<>(); try { - mService.setSchema(DEFAULT_DATABASE, schemaBundles, request.isForceOverride(), future); + mService.setSchema(DEFAULT_DATABASE_NAME, schemaBundles, request.isForceOverride(), + new IAppSearchResultCallback.Stub() { + public void onResult(AppSearchResult result) { + future.complete(result); + } + }); } catch (RemoteException e) { - future.completeExceptionally(e); + throw e.rethrowFromSystemServer(); } return getFutureOrThrow(future); } @@ -134,6 +231,9 @@ public class AppSearchManager { * {@link AppSearchBatchResult} are the URIs of the input documents. The values are * {@code null} if they were successfully indexed, or a failed {@link AppSearchResult} * otherwise. + * @throws RuntimeException If an error occurred during the execution. + * + * @deprecated use {@link AppSearchSession#putDocuments} instead. * @hide */ public AppSearchBatchResult<String, Void> putDocuments(@NonNull PutDocumentsRequest request) { @@ -146,7 +246,16 @@ public class AppSearchManager { } AndroidFuture<AppSearchBatchResult> future = new AndroidFuture<>(); try { - mService.putDocuments(DEFAULT_DATABASE, documentBundles, future); + mService.putDocuments(DEFAULT_DATABASE_NAME, documentBundles, + new IAppSearchBatchResultCallback.Stub() { + public void onResult(AppSearchBatchResult result) { + future.complete(result); + } + + public void onSystemError(ParcelableException exception) { + future.completeExceptionally(exception); + } + }); } catch (RemoteException e) { future.completeExceptionally(e); } @@ -165,6 +274,9 @@ public class AppSearchManager { * {@link GenericDocument}s on success, or a failed {@link AppSearchResult} otherwise. * URIs that are not found will return a failed {@link AppSearchResult} with a result code * of {@link AppSearchResult#RESULT_NOT_FOUND}. + * @throws RuntimeException If an error occurred during the execution. + * + * @deprecated use {@link AppSearchSession#getByUri} instead. */ public AppSearchBatchResult<String, GenericDocument> getByUri( @NonNull GetByUriRequest request) { @@ -173,7 +285,16 @@ public class AppSearchManager { List<String> uris = new ArrayList<>(request.getUris()); AndroidFuture<AppSearchBatchResult> future = new AndroidFuture<>(); try { - mService.getDocuments(DEFAULT_DATABASE, request.getNamespace(), uris, future); + mService.getDocuments(DEFAULT_DATABASE_NAME, request.getNamespace(), uris, + new IAppSearchBatchResultCallback.Stub() { + public void onResult(AppSearchBatchResult result) { + future.complete(result); + } + + public void onSystemError(ParcelableException exception) { + future.completeExceptionally(exception); + } + }); } catch (RemoteException e) { future.completeExceptionally(e); } @@ -252,6 +373,9 @@ public class AppSearchManager { * * @param queryExpression Query String to search. * @param searchSpec Spec for setting filters, raw query etc. + * @throws RuntimeException If an error occurred during the execution. + * + * @deprecated use AppSearchSession#query instead. * @hide */ @NonNull @@ -261,7 +385,7 @@ public class AppSearchManager { // them in one big list. AndroidFuture<AppSearchResult> searchResultsFuture = new AndroidFuture<>(); try { - mService.query(DEFAULT_DATABASE, queryExpression, + mService.query(DEFAULT_DATABASE_NAME, queryExpression, searchSpec.getBundle(), searchResultsFuture); } catch (RemoteException e) { searchResultsFuture.completeExceptionally(e); @@ -278,7 +402,7 @@ public class AppSearchManager { } /** - * Deletes {@link GenericDocument}s by URI. + * Removes {@link GenericDocument}s by URI. * * <p>You should not call this method directly; instead, use the {@code AppSearch#delete()} API * provided by JetPack. @@ -289,12 +413,24 @@ public class AppSearchManager { * or a failed {@link AppSearchResult} otherwise. URIs that are not found will return a * failed {@link AppSearchResult} with a result code of * {@link AppSearchResult#RESULT_NOT_FOUND}. + * @throws RuntimeException If an error occurred during the execution. + * + * @deprecated use {@link AppSearchSession#removeByUri} instead. */ public AppSearchBatchResult<String, Void> removeByUri(@NonNull RemoveByUriRequest request) { List<String> uris = new ArrayList<>(request.getUris()); AndroidFuture<AppSearchBatchResult> future = new AndroidFuture<>(); try { - mService.removeByUri(DEFAULT_DATABASE, request.getNamespace(), uris, future); + mService.removeByUri(DEFAULT_DATABASE_NAME, request.getNamespace(), uris, + new IAppSearchBatchResultCallback.Stub() { + public void onResult(AppSearchBatchResult result) { + future.complete(result); + } + + public void onSystemError(ParcelableException exception) { + future.completeExceptionally(exception); + } + }); } catch (RemoteException e) { future.completeExceptionally(e); } diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.aidl b/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.aidl new file mode 100644 index 000000000000..f0b29964b895 --- /dev/null +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.aidl @@ -0,0 +1,19 @@ +/** + * Copyright 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.app.appsearch; + +/** {@hide} */ +parcelable AppSearchResult;
\ No newline at end of file diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java index 979eab90a980..6e2ed70cca01 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java @@ -19,9 +19,11 @@ package android.app.appsearch; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.appsearch.exceptions.AppSearchException; import android.os.Parcel; import android.os.Parcelable; +import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Objects; @@ -218,4 +220,25 @@ public final class AppSearchResult<ValueType> implements Parcelable { @ResultCode int resultCode, @Nullable String errorMessage) { return new AppSearchResult<>(resultCode, /*resultValue=*/ null, errorMessage); } + + /** @hide */ + @NonNull + public static <ValueType> AppSearchResult<ValueType> throwableToFailedResult( + @NonNull Throwable t) { + if (t instanceof AppSearchException) { + return ((AppSearchException) t).toAppSearchResult(); + } + + @AppSearchResult.ResultCode int resultCode; + if (t instanceof IllegalStateException) { + resultCode = AppSearchResult.RESULT_INTERNAL_ERROR; + } else if (t instanceof IllegalArgumentException) { + resultCode = AppSearchResult.RESULT_INVALID_ARGUMENT; + } else if (t instanceof IOException) { + resultCode = AppSearchResult.RESULT_IO_ERROR; + } else { + resultCode = AppSearchResult.RESULT_UNKNOWN_ERROR; + } + return AppSearchResult.newFailedResult(resultCode, t.toString()); + } } diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSchema.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSchema.java index 3933726d6729..2db74a8fb798 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSchema.java +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSchema.java @@ -16,15 +16,14 @@ package android.app.appsearch; -import android.annotation.SuppressLint; -import android.os.Bundle; - import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; - +import android.annotation.SuppressLint; import android.app.appsearch.exceptions.IllegalSchemaException; +import android.os.Bundle; import android.util.ArraySet; + import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; @@ -40,7 +39,8 @@ import java.util.Set; * <p>For example, an e-mail message or a music recording could be a schema type. * * <p>The schema consists of type information, properties, and config (like tokenization type). - * @hide + * + * @see AppSearchManager#setSchema */ public final class AppSearchSchema { private static final String SCHEMA_TYPE_FIELD = "schemaType"; @@ -49,7 +49,6 @@ public final class AppSearchSchema { private final Bundle mBundle; /** @hide */ - public AppSearchSchema(@NonNull Bundle bundle) { Preconditions.checkNotNull(bundle); mBundle = bundle; @@ -57,9 +56,9 @@ public final class AppSearchSchema { /** * Returns the {@link Bundle} populated by this builder. + * * @hide */ - @NonNull public Bundle getBundle() { return mBundle; @@ -72,7 +71,7 @@ public final class AppSearchSchema { /** Returns the name of this schema type, e.g. Email. */ @NonNull - public String getSchemaTypeName() { + public String getSchemaType() { return mBundle.getString(SCHEMA_TYPE_FIELD, ""); } @@ -144,8 +143,8 @@ public final class AppSearchSchema { /** * Configuration for a single property (field) of a document type. * - * <p>For example, an {@code EmailMessage} would be a type and the {@code subject} would be - * a property. + * <p>For example, an {@code EmailMessage} would be a type and the {@code subject} would be a + * property. */ public static final class PropertyConfig { private static final String NAME_FIELD = "name"; @@ -157,18 +156,20 @@ public final class AppSearchSchema { /** * Physical data-types of the contents of the property. + * * @hide */ // NOTE: The integer values of these constants must match the proto enum constants in // com.google.android.icing.proto.PropertyConfigProto.DataType.Code. - @IntDef(value = { - DATA_TYPE_STRING, - DATA_TYPE_INT64, - DATA_TYPE_DOUBLE, - DATA_TYPE_BOOLEAN, - DATA_TYPE_BYTES, - DATA_TYPE_DOCUMENT, - }) + @IntDef( + value = { + DATA_TYPE_STRING, + DATA_TYPE_INT64, + DATA_TYPE_DOUBLE, + DATA_TYPE_BOOLEAN, + DATA_TYPE_BYTES, + DATA_TYPE_DOCUMENT, + }) @Retention(RetentionPolicy.SOURCE) public @interface DataType {} @@ -181,23 +182,25 @@ public final class AppSearchSchema { public static final int DATA_TYPE_BYTES = 5; /** - * Indicates that the property itself is an Document, making it part a hierarchical - * Document schema. Any property using this DataType MUST have a valid - * {@code schemaType}. + * Indicates that the property is itself a {@link GenericDocument}, making it part of a + * hierarchical schema. Any property using this DataType MUST have a valid {@link + * PropertyConfig#getSchemaType}. */ public static final int DATA_TYPE_DOCUMENT = 6; /** * The cardinality of the property (whether it is required, optional or repeated). + * * @hide */ // NOTE: The integer values of these constants must match the proto enum constants in // com.google.android.icing.proto.PropertyConfigProto.Cardinality.Code. - @IntDef(value = { - CARDINALITY_REPEATED, - CARDINALITY_OPTIONAL, - CARDINALITY_REQUIRED, - }) + @IntDef( + value = { + CARDINALITY_REPEATED, + CARDINALITY_OPTIONAL, + CARDINALITY_REQUIRED, + }) @Retention(RetentionPolicy.SOURCE) public @interface Cardinality {} @@ -212,23 +215,25 @@ public final class AppSearchSchema { /** * Encapsulates the configurations on how AppSearch should query/index these terms. + * * @hide */ - @IntDef(value = { - INDEXING_TYPE_NONE, - INDEXING_TYPE_EXACT_TERMS, - INDEXING_TYPE_PREFIXES, - }) + @IntDef( + value = { + INDEXING_TYPE_NONE, + INDEXING_TYPE_EXACT_TERMS, + INDEXING_TYPE_PREFIXES, + }) @Retention(RetentionPolicy.SOURCE) public @interface IndexingType {} /** * Content in this property will not be tokenized or indexed. * - * <p>Useful if the data type is not made up of terms (e.g. - * {@link PropertyConfig#DATA_TYPE_DOCUMENT} or {@link PropertyConfig#DATA_TYPE_BYTES} - * type). All the properties inside the nested property won't be indexed regardless of the - * value of {@code indexingType} for the nested properties. + * <p>Useful if the data type is not made up of terms (e.g. {@link + * PropertyConfig#DATA_TYPE_DOCUMENT} or {@link PropertyConfig#DATA_TYPE_BYTES} type). None + * of the properties inside the nested property will be indexed regardless of the value of + * {@code indexingType} for the nested properties. */ public static final int INDEXING_TYPE_NONE = 0; @@ -250,20 +255,22 @@ public final class AppSearchSchema { /** * Configures how tokens should be extracted from this property. + * * @hide */ // NOTE: The integer values of these constants must match the proto enum constants in // com.google.android.icing.proto.IndexingConfig.TokenizerType.Code. - @IntDef(value = { - TOKENIZER_TYPE_NONE, - TOKENIZER_TYPE_PLAIN, - }) + @IntDef( + value = { + TOKENIZER_TYPE_NONE, + TOKENIZER_TYPE_PLAIN, + }) @Retention(RetentionPolicy.SOURCE) public @interface TokenizerType {} /** - * It is only valid for tokenizer_type to be 'NONE' if the data type is - * {@link PropertyConfig#DATA_TYPE_DOCUMENT}. + * It is only valid for tokenizer_type to be 'NONE' if the data type is {@link + * PropertyConfig#DATA_TYPE_DOCUMENT}. */ public static final int TOKENIZER_TYPE_NONE = 0; @@ -295,8 +302,8 @@ public final class AppSearchSchema { /** * Returns the logical schema-type of the contents of this property. * - * <p>Only set when {@link #getDataType} is set to {@link #DATA_TYPE_DOCUMENT}. - * Otherwise, it is {@code null}. + * <p>Only set when {@link #getDataType} is set to {@link #DATA_TYPE_DOCUMENT}. Otherwise, + * it is {@code null}. */ @Nullable public String getSchemaType() { @@ -325,9 +332,10 @@ public final class AppSearchSchema { * * <p>The following properties must be set, or {@link PropertyConfig} construction will * fail: + * * <ul> - * <li>dataType - * <li>cardinality + * <li>dataType + * <li>cardinality * </ul> * * <p>In addition, if {@code schemaType} is {@link #DATA_TYPE_DOCUMENT}, {@code schemaType} @@ -359,8 +367,8 @@ public final class AppSearchSchema { /** * The logical schema-type of the contents of this property. * - * <p>Only required when {@link #setDataType} is set to - * {@link #DATA_TYPE_DOCUMENT}. Otherwise, it is ignored. + * <p>Only required when {@link #setDataType} is set to {@link #DATA_TYPE_DOCUMENT}. + * Otherwise, it is ignored. */ @NonNull public PropertyConfig.Builder setSchemaType(@NonNull String schemaType) { diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java new file mode 100644 index 000000000000..531436ecaf0c --- /dev/null +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java @@ -0,0 +1,312 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.appsearch; + +import android.annotation.CallbackExecutor; +import android.annotation.NonNull; +import android.app.appsearch.exceptions.AppSearchException; +import android.os.Bundle; +import android.os.ParcelableException; +import android.os.RemoteException; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.Executor; +import java.util.function.Consumer; + +/** + * Represents a connection to an AppSearch storage system where {@link GenericDocument}s can be + * placed and queried. + * @hide + */ +public final class AppSearchSession { + private final String mDatabaseName; + private final IAppSearchManager mService; + + static void createSearchSession( + @NonNull AppSearchManager.SearchContext searchContext, + @NonNull IAppSearchManager service, + @NonNull @CallbackExecutor Executor executor, + @NonNull Consumer<AppSearchResult<AppSearchSession>> callback) { + AppSearchSession searchSession = + new AppSearchSession(searchContext.mDatabaseName, service); + searchSession.initialize(executor, callback); + } + + // NOTE: No instance of this class should be created or returned except via initialize(). + // Once the callback.accept has been called here, the class is ready to use. + private void initialize( + @NonNull @CallbackExecutor Executor executor, + @NonNull Consumer<AppSearchResult<AppSearchSession>> callback) { + try { + mService.initialize(new IAppSearchResultCallback.Stub() { + public void onResult(AppSearchResult result) { + executor.execute(() -> { + if (result.isSuccess()) { + callback.accept( + AppSearchResult.newSuccessfulResult(AppSearchSession.this)); + } else { + callback.accept(result); + } + }); + } + }); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + private AppSearchSession(@NonNull String databaseName, @NonNull IAppSearchManager service) { + mDatabaseName = databaseName; + mService = service; + } + + /** + * Sets the schema will be used by documents provided to the {@link #putDocuments} method. + * + * <p>The schema provided here is compared to the stored copy of the schema previously supplied + * to {@link #setSchema}, if any, to determine how to treat existing documents. The following + * types of schema modifications are always safe and are made without deleting any existing + * documents: + * <ul> + * <li>Addition of new types + * <li>Addition of new + * {@link AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL OPTIONAL} or + * {@link AppSearchSchema.PropertyConfig#CARDINALITY_REPEATED REPEATED} properties to a + * type + * <li>Changing the cardinality of a data type to be less restrictive (e.g. changing an + * {@link AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL OPTIONAL} property into a + * {@link AppSearchSchema.PropertyConfig#CARDINALITY_REPEATED REPEATED} property. + * </ul> + * + * <p>The following types of schema changes are not backwards-compatible: + * <ul> + * <li>Removal of an existing type + * <li>Removal of a property from a type + * <li>Changing the data type ({@code boolean}, {@code long}, etc.) of an existing property + * <li>For properties of {@code Document} type, changing the schema type of + * {@code Document}s of that property + * <li>Changing the cardinality of a data type to be more restrictive (e.g. changing an + * {@link AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL OPTIONAL} property into a + * {@link AppSearchSchema.PropertyConfig#CARDINALITY_REQUIRED REQUIRED} property). + * <li>Adding a + * {@link AppSearchSchema.PropertyConfig#CARDINALITY_REQUIRED REQUIRED} property. + * </ul> + * <p>Supplying a schema with such changes will, by default, result in this call returning an + * {@link AppSearchResult} with a code of {@link AppSearchResult#RESULT_INVALID_SCHEMA} and an + * error message describing the incompatibility. In this case the previously set schema will + * remain active. + * + * <p>If you need to make non-backwards-compatible changes as described above, you can set the + * {@link SetSchemaRequest.Builder#setForceOverride} method to {@code true}. In this case, + * instead of returning an {@link AppSearchResult} with the + * {@link AppSearchResult#RESULT_INVALID_SCHEMA} error code, all documents which are not + * compatible with the new schema will be deleted and the incompatible schema will be applied. + * + * <p>It is a no-op to set the same schema as has been previously set; this is handled + * efficiently. + * + * @param request The schema update request. + * @param executor Executor on which to invoke the callback. + * @param callback Callback to receive errors resulting from setting the schema. If the + * operation succeeds, the callback will be invoked with {@code null}. + */ + public void setSchema( + @NonNull SetSchemaRequest request, + @NonNull @CallbackExecutor Executor executor, + @NonNull Consumer<AppSearchResult<Void>> callback) { + Objects.requireNonNull(request); + Objects.requireNonNull(executor); + Objects.requireNonNull(callback); + List<Bundle> schemaBundles = new ArrayList<>(request.getSchemas().size()); + for (AppSearchSchema schema : request.getSchemas()) { + schemaBundles.add(schema.getBundle()); + } + try { + mService.setSchema(mDatabaseName, schemaBundles, request.isForceOverride(), + new IAppSearchResultCallback.Stub() { + public void onResult(AppSearchResult result) { + executor.execute(() -> callback.accept(result)); + } + }); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Indexes documents into AppSearch. + * + * <p>Each {@link GenericDocument}'s {@code schemaType} field must be set to the name of a + * schema type previously registered via the {@link #setSchema} method. + * + * @param request {@link PutDocumentsRequest} containing documents to be indexed + * @param executor Executor on which to invoke the callback. + * @param callback Callback to receive pending result of performing this operation. The keys + * of the returned {@link AppSearchBatchResult} are the URIs of the input + * documents. The values are {@code null} if they were successfully indexed, + * or a failed {@link AppSearchResult} otherwise. + * Or {@link BatchResultCallback#onSystemError} will be invoked with an + * {@link AppSearchException} if an error occurred in AppSearch initialization + * or a cause {@link Throwable} if other error occurred in AppSearch service. + */ + public void putDocuments( + @NonNull PutDocumentsRequest request, + @NonNull @CallbackExecutor Executor executor, + @NonNull BatchResultCallback<String, Void> callback) { + Objects.requireNonNull(request); + Objects.requireNonNull(executor); + Objects.requireNonNull(callback); + List<GenericDocument> documents = request.getDocuments(); + List<Bundle> documentBundles = new ArrayList<>(documents.size()); + for (int i = 0; i < documents.size(); i++) { + documentBundles.add(documents.get(i).getBundle()); + } + try { + mService.putDocuments(mDatabaseName, documentBundles, + new IAppSearchBatchResultCallback.Stub() { + public void onResult(AppSearchBatchResult result) { + executor.execute(() -> callback.onResult(result)); + } + + public void onSystemError(ParcelableException exception) { + executor.execute(() -> callback.onSystemError(exception.getCause())); + } + }); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Retrieves {@link GenericDocument}s by URI. + * + * @param request {@link GetByUriRequest} containing URIs to be retrieved. + * @param executor Executor on which to invoke the callback. + * @param callback Callback to receive the pending result of performing this operation. The keys + * of the returned {@link AppSearchBatchResult} are the input URIs. The values + * are the returned {@link GenericDocument}s on success, or a failed + * {@link AppSearchResult} otherwise. URIs that are not found will return a + * failed {@link AppSearchResult} with a result code of + * {@link AppSearchResult#RESULT_NOT_FOUND}. + * Or {@link BatchResultCallback#onSystemError} will be invoked with an + * {@link AppSearchException} if an error occurred in AppSearch initialization + * or a cause {@link Throwable} if other error occurred in AppSearch service. + */ + public void getByUri( + @NonNull GetByUriRequest request, + @NonNull @CallbackExecutor Executor executor, + @NonNull BatchResultCallback<String, GenericDocument> callback) { + Objects.requireNonNull(request); + Objects.requireNonNull(executor); + Objects.requireNonNull(callback); + try { + mService.getDocuments(mDatabaseName, request.getNamespace(), + new ArrayList<>(request.getUris()), + new IAppSearchBatchResultCallback.Stub() { + public void onResult(AppSearchBatchResult result) { + executor.execute(() -> { + AppSearchBatchResult.Builder<String, GenericDocument> + documentResultBuilder = + new AppSearchBatchResult.Builder<>(); + + // Translate successful results + for (Map.Entry<String, Bundle> bundleEntry : + (Set<Map.Entry<String, Bundle>>) + result.getSuccesses().entrySet()) { + GenericDocument document; + try { + document = new GenericDocument(bundleEntry.getValue()); + } catch (Throwable t) { + // These documents went through validation, so how could + // this fail? We must have done something wrong. + documentResultBuilder.setFailure( + bundleEntry.getKey(), + AppSearchResult.RESULT_INTERNAL_ERROR, + t.getMessage()); + continue; + } + documentResultBuilder.setSuccess( + bundleEntry.getKey(), document); + } + + // Translate failed results + for (Map.Entry<String, AppSearchResult<Bundle>> bundleEntry : + (Set<Map.Entry<String, AppSearchResult<Bundle>>>) + result.getFailures().entrySet()) { + documentResultBuilder.setFailure( + bundleEntry.getKey(), + bundleEntry.getValue().getResultCode(), + bundleEntry.getValue().getErrorMessage()); + } + callback.onResult(documentResultBuilder.build()); + }); + } + + public void onSystemError(ParcelableException exception) { + executor.execute(() -> callback.onSystemError(exception.getCause())); + } + }); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Removes {@link GenericDocument}s from the index by URI. + * + * @param request Request containing URIs to be removed. + * @param executor Executor on which to invoke the callback. + * @param callback Callback to receive the pending result of performing this operation. The keys + * of the returned {@link AppSearchBatchResult} are the input URIs. The values + * are {@code null} on success, or a failed {@link AppSearchResult} otherwise. + * URIs that are not found will return a failed {@link AppSearchResult} with a + * result code of {@link AppSearchResult#RESULT_NOT_FOUND}. + * Or {@link BatchResultCallback#onSystemError} will be invoked with an + * {@link AppSearchException} if an error occurred in AppSearch initialization + * or a cause {@link Throwable} if other error occurred in AppSearch service. + */ + public void removeByUri( + @NonNull RemoveByUriRequest request, + @NonNull @CallbackExecutor Executor executor, + @NonNull BatchResultCallback<String, Void> callback) { + Objects.requireNonNull(request); + Objects.requireNonNull(executor); + Objects.requireNonNull(callback); + try { + mService.removeByUri(mDatabaseName, request.getNamespace(), + new ArrayList<>(request.getUris()), + new IAppSearchBatchResultCallback.Stub() { + public void onResult(AppSearchBatchResult result) { + executor.execute(() -> callback.onResult(result)); + } + + public void onSystemError(ParcelableException exception) { + executor.execute(() -> callback.onSystemError(exception.getCause())); + } + }); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + // TODO(b/162450968) port query() and SearchResults.java to platform. + // TODO(b/162450968) port removeByQuery() to platform. +} diff --git a/apex/appsearch/framework/java/android/app/appsearch/BatchResultCallback.java b/apex/appsearch/framework/java/android/app/appsearch/BatchResultCallback.java new file mode 100644 index 000000000000..1689e02c7ce4 --- /dev/null +++ b/apex/appsearch/framework/java/android/app/appsearch/BatchResultCallback.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.appsearch; + +/** + * The callback interface to return {@link AppSearchBatchResult}. + * + * @param <KeyType> The type of the keys for {@link AppSearchBatchResult#getSuccesses} and + * {@link AppSearchBatchResult#getFailures}. + * @param <ValueType> The type of result objects associated with the keys. + * @hide + */ +public interface BatchResultCallback<KeyType, ValueType> { + + /** + * Called when {@link AppSearchBatchResult} results are ready. + * + * @param result The result of the executed request. + */ + void onResult(AppSearchBatchResult<KeyType, ValueType> result); + + + /** + * Called when a system error occurred. + * + * @param throwable The cause throwable. + */ + default void onSystemError(Throwable throwable) { + if (throwable != null) { + throw new RuntimeException(throwable); + } + } +} diff --git a/apex/appsearch/framework/java/android/app/appsearch/GenericDocument.java b/apex/appsearch/framework/java/android/app/appsearch/GenericDocument.java index 48d3ac09d997..0056377f007a 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/GenericDocument.java +++ b/apex/appsearch/framework/java/android/app/appsearch/GenericDocument.java @@ -16,15 +16,14 @@ package android.app.appsearch; -import android.annotation.SuppressLint; -import android.os.Bundle; -import android.util.Log; - import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; - +import android.annotation.SuppressLint; import android.app.appsearch.exceptions.AppSearchException; +import android.os.Bundle; +import android.util.Log; + import com.android.internal.util.Preconditions; import java.lang.reflect.Array; @@ -37,17 +36,20 @@ import java.util.Set; * Represents a document unit. * * <p>Documents are constructed via {@link GenericDocument.Builder}. - * @hide + * + * @see AppSearchManager#putDocuments + * @see AppSearchManager#getByUri + * @see AppSearchManager#query */ public class GenericDocument { private static final String TAG = "GenericDocument"; - /** The default empty namespace.*/ + /** The default empty namespace. */ public static final String DEFAULT_NAMESPACE = ""; /** - * The maximum number of elements in a repeatable field. Will reject the request if exceed - * this limit. + * The maximum number of elements in a repeatable field. Will reject the request if exceed this + * limit. */ private static final int MAX_REPEATED_PROPERTY_LENGTH = 100; @@ -78,47 +80,43 @@ public class GenericDocument { /** * The maximum number of indexed properties a document can have. * - * <p>Indexed properties are properties where the - * {@link android.app.appsearch.annotation.AppSearchDocument.Property#indexingType} constant is - * anything other than {@link - * android.app.appsearch.AppSearchSchema.PropertyConfig.IndexingType#INDEXING_TYPE_NONE}. + * <p>Indexed properties are properties where the {@link + * AppSearchSchema.PropertyConfig#getIndexingType()} constant is anything other than {@link + * AppSearchSchema.PropertyConfig.IndexingType#INDEXING_TYPE_NONE}. */ public static int getMaxIndexedProperties() { return MAX_INDEXED_PROPERTIES; } - /** Contains {@link GenericDocument} basic information (uri, schemaType etc).*/ - @NonNull - final Bundle mBundle; + /** Contains {@link GenericDocument} basic information (uri, schemaType etc). */ + @NonNull final Bundle mBundle; - /** Contains all properties in {@link GenericDocument} to support getting properties via keys.*/ - @NonNull - private final Bundle mProperties; + /** + * Contains all properties in {@link GenericDocument} to support getting properties via keys. + */ + @NonNull private final Bundle mProperties; - @NonNull - private final String mUri; - @NonNull - private final String mSchemaType; + @NonNull private final String mUri; + @NonNull private final String mSchemaType; private final long mCreationTimestampMillis; - @Nullable - private Integer mHashCode; + @Nullable private Integer mHashCode; /** * Rebuilds a {@link GenericDocument} by the a bundle. - * @param bundle Contains {@link GenericDocument} basic information (uri, schemaType etc) and - * a properties bundle contains all properties in {@link GenericDocument} to - * support getting properties via keys. + * + * @param bundle Contains {@link GenericDocument} basic information (uri, schemaType etc) and a + * properties bundle contains all properties in {@link GenericDocument} to support getting + * properties via keys. * @hide */ - public GenericDocument(@NonNull Bundle bundle) { Preconditions.checkNotNull(bundle); mBundle = bundle; mProperties = Preconditions.checkNotNull(bundle.getParcelable(PROPERTIES_FIELD)); mUri = Preconditions.checkNotNull(mBundle.getString(URI_FIELD)); mSchemaType = Preconditions.checkNotNull(mBundle.getString(SCHEMA_TYPE_FIELD)); - mCreationTimestampMillis = mBundle.getLong(CREATION_TIMESTAMP_MILLIS_FIELD, - System.currentTimeMillis()); + mCreationTimestampMillis = + mBundle.getLong(CREATION_TIMESTAMP_MILLIS_FIELD, System.currentTimeMillis()); } /** @@ -132,9 +130,9 @@ public class GenericDocument { /** * Returns the {@link Bundle} populated by this builder. + * * @hide */ - @NonNull public Bundle getBundle() { return mBundle; @@ -158,7 +156,11 @@ public class GenericDocument { return mSchemaType; } - /** Returns the creation timestamp of the {@link GenericDocument}, in milliseconds. */ + /** + * Returns the creation timestamp of the {@link GenericDocument}, in milliseconds. + * + * <p>The value is in the {@link System#currentTimeMillis} time base. + */ public long getCreationTimestampMillis() { return mCreationTimestampMillis; } @@ -166,8 +168,12 @@ public class GenericDocument { /** * Returns the TTL (Time To Live) of the {@link GenericDocument}, in milliseconds. * + * <p>The TTL is measured against {@link #getCreationTimestampMillis}. At the timestamp of + * {@code creationTimestampMillis + ttlMillis}, measured in the {@link System#currentTimeMillis} + * time base, the document will be auto-deleted. + * * <p>The default value is 0, which means the document is permanent and won't be auto-deleted - * until the app is uninstalled. + * until the app is uninstalled. */ public long getTtlMillis() { return mBundle.getLong(TTL_MILLIS_FIELD, DEFAULT_TTL_MILLIS); @@ -179,7 +185,10 @@ public class GenericDocument { * <p>The score is a query-independent measure of the document's quality, relative to other * {@link GenericDocument}s of the same type. * - * <p>The default value is 0. + * <p>Results may be sorted by score using {@link SearchSpec.Builder#setRankingStrategy}. + * Documents with higher scores are considered better than documents with lower scores. + * + * <p>Any nonnegative integer can be used a score. */ public int getScore() { return mBundle.getInt(SCORE_FIELD, DEFAULT_SCORE); @@ -195,8 +204,8 @@ public class GenericDocument { * Retrieves a {@link String} value by key. * * @param key The key to look for. - * @return The first {@link String} associated with the given key or {@code null} if there - * is no such key or the value is of a different type. + * @return The first {@link String} associated with the given key or {@code null} if there is no + * such key or the value is of a different type. */ @Nullable public String getPropertyString(@NonNull String key) { @@ -214,7 +223,7 @@ public class GenericDocument { * * @param key The key to look for. * @return The first {@code long} associated with the given key or default value {@code 0} if - * there is no such key or the value is of a different type. + * there is no such key or the value is of a different type. */ public long getPropertyLong(@NonNull String key) { Preconditions.checkNotNull(key); @@ -231,7 +240,7 @@ public class GenericDocument { * * @param key The key to look for. * @return The first {@code double} associated with the given key or default value {@code 0.0} - * if there is no such key or the value is of a different type. + * if there is no such key or the value is of a different type. */ public double getPropertyDouble(@NonNull String key) { Preconditions.checkNotNull(key); @@ -247,8 +256,8 @@ public class GenericDocument { * Retrieves a {@code boolean} value by key. * * @param key The key to look for. - * @return The first {@code boolean} associated with the given key or default value - * {@code false} if there is no such key or the value is of a different type. + * @return The first {@code boolean} associated with the given key or default value {@code + * false} if there is no such key or the value is of a different type. */ public boolean getPropertyBoolean(@NonNull String key) { Preconditions.checkNotNull(key); @@ -264,8 +273,8 @@ public class GenericDocument { * Retrieves a {@code byte[]} value by key. * * @param key The key to look for. - * @return The first {@code byte[]} associated with the given key or {@code null} if there - * is no such key or the value is of a different type. + * @return The first {@code byte[]} associated with the given key or {@code null} if there is no + * such key or the value is of a different type. */ @Nullable public byte[] getPropertyBytes(@NonNull String key) { @@ -283,7 +292,7 @@ public class GenericDocument { * * @param key The key to look for. * @return The first {@link GenericDocument} associated with the given key or {@code null} if - * there is no such key or the value is of a different type. + * there is no such key or the value is of a different type. */ @Nullable public GenericDocument getPropertyDocument(@NonNull String key) { @@ -300,10 +309,18 @@ public class GenericDocument { private static void warnIfSinglePropertyTooLong( @NonNull String propertyType, @NonNull String key, int propertyLength) { if (propertyLength > 1) { - Log.w(TAG, "The value for \"" + key + "\" contains " + propertyLength - + " elements. Only the first one will be returned from " - + "getProperty" + propertyType + "(). Try getProperty" + propertyType - + "Array()."); + Log.w( + TAG, + "The value for \"" + + key + + "\" contains " + + propertyLength + + " elements. Only the first one will be returned from " + + "getProperty" + + propertyType + + "(). Try getProperty" + + propertyType + + "Array()."); } } @@ -311,8 +328,8 @@ public class GenericDocument { * Retrieves a repeated {@code String} property by key. * * @param key The key to look for. - * @return The {@code String[]} associated with the given key, or {@code null} if no value - * is set or the value is of a different type. + * @return The {@code String[]} associated with the given key, or {@code null} if no value is + * set or the value is of a different type. */ @Nullable public String[] getPropertyStringArray(@NonNull String key) { @@ -324,8 +341,8 @@ public class GenericDocument { * Retrieves a repeated {@link String} property by key. * * @param key The key to look for. - * @return The {@code long[]} associated with the given key, or {@code null} if no value is - * set or the value is of a different type. + * @return The {@code long[]} associated with the given key, or {@code null} if no value is set + * or the value is of a different type. */ @Nullable public long[] getPropertyLongArray(@NonNull String key) { @@ -337,8 +354,8 @@ public class GenericDocument { * Retrieves a repeated {@code double} property by key. * * @param key The key to look for. - * @return The {@code double[]} associated with the given key, or {@code null} if no value - * is set or the value is of a different type. + * @return The {@code double[]} associated with the given key, or {@code null} if no value is + * set or the value is of a different type. */ @Nullable public double[] getPropertyDoubleArray(@NonNull String key) { @@ -350,8 +367,8 @@ public class GenericDocument { * Retrieves a repeated {@code boolean} property by key. * * @param key The key to look for. - * @return The {@code boolean[]} associated with the given key, or {@code null} if no value - * is set or the value is of a different type. + * @return The {@code boolean[]} associated with the given key, or {@code null} if no value is + * set or the value is of a different type. */ @Nullable public boolean[] getPropertyBooleanArray(@NonNull String key) { @@ -363,8 +380,8 @@ public class GenericDocument { * Retrieves a {@code byte[][]} property by key. * * @param key The key to look for. - * @return The {@code byte[][]} associated with the given key, or {@code null} if no value - * is set or the value is of a different type. + * @return The {@code byte[][]} associated with the given key, or {@code null} if no value is + * set or the value is of a different type. */ @SuppressLint("ArrayReturn") @Nullable @@ -397,7 +414,7 @@ public class GenericDocument { * * @param key The key to look for. * @return The {@link GenericDocument}[] associated with the given key, or {@code null} if no - * value is set or the value is of a different type. + * value is set or the value is of a different type. */ @SuppressLint("ArrayReturn") @Nullable @@ -419,8 +436,8 @@ public class GenericDocument { } /** - * Gets a repeated property of the given key, and casts it to the given class type, which - * must be an array class type. + * Gets a repeated property of the given key, and casts it to the given class type, which must + * be an array class type. */ @Nullable private <T> T getAndCastPropertyArray(@NonNull String key, @NonNull Class<T> tClass) { @@ -449,8 +466,9 @@ public class GenericDocument { } /** - * Deeply checks two bundles are equally or not. - * <p> Two bundles will be considered equally if they contain same content. + * Deeply checks whether two bundles are equal. + * + * <p>Two bundles will be considered equal if they contain the same content. */ @SuppressWarnings("unchecked") private static boolean bundleEquals(Bundle one, Bundle two) { @@ -537,8 +555,9 @@ public class GenericDocument { /** * Calculates the hash code for a bundle. - * <p> The hash code is only effected by the contents in the bundle. Bundles will get - * consistent hash code if they have same contents. + * + * <p>The hash code is only effected by the contents in the bundle. Bundles will get consistent + * hash code if they have same contents. */ @SuppressWarnings("unchecked") private static int bundleHashCode(Bundle bundle) { @@ -663,11 +682,11 @@ public class GenericDocument { * Create a new {@link GenericDocument.Builder}. * * @param uri The uri of {@link GenericDocument}. - * @param schemaType The schema type of the {@link GenericDocument}. The passed-in - * {@code schemaType} must be defined using {@link AppSearchSession#setSchema} prior - * to inserting a document of this {@code schemaType} into the AppSearch index using - * {@link AppSearchSession#putDocuments}. Otherwise, the document will be - * rejected by {@link AppSearchSession#putDocuments}. + * @param schemaType The schema type of the {@link GenericDocument}. The passed-in {@code + * schemaType} must be defined using {@link AppSearchManager#setSchema} prior to + * inserting a document of this {@code schemaType} into the AppSearch index using {@link + * AppSearchManager#putDocuments}. Otherwise, the document will be rejected by {@link + * AppSearchManager#putDocuments}. */ @SuppressWarnings("unchecked") public Builder(@NonNull String uri, @NonNull String schemaType) { @@ -678,16 +697,16 @@ public class GenericDocument { mBundle.putString(GenericDocument.SCHEMA_TYPE_FIELD, schemaType); mBundle.putString(GenericDocument.NAMESPACE_FIELD, DEFAULT_NAMESPACE); // Set current timestamp for creation timestamp by default. - mBundle.putLong(GenericDocument.CREATION_TIMESTAMP_MILLIS_FIELD, - System.currentTimeMillis()); + mBundle.putLong( + GenericDocument.CREATION_TIMESTAMP_MILLIS_FIELD, System.currentTimeMillis()); mBundle.putLong(GenericDocument.TTL_MILLIS_FIELD, DEFAULT_TTL_MILLIS); mBundle.putInt(GenericDocument.SCORE_FIELD, DEFAULT_SCORE); mBundle.putBundle(PROPERTIES_FIELD, mProperties); } /** - * Sets the app-defined namespace this Document resides in. No special values are - * reserved or understood by the infrastructure. + * Sets the app-defined namespace this Document resides in. No special values are reserved + * or understood by the infrastructure. * * <p>URIs are unique within a namespace. * @@ -702,8 +721,13 @@ public class GenericDocument { /** * Sets the score of the {@link GenericDocument}. * - * <p>The score is a query-independent measure of the document's quality, relative to - * other {@link GenericDocument}s of the same type. + * <p>The score is a query-independent measure of the document's quality, relative to other + * {@link GenericDocument}s of the same type. + * + * <p>Results may be sorted by score using {@link SearchSpec.Builder#setRankingStrategy}. + * Documents with higher scores are considered better than documents with lower scores. + * + * <p>Any nonnegative integer can be used a score. * * @throws IllegalArgumentException If the provided value is negative. */ @@ -718,22 +742,28 @@ public class GenericDocument { } /** - * Sets the creation timestamp of the {@link GenericDocument}, in milliseconds. Should be - * set using a value obtained from the {@link System#currentTimeMillis()} time base. + * Sets the creation timestamp of the {@link GenericDocument}, in milliseconds. + * + * <p>Should be set using a value obtained from the {@link System#currentTimeMillis} time + * base. */ @NonNull public BuilderType setCreationTimestampMillis(long creationTimestampMillis) { Preconditions.checkState(!mBuilt, "Builder has already been used"); - mBundle.putLong(GenericDocument.CREATION_TIMESTAMP_MILLIS_FIELD, - creationTimestampMillis); + mBundle.putLong( + GenericDocument.CREATION_TIMESTAMP_MILLIS_FIELD, creationTimestampMillis); return mBuilderTypeInstance; } /** * Sets the TTL (Time To Live) of the {@link GenericDocument}, in milliseconds. * - * <p>After this many milliseconds since the {@link #setCreationTimestampMillis creation - * timestamp}, the document is deleted. + * <p>The TTL is measured against {@link #getCreationTimestampMillis}. At the timestamp of + * {@code creationTimestampMillis + ttlMillis}, measured in the {@link + * System#currentTimeMillis} time base, the document will be auto-deleted. + * + * <p>The default value is 0, which means the document is permanent and won't be + * auto-deleted until the app is uninstalled. * * @param ttlMillis A non-negative duration in milliseconds. * @throws IllegalArgumentException If the provided value is negative. @@ -749,8 +779,7 @@ public class GenericDocument { } /** - * Sets one or multiple {@code String} values for a property, replacing its previous - * values. + * Sets one or multiple {@code String} values for a property, replacing its previous values. * * @param key The key associated with the {@code values}. * @param values The {@code String} values of the property. @@ -781,8 +810,7 @@ public class GenericDocument { } /** - * Sets one or multiple {@code long} values for a property, replacing its previous - * values. + * Sets one or multiple {@code long} values for a property, replacing its previous values. * * @param key The key associated with the {@code values}. * @param values The {@code long} values of the property. @@ -797,8 +825,7 @@ public class GenericDocument { } /** - * Sets one or multiple {@code double} values for a property, replacing its previous - * values. + * Sets one or multiple {@code double} values for a property, replacing its previous values. * * @param key The key associated with the {@code values}. * @param values The {@code double} values of the property. @@ -851,9 +878,14 @@ public class GenericDocument { if (values[i] == null) { throw new IllegalArgumentException("The String at " + i + " is null."); } else if (values[i].length() > MAX_STRING_LENGTH) { - throw new IllegalArgumentException("The String at " + i + " length is: " - + values[i].length() + ", which exceeds length limit: " - + MAX_STRING_LENGTH + "."); + throw new IllegalArgumentException( + "The String at " + + i + + " length is: " + + values[i].length() + + ", which exceeds length limit: " + + MAX_STRING_LENGTH + + "."); } } mProperties.putStringArray(key, values); @@ -911,7 +943,10 @@ public class GenericDocument { throw new IllegalArgumentException("The input array is empty."); } else if (length > MAX_REPEATED_PROPERTY_LENGTH) { throw new IllegalArgumentException( - "Repeated property \"" + key + "\" has length " + length + "Repeated property \"" + + key + + "\" has length " + + length + ", which exceeds the limit of " + MAX_REPEATED_PROPERTY_LENGTH); } diff --git a/apex/appsearch/framework/java/android/app/appsearch/GetByUriRequest.java b/apex/appsearch/framework/java/android/app/appsearch/GetByUriRequest.java index e1e0eda7558c..053d401d06dc 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/GetByUriRequest.java +++ b/apex/appsearch/framework/java/android/app/appsearch/GetByUriRequest.java @@ -18,6 +18,7 @@ package android.app.appsearch; import android.annotation.NonNull; import android.util.ArraySet; + import com.android.internal.util.Preconditions; import java.util.Arrays; @@ -28,7 +29,7 @@ import java.util.Set; /** * Encapsulates a request to retrieve documents by namespace and URI. * - * @see AppSearchManager#getByUri + * @see AppSearchSession#getByUri * @hide */ public final class GetByUriRequest { diff --git a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchBatchResultCallback.aidl b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchBatchResultCallback.aidl new file mode 100644 index 000000000000..b1bbd18b98e2 --- /dev/null +++ b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchBatchResultCallback.aidl @@ -0,0 +1,25 @@ +/** + * Copyright 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.app.appsearch; + +import android.app.appsearch.AppSearchBatchResult; +import android.os.ParcelableException; + +/** {@hide} */ +oneway interface IAppSearchBatchResultCallback { + void onResult(in AppSearchBatchResult result); + void onSystemError(in ParcelableException exception); +}
\ No newline at end of file diff --git a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl index 01260ea193f6..4a981022da73 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl +++ b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl @@ -17,10 +17,12 @@ package android.app.appsearch; import android.os.Bundle; +import android.app.appsearch.AppSearchBatchResult; +import android.app.appsearch.AppSearchResult; +import android.app.appsearch.IAppSearchBatchResultCallback; +import android.app.appsearch.IAppSearchResultCallback; import com.android.internal.infra.AndroidFuture; -parcelable AppSearchResult; -parcelable AppSearchBatchResult; parcelable SearchResults; /** {@hide} */ @@ -32,14 +34,14 @@ interface IAppSearchManager { * @param schemaBundles List of AppSearchSchema bundles. * @param forceOverride Whether to apply the new schema even if it is incompatible. All * incompatible documents will be deleted. - * @param callback {@link AndroidFuture}<{@link AppSearchResult}<{@link Void}>>. - * The results of the call. + * @param callback {@link IAppSearchResultCallback#onResult} will be called with an + * {@link AppSearchResult}<{@link Void}>. */ void setSchema( in String databaseName, in List<Bundle> schemaBundles, boolean forceOverride, - in AndroidFuture<AppSearchResult> callback); + in IAppSearchResultCallback callback); /** * Inserts documents into the index. @@ -47,16 +49,16 @@ interface IAppSearchManager { * @param databaseName The name of the database where this document lives. * @param documentBundes List of GenericDocument bundles. * @param callback - * {@link AndroidFuture}<{@link AppSearchBatchResult}<{@link String}, {@link Void}>>. - * If the call fails to start, {@code callback} will be completed exceptionally. Otherwise, - * {@code callback} will be completed with an + * If the call fails to start, {@link IAppSearchBatchResultCallback#onSystemError} + * will be called with the cause throwable. Otherwise, + * {@link IAppSearchBatchResultCallback#onResult} will be called with an * {@link AppSearchBatchResult}<{@link String}, {@link Void}> * where the keys are document URIs, and the values are {@code null}. */ void putDocuments( in String databaseName, in List<Bundle> documentBundles, - in AndroidFuture<AppSearchBatchResult> callback); + in IAppSearchBatchResultCallback callback); /** * Retrieves documents from the index. @@ -65,9 +67,9 @@ interface IAppSearchManager { * @param namespace The namespace this document resides in. * @param uris The URIs of the documents to retrieve * @param callback - * {@link AndroidFuture}<{@link AppSearchBatchResult}<{@link String}, {@link Bundle}>>. - * If the call fails to start, {@code callback} will be completed exceptionally. Otherwise, - * {@code callback} will be completed with an + * If the call fails to start, {@link IAppSearchBatchResultCallback#onSystemError} + * will be called with the cause throwable. Otherwise, + * {@link IAppSearchBatchResultCallback#onResult} will be called with an * {@link AppSearchBatchResult}<{@link String}, {@link Bundle}> * where the keys are document URIs, and the values are Document bundles. */ @@ -75,7 +77,7 @@ interface IAppSearchManager { in String databaseName, in String namespace, in List<String> uris, - in AndroidFuture<AppSearchBatchResult> callback); + in IAppSearchBatchResultCallback callback); /** * Searches a document based on a given specifications. @@ -98,9 +100,9 @@ interface IAppSearchManager { * @param namespace Namespace of the document to remove. * @param uris The URIs of the documents to delete * @param callback - * {@link AndroidFuture}<{@link AppSearchBatchResult}<{@link String}, {@link Void}>>. - * If the call fails to start, {@code callback} will be completed exceptionally. Otherwise, - * {@code callback} will be completed with an + * If the call fails to start, {@link IAppSearchBatchResultCallback#onSystemError} + * will be called with the cause throwable. Otherwise, + * {@link IAppSearchBatchResultCallback#onResult} will be called with an * {@link AppSearchBatchResult}<{@link String}, {@link Void}> * where the keys are document URIs. If a document doesn't exist, it will be reported as a * failure where the {@code throwable} is {@code null}. @@ -109,7 +111,7 @@ interface IAppSearchManager { in String databaseName, in String namespace, in List<String> uris, - in AndroidFuture<AppSearchBatchResult> callback); + in IAppSearchBatchResultCallback callback); /** * Removes documents by given query. @@ -124,4 +126,12 @@ interface IAppSearchManager { in String queryExpression, in Bundle searchSpecBundle, in AndroidFuture<AppSearchResult> callback); + + /** + * Creates and initializes AppSearchImpl for the calling app. + * + * @param callback {@link IAppSearchResultCallback#onResult} will be called with an + * {@link AppSearchResult}<{@link Void}>. + */ + void initialize(in IAppSearchResultCallback callback); } diff --git a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchResultCallback.aidl b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchResultCallback.aidl new file mode 100644 index 000000000000..27729a5ad058 --- /dev/null +++ b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchResultCallback.aidl @@ -0,0 +1,24 @@ +/** + * Copyright 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.app.appsearch; + +import android.app.appsearch.AppSearchResult; +import android.os.ParcelableException; + +/** {@hide} */ +oneway interface IAppSearchResultCallback { + void onResult(in AppSearchResult result); +}
\ No newline at end of file diff --git a/apex/appsearch/framework/java/android/app/appsearch/PutDocumentsRequest.java b/apex/appsearch/framework/java/android/app/appsearch/PutDocumentsRequest.java index 1f90bc184f6f..42f1ff2a716a 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/PutDocumentsRequest.java +++ b/apex/appsearch/framework/java/android/app/appsearch/PutDocumentsRequest.java @@ -16,10 +16,10 @@ package android.app.appsearch; -import android.annotation.SuppressLint; - import android.annotation.NonNull; +import android.annotation.SuppressLint; import android.app.appsearch.exceptions.AppSearchException; + import com.android.internal.util.Preconditions; import java.util.ArrayList; @@ -29,9 +29,9 @@ import java.util.Collections; import java.util.List; /** - * Encapsulates a request to index a document into an {@link AppSearchManager} database. + * Encapsulates a request to index a document into an {@link AppSearchSession} database. * - * @see AppSearchManager#putDocuments + * @see AppSearchSession#putDocuments * @hide */ public final class PutDocumentsRequest { @@ -53,7 +53,7 @@ public final class PutDocumentsRequest { private boolean mBuilt = false; /** Adds one or more documents to the request. */ - @SuppressLint("MissingGetterMatchingBuilder") // Merged list available from getDocuments() + @SuppressLint("MissingGetterMatchingBuilder") // Merged list available from getDocuments() @NonNull public Builder addGenericDocument(@NonNull GenericDocument... documents) { Preconditions.checkNotNull(documents); @@ -61,7 +61,7 @@ public final class PutDocumentsRequest { } /** Adds one or more documents to the request. */ - @SuppressLint("MissingGetterMatchingBuilder") // Merged list available from getDocuments() + @SuppressLint("MissingGetterMatchingBuilder") // Merged list available from getDocuments() @NonNull public Builder addGenericDocument(@NonNull Collection<GenericDocument> documents) { Preconditions.checkState(!mBuilt, "Builder has already been used"); diff --git a/apex/appsearch/framework/java/android/app/appsearch/RemoveByUriRequest.java b/apex/appsearch/framework/java/android/app/appsearch/RemoveByUriRequest.java index 486857fba1de..3d83c390fbad 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/RemoveByUriRequest.java +++ b/apex/appsearch/framework/java/android/app/appsearch/RemoveByUriRequest.java @@ -18,6 +18,7 @@ package android.app.appsearch; import android.annotation.NonNull; import android.util.ArraySet; + import com.android.internal.util.Preconditions; import java.util.Arrays; @@ -28,7 +29,7 @@ import java.util.Set; /** * Encapsulates a request to remove documents by namespace and URI. * - * @see AppSearchManager#removeByUri + * @see AppSearchSession#removeByUri * @hide */ public final class RemoveByUriRequest { diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchResult.java b/apex/appsearch/framework/java/android/app/appsearch/SearchResult.java index 99cb2f16ca4d..5ffa7c94087c 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/SearchResult.java +++ b/apex/appsearch/framework/java/android/app/appsearch/SearchResult.java @@ -16,60 +16,60 @@ package android.app.appsearch; -import android.os.Bundle; - import android.annotation.NonNull; import android.annotation.Nullable; +import android.os.Bundle; -import java.util.Objects; import com.android.internal.util.Preconditions; import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** - * This class represents one of the results obtained from the query. + * This class represents one of the results obtained from an AppSearch query. + * + * <p>This allows clients to obtain: * - * <p>It contains the document which matched, information about which section(s) in the document - * matched, and snippet information containing textual summaries of the document's match(es). - * @hide + * <ul> + * <li>The document which matched, using {@link #getDocument} + * <li>Information about which properties in the document matched, and "snippet" information + * containing textual summaries of the document's matches, using {@link #getMatches} + * </ul> + * + * <p>"Snippet" refers to a substring of text from the content of document that is returned as a + * part of search result. + * + * @see SearchResults */ public final class SearchResult { /** @hide */ - public static final String DOCUMENT_FIELD = "document"; /** @hide */ - public static final String MATCHES_FIELD = "matches"; - @NonNull - private final Bundle mBundle; + @NonNull private final Bundle mBundle; - @NonNull - private final Bundle mDocumentBundle; + @NonNull private final Bundle mDocumentBundle; /** Cache of the inflated document. Comes from inflating mDocumentBundle at first use. */ - @Nullable - private GenericDocument mDocument; + @Nullable private GenericDocument mDocument; /** * Contains a list of MatchInfo bundles that matched the request. * - * Only populated when requested in both {@link SearchSpec.Builder#setSnippetCount} and + * <p>Only populated when requested in both {@link SearchSpec.Builder#setSnippetCount} and * {@link SearchSpec.Builder#setSnippetCountPerProperty}. * * @see #getMatches() */ - @NonNull - private final List<Bundle> mMatchBundles; + @NonNull private final List<Bundle> mMatchBundles; /** Cache of the inflated matches. Comes from inflating mMatchBundles at first use. */ - @Nullable - private List<MatchInfo> mMatches; + @Nullable private List<MatchInfo> mMatches; /** @hide */ - public SearchResult(@NonNull Bundle bundle) { mBundle = Preconditions.checkNotNull(bundle); mDocumentBundle = Preconditions.checkNotNull(bundle.getBundle(DOCUMENT_FIELD)); @@ -77,7 +77,6 @@ public final class SearchResult { } /** @hide */ - @NonNull public Bundle getBundle() { return mBundle; @@ -85,6 +84,7 @@ public final class SearchResult { /** * Contains the matching {@link GenericDocument}. + * * @return Document object which matched the query. */ @NonNull @@ -98,10 +98,10 @@ public final class SearchResult { /** * Contains a list of Snippets that matched the request. * - * @return List of matches based on {@link SearchSpec}. If snippeting is disabled using - * {@link SearchSpec.Builder#setSnippetCount} or - * {@link SearchSpec.Builder#setSnippetCountPerProperty}, for all results after that - * value, this method returns an empty list. + * @return List of matches based on {@link SearchSpec}. If snippeting is disabled using {@link + * SearchSpec.Builder#setSnippetCount} or {@link + * SearchSpec.Builder#setSnippetCountPerProperty}, for all results after that value, this + * method returns an empty list. */ @NonNull public List<MatchInfo> getMatches() { @@ -116,79 +116,94 @@ public final class SearchResult { } /** - * Snippet: It refers to a substring of text from the content of document that is returned as a - * part of search result. - * This class represents a match objects for any Snippets that might be present in - * {@link SearchResults} from query. Using this class - * user can get the full text, exact matches and Snippets of document content for a given match. - * - * <p>Class Example 1: - * A document contains following text in property subject: + * This class represents a match objects for any Snippets that might be present in {@link + * SearchResults} from query. Using this class user can get the full text, exact matches and + * Snippets of document content for a given match. + * + * <p>Class Example 1: A document contains following text in property subject: + * * <p>A commonly used fake word is foo. Another nonsense word that’s used a lot is bar. * * <p>If the queryExpression is "foo". * * <p>{@link MatchInfo#getPropertyPath()} returns "subject" + * * <p>{@link MatchInfo#getFullText()} returns "A commonly used fake word is foo. Another * nonsense word that’s used a lot is bar." + * * <p>{@link MatchInfo#getExactMatchPosition()} returns [29, 32] + * * <p>{@link MatchInfo#getExactMatch()} returns "foo" + * * <p>{@link MatchInfo#getSnippetPosition()} returns [26, 33] + * * <p>{@link MatchInfo#getSnippet()} returns "is foo." + * * <p> - * <p>Class Example 2: - * A document contains a property name sender which contains 2 property names name and email, so - * we will have 2 property paths: {@code sender.name} and {@code sender.email}. - * <p>Let {@code sender.name = "Test Name Jr."} and - * {@code sender.email = "TestNameJr@gmail.com"} + * + * <p>Class Example 2: A document contains a property name sender which contains 2 property + * names name and email, so we will have 2 property paths: {@code sender.name} and {@code + * sender.email}. + * + * <p>Let {@code sender.name = "Test Name Jr."} and {@code sender.email = + * "TestNameJr@gmail.com"} * * <p>If the queryExpression is "Test". We will have 2 matches. * - * <p> Match-1 + * <p>Match-1 + * * <p>{@link MatchInfo#getPropertyPath()} returns "sender.name" + * * <p>{@link MatchInfo#getFullText()} returns "Test Name Jr." + * * <p>{@link MatchInfo#getExactMatchPosition()} returns [0, 4] + * * <p>{@link MatchInfo#getExactMatch()} returns "Test" + * * <p>{@link MatchInfo#getSnippetPosition()} returns [0, 9] + * * <p>{@link MatchInfo#getSnippet()} returns "Test Name" - * <p> Match-2 + * + * <p>Match-2 + * * <p>{@link MatchInfo#getPropertyPath()} returns "sender.email" + * * <p>{@link MatchInfo#getFullText()} returns "TestNameJr@gmail.com" + * * <p>{@link MatchInfo#getExactMatchPosition()} returns [0, 20] + * * <p>{@link MatchInfo#getExactMatch()} returns "TestNameJr@gmail.com" + * * <p>{@link MatchInfo#getSnippetPosition()} returns [0, 20] + * * <p>{@link MatchInfo#getSnippet()} returns "TestNameJr@gmail.com" */ public static final class MatchInfo { /** * The path of the matching snippet property. + * * @hide */ - public static final String PROPERTY_PATH_FIELD = "propertyPath"; /** * The index of matching value in its property. A property may have multiple values. This * index indicates which value is the match. + * * @hide */ - public static final String VALUES_INDEX_FIELD = "valuesIndex"; /** @hide */ - public static final String EXACT_MATCH_POSITION_LOWER_FIELD = "exactMatchPositionLower"; /** @hide */ - public static final String EXACT_MATCH_POSITION_UPPER_FIELD = "exactMatchPositionUpper"; /** @hide */ - public static final String WINDOW_POSITION_LOWER_FIELD = "windowPositionLower"; /** @hide */ - public static final String WINDOW_POSITION_UPPER_FIELD = "windowPositionUpper"; private final String mFullText; @@ -201,16 +216,18 @@ public final class SearchResult { mBundle = Preconditions.checkNotNull(bundle); Preconditions.checkNotNull(document); mPropertyPath = Preconditions.checkNotNull(bundle.getString(PROPERTY_PATH_FIELD)); - mFullText = getPropertyValues( - document, mPropertyPath, mBundle.getInt(VALUES_INDEX_FIELD)); + mFullText = + getPropertyValues(document, mPropertyPath, mBundle.getInt(VALUES_INDEX_FIELD)); } /** * Gets the property path corresponding to the given entry. + * * <p>Property Path: '.' - delimited sequence of property names indicating which property in * the Document these snippets correspond to. - * <p>Example properties: 'body', 'sender.name', 'sender.emailaddress', etc. - * For class example 1 this returns "subject" + * + * <p>Example properties: 'body', 'sender.name', 'sender.emailaddress', etc. For class + * example 1 this returns "subject" */ @NonNull public String getPropertyPath() { @@ -219,6 +236,7 @@ public final class SearchResult { /** * Gets the full text corresponding to the given entry. + * * <p>For class example this returns "A commonly used fake word is foo. Another nonsense * word that's used a lot is bar." */ @@ -229,20 +247,23 @@ public final class SearchResult { /** * Gets the exact {@link MatchRange} corresponding to the given entry. + * * <p>For class example 1 this returns [29, 32] */ @NonNull public MatchRange getExactMatchPosition() { if (mExactMatchRange == null) { - mExactMatchRange = new MatchRange( - mBundle.getInt(EXACT_MATCH_POSITION_LOWER_FIELD), - mBundle.getInt(EXACT_MATCH_POSITION_UPPER_FIELD)); + mExactMatchRange = + new MatchRange( + mBundle.getInt(EXACT_MATCH_POSITION_LOWER_FIELD), + mBundle.getInt(EXACT_MATCH_POSITION_UPPER_FIELD)); } return mExactMatchRange; } /** - * Gets the {@link MatchRange} corresponding to the given entry. + * Gets the {@link MatchRange} corresponding to the given entry. + * * <p>For class example 1 this returns "foo" */ @NonNull @@ -252,26 +273,31 @@ public final class SearchResult { /** * Gets the snippet {@link MatchRange} corresponding to the given entry. - * <p>Only populated when set maxSnippetSize > 0 in - * {@link SearchSpec.Builder#setMaxSnippetSize}. + * + * <p>Only populated when set maxSnippetSize > 0 in {@link + * SearchSpec.Builder#setMaxSnippetSize}. + * * <p>For class example 1 this returns [29, 41]. */ @NonNull public MatchRange getSnippetPosition() { if (mWindowRange == null) { - mWindowRange = new MatchRange( - mBundle.getInt(WINDOW_POSITION_LOWER_FIELD), - mBundle.getInt(WINDOW_POSITION_UPPER_FIELD)); + mWindowRange = + new MatchRange( + mBundle.getInt(WINDOW_POSITION_LOWER_FIELD), + mBundle.getInt(WINDOW_POSITION_UPPER_FIELD)); } return mWindowRange; } /** * Gets the snippet corresponding to the given entry. + * * <p>Snippet - Provides a subset of the content to display. Only populated when requested - * maxSnippetSize > 0. The size of this content can be changed by - * {@link SearchSpec.Builder#setMaxSnippetSize}. Windowing is centered around the middle of - * the matched token with content on either side clipped to token boundaries. + * maxSnippetSize > 0. The size of this content can be changed by {@link + * SearchSpec.Builder#setMaxSnippetSize}. Windowing is centered around the middle of the + * matched token with content on either side clipped to token boundaries. + * * <p>For class example 1 this returns "foo. Another" */ @NonNull @@ -302,11 +328,10 @@ public final class SearchResult { /** * Class providing the position range of matching information. * - * <p> All ranges are finite, and the left side of the range is always {@code <=} the right - * side of the range. - * - * <p> Example: MatchRange(0, 100) represent a hundred ints from 0 to 99." + * <p>All ranges are finite, and the left side of the range is always {@code <=} the right side + * of the range. * + * <p>Example: MatchRange(0, 100) represent a hundred ints from 0 to 99." */ public static final class MatchRange { private final int mEnd; @@ -314,18 +339,18 @@ public final class SearchResult { /** * Creates a new immutable range. - * <p> The endpoints are {@code [start, end)}; that is the range is bounded. {@code start} + * + * <p>The endpoints are {@code [start, end)}; that is the range is bounded. {@code start} * must be lesser or equal to {@code end}. * * @param start The start point (inclusive) * @param end The end point (exclusive) * @hide */ - public MatchRange(int start, int end) { if (start > end) { - throw new IllegalArgumentException("Start point must be less than or equal to " - + "end point"); + throw new IllegalArgumentException( + "Start point must be less than or equal to " + "end point"); } mStart = start; mEnd = end; diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchResultPage.java b/apex/appsearch/framework/java/android/app/appsearch/SearchResultPage.java index 756d1b5d673f..dbd09d6bb91b 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/SearchResultPage.java +++ b/apex/appsearch/framework/java/android/app/appsearch/SearchResultPage.java @@ -16,10 +16,9 @@ package android.app.appsearch; -import android.os.Bundle; - import android.annotation.NonNull; import android.annotation.Nullable; +import android.os.Bundle; import com.android.internal.util.Preconditions; @@ -29,19 +28,17 @@ import java.util.List; /** * This class represents a page of {@link SearchResult}s + * * @hide */ - public class SearchResultPage { public static final String RESULTS_FIELD = "results"; public static final String NEXT_PAGE_TOKEN_FIELD = "nextPageToken"; private final long mNextPageToken; - @Nullable - private List<SearchResult> mResults; + @Nullable private List<SearchResult> mResults; - @NonNull - private final Bundle mBundle; + @NonNull private final Bundle mBundle; public SearchResultPage(@NonNull Bundle bundle) { mBundle = Preconditions.checkNotNull(bundle); diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java b/apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java index 15acf103f2e6..68e31f03fb4d 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java +++ b/apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java @@ -16,14 +16,14 @@ package android.app.appsearch; -import android.annotation.SuppressLint; -import android.os.Bundle; - import android.annotation.IntDef; +import android.annotation.IntRange; import android.annotation.NonNull; - +import android.annotation.SuppressLint; import android.app.appsearch.exceptions.AppSearchException; import android.app.appsearch.exceptions.IllegalSearchSpecException; +import android.os.Bundle; + import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; @@ -37,7 +37,6 @@ import java.util.List; /** * This class represents the specification logic for AppSearch. It can be used to set the type of * search, like prefix or exact only or apply filters to search for a specific schema type only etc. - * @hide */ // TODO(sidchhabra) : AddResultSpec fields for Snippets etc. public final class SearchSpec { @@ -52,10 +51,10 @@ public final class SearchSpec { static final String MAX_SNIPPET_FIELD = "maxSnippet"; /** @hide */ - public static final int DEFAULT_NUM_PER_PAGE = 10; - // TODO(b/170371356): In framework, we may want these limits might be flag controlled. + // TODO(b/170371356): In framework, we may want these limits to be flag controlled. + // If that happens, the @IntRange() directives in this class may have to change. private static final int MAX_NUM_PER_PAGE = 10_000; private static final int MAX_SNIPPET_COUNT = 10_000; private static final int MAX_SNIPPET_PER_PROPERTY_COUNT = 10_000; @@ -63,43 +62,45 @@ public final class SearchSpec { /** * Term Match Type for the query. + * * @hide */ // NOTE: The integer values of these constants must match the proto enum constants in // {@link com.google.android.icing.proto.SearchSpecProto.termMatchType} - @IntDef(value = { - TERM_MATCH_EXACT_ONLY, - TERM_MATCH_PREFIX - }) + @IntDef(value = {TERM_MATCH_EXACT_ONLY, TERM_MATCH_PREFIX}) @Retention(RetentionPolicy.SOURCE) public @interface TermMatch {} /** * Query terms will only match exact tokens in the index. + * * <p>Ex. A query term "foo" will only match indexed token "foo", and not "foot" or "football". */ public static final int TERM_MATCH_EXACT_ONLY = 1; /** * Query terms will match indexed tokens when the query term is a prefix of the token. + * * <p>Ex. A query term "foo" will match indexed tokens like "foo", "foot", and "football". */ public static final int TERM_MATCH_PREFIX = 2; /** * Ranking Strategy for query result. + * * @hide */ // NOTE: The integer values of these constants must match the proto enum constants in // {@link ScoringSpecProto.RankingStrategy.Code} - @IntDef(value = { - RANKING_STRATEGY_NONE, - RANKING_STRATEGY_DOCUMENT_SCORE, - RANKING_STRATEGY_CREATION_TIMESTAMP - }) + @IntDef( + value = { + RANKING_STRATEGY_NONE, + RANKING_STRATEGY_DOCUMENT_SCORE, + RANKING_STRATEGY_CREATION_TIMESTAMP + }) @Retention(RetentionPolicy.SOURCE) public @interface RankingStrategy {} - /** No Ranking, results are returned in arbitrary order.*/ + /** No Ranking, results are returned in arbitrary order. */ public static final int RANKING_STRATEGY_NONE = 0; /** Ranked by app-provided document scores. */ public static final int RANKING_STRATEGY_DOCUMENT_SCORE = 1; @@ -108,14 +109,12 @@ public final class SearchSpec { /** * Order for query result. + * * @hide */ // NOTE: The integer values of these constants must match the proto enum constants in // {@link ScoringSpecProto.Order.Code} - @IntDef(value = { - ORDER_DESCENDING, - ORDER_ASCENDING - }) + @IntDef(value = {ORDER_DESCENDING, ORDER_ASCENDING}) @Retention(RetentionPolicy.SOURCE) public @interface Order {} @@ -127,7 +126,6 @@ public final class SearchSpec { private final Bundle mBundle; /** @hide */ - public SearchSpec(@NonNull Bundle bundle) { Preconditions.checkNotNull(bundle); mBundle = bundle; @@ -135,9 +133,9 @@ public final class SearchSpec { /** * Returns the {@link Bundle} populated by this builder. + * * @hide */ - @NonNull public Bundle getBundle() { return mBundle; @@ -154,12 +152,12 @@ public final class SearchSpec { * <p>If empty, the query will search over all schema types. */ @NonNull - public List<String> getSchemas() { - List<String> schemas = mBundle.getStringArrayList(SCHEMA_TYPE_FIELD); - if (schemas == null) { + public List<String> getSchemaTypes() { + List<String> schemaTypes = mBundle.getStringArrayList(SCHEMA_TYPE_FIELD); + if (schemaTypes == null) { return Collections.emptyList(); } - return Collections.unmodifiableList(schemas); + return Collections.unmodifiableList(schemaTypes); } /** @@ -176,8 +174,8 @@ public final class SearchSpec { return Collections.unmodifiableList(namespaces); } - /** Returns the number of results per page in the returned object. */ - public int getNumPerPage() { + /** Returns the number of results per page in the result set. */ + public int getResultCountPerPage() { return mBundle.getInt(NUM_PER_PAGE_FIELD, DEFAULT_NUM_PER_PAGE); } @@ -222,14 +220,12 @@ public final class SearchSpec { mBundle.putInt(NUM_PER_PAGE_FIELD, DEFAULT_NUM_PER_PAGE); } - /** - * Indicates how the query terms should match {@code TermMatchCode} in the index. - */ + /** Indicates how the query terms should match {@code TermMatchCode} in the index. */ @NonNull public Builder setTermMatch(@TermMatch int termMatchTypeCode) { Preconditions.checkState(!mBuilt, "Builder has already been used"); - Preconditions.checkArgumentInRange(termMatchTypeCode, TERM_MATCH_EXACT_ONLY, - TERM_MATCH_PREFIX, "Term match type"); + Preconditions.checkArgumentInRange( + termMatchTypeCode, TERM_MATCH_EXACT_ONLY, TERM_MATCH_PREFIX, "Term match type"); mBundle.putInt(TERM_MATCH_TYPE_FIELD, termMatchTypeCode); return this; } @@ -241,10 +237,10 @@ public final class SearchSpec { * <p>If unset, the query will search over all schema types. */ @NonNull - public Builder addSchema(@NonNull String... schemaTypes) { + public Builder addSchemaType(@NonNull String... schemaTypes) { Preconditions.checkNotNull(schemaTypes); Preconditions.checkState(!mBuilt, "Builder has already been used"); - return addSchema(Arrays.asList(schemaTypes)); + return addSchemaType(Arrays.asList(schemaTypes)); } /** @@ -254,7 +250,7 @@ public final class SearchSpec { * <p>If unset, the query will search over all schema types. */ @NonNull - public Builder addSchema(@NonNull Collection<String> schemaTypes) { + public Builder addSchemaType(@NonNull Collection<String> schemaTypes) { Preconditions.checkNotNull(schemaTypes); Preconditions.checkState(!mBuilt, "Builder has already been used"); mSchemaTypes.addAll(schemaTypes); @@ -262,8 +258,9 @@ public final class SearchSpec { } /** - * Adds a namespace filter to {@link SearchSpec} Entry. Only search for documents that - * have the specified namespaces. + * Adds a namespace filter to {@link SearchSpec} Entry. Only search for documents that have + * the specified namespaces. + * * <p>If unset, the query will search over all namespaces. */ @NonNull @@ -274,8 +271,9 @@ public final class SearchSpec { } /** - * Adds a namespace filter to {@link SearchSpec} Entry. Only search for documents that - * have the specified namespaces. + * Adds a namespace filter to {@link SearchSpec} Entry. Only search for documents that have + * the specified namespaces. + * * <p>If unset, the query will search over all namespaces. */ @NonNull @@ -288,51 +286,56 @@ public final class SearchSpec { /** * Sets the number of results per page in the returned object. - * <p> The default number of results per page is 10. And should be set in range [0, 10k]. + * + * <p>The default number of results per page is 10. */ @NonNull - public SearchSpec.Builder setNumPerPage(int numPerPage) { + public SearchSpec.Builder setResultCountPerPage( + @IntRange(from = 0, to = MAX_NUM_PER_PAGE) int numPerPage) { Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkArgumentInRange(numPerPage, 0, MAX_NUM_PER_PAGE, "NumPerPage"); mBundle.putInt(NUM_PER_PAGE_FIELD, numPerPage); return this; } - /** Sets ranking strategy for AppSearch results.*/ + /** Sets ranking strategy for AppSearch results. */ @NonNull public Builder setRankingStrategy(@RankingStrategy int rankingStrategy) { Preconditions.checkState(!mBuilt, "Builder has already been used"); - Preconditions.checkArgumentInRange(rankingStrategy, RANKING_STRATEGY_NONE, - RANKING_STRATEGY_CREATION_TIMESTAMP, "Result ranking strategy"); + Preconditions.checkArgumentInRange( + rankingStrategy, + RANKING_STRATEGY_NONE, + RANKING_STRATEGY_CREATION_TIMESTAMP, + "Result ranking strategy"); mBundle.putInt(RANKING_STRATEGY_FIELD, rankingStrategy); return this; } /** - * Indicates the order of returned search results, the default is DESC, meaning that results - * with higher scores come first. + * Indicates the order of returned search results, the default is {@link #ORDER_DESCENDING}, + * meaning that results with higher scores come first. + * * <p>This order field will be ignored if RankingStrategy = {@code RANKING_STRATEGY_NONE}. */ @NonNull public Builder setOrder(@Order int order) { Preconditions.checkState(!mBuilt, "Builder has already been used"); - Preconditions.checkArgumentInRange(order, ORDER_DESCENDING, ORDER_ASCENDING, - "Result ranking order"); + Preconditions.checkArgumentInRange( + order, ORDER_DESCENDING, ORDER_ASCENDING, "Result ranking order"); mBundle.putInt(ORDER_FIELD, order); return this; } /** - * Only the first {@code snippetCount} documents based on the ranking strategy - * will have snippet information provided. + * Only the first {@code snippetCount} documents based on the ranking strategy will have + * snippet information provided. * * <p>If set to 0 (default), snippeting is disabled and {@link SearchResult#getMatches} will * return {@code null} for that result. - * - * <p>The value should be set in range[0, 10k]. */ @NonNull - public SearchSpec.Builder setSnippetCount(int snippetCount) { + public SearchSpec.Builder setSnippetCount( + @IntRange(from = 0, to = MAX_SNIPPET_COUNT) int snippetCount) { Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkArgumentInRange(snippetCount, 0, MAX_SNIPPET_COUNT, "snippetCount"); mBundle.putInt(SNIPPET_COUNT_FIELD, snippetCount); @@ -341,39 +344,40 @@ public final class SearchSpec { /** * Sets {@code snippetCountPerProperty}. Only the first {@code snippetCountPerProperty} - * snippets for a every property of {@link GenericDocument} will contain snippet - * information. + * snippets for each property of {@link GenericDocument} will contain snippet information. * - * <p>If set to 0, snippeting is disabled and {@link SearchResult#getMatches} - * will return {@code null} for that result. - * - * <p>The value should be set in range[0, 10k]. + * <p>If set to 0, snippeting is disabled and {@link SearchResult#getMatches} will return + * {@code null} for that result. */ @NonNull - public SearchSpec.Builder setSnippetCountPerProperty(int snippetCountPerProperty) { + public SearchSpec.Builder setSnippetCountPerProperty( + @IntRange(from = 0, to = MAX_SNIPPET_PER_PROPERTY_COUNT) + int snippetCountPerProperty) { Preconditions.checkState(!mBuilt, "Builder has already been used"); - Preconditions.checkArgumentInRange(snippetCountPerProperty, - 0, MAX_SNIPPET_PER_PROPERTY_COUNT, "snippetCountPerProperty"); + Preconditions.checkArgumentInRange( + snippetCountPerProperty, + 0, + MAX_SNIPPET_PER_PROPERTY_COUNT, + "snippetCountPerProperty"); mBundle.putInt(SNIPPET_COUNT_PER_PROPERTY_FIELD, snippetCountPerProperty); return this; } /** - * Sets {@code maxSnippetSize}, the maximum snippet size. Snippet windows start at - * {@code maxSnippetSize/2} bytes before the middle of the matching token and end at - * {@code maxSnippetSize/2} bytes after the middle of the matching token. It respects - * token boundaries, therefore the returned window may be smaller than requested. + * Sets {@code maxSnippetSize}, the maximum snippet size. Snippet windows start at {@code + * maxSnippetSize/2} bytes before the middle of the matching token and end at {@code + * maxSnippetSize/2} bytes after the middle of the matching token. It respects token + * boundaries, therefore the returned window may be smaller than requested. * - * <p> Setting {@code maxSnippetSize} to 0 will disable windowing and an empty string will - * be returned. If matches enabled is also set to false, then snippeting is disabled. + * <p>Setting {@code maxSnippetSize} to 0 will disable windowing and an empty string will be + * returned. If matches enabled is also set to false, then snippeting is disabled. * * <p>Ex. {@code maxSnippetSize} = 16. "foo bar baz bat rat" with a query of "baz" will * return a window of "bar baz bat" which is only 11 bytes long. - * - * <p>The value should be in range[0, 10k]. */ @NonNull - public SearchSpec.Builder setMaxSnippetSize(int maxSnippetSize) { + public SearchSpec.Builder setMaxSnippetSize( + @IntRange(from = 0, to = MAX_SNIPPET_SIZE_LIMIT) int maxSnippetSize) { Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkArgumentInRange( maxSnippetSize, 0, MAX_SNIPPET_SIZE_LIMIT, "maxSnippetSize"); diff --git a/apex/appsearch/framework/java/android/app/appsearch/SetSchemaRequest.java b/apex/appsearch/framework/java/android/app/appsearch/SetSchemaRequest.java index f2c81564908c..3e472fd01939 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/SetSchemaRequest.java +++ b/apex/appsearch/framework/java/android/app/appsearch/SetSchemaRequest.java @@ -16,11 +16,11 @@ package android.app.appsearch; -import android.annotation.SuppressLint; - import android.annotation.NonNull; +import android.annotation.SuppressLint; import android.app.appsearch.exceptions.AppSearchException; import android.util.ArraySet; + import com.android.internal.util.Preconditions; import java.util.ArrayList; @@ -30,9 +30,9 @@ import java.util.List; import java.util.Set; /** - * Encapsulates a request to update the schema of an {@link AppSearchManager} database. + * Encapsulates a request to update the schema of an {@link AppSearchSession} database. * - * @see AppSearchManager#setSchema + * @see AppSearchSession#setSchema * @hide */ public final class SetSchemaRequest { @@ -81,10 +81,10 @@ public final class SetSchemaRequest { * Configures the {@link SetSchemaRequest} to delete any existing documents that don't * follow the new schema. * - * <p>By default, this is {@code false} and schema incompatibility causes the - * {@link AppSearchManager#setSchema} call to fail. + * <p>By default, this is {@code false} and schema incompatibility causes the {@link + * AppSearchSession#setSchema} call to fail. * - * @see AppSearchManager#setSchema + * @see AppSearchSession#setSchema */ @NonNull public Builder setForceOverride(boolean forceOverride) { diff --git a/apex/appsearch/framework/java/android/app/appsearch/exceptions/AppSearchException.java b/apex/appsearch/framework/java/android/app/appsearch/exceptions/AppSearchException.java index 15d0992cd081..704f180bffc4 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/exceptions/AppSearchException.java +++ b/apex/appsearch/framework/java/android/app/appsearch/exceptions/AppSearchException.java @@ -21,18 +21,19 @@ import android.annotation.Nullable; import android.app.appsearch.AppSearchResult; /** - * An exception thrown by {@code android.app.appsearch.AppSearchManager} or a subcomponent. + * An exception thrown by {@link android.app.appsearch.AppSearchSession} or a subcomponent. + * + * <p>These exceptions can be converted into a failed {@link AppSearchResult} for propagating to the + * client. * - * <p>These exceptions can be converted into a failed {@link AppSearchResult} - * for propagating to the client. * @hide */ -//TODO(b/157082794): Linkify to AppSearchManager once that API is public public class AppSearchException extends Exception { private final @AppSearchResult.ResultCode int mResultCode; /** * Initializes an {@link AppSearchException} with no message. + * * @hide */ public AppSearchException(@AppSearchResult.ResultCode int resultCode) { @@ -59,9 +60,7 @@ public class AppSearchException extends Exception { return mResultCode; } - /** - * Converts this {@link java.lang.Exception} into a failed {@link AppSearchResult} - */ + /** Converts this {@link java.lang.Exception} into a failed {@link AppSearchResult} */ @NonNull public <T> AppSearchResult<T> toAppSearchResult() { return AppSearchResult.newFailedResult(mResultCode, getMessage()); diff --git a/apex/appsearch/framework/java/android/app/appsearch/exceptions/IllegalSchemaException.java b/apex/appsearch/framework/java/android/app/appsearch/exceptions/IllegalSchemaException.java index 6dd86f5e6de1..5f8da7fe067a 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/exceptions/IllegalSchemaException.java +++ b/apex/appsearch/framework/java/android/app/appsearch/exceptions/IllegalSchemaException.java @@ -18,14 +18,12 @@ package android.app.appsearch.exceptions; import android.annotation.NonNull; - /** * Indicates that a {@link android.app.appsearch.AppSearchSchema} has logical inconsistencies such * as unpopulated mandatory fields or illegal combinations of parameters. * * @hide */ - public class IllegalSchemaException extends IllegalArgumentException { /** * Constructs a new {@link IllegalSchemaException}. diff --git a/apex/appsearch/framework/java/android/app/appsearch/exceptions/IllegalSearchSpecException.java b/apex/appsearch/framework/java/android/app/appsearch/exceptions/IllegalSearchSpecException.java index 3ef887f09eab..0b5dc2ece6f1 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/exceptions/IllegalSearchSpecException.java +++ b/apex/appsearch/framework/java/android/app/appsearch/exceptions/IllegalSearchSpecException.java @@ -18,14 +18,12 @@ package android.app.appsearch.exceptions; import android.annotation.NonNull; - /** - * Indicates that a {@link android.app.appsearch.SearchResult} has logical inconsistencies such - * as unpopulated mandatory fields or illegal combinations of parameters. + * Indicates that a {@link android.app.appsearch.SearchResult} has logical inconsistencies such as + * unpopulated mandatory fields or illegal combinations of parameters. * * @hide */ - public class IllegalSearchSpecException extends IllegalArgumentException { /** * Constructs a new {@link IllegalSearchSpecException}. diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java index 1dfde528e69b..8269799d5a0d 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java @@ -15,27 +15,32 @@ */ package com.android.server.appsearch; +import static android.app.appsearch.AppSearchResult.throwableToFailedResult; + import android.annotation.NonNull; import android.app.appsearch.AppSearchBatchResult; import android.app.appsearch.AppSearchResult; import android.app.appsearch.AppSearchSchema; import android.app.appsearch.GenericDocument; +import android.app.appsearch.IAppSearchBatchResultCallback; import android.app.appsearch.IAppSearchManager; +import android.app.appsearch.IAppSearchResultCallback; import android.app.appsearch.SearchResultPage; import android.app.appsearch.SearchSpec; -import android.app.appsearch.exceptions.AppSearchException; import android.content.Context; import android.os.Binder; import android.os.Bundle; +import android.os.ParcelableException; +import android.os.RemoteException; import android.os.UserHandle; import android.util.ArraySet; +import android.util.Log; import com.android.internal.infra.AndroidFuture; import com.android.internal.util.Preconditions; import com.android.server.SystemService; import com.android.server.appsearch.external.localstorage.AppSearchImpl; -import java.io.IOException; import java.util.List; import java.util.Set; @@ -61,10 +66,9 @@ public class AppSearchManagerService extends SystemService { @NonNull String databaseName, @NonNull List<Bundle> schemaBundles, boolean forceOverride, - @NonNull AndroidFuture<AppSearchResult> callback) { + @NonNull IAppSearchResultCallback callback) { Preconditions.checkNotNull(databaseName); Preconditions.checkNotNull(schemaBundles); - Preconditions.checkNotNull(callback); int callingUid = Binder.getCallingUidOrThrow(); int callingUserId = UserHandle.getUserId(callingUid); final long callingIdentity = Binder.clearCallingIdentity(); @@ -76,9 +80,10 @@ public class AppSearchManagerService extends SystemService { AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); databaseName = rewriteDatabaseNameWithUid(databaseName, callingUid); impl.setSchema(databaseName, schemas, forceOverride); - callback.complete(AppSearchResult.newSuccessfulResult(/*result=*/ null)); + invokeCallbackOnResult(callback, + AppSearchResult.newSuccessfulResult(/*result=*/ null)); } catch (Throwable t) { - callback.complete(throwableToFailedResult(t)); + invokeCallbackOnError(callback, t); } finally { Binder.restoreCallingIdentity(callingIdentity); } @@ -88,7 +93,7 @@ public class AppSearchManagerService extends SystemService { public void putDocuments( @NonNull String databaseName, @NonNull List<Bundle> documentBundles, - @NonNull AndroidFuture<AppSearchBatchResult> callback) { + @NonNull IAppSearchBatchResultCallback callback) { Preconditions.checkNotNull(databaseName); Preconditions.checkNotNull(documentBundles); Preconditions.checkNotNull(callback); @@ -96,22 +101,24 @@ public class AppSearchManagerService extends SystemService { int callingUserId = UserHandle.getUserId(callingUid); final long callingIdentity = Binder.clearCallingIdentity(); try { - AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); - databaseName = rewriteDatabaseNameWithUid(databaseName, callingUid); AppSearchBatchResult.Builder<String, Void> resultBuilder = new AppSearchBatchResult.Builder<>(); + AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); + databaseName = rewriteDatabaseNameWithUid(databaseName, callingUid); for (int i = 0; i < documentBundles.size(); i++) { GenericDocument document = new GenericDocument(documentBundles.get(i)); try { + // TODO(b/173451571): reduce burden of binder thread by enqueue request onto + // a separate thread. impl.putDocument(databaseName, document); resultBuilder.setSuccess(document.getUri(), /*result=*/ null); } catch (Throwable t) { resultBuilder.setResult(document.getUri(), throwableToFailedResult(t)); } } - callback.complete(resultBuilder.build()); + invokeCallbackOnResult(callback, resultBuilder.build()); } catch (Throwable t) { - callback.completeExceptionally(t); + invokeCallbackOnError(callback, t); } finally { Binder.restoreCallingIdentity(callingIdentity); } @@ -119,7 +126,8 @@ public class AppSearchManagerService extends SystemService { @Override public void getDocuments(@NonNull String databaseName, @NonNull String namespace, - @NonNull List<String> uris, @NonNull AndroidFuture<AppSearchBatchResult> callback) { + @NonNull List<String> uris, + @NonNull IAppSearchBatchResultCallback callback) { Preconditions.checkNotNull(databaseName); Preconditions.checkNotNull(namespace); Preconditions.checkNotNull(uris); @@ -128,10 +136,10 @@ public class AppSearchManagerService extends SystemService { int callingUserId = UserHandle.getUserId(callingUid); final long callingIdentity = Binder.clearCallingIdentity(); try { - AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); - databaseName = rewriteDatabaseNameWithUid(databaseName, callingUid); AppSearchBatchResult.Builder<String, Bundle> resultBuilder = new AppSearchBatchResult.Builder<>(); + AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); + databaseName = rewriteDatabaseNameWithUid(databaseName, callingUid); for (int i = 0; i < uris.size(); i++) { String uri = uris.get(i); try { @@ -141,9 +149,9 @@ public class AppSearchManagerService extends SystemService { resultBuilder.setResult(uri, throwableToFailedResult(t)); } } - callback.complete(resultBuilder.build()); + invokeCallbackOnResult(callback, resultBuilder.build()); } catch (Throwable t) { - callback.completeExceptionally(t); + invokeCallbackOnError(callback, t); } finally { Binder.restoreCallingIdentity(callingIdentity); } @@ -182,19 +190,19 @@ public class AppSearchManagerService extends SystemService { @Override public void removeByUri(@NonNull String databaseName, @NonNull String namespace, - List<String> uris, AndroidFuture<AppSearchBatchResult> callback) { + @NonNull List<String> uris, + @NonNull IAppSearchBatchResultCallback callback) { Preconditions.checkNotNull(databaseName); - Preconditions.checkNotNull(namespace); Preconditions.checkNotNull(uris); Preconditions.checkNotNull(callback); int callingUid = Binder.getCallingUidOrThrow(); int callingUserId = UserHandle.getUserId(callingUid); final long callingIdentity = Binder.clearCallingIdentity(); + AppSearchBatchResult.Builder<String, Void> resultBuilder = + new AppSearchBatchResult.Builder<>(); try { AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); databaseName = rewriteDatabaseNameWithUid(databaseName, callingUid); - AppSearchBatchResult.Builder<String, Void> resultBuilder = - new AppSearchBatchResult.Builder<>(); for (int i = 0; i < uris.size(); i++) { String uri = uris.get(i); try { @@ -204,9 +212,9 @@ public class AppSearchManagerService extends SystemService { resultBuilder.setResult(uri, throwableToFailedResult(t)); } } - callback.complete(resultBuilder.build()); + invokeCallbackOnResult(callback, resultBuilder.build()); } catch (Throwable t) { - callback.completeExceptionally(t); + invokeCallbackOnError(callback, t); } finally { Binder.restoreCallingIdentity(callingIdentity); } @@ -237,6 +245,21 @@ public class AppSearchManagerService extends SystemService { } } + @Override + public void initialize(@NonNull IAppSearchResultCallback callback) { + int callingUid = Binder.getCallingUidOrThrow(); + int callingUserId = UserHandle.getUserId(callingUid); + final long callingIdentity = Binder.clearCallingIdentity(); + try { + ImplInstanceManager.getInstance(getContext(), callingUserId); + invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null)); + } catch (Throwable t) { + invokeCallbackOnError(callback, t); + } finally { + Binder.restoreCallingIdentity(callingIdentity); + } + } + /** * Rewrites the database name by adding a prefix of unique name for the given uid. * @@ -256,23 +279,51 @@ public class AppSearchManagerService extends SystemService { return callingUidName + CALLING_NAME_DATABASE_DELIMITER + databaseName; } - private <ValueType> AppSearchResult<ValueType> throwableToFailedResult( - @NonNull Throwable t) { - if (t instanceof AppSearchException) { - return ((AppSearchException) t).toAppSearchResult(); + /** Invokes the {@link IAppSearchResultCallback} with the result. */ + private void invokeCallbackOnResult(IAppSearchResultCallback callback, + AppSearchResult result) { + try { + callback.onResult(result); + } catch (RemoteException e) { + Log.d(TAG, "Unable to send result to the callback", e); } + } - @AppSearchResult.ResultCode int resultCode; - if (t instanceof IllegalStateException) { - resultCode = AppSearchResult.RESULT_INTERNAL_ERROR; - } else if (t instanceof IllegalArgumentException) { - resultCode = AppSearchResult.RESULT_INVALID_ARGUMENT; - } else if (t instanceof IOException) { - resultCode = AppSearchResult.RESULT_IO_ERROR; - } else { - resultCode = AppSearchResult.RESULT_UNKNOWN_ERROR; + /** Invokes the {@link IAppSearchBatchResultCallback} with the result. */ + private void invokeCallbackOnResult(IAppSearchBatchResultCallback callback, + AppSearchBatchResult result) { + try { + callback.onResult(result); + } catch (RemoteException e) { + Log.d(TAG, "Unable to send result to the callback", e); + } + } + + /** + * Invokes the {@link IAppSearchResultCallback} with an throwable. + * + * <p>The throwable is convert to a {@link AppSearchResult}; + */ + private void invokeCallbackOnError(IAppSearchResultCallback callback, Throwable throwable) { + try { + callback.onResult(throwableToFailedResult(throwable)); + } catch (RemoteException e) { + Log.d(TAG, "Unable to send result to the callback", e); + } + } + + /** + * Invokes the {@link IAppSearchBatchResultCallback} with an throwable. + * + * <p>The throwable is converted to {@link ParcelableException}. + */ + private void invokeCallbackOnError(IAppSearchBatchResultCallback callback, + Throwable throwable) { + try { + callback.onSystemError(new ParcelableException(throwable)); + } catch (RemoteException e) { + Log.d(TAG, "Unable to send error to the callback", e); } - return AppSearchResult.newFailedResult(resultCode, t.getMessage()); } } } diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java index e021544976b5..247089ba258b 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java @@ -16,13 +16,7 @@ package com.android.server.appsearch.external.localstorage; -import android.os.Bundle; -import android.util.Log; - -import com.android.internal.annotations.GuardedBy; import android.annotation.NonNull; - -import com.android.internal.annotations.VisibleForTesting; import android.annotation.WorkerThread; import android.app.appsearch.AppSearchResult; import android.app.appsearch.AppSearchSchema; @@ -30,6 +24,12 @@ import android.app.appsearch.GenericDocument; import android.app.appsearch.SearchResultPage; import android.app.appsearch.SearchSpec; import android.app.appsearch.exceptions.AppSearchException; +import android.os.Bundle; +import android.util.ArraySet; +import android.util.Log; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; import com.android.server.appsearch.external.localstorage.converter.GenericDocumentToProtoConverter; import com.android.server.appsearch.external.localstorage.converter.SchemaToProtoConverter; @@ -62,7 +62,6 @@ import com.google.android.icing.proto.StatusProto; import java.io.File; import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -77,55 +76,65 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; * * <p>A single instance of {@link AppSearchImpl} can support all databases. Schemas and documents * are physically saved together in {@link IcingSearchEngine}, but logically isolated: + * * <ul> - * <li>Rewrite SchemaType in SchemaProto by adding database name prefix and save into - * SchemaTypes set in {@link #setSchema}. - * <li>Rewrite namespace and SchemaType in DocumentProto by adding database name prefix and - * save to namespaces set in {@link #putDocument}. - * <li>Remove database name prefix when retrieve documents in {@link #getDocument} and - * {@link #query}. - * <li>Rewrite filters in {@link SearchSpecProto} to have all namespaces and schema types of - * the queried database when user using empty filters in {@link #query}. + * <li>Rewrite SchemaType in SchemaProto by adding database name prefix and save into SchemaTypes + * set in {@link #setSchema}. + * <li>Rewrite namespace and SchemaType in DocumentProto by adding database name prefix and save + * to namespaces set in {@link #putDocument}. + * <li>Remove database name prefix when retrieve documents in {@link #getDocument} and {@link + * #query}. + * <li>Rewrite filters in {@link SearchSpecProto} to have all namespaces and schema types of the + * queried database when user using empty filters in {@link #query}. * </ul> * * <p>Methods in this class belong to two groups, the query group and the mutate group. + * * <ul> - * <li>All methods are going to modify global parameters and data in Icing are executed under - * WRITE lock to keep thread safety. - * <li>All methods are going to access global parameters or query data from Icing are executed - * under READ lock to improve query performance. + * <li>All methods are going to modify global parameters and data in Icing are executed under + * WRITE lock to keep thread safety. + * <li>All methods are going to access global parameters or query data from Icing are executed + * under READ lock to improve query performance. * </ul> * * <p>This class is thread safe. * * @hide */ - @WorkerThread public final class AppSearchImpl { private static final String TAG = "AppSearchImpl"; - private static final char DATABASE_DELIMITER = '/'; - @VisibleForTesting - static final int OPTIMIZE_THRESHOLD_DOC_COUNT = 1000; - @VisibleForTesting - static final int OPTIMIZE_THRESHOLD_BYTES = 1_000_000; // 1MB - @VisibleForTesting - static final int CHECK_OPTIMIZE_INTERVAL = 100; + @VisibleForTesting static final char DATABASE_DELIMITER = '/'; + + @VisibleForTesting static final int OPTIMIZE_THRESHOLD_DOC_COUNT = 1000; + @VisibleForTesting static final int OPTIMIZE_THRESHOLD_BYTES = 1_000_000; // 1MB + @VisibleForTesting static final int CHECK_OPTIMIZE_INTERVAL = 100; private final ReadWriteLock mReadWriteLock = new ReentrantReadWriteLock(); - private final IcingSearchEngine mIcingSearchEngine; + + @GuardedBy("mReadWriteLock") + private final IcingSearchEngine mIcingSearchEngineLocked; + + @GuardedBy("mReadWriteLock") + private final VisibilityStore mVisibilityStoreLocked; // The map contains schemaTypes and namespaces for all database. All values in the map have - // been already added database name prefix. - private final Map<String, Set<String>> mSchemaMap = new HashMap<>(); - private final Map<String, Set<String>> mNamespaceMap = new HashMap<>(); + // the database name prefix. + // TODO(b/172360376): Check if this can be replaced with an ArrayMap + @GuardedBy("mReadWriteLock") + private final Map<String, Set<String>> mSchemaMapLocked = new HashMap<>(); + + // TODO(b/172360376): Check if this can be replaced with an ArrayMap + @GuardedBy("mReadWriteLock") + private final Map<String, Set<String>> mNamespaceMapLocked = new HashMap<>(); /** - * The counter to check when to call {@link #checkForOptimize(boolean)}. The interval is + * The counter to check when to call {@link #checkForOptimizeLocked(boolean)}. The interval is * {@link #CHECK_OPTIMIZE_INTERVAL}. */ - private int mOptimizeIntervalCount = 0; + @GuardedBy("mReadWriteLock") + private int mOptimizeIntervalCountLocked = 0; /** * Creates and initializes an instance of {@link AppSearchImpl} which writes data to the given @@ -134,44 +143,77 @@ public final class AppSearchImpl { @NonNull public static AppSearchImpl create(@NonNull File icingDir) throws AppSearchException { Preconditions.checkNotNull(icingDir); - return new AppSearchImpl(icingDir); + AppSearchImpl appSearchImpl = new AppSearchImpl(icingDir); + appSearchImpl.initializeVisibilityStore(); + return appSearchImpl; } private AppSearchImpl(@NonNull File icingDir) throws AppSearchException { boolean isReset = false; mReadWriteLock.writeLock().lock(); + try { // We synchronize here because we don't want to call IcingSearchEngine.initialize() more // than once. It's unnecessary and can be a costly operation. - IcingSearchEngineOptions options = IcingSearchEngineOptions.newBuilder() - .setBaseDir(icingDir.getAbsolutePath()).build(); - mIcingSearchEngine = new IcingSearchEngine(options); + IcingSearchEngineOptions options = + IcingSearchEngineOptions.newBuilder() + .setBaseDir(icingDir.getAbsolutePath()) + .build(); + mIcingSearchEngineLocked = new IcingSearchEngine(options); - InitializeResultProto initializeResultProto = mIcingSearchEngine.initialize(); + InitializeResultProto initializeResultProto = mIcingSearchEngineLocked.initialize(); SchemaProto schemaProto = null; GetAllNamespacesResultProto getAllNamespacesResultProto = null; try { checkSuccess(initializeResultProto.getStatus()); - schemaProto = getSchemaProto(); - getAllNamespacesResultProto = mIcingSearchEngine.getAllNamespaces(); + schemaProto = getSchemaProtoLocked(); + getAllNamespacesResultProto = mIcingSearchEngineLocked.getAllNamespaces(); checkSuccess(getAllNamespacesResultProto.getStatus()); } catch (AppSearchException e) { + Log.w(TAG, "Error initializing, resetting IcingSearchEngine.", e); // Some error. Reset and see if it fixes it. reset(); isReset = true; } + + // Populate schema map for (SchemaTypeConfigProto schema : schemaProto.getTypesList()) { String qualifiedSchemaType = schema.getSchemaType(); - addToMap(mSchemaMap, getDatabaseName(qualifiedSchemaType), qualifiedSchemaType); + addToMap( + mSchemaMapLocked, + getDatabaseName(qualifiedSchemaType), + qualifiedSchemaType); } + + // Populate namespace map for (String qualifiedNamespace : getAllNamespacesResultProto.getNamespacesList()) { - addToMap(mNamespaceMap, getDatabaseName(qualifiedNamespace), qualifiedNamespace); + addToMap( + mNamespaceMapLocked, + getDatabaseName(qualifiedNamespace), + qualifiedNamespace); } + // TODO(b/155939114): It's possible to optimize after init, which would reduce the time // to when we're able to serve queries. Consider moving this optimize call out. if (!isReset) { - checkForOptimize(/* force= */ true); + checkForOptimizeLocked(/* force= */ true); } + + mVisibilityStoreLocked = new VisibilityStore(this); + } finally { + mReadWriteLock.writeLock().unlock(); + } + } + + /** + * Initialize the visibility store in AppSearchImpl. + * + * @throws AppSearchException on IcingSearchEngine error. + */ + void initializeVisibilityStore() throws AppSearchException { + mReadWriteLock.writeLock().lock(); + try { + mVisibilityStoreLocked.initialize(); } finally { mReadWriteLock.writeLock().unlock(); } @@ -182,35 +224,36 @@ public final class AppSearchImpl { * * <p>This method belongs to mutate group. * - * @param databaseName The name of the database where this schema lives. - * @param schemas Schemas to set for this app. + * @param databaseName The name of the database where this schema lives. + * @param schemas Schemas to set for this app. * @param forceOverride Whether to force-apply the schema even if it is incompatible. Documents - * which do not comply with the new schema will be deleted. + * which do not comply with the new schema will be deleted. * @throws AppSearchException on IcingSearchEngine error. */ - public void setSchema(@NonNull String databaseName, @NonNull Set<AppSearchSchema> schemas, - boolean forceOverride) throws AppSearchException { - SchemaProto schemaProto = getSchemaProto(); - - SchemaProto.Builder existingSchemaBuilder = schemaProto.toBuilder(); + public void setSchema( + @NonNull String databaseName, + @NonNull Set<AppSearchSchema> schemas, + boolean forceOverride) + throws AppSearchException { + mReadWriteLock.writeLock().lock(); + try { + SchemaProto.Builder existingSchemaBuilder = getSchemaProtoLocked().toBuilder(); - SchemaProto.Builder newSchemaBuilder = SchemaProto.newBuilder(); - for (AppSearchSchema schema : schemas) { - SchemaTypeConfigProto schemaTypeProto = SchemaToProtoConverter.convert(schema); - newSchemaBuilder.addTypes(schemaTypeProto); - } + SchemaProto.Builder newSchemaBuilder = SchemaProto.newBuilder(); + for (AppSearchSchema schema : schemas) { + SchemaTypeConfigProto schemaTypeProto = SchemaToProtoConverter.convert(schema); + newSchemaBuilder.addTypes(schemaTypeProto); + } - // Combine the existing schema (which may have types from other databases) with this - // database's new schema. Modifies the existingSchemaBuilder. - Set<String> newTypeNames = rewriteSchema(databaseName, existingSchemaBuilder, - newSchemaBuilder.build()); + // Combine the existing schema (which may have types from other databases) with this + // database's new schema. Modifies the existingSchemaBuilder. + RewrittenSchemaResults rewrittenSchemaResults = + rewriteSchema(databaseName, existingSchemaBuilder, newSchemaBuilder.build()); - SetSchemaResultProto setSchemaResultProto; - mReadWriteLock.writeLock().lock(); - try { // Apply schema - setSchemaResultProto = - mIcingSearchEngine.setSchema(existingSchemaBuilder.build(), forceOverride); + SetSchemaResultProto setSchemaResultProto = + mIcingSearchEngineLocked.setSchema( + existingSchemaBuilder.build(), forceOverride); // Determine whether it succeeded. try { @@ -219,11 +262,12 @@ public final class AppSearchImpl { // Improve the error message by merging in information about incompatible types. if (setSchemaResultProto.getDeletedSchemaTypesCount() > 0 || setSchemaResultProto.getIncompatibleSchemaTypesCount() > 0) { - String newMessage = e.getMessage() - + "\n Deleted types: " - + setSchemaResultProto.getDeletedSchemaTypesList() - + "\n Incompatible types: " - + setSchemaResultProto.getIncompatibleSchemaTypesList(); + String newMessage = + e.getMessage() + + "\n Deleted types: " + + setSchemaResultProto.getDeletedSchemaTypesList() + + "\n Incompatible types: " + + setSchemaResultProto.getIncompatibleSchemaTypesList(); throw new AppSearchException(e.getResultCode(), newMessage, e.getCause()); } else { throw e; @@ -231,16 +275,18 @@ public final class AppSearchImpl { } // Update derived data structures. - mSchemaMap.put(databaseName, newTypeNames); + mSchemaMapLocked.put(databaseName, rewrittenSchemaResults.mRewrittenQualifiedTypes); + mVisibilityStoreLocked.updateSchemas( + databaseName, rewrittenSchemaResults.mDeletedQualifiedTypes); // Determine whether to schedule an immediate optimize. if (setSchemaResultProto.getDeletedSchemaTypesCount() > 0 || (setSchemaResultProto.getIncompatibleSchemaTypesCount() > 0 - && forceOverride)) { + && forceOverride)) { // Any existing schemas which is not in 'schemas' will be deleted, and all // documents of these types were also deleted. And so well if we force override // incompatible schemas. - checkForOptimize(/* force= */true); + checkForOptimizeLocked(/* force= */ true); } } finally { mReadWriteLock.writeLock().unlock(); @@ -248,28 +294,63 @@ public final class AppSearchImpl { } /** + * Update the visibility settings for this app. + * + * <p>This method belongs to the mutate group + * + * @param databaseName The name of the database where the visibility settings will apply. + * @param schemasHiddenFromPlatformSurfaces Schemas that should be hidden from platform surfaces + * @throws AppSearchException on IcingSearchEngine error + */ + public void setVisibility( + @NonNull String databaseName, @NonNull Set<String> schemasHiddenFromPlatformSurfaces) + throws AppSearchException { + mReadWriteLock.writeLock().lock(); + try { + String databasePrefix = getDatabasePrefix(databaseName); + Set<String> qualifiedSchemasHiddenFromPlatformSurface = + new ArraySet<>(schemasHiddenFromPlatformSurfaces.size()); + for (String schema : schemasHiddenFromPlatformSurfaces) { + Set<String> existingSchemas = mSchemaMapLocked.get(databaseName); + if (existingSchemas == null || !existingSchemas.contains(databasePrefix + schema)) { + throw new AppSearchException( + AppSearchResult.RESULT_NOT_FOUND, + "Unknown schema(s): " + + schemasHiddenFromPlatformSurfaces + + " provided during setVisibility."); + } + qualifiedSchemasHiddenFromPlatformSurface.add(databasePrefix + schema); + } + mVisibilityStoreLocked.setVisibility( + databaseName, qualifiedSchemasHiddenFromPlatformSurface); + } finally { + mReadWriteLock.writeLock().lock(); + } + } + + /** * Adds a document to the AppSearch index. * * <p>This method belongs to mutate group. * * @param databaseName The databaseName this document resides in. - * @param document The document to index. + * @param document The document to index. * @throws AppSearchException on IcingSearchEngine error. */ public void putDocument(@NonNull String databaseName, @NonNull GenericDocument document) throws AppSearchException { - DocumentProto.Builder documentBuilder = GenericDocumentToProtoConverter.convert( - document).toBuilder(); + DocumentProto.Builder documentBuilder = + GenericDocumentToProtoConverter.convert(document).toBuilder(); addPrefixToDocument(documentBuilder, getDatabasePrefix(databaseName)); PutResultProto putResultProto; mReadWriteLock.writeLock().lock(); try { - putResultProto = mIcingSearchEngine.put(documentBuilder.build()); - addToMap(mNamespaceMap, databaseName, documentBuilder.getNamespace()); + putResultProto = mIcingSearchEngineLocked.put(documentBuilder.build()); + addToMap(mNamespaceMapLocked, databaseName, documentBuilder.getNamespace()); // The existing documents with same URI will be deleted, so there maybe some resources // could be released after optimize(). - checkForOptimize(/* force= */false); + checkForOptimizeLocked(/* force= */ false); } finally { mReadWriteLock.writeLock().unlock(); } @@ -282,19 +363,20 @@ public final class AppSearchImpl { * <p>This method belongs to query group. * * @param databaseName The databaseName this document resides in. - * @param namespace The namespace this document resides in. - * @param uri The URI of the document to get. + * @param namespace The namespace this document resides in. + * @param uri The URI of the document to get. * @return The Document contents * @throws AppSearchException on IcingSearchEngine error. */ @NonNull - public GenericDocument getDocument(@NonNull String databaseName, @NonNull String namespace, - @NonNull String uri) throws AppSearchException { + public GenericDocument getDocument( + @NonNull String databaseName, @NonNull String namespace, @NonNull String uri) + throws AppSearchException { GetResultProto getResultProto; mReadWriteLock.readLock().lock(); try { - getResultProto = mIcingSearchEngine.get( - getDatabasePrefix(databaseName) + namespace, uri); + getResultProto = + mIcingSearchEngineLocked.get(getDatabasePrefix(databaseName) + namespace, uri); } finally { mReadWriteLock.readLock().unlock(); } @@ -310,19 +392,25 @@ public final class AppSearchImpl { * * <p>This method belongs to query group. * - * @param databaseName The databaseName this query for. + * @param databaseName The databaseName this query for. * @param queryExpression Query String to search. - * @param searchSpec Spec for setting filters, raw query etc. - * @return The results of performing this search. It may contain an empty list of results if - * no documents matched the query. + * @param searchSpec Spec for setting filters, raw query etc. + * @return The results of performing this search. It may contain an empty list of results if no + * documents matched the query. * @throws AppSearchException on IcingSearchEngine error. */ @NonNull public SearchResultPage query( @NonNull String databaseName, @NonNull String queryExpression, - @NonNull SearchSpec searchSpec) throws AppSearchException { - return doQuery(Collections.singleton(databaseName), queryExpression, searchSpec); + @NonNull SearchSpec searchSpec) + throws AppSearchException { + mReadWriteLock.readLock().lock(); + try { + return doQueryLocked(Collections.singleton(databaseName), queryExpression, searchSpec); + } finally { + mReadWriteLock.readLock().unlock(); + } } /** @@ -332,45 +420,54 @@ public final class AppSearchImpl { * <p>This method belongs to query group. * * @param queryExpression Query String to search. - * @param searchSpec Spec for setting filters, raw query etc. - * @return The results of performing this search. It may contain an empty list of results if - * no documents matched the query. + * @param searchSpec Spec for setting filters, raw query etc. + * @return The results of performing this search. It may contain an empty list of results if no + * documents matched the query. * @throws AppSearchException on IcingSearchEngine error. */ @NonNull public SearchResultPage globalQuery( - @NonNull String queryExpression, - @NonNull SearchSpec searchSpec) throws AppSearchException { - return doQuery(mNamespaceMap.keySet(), queryExpression, searchSpec); + @NonNull String queryExpression, @NonNull SearchSpec searchSpec) + throws AppSearchException { + // TODO(b/169883602): Check if the platform is querying us at a higher level. At this + // point, we should add all platform-surfaceable schemas assuming the querier has been + // verified. + mReadWriteLock.readLock().lock(); + try { + // We use the mNamespaceMap.keySet here because it's the smaller set of valid databases + // that could exist. + return doQueryLocked(mNamespaceMapLocked.keySet(), queryExpression, searchSpec); + } finally { + mReadWriteLock.readLock().unlock(); + } } - private SearchResultPage doQuery( - @NonNull Set<String> databases, @NonNull String queryExpression, + @GuardedBy("mReadWriteLock") + private SearchResultPage doQueryLocked( + @NonNull Set<String> databases, + @NonNull String queryExpression, @NonNull SearchSpec searchSpec) throws AppSearchException { - SearchSpecProto searchSpecProto = - SearchSpecToProtoConverter.toSearchSpecProto(searchSpec); - SearchSpecProto.Builder searchSpecBuilder = searchSpecProto.toBuilder() - .setQuery(queryExpression); + SearchSpecProto searchSpecProto = SearchSpecToProtoConverter.toSearchSpecProto(searchSpec); + SearchSpecProto.Builder searchSpecBuilder = + searchSpecProto.toBuilder().setQuery(queryExpression); ResultSpecProto resultSpec = SearchSpecToProtoConverter.toResultSpecProto(searchSpec); ScoringSpecProto scoringSpec = SearchSpecToProtoConverter.toScoringSpecProto(searchSpec); SearchResultProto searchResultProto; - mReadWriteLock.readLock().lock(); - try { - // rewriteSearchSpecForDatabases will return false if none of the databases have - // documents, so we can return an empty SearchResult and skip sending request to Icing. - // We use the mNamespaceMap.keySet here because it's the smaller set of valid databases - // that could exist. - if (!rewriteSearchSpecForDatabases(searchSpecBuilder, databases)) { - return new SearchResultPage(Bundle.EMPTY); - } - searchResultProto = mIcingSearchEngine.search( - searchSpecBuilder.build(), scoringSpec, resultSpec); - } finally { - mReadWriteLock.readLock().unlock(); + + // rewriteSearchSpecForDatabases will return false if none of the databases that the + // client is trying to search on exist, so we can return an empty SearchResult and skip + // sending request to Icing. + // We use the mNamespaceMap.keySet here because it's the smaller set of valid databases + // that could exist. + if (!rewriteSearchSpecForDatabasesLocked(searchSpecBuilder, databases)) { + return new SearchResultPage(Bundle.EMPTY); } + searchResultProto = + mIcingSearchEngineLocked.search(searchSpecBuilder.build(), scoringSpec, resultSpec); checkSuccess(searchResultProto.getStatus()); + return rewriteSearchResultProto(searchResultProto); } @@ -385,11 +482,16 @@ public final class AppSearchImpl { * @throws AppSearchException on IcingSearchEngine error. */ @NonNull - public SearchResultPage getNextPage(long nextPageToken) - throws AppSearchException { - SearchResultProto searchResultProto = mIcingSearchEngine.getNextPage(nextPageToken); - checkSuccess(searchResultProto.getStatus()); - return rewriteSearchResultProto(searchResultProto); + public SearchResultPage getNextPage(long nextPageToken) throws AppSearchException { + mReadWriteLock.readLock().lock(); + try { + SearchResultProto searchResultProto = + mIcingSearchEngineLocked.getNextPage(nextPageToken); + checkSuccess(searchResultProto.getStatus()); + return rewriteSearchResultProto(searchResultProto); + } finally { + mReadWriteLock.readLock().unlock(); + } } /** @@ -398,10 +500,15 @@ public final class AppSearchImpl { * <p>This method belongs to query group. * * @param nextPageToken The token of pre-loaded results of previously executed query to be - * Invalidated. + * Invalidated. */ public void invalidateNextPageToken(long nextPageToken) { - mIcingSearchEngine.invalidateNextPageToken(nextPageToken); + mReadWriteLock.readLock().lock(); + try { + mIcingSearchEngineLocked.invalidateNextPageToken(nextPageToken); + } finally { + mReadWriteLock.readLock().unlock(); + } } /** @@ -410,18 +517,18 @@ public final class AppSearchImpl { * <p>This method belongs to mutate group. * * @param databaseName The databaseName the document is in. - * @param namespace Namespace of the document to remove. - * @param uri URI of the document to remove. + * @param namespace Namespace of the document to remove. + * @param uri URI of the document to remove. * @throws AppSearchException on IcingSearchEngine error. */ - public void remove(@NonNull String databaseName, @NonNull String namespace, - @NonNull String uri) throws AppSearchException { + public void remove(@NonNull String databaseName, @NonNull String namespace, @NonNull String uri) + throws AppSearchException { String qualifiedNamespace = getDatabasePrefix(databaseName) + namespace; DeleteResultProto deleteResultProto; mReadWriteLock.writeLock().lock(); try { - deleteResultProto = mIcingSearchEngine.delete(qualifiedNamespace, uri); - checkForOptimize(/* force= */false); + deleteResultProto = mIcingSearchEngineLocked.delete(qualifiedNamespace, uri); + checkForOptimizeLocked(/* force= */ false); } finally { mReadWriteLock.writeLock().unlock(); } @@ -433,39 +540,38 @@ public final class AppSearchImpl { * * <p>This method belongs to mutate group. * - * @param databaseName The databaseName the document is in. + * @param databaseName The databaseName the document is in. * @param queryExpression Query String to search. - * @param searchSpec Defines what and how to remove + * @param searchSpec Defines what and how to remove * @throws AppSearchException on IcingSearchEngine error. */ - public void removeByQuery(@NonNull String databaseName, @NonNull String queryExpression, + public void removeByQuery( + @NonNull String databaseName, + @NonNull String queryExpression, @NonNull SearchSpec searchSpec) throws AppSearchException { - - SearchSpecProto searchSpecProto = - SearchSpecToProtoConverter.toSearchSpecProto(searchSpec); - SearchSpecProto.Builder searchSpecBuilder = searchSpecProto.toBuilder() - .setQuery(queryExpression); + SearchSpecProto searchSpecProto = SearchSpecToProtoConverter.toSearchSpecProto(searchSpec); + SearchSpecProto.Builder searchSpecBuilder = + searchSpecProto.toBuilder().setQuery(queryExpression); DeleteResultProto deleteResultProto; mReadWriteLock.writeLock().lock(); try { // Only rewrite SearchSpec for non empty database. // rewriteSearchSpecForNonEmptyDatabase will return false for empty database, we // should skip sending request to Icing and return in here. - if (!rewriteSearchSpecForDatabases(searchSpecBuilder, - Collections.singleton(databaseName))) { + if (!rewriteSearchSpecForDatabasesLocked( + searchSpecBuilder, Collections.singleton(databaseName))) { return; } - deleteResultProto = mIcingSearchEngine.deleteByQuery( - searchSpecBuilder.build()); - checkForOptimize(/* force= */true); + deleteResultProto = mIcingSearchEngineLocked.deleteByQuery(searchSpecBuilder.build()); + checkForOptimizeLocked(/* force= */ true); } finally { mReadWriteLock.writeLock().unlock(); } // It seems that the caller wants to get success if the data matching the query is not in // the DB because it was not there or was successfully deleted. - checkCodeOneOf(deleteResultProto.getStatus(), - StatusProto.Code.OK, StatusProto.Code.NOT_FOUND); + checkCodeOneOf( + deleteResultProto.getStatus(), StatusProto.Code.OK, StatusProto.Code.NOT_FOUND); } /** @@ -475,36 +581,53 @@ public final class AppSearchImpl { * * @throws AppSearchException on IcingSearchEngine error. */ - @VisibleForTesting - public void reset() throws AppSearchException { + private void reset() throws AppSearchException { ResetResultProto resetResultProto; mReadWriteLock.writeLock().lock(); try { - resetResultProto = mIcingSearchEngine.reset(); - mOptimizeIntervalCount = 0; - mSchemaMap.clear(); - mNamespaceMap.clear(); + resetResultProto = mIcingSearchEngineLocked.reset(); + mOptimizeIntervalCountLocked = 0; + mSchemaMapLocked.clear(); + mNamespaceMapLocked.clear(); + + // Must be called after everything else since VisibilityStore may repopulate + // IcingSearchEngine with an initial schema. + mVisibilityStoreLocked.handleReset(); } finally { mReadWriteLock.writeLock().unlock(); } checkSuccess(resetResultProto.getStatus()); } + /** Wrapper around schema changes */ + @VisibleForTesting + static class RewrittenSchemaResults { + // Any database-qualified types that used to exist in the schema, but are deleted in the + // new one. + final Set<String> mDeletedQualifiedTypes = new ArraySet<>(); + + // Database-qualified types that were part of the new schema. + final Set<String> mRewrittenQualifiedTypes = new ArraySet<>(); + } + /** * Rewrites all types mentioned in the given {@code newSchema} to prepend {@code prefix}. * Rewritten types will be added to the {@code existingSchema}. * - * @param databaseName The name of the database where this schema lives. + * @param databaseName The name of the database where this schema lives. * @param existingSchema A schema that may contain existing types from across all database - * instances. Will be mutated to contain the properly rewritten schema - * types from {@code newSchema}. - * @param newSchema Schema with types to add to the {@code existingSchema}. - * @return a Set contains all remaining qualified schema type names in given database. + * instances. Will be mutated to contain the properly rewritten schema types from {@code + * newSchema}. + * @param newSchema Schema with types to add to the {@code existingSchema}. + * @return a RewrittenSchemaResults contains all qualified schema type names in the given + * database as well as a set of schema types that were deleted from the database. */ @VisibleForTesting - Set<String> rewriteSchema(@NonNull String databaseName, + static RewrittenSchemaResults rewriteSchema( + @NonNull String databaseName, @NonNull SchemaProto.Builder existingSchema, - @NonNull SchemaProto newSchema) throws AppSearchException { + @NonNull SchemaProto newSchema) + throws AppSearchException { String prefix = getDatabasePrefix(databaseName); HashMap<String, SchemaTypeConfigProto> newTypesToProto = new HashMap<>(); // Rewrite the schema type to include the typePrefix. @@ -523,8 +646,7 @@ public final class AppSearchImpl { PropertyConfigProto.Builder propertyConfigBuilder = typeConfigBuilder.getProperties(propertyIdx).toBuilder(); if (!propertyConfigBuilder.getSchemaType().isEmpty()) { - String newPropertySchemaType = - prefix + propertyConfigBuilder.getSchemaType(); + String newPropertySchemaType = prefix + propertyConfigBuilder.getSchemaType(); propertyConfigBuilder.setSchemaType(newPropertySchemaType); typeConfigBuilder.setProperties(propertyIdx, propertyConfigBuilder); } @@ -533,7 +655,9 @@ public final class AppSearchImpl { newTypesToProto.put(newSchemaType, typeConfigBuilder.build()); } - Set<String> newSchemaTypesName = newTypesToProto.keySet(); + // newTypesToProto is modified below, so we need a copy first + RewrittenSchemaResults rewrittenSchemaResults = new RewrittenSchemaResults(); + rewrittenSchemaResults.mRewrittenQualifiedTypes.addAll(newTypesToProto.keySet()); // Combine the existing schema (which may have types from other databases) with this // database's new schema. Modifies the existingSchemaBuilder. @@ -548,26 +672,26 @@ public final class AppSearchImpl { // All types existing before but not in newSchema should be removed. existingSchema.removeTypes(i); --i; + rewrittenSchemaResults.mDeletedQualifiedTypes.add(schemaType); } } // We've been removing existing types from newTypesToProto, so everything that remains is // new. existingSchema.addAllTypes(newTypesToProto.values()); - return newSchemaTypesName; + return rewrittenSchemaResults; } /** - * Prepends {@code prefix} to all types and namespaces mentioned anywhere in - * {@code documentBuilder}. + * Prepends {@code prefix} to all types and namespaces mentioned anywhere in {@code + * documentBuilder}. * * @param documentBuilder The document to mutate - * @param prefix The prefix to add + * @param prefix The prefix to add */ @VisibleForTesting - void addPrefixToDocument( - @NonNull DocumentProto.Builder documentBuilder, - @NonNull String prefix) { + static void addPrefixToDocument( + @NonNull DocumentProto.Builder documentBuilder, @NonNull String prefix) { // Rewrite the type name to include/remove the prefix. String newSchema = prefix + documentBuilder.getSchema(); documentBuilder.setSchema(newSchema); @@ -595,27 +719,17 @@ public final class AppSearchImpl { } /** - * Removes any database names from types and namespaces mentioned anywhere in - * {@code documentBuilder}. + * Removes any database names from types and namespaces mentioned anywhere in {@code + * documentBuilder}. * * @param documentBuilder The document to mutate */ @VisibleForTesting - void removeDatabasesFromDocument(@NonNull DocumentProto.Builder documentBuilder) { - int delimiterIndex; - if ((delimiterIndex = documentBuilder.getSchema().indexOf(DATABASE_DELIMITER)) != -1) { - // Rewrite the type name to remove the prefix. - // Add 1 to include the char size of the DATABASE_DELIMITER - String newSchema = documentBuilder.getSchema().substring(delimiterIndex + 1); - documentBuilder.setSchema(newSchema); - } - - if ((delimiterIndex = documentBuilder.getNamespace().indexOf(DATABASE_DELIMITER)) != -1) { - // Rewrite the namespace to remove the prefix. - // Add 1 to include the char size of the DATABASE_DELIMITER - String newNamespace = documentBuilder.getNamespace().substring(delimiterIndex + 1); - documentBuilder.setNamespace(newNamespace); - } + static void removeDatabasesFromDocument(@NonNull DocumentProto.Builder documentBuilder) + throws AppSearchException { + // Rewrite the type name and namespace to remove the prefix. + documentBuilder.setSchema(removeDatabasePrefix(documentBuilder.getSchema())); + documentBuilder.setNamespace(removeDatabasePrefix(documentBuilder.getNamespace())); // Recurse into derived documents for (int propertyIdx = 0; @@ -639,8 +753,9 @@ public final class AppSearchImpl { /** * Rewrites the schemaTypeFilters and namespacesFilters that exist in {@code databaseNames}. * - * <p>If the searchSpec has empty filter lists, all existing databases from - * {@code databaseNames} will be added. + * <p>If the searchSpec has empty filter lists, all existing databases from {@code + * databaseNames} will be added. + * * <p>This method should be only called in query methods and get the READ lock to keep thread * safety. * @@ -648,11 +763,11 @@ public final class AppSearchImpl { */ @VisibleForTesting @GuardedBy("mReadWriteLock") - boolean rewriteSearchSpecForDatabases( + boolean rewriteSearchSpecForDatabasesLocked( @NonNull SearchSpecProto.Builder searchSpecBuilder, @NonNull Set<String> databaseNames) { // Create a copy since retainAll() modifies the original set. - Set<String> existingDatabases = new HashSet<>(mNamespaceMap.keySet()); + Set<String> existingDatabases = new ArraySet<>(mNamespaceMapLocked.keySet()); existingDatabases.retainAll(databaseNames); if (existingDatabases.isEmpty()) { @@ -669,29 +784,29 @@ public final class AppSearchImpl { // Rewrite filters to include a database prefix. for (String databaseName : existingDatabases) { - Set<String> existingSchemaTypes = mSchemaMap.get(databaseName); + Set<String> existingSchemaTypes = mSchemaMapLocked.get(databaseName); + String databaseNamePrefix = getDatabasePrefix(databaseName); if (schemaTypeFilters.isEmpty()) { // Include all schema types searchSpecBuilder.addAllSchemaTypeFilters(existingSchemaTypes); } else { // Qualify the given schema types - for (String schemaType : schemaTypeFilters) { - String qualifiedType = getDatabasePrefix(databaseName) + schemaType; + for (int i = 0; i < schemaTypeFilters.size(); i++) { + String qualifiedType = databaseNamePrefix + schemaTypeFilters.get(i); if (existingSchemaTypes.contains(qualifiedType)) { searchSpecBuilder.addSchemaTypeFilters(qualifiedType); } - } } - Set<String> existingNamespaces = mNamespaceMap.get(databaseName); + Set<String> existingNamespaces = mNamespaceMapLocked.get(databaseName); if (namespaceFilters.isEmpty()) { // Include all namespaces searchSpecBuilder.addAllNamespaceFilters(existingNamespaces); } else { // Qualify the given namespaces. - for (String namespace : namespaceFilters) { - String qualifiedNamespace = getDatabasePrefix(databaseName) + namespace; + for (int i = 0; i < namespaceFilters.size(); i++) { + String qualifiedNamespace = databaseNamePrefix + namespaceFilters.get(i); if (existingNamespaces.contains(qualifiedNamespace)) { searchSpecBuilder.addNamespaceFilters(qualifiedNamespace); } @@ -703,35 +818,71 @@ public final class AppSearchImpl { } @VisibleForTesting - SchemaProto getSchemaProto() throws AppSearchException { - GetSchemaResultProto schemaProto = mIcingSearchEngine.getSchema(); + @GuardedBy("mReadWriteLock") + SchemaProto getSchemaProtoLocked() throws AppSearchException { + GetSchemaResultProto schemaProto = mIcingSearchEngineLocked.getSchema(); // TODO(b/161935693) check GetSchemaResultProto is success or not. Call reset() if it's not. // TODO(b/161935693) only allow GetSchemaResultProto NOT_FOUND on first run checkCodeOneOf(schemaProto.getStatus(), StatusProto.Code.OK, StatusProto.Code.NOT_FOUND); return schemaProto.getSchema(); } + /** Returns true if {@code databaseName} has a {@code schemaType} */ + @GuardedBy("mReadWriteLock") + boolean hasSchemaTypeLocked(@NonNull String databaseName, @NonNull String schemaType) { + Preconditions.checkNotNull(databaseName); + Preconditions.checkNotNull(schemaType); + + Set<String> schemaTypes = mSchemaMapLocked.get(databaseName); + if (schemaTypes == null) { + return false; + } + + return schemaTypes.contains(getDatabasePrefix(databaseName) + schemaType); + } + + /** Returns a set of all databases AppSearchImpl knows about. */ + @GuardedBy("mReadWriteLock") @NonNull - private String getDatabasePrefix(@NonNull String databaseName) { + Set<String> getDatabasesLocked() { + return mSchemaMapLocked.keySet(); + } + + @NonNull + private static String getDatabasePrefix(@NonNull String databaseName) { // TODO(b/170370381): Reconsider the way we separate database names for security reasons. return databaseName + DATABASE_DELIMITER; } @NonNull - private String getDatabaseName(@NonNull String prefixedValue) throws AppSearchException { + private static String removeDatabasePrefix(@NonNull String prefixedString) + throws AppSearchException { + int delimiterIndex; + if ((delimiterIndex = prefixedString.indexOf(DATABASE_DELIMITER)) != -1) { + // Add 1 to include the char size of the DATABASE_DELIMITER + return prefixedString.substring(delimiterIndex + 1); + } + throw new AppSearchException( + AppSearchResult.RESULT_UNKNOWN_ERROR, + "The prefixed value doesn't contains a valid database name."); + } + + @NonNull + private static String getDatabaseName(@NonNull String prefixedValue) throws AppSearchException { int delimiterIndex = prefixedValue.indexOf(DATABASE_DELIMITER); if (delimiterIndex == -1) { - throw new AppSearchException(AppSearchResult.RESULT_UNKNOWN_ERROR, + throw new AppSearchException( + AppSearchResult.RESULT_UNKNOWN_ERROR, "The databaseName prefixed value doesn't contains a valid database name."); } return prefixedValue.substring(0, delimiterIndex); } - @GuardedBy("mReadWriteLock") - private void addToMap(Map<String, Set<String>> map, String databaseName, String prefixedValue) { + private static void addToMap( + Map<String, Set<String>> map, String databaseName, String prefixedValue) { Set<String> values = map.get(databaseName); if (values == null) { - values = new HashSet<>(); + values = new ArraySet<>(); map.put(databaseName, values); } values.add(prefixedValue); @@ -742,15 +893,15 @@ public final class AppSearchImpl { * * @throws AppSearchException on error codes. */ - private void checkSuccess(StatusProto statusProto) throws AppSearchException { + private static void checkSuccess(StatusProto statusProto) throws AppSearchException { checkCodeOneOf(statusProto, StatusProto.Code.OK); } /** - * Checks the given status code is one of the provided codes, and throws an - * {@link AppSearchException} if it is not. + * Checks the given status code is one of the provided codes, and throws an {@link + * AppSearchException} if it is not. */ - private void checkCodeOneOf(StatusProto statusProto, StatusProto.Code... codes) + private static void checkCodeOneOf(StatusProto statusProto, StatusProto.Code... codes) throws AppSearchException { for (int i = 0; i < codes.length; i++) { if (codes[i] == statusProto.getCode()) { @@ -774,27 +925,28 @@ public final class AppSearchImpl { * * <p>This method should be only called in mutate methods and get the WRITE lock to keep thread * safety. - * <p>{@link IcingSearchEngine#optimize()} should be called only if - * {@link GetOptimizeInfoResultProto} shows there is enough resources could be released. - * <p>{@link IcingSearchEngine#getOptimizeInfo()} should be called once per - * {@link #CHECK_OPTIMIZE_INTERVAL} of remove executions. + * + * <p>{@link IcingSearchEngine#optimize()} should be called only if {@link + * GetOptimizeInfoResultProto} shows there is enough resources could be released. + * + * <p>{@link IcingSearchEngine#getOptimizeInfo()} should be called once per {@link + * #CHECK_OPTIMIZE_INTERVAL} of remove executions. * * @param force whether we should directly call {@link IcingSearchEngine#getOptimizeInfo()}. */ @GuardedBy("mReadWriteLock") - private void checkForOptimize(boolean force) throws AppSearchException { - ++mOptimizeIntervalCount; - if (force || mOptimizeIntervalCount >= CHECK_OPTIMIZE_INTERVAL) { - mOptimizeIntervalCount = 0; - GetOptimizeInfoResultProto optimizeInfo = getOptimizeInfoResult(); + private void checkForOptimizeLocked(boolean force) throws AppSearchException { + ++mOptimizeIntervalCountLocked; + if (force || mOptimizeIntervalCountLocked >= CHECK_OPTIMIZE_INTERVAL) { + mOptimizeIntervalCountLocked = 0; + GetOptimizeInfoResultProto optimizeInfo = getOptimizeInfoResultLocked(); checkSuccess(optimizeInfo.getStatus()); // Second threshold, decide when to call optimize(). if (optimizeInfo.getOptimizableDocs() >= OPTIMIZE_THRESHOLD_DOC_COUNT - || optimizeInfo.getEstimatedOptimizableBytes() - >= OPTIMIZE_THRESHOLD_BYTES) { + || optimizeInfo.getEstimatedOptimizableBytes() >= OPTIMIZE_THRESHOLD_BYTES) { // TODO(b/155939114): call optimize in the same thread will slow down api calls // significantly. Move this call to background. - OptimizeResultProto optimizeResultProto = mIcingSearchEngine.optimize(); + OptimizeResultProto optimizeResultProto = mIcingSearchEngineLocked.optimize(); checkSuccess(optimizeResultProto.getStatus()); } // TODO(b/147699081): Return OptimizeResultProto & log lost data detail once we add @@ -804,8 +956,8 @@ public final class AppSearchImpl { } /** Remove the rewritten schema types from any result documents. */ - private SearchResultPage rewriteSearchResultProto( - @NonNull SearchResultProto searchResultProto) { + private static SearchResultPage rewriteSearchResultProto( + @NonNull SearchResultProto searchResultProto) throws AppSearchException { SearchResultProto.Builder resultsBuilder = searchResultProto.toBuilder(); for (int i = 0; i < searchResultProto.getResultsCount(); i++) { if (searchResultProto.getResults(i).hasDocument()) { @@ -820,40 +972,48 @@ public final class AppSearchImpl { return SearchResultToProtoConverter.convertToSearchResultPage(resultsBuilder); } + @GuardedBy("mReadWriteLock") + @VisibleForTesting + GetOptimizeInfoResultProto getOptimizeInfoResultLocked() { + return mIcingSearchEngineLocked.getOptimizeInfo(); + } + + @GuardedBy("mReadWriteLock") @VisibleForTesting - GetOptimizeInfoResultProto getOptimizeInfoResult() { - return mIcingSearchEngine.getOptimizeInfo(); + VisibilityStore getVisibilityStoreLocked() { + return mVisibilityStoreLocked; } /** - * Converts an erroneous status code to an AppSearchException. Callers should ensure that - * the status code is not OK or WARNING_DATA_LOSS. + * Converts an erroneous status code to an AppSearchException. Callers should ensure that the + * status code is not OK or WARNING_DATA_LOSS. * * @param statusProto StatusProto with error code and message to translate into - * AppSearchException. + * AppSearchException. * @return AppSearchException with the parallel error code. */ - private AppSearchException statusProtoToAppSearchException(StatusProto statusProto) { + private static AppSearchException statusProtoToAppSearchException(StatusProto statusProto) { switch (statusProto.getCode()) { case INVALID_ARGUMENT: - return new AppSearchException(AppSearchResult.RESULT_INVALID_ARGUMENT, - statusProto.getMessage()); + return new AppSearchException( + AppSearchResult.RESULT_INVALID_ARGUMENT, statusProto.getMessage()); case NOT_FOUND: - return new AppSearchException(AppSearchResult.RESULT_NOT_FOUND, - statusProto.getMessage()); + return new AppSearchException( + AppSearchResult.RESULT_NOT_FOUND, statusProto.getMessage()); case FAILED_PRECONDITION: // Fallthrough case ABORTED: // Fallthrough case INTERNAL: - return new AppSearchException(AppSearchResult.RESULT_INTERNAL_ERROR, - statusProto.getMessage()); + return new AppSearchException( + AppSearchResult.RESULT_INTERNAL_ERROR, statusProto.getMessage()); case OUT_OF_SPACE: - return new AppSearchException(AppSearchResult.RESULT_OUT_OF_SPACE, - statusProto.getMessage()); + return new AppSearchException( + AppSearchResult.RESULT_OUT_OF_SPACE, statusProto.getMessage()); default: // Some unknown/unsupported error - return new AppSearchException(AppSearchResult.RESULT_UNKNOWN_ERROR, + return new AppSearchException( + AppSearchResult.RESULT_UNKNOWN_ERROR, "Unknown IcingSearchEngine status code: " + statusProto.getCode()); } } diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/VisibilityStore.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/VisibilityStore.java new file mode 100644 index 000000000000..47228221a1f5 --- /dev/null +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/VisibilityStore.java @@ -0,0 +1,257 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.appsearch.external.localstorage; + +import android.annotation.NonNull; +import android.app.appsearch.AppSearchResult; +import android.app.appsearch.AppSearchSchema; +import android.app.appsearch.GenericDocument; +import android.app.appsearch.exceptions.AppSearchException; +import android.util.ArrayMap; +import android.util.ArraySet; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.Preconditions; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +/** + * Manages any visibility settings for all the databases that AppSearchImpl knows about. Persists + * the visibility settings and reloads them on initialization. + * + * <p>The VisibilityStore creates a document for each database. This document holds the visibility + * settings that apply to that database. The VisibilityStore also creates a schema for these + * documents and has its own database so that its data doesn't interfere with any clients' data. It + * persists the document and schema through AppSearchImpl. + * + * <p>These visibility settings are used to ensure AppSearch queries respect the clients' settings + * on who their data is visible to. + * + * <p>This class doesn't handle any locking itself. Its callers should handle the locking at a + * higher level. + * + * <p>NOTE: This class holds an instance of AppSearchImpl and AppSearchImpl holds an instance of + * this class. Take care to not cause any circular dependencies. + */ +class VisibilityStore { + // Schema type for documents that hold AppSearch's metadata, e.g. visibility settings + @VisibleForTesting static final String SCHEMA_TYPE = "Visibility"; + // Property that holds the list of platform-hidden schemas, as part of the visibility + // settings. + @VisibleForTesting static final String PLATFORM_HIDDEN_PROPERTY = "platformHidden"; + // Database name to prefix all visibility schemas and documents with. Special-cased to + // minimize the chance of collision with a client-supplied database. + @VisibleForTesting static final String DATABASE_NAME = "$$__AppSearch__Database"; + // Namespace of documents that contain visibility settings + private static final String NAMESPACE = "namespace"; + private final AppSearchImpl mAppSearchImpl; + + // The map contains schemas that are platform-hidden for each database. All schemas in the map + // have a database name prefix. + private final Map<String, Set<String>> mPlatformHiddenMap = new ArrayMap<>(); + + /** + * Creates an uninitialized VisibilityStore object. Callers must also call {@link #initialize()} + * before using the object. + * + * @param appSearchImpl AppSearchImpl instance + */ + VisibilityStore(@NonNull AppSearchImpl appSearchImpl) { + mAppSearchImpl = appSearchImpl; + } + + /** + * Initializes schemas and member variables to track visibility settings. + * + * <p>This is kept separate from the constructor because this will call methods on + * AppSearchImpl. Some may even then recursively call back into VisibilityStore (for example, + * {@link AppSearchImpl#setSchema} will call {@link #updateSchemas}. We need to have both + * AppSearchImpl and VisibilityStore fully initialized for this call flow to work. + * + * @throws AppSearchException AppSearchException on AppSearchImpl error. + */ + public void initialize() throws AppSearchException { + if (!mAppSearchImpl.hasSchemaTypeLocked(DATABASE_NAME, SCHEMA_TYPE)) { + // Schema type doesn't exist yet. Add it. + mAppSearchImpl.setSchema( + DATABASE_NAME, + Collections.singleton( + new AppSearchSchema.Builder(SCHEMA_TYPE) + .addProperty( + new AppSearchSchema.PropertyConfig.Builder( + PLATFORM_HIDDEN_PROPERTY) + .setDataType( + AppSearchSchema.PropertyConfig + .DATA_TYPE_STRING) + .setCardinality( + AppSearchSchema.PropertyConfig + .CARDINALITY_REPEATED) + .build()) + .build()), + /*forceOverride=*/ false); + } + + // Populate visibility settings map + for (String database : mAppSearchImpl.getDatabasesLocked()) { + if (database.equals(DATABASE_NAME)) { + // Our own database. Skip + continue; + } + + try { + // Note: We use the other clients' database names as uris + GenericDocument document = + mAppSearchImpl.getDocument(DATABASE_NAME, NAMESPACE, /*uri=*/ database); + + String[] schemas = document.getPropertyStringArray(PLATFORM_HIDDEN_PROPERTY); + mPlatformHiddenMap.put(database, new ArraySet<>(Arrays.asList(schemas))); + } catch (AppSearchException e) { + if (e.getResultCode() == AppSearchResult.RESULT_NOT_FOUND) { + // TODO(b/172068212): This indicates some desync error. We were expecting a + // document, but didn't find one. Should probably reset AppSearch instead of + // ignoring it. + continue; + } + // Otherwise, this is some other error we should pass up. + throw e; + } + } + } + + /** + * Update visibility settings for the {@code databaseName}. + * + * @param schemasToRemove Database-prefixed schemas that should be removed + */ + public void updateSchemas(@NonNull String databaseName, @NonNull Set<String> schemasToRemove) + throws AppSearchException { + Preconditions.checkNotNull(databaseName); + Preconditions.checkNotNull(schemasToRemove); + + GenericDocument visibilityDocument; + try { + visibilityDocument = + mAppSearchImpl.getDocument(DATABASE_NAME, NAMESPACE, /*uri=*/ databaseName); + } catch (AppSearchException e) { + if (e.getResultCode() == AppSearchResult.RESULT_NOT_FOUND) { + // This might be the first time we're seeing visibility changes for a database. + // Create a new visibility document. + mAppSearchImpl.putDocument( + DATABASE_NAME, + new GenericDocument.Builder(/*uri=*/ databaseName, SCHEMA_TYPE) + .setNamespace(NAMESPACE) + .build()); + + // Since we know there was nothing that existed before, we don't need to remove + // anything either. Return early. + return; + } + // Otherwise, this is some real error we should pass up. + throw e; + } + + String[] hiddenSchemas = + visibilityDocument.getPropertyStringArray(PLATFORM_HIDDEN_PROPERTY); + if (hiddenSchemas == null) { + // Nothing to remove. + return; + } + + // Create a new set so we can remove from it. + Set<String> remainingSchemas = new ArraySet<>(Arrays.asList(hiddenSchemas)); + boolean changed = remainingSchemas.removeAll(schemasToRemove); + if (!changed) { + // Nothing was actually removed. Can return early. + return; + } + + // Update our persisted document + // TODO(b/171882200): Switch to a .toBuilder API when it's available. + GenericDocument.Builder newVisibilityDocument = + new GenericDocument.Builder(/*uri=*/ databaseName, SCHEMA_TYPE) + .setNamespace(NAMESPACE); + if (!remainingSchemas.isEmpty()) { + newVisibilityDocument.setPropertyString( + PLATFORM_HIDDEN_PROPERTY, remainingSchemas.toArray(new String[0])); + } + mAppSearchImpl.putDocument(DATABASE_NAME, newVisibilityDocument.build()); + + // Update derived data structures + mPlatformHiddenMap.put(databaseName, remainingSchemas); + } + + /** + * Sets visibility settings for {@code databaseName}. Any previous visibility settings will be + * overwritten. + * + * @param databaseName Database name that owns the {@code platformHiddenSchemas}. + * @param platformHiddenSchemas Set of database-qualified schemas that should be hidden from the + * platform. + * @throws AppSearchException on AppSearchImpl error. + */ + public void setVisibility( + @NonNull String databaseName, @NonNull Set<String> platformHiddenSchemas) + throws AppSearchException { + Preconditions.checkNotNull(databaseName); + Preconditions.checkNotNull(platformHiddenSchemas); + + // Persist the document + GenericDocument.Builder visibilityDocument = + new GenericDocument.Builder(/*uri=*/ databaseName, SCHEMA_TYPE) + .setNamespace(NAMESPACE); + if (!platformHiddenSchemas.isEmpty()) { + visibilityDocument.setPropertyString( + PLATFORM_HIDDEN_PROPERTY, platformHiddenSchemas.toArray(new String[0])); + } + mAppSearchImpl.putDocument(DATABASE_NAME, visibilityDocument.build()); + + // Update derived data structures. + mPlatformHiddenMap.put(databaseName, platformHiddenSchemas); + } + + /** + * Returns the set of database-qualified schemas in {@code databaseName} that are hidden from + * the platform. + * + * @param databaseName Database name to retrieve schemas for + * @return Set of database-qualified schemas that are hidden from the platform. Empty set if + * none exist. + */ + @NonNull + public Set<String> getPlatformHiddenSchemas(@NonNull String databaseName) { + Preconditions.checkNotNull(databaseName); + Set<String> platformHiddenSchemas = mPlatformHiddenMap.get(databaseName); + if (platformHiddenSchemas == null) { + return Collections.emptySet(); + } + return platformHiddenSchemas; + } + + /** + * Handles an {@link AppSearchImpl#reset()} by clearing any cached state and resetting to a + * first-initialized state. + * + * @throws AppSearchException on AppSearchImpl error. + */ + public void handleReset() throws AppSearchException { + mPlatformHiddenMap.clear(); + initialize(); + } +} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java index 60684f09a202..8f4e7ff69d7c 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java @@ -17,8 +17,8 @@ package com.android.server.appsearch.external.localstorage.converter; import android.annotation.NonNull; - import android.app.appsearch.GenericDocument; + import com.android.internal.util.Preconditions; import com.google.android.icing.proto.DocumentProto; @@ -30,9 +30,9 @@ import java.util.Collections; /** * Translates a {@link GenericDocument} into a {@link DocumentProto}. + * * @hide */ - public final class GenericDocumentToProtoConverter { private GenericDocumentToProtoConverter() {} @@ -42,7 +42,8 @@ public final class GenericDocumentToProtoConverter { public static DocumentProto convert(@NonNull GenericDocument document) { Preconditions.checkNotNull(document); DocumentProto.Builder mProtoBuilder = DocumentProto.newBuilder(); - mProtoBuilder.setUri(document.getUri()) + mProtoBuilder + .setUri(document.getUri()) .setSchema(document.getSchemaType()) .setNamespace(document.getNamespace()) .setScore(document.getScore()) diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverter.java index 403711f29544..642c2a713930 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverter.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverter.java @@ -17,34 +17,34 @@ package com.android.server.appsearch.external.localstorage.converter; import android.annotation.NonNull; - import android.app.appsearch.AppSearchSchema; + import com.android.internal.util.Preconditions; -import com.google.android.icing.proto.IndexingConfig; import com.google.android.icing.proto.PropertyConfigProto; import com.google.android.icing.proto.SchemaTypeConfigProto; +import com.google.android.icing.proto.StringIndexingConfig; import com.google.android.icing.proto.TermMatchType; import java.util.List; /** * Translates an {@link AppSearchSchema} into a {@link SchemaTypeConfigProto}. + * * @hide */ - public final class SchemaToProtoConverter { private SchemaToProtoConverter() {} /** - * Converts an {@link android.app.appsearch.AppSearchSchema} into a - * {@link SchemaTypeConfigProto}. + * Converts an {@link android.app.appsearch.AppSearchSchema} into a {@link + * SchemaTypeConfigProto}. */ @NonNull public static SchemaTypeConfigProto convert(@NonNull AppSearchSchema schema) { Preconditions.checkNotNull(schema); SchemaTypeConfigProto.Builder protoBuilder = - SchemaTypeConfigProto.newBuilder().setSchemaType(schema.getSchemaTypeName()); + SchemaTypeConfigProto.newBuilder().setSchemaType(schema.getSchemaType()); List<AppSearchSchema.PropertyConfig> properties = schema.getProperties(); for (int i = 0; i < properties.size(); i++) { PropertyConfigProto propertyProto = convertProperty(properties.get(i)); @@ -57,9 +57,9 @@ public final class SchemaToProtoConverter { private static PropertyConfigProto convertProperty( @NonNull AppSearchSchema.PropertyConfig property) { Preconditions.checkNotNull(property); - PropertyConfigProto.Builder propertyConfigProto = PropertyConfigProto.newBuilder() - .setPropertyName(property.getName()); - IndexingConfig.Builder indexingConfig = IndexingConfig.newBuilder(); + PropertyConfigProto.Builder propertyConfigProto = + PropertyConfigProto.newBuilder().setPropertyName(property.getName()); + StringIndexingConfig.Builder indexingConfig = StringIndexingConfig.newBuilder(); // Set dataType @AppSearchSchema.PropertyConfig.DataType int dataType = property.getDataType(); @@ -104,17 +104,17 @@ public final class SchemaToProtoConverter { indexingConfig.setTermMatchType(termMatchTypeProto); // Set tokenizerType - @AppSearchSchema.PropertyConfig.TokenizerType int tokenizerType = - property.getTokenizerType(); - IndexingConfig.TokenizerType.Code tokenizerTypeProto = - IndexingConfig.TokenizerType.Code.forNumber(tokenizerType); + @AppSearchSchema.PropertyConfig.TokenizerType + int tokenizerType = property.getTokenizerType(); + StringIndexingConfig.TokenizerType.Code tokenizerTypeProto = + StringIndexingConfig.TokenizerType.Code.forNumber(tokenizerType); if (tokenizerTypeProto == null) { throw new IllegalArgumentException("Invalid tokenizerType: " + tokenizerType); } indexingConfig.setTokenizerType(tokenizerTypeProto); // Build! - propertyConfigProto.setIndexingConfig(indexingConfig); + propertyConfigProto.setStringIndexingConfig(indexingConfig); return propertyConfigProto.build(); } } diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java index 4310b4216266..b91a393cab4a 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java @@ -16,13 +16,11 @@ package com.android.server.appsearch.external.localstorage.converter; -import android.os.Bundle; - import android.annotation.NonNull; - import android.app.appsearch.GenericDocument; import android.app.appsearch.SearchResult; import android.app.appsearch.SearchResultPage; +import android.os.Bundle; import com.google.android.icing.proto.SearchResultProto; import com.google.android.icing.proto.SearchResultProtoOrBuilder; @@ -36,11 +34,9 @@ import java.util.ArrayList; * * @hide */ - public class SearchResultToProtoConverter { private SearchResultToProtoConverter() {} - /** Translate a {@link SearchResultProto} into {@link SearchResultPage}. */ @NonNull public static SearchResultPage convertToSearchResultPage( @@ -68,8 +64,9 @@ public class SearchResultToProtoConverter { for (int i = 0; i < proto.getSnippet().getEntriesCount(); i++) { SnippetProto.EntryProto entry = proto.getSnippet().getEntries(i); for (int j = 0; j < entry.getSnippetMatchesCount(); j++) { - Bundle matchInfoBundle = convertToMatchInfoBundle( - entry.getSnippetMatches(j), entry.getPropertyName()); + Bundle matchInfoBundle = + convertToMatchInfoBundle( + entry.getSnippetMatches(j), entry.getPropertyName()); matchList.add(matchInfoBundle); } } diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchSpecToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchSpecToProtoConverter.java index 14822dcdc793..814ee4f7636a 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchSpecToProtoConverter.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchSpecToProtoConverter.java @@ -17,8 +17,8 @@ package com.android.server.appsearch.external.localstorage.converter; import android.annotation.NonNull; - import android.app.appsearch.SearchSpec; + import com.android.internal.util.Preconditions; import com.google.android.icing.proto.ResultSpecProto; @@ -28,9 +28,9 @@ import com.google.android.icing.proto.TermMatchType; /** * Translates a {@link SearchSpec} into icing search protos. + * * @hide */ - public final class SearchSpecToProtoConverter { private SearchSpecToProtoConverter() {} @@ -38,9 +38,10 @@ public final class SearchSpecToProtoConverter { @NonNull public static SearchSpecProto toSearchSpecProto(@NonNull SearchSpec spec) { Preconditions.checkNotNull(spec); - SearchSpecProto.Builder protoBuilder = SearchSpecProto.newBuilder() - .addAllSchemaTypeFilters(spec.getSchemas()) - .addAllNamespaceFilters(spec.getNamespaces()); + SearchSpecProto.Builder protoBuilder = + SearchSpecProto.newBuilder() + .addAllSchemaTypeFilters(spec.getSchemaTypes()) + .addAllNamespaceFilters(spec.getNamespaces()); @SearchSpec.TermMatch int termMatchCode = spec.getTermMatch(); TermMatchType.Code termMatchCodeProto = TermMatchType.Code.forNumber(termMatchCode); @@ -57,7 +58,7 @@ public final class SearchSpecToProtoConverter { public static ResultSpecProto toResultSpecProto(@NonNull SearchSpec spec) { Preconditions.checkNotNull(spec); return ResultSpecProto.newBuilder() - .setNumPerPage(spec.getNumPerPage()) + .setNumPerPage(spec.getResultCountPerPage()) .setSnippetSpec( ResultSpecProto.SnippetSpecProto.newBuilder() .setNumToSnippet(spec.getSnippetCount()) @@ -84,8 +85,8 @@ public final class SearchSpecToProtoConverter { ScoringSpecProto.RankingStrategy.Code rankingStrategyCodeProto = ScoringSpecProto.RankingStrategy.Code.forNumber(rankingStrategyCode); if (rankingStrategyCodeProto == null) { - throw new IllegalArgumentException("Invalid result ranking strategy: " - + rankingStrategyCode); + throw new IllegalArgumentException( + "Invalid result ranking strategy: " + rankingStrategyCode); } protoBuilder.setRankBy(rankingStrategyCodeProto); diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt new file mode 100644 index 000000000000..a2bf0d504a1f --- /dev/null +++ b/apex/appsearch/synced_jetpack_changeid.txt @@ -0,0 +1 @@ +I2decd83fab4c4d58fe38c9970f804046479c942c diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java index c2d530d00058..2c8a5589ca51 100644 --- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java +++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java @@ -1506,56 +1506,16 @@ public class JobInfo implements Parcelable { * @return The job object to hand to the JobScheduler. This object is immutable. */ public JobInfo build() { - // Check that network estimates require network type - if ((mNetworkDownloadBytes > 0 || mNetworkUploadBytes > 0) && mNetworkRequest == null) { - throw new IllegalArgumentException( - "Can't provide estimated network usage without requiring a network"); - } - // We can't serialize network specifiers - if (mIsPersisted && mNetworkRequest != null - && mNetworkRequest.networkCapabilities.getNetworkSpecifier() != null) { - throw new IllegalArgumentException( - "Network specifiers aren't supported for persistent jobs"); - } - // Check that a deadline was not set on a periodic job. - if (mIsPeriodic) { - if (mMaxExecutionDelayMillis != 0L) { - throw new IllegalArgumentException("Can't call setOverrideDeadline() on a " + - "periodic job."); - } - if (mMinLatencyMillis != 0L) { - throw new IllegalArgumentException("Can't call setMinimumLatency() on a " + - "periodic job"); - } - if (mTriggerContentUris != null) { - throw new IllegalArgumentException("Can't call addTriggerContentUri() on a " + - "periodic job"); - } - } - if (mIsPersisted) { - if (mTriggerContentUris != null) { - throw new IllegalArgumentException("Can't call addTriggerContentUri() on a " + - "persisted job"); - } - if (!mTransientExtras.isEmpty()) { - throw new IllegalArgumentException("Can't call setTransientExtras() on a " + - "persisted job"); - } - if (mClipData != null) { - throw new IllegalArgumentException("Can't call setClipData() on a " + - "persisted job"); - } - } - if ((mFlags & FLAG_IMPORTANT_WHILE_FOREGROUND) != 0 && mHasEarlyConstraint) { - throw new IllegalArgumentException("An important while foreground job cannot " - + "have a time delay"); - } + // This check doesn't need to be inside enforceValidity. It's an unnecessary legacy + // check that would ideally be phased out instead. if (mBackoffPolicySet && (mConstraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0) { throw new IllegalArgumentException("An idle mode job will not respect any" + " back-off policy, so calling setBackoffCriteria with" + " setRequiresDeviceIdle is an error."); } - return new JobInfo(this); + JobInfo jobInfo = new JobInfo(this); + jobInfo.enforceValidity(); + return jobInfo; } /** @@ -1570,6 +1530,59 @@ public class JobInfo implements Parcelable { } /** + * @hide + */ + public void enforceValidity() { + // Check that network estimates require network type + if ((networkDownloadBytes > 0 || networkUploadBytes > 0) && networkRequest == null) { + throw new IllegalArgumentException( + "Can't provide estimated network usage without requiring a network"); + } + + // Check that a deadline was not set on a periodic job. + if (isPeriodic) { + if (maxExecutionDelayMillis != 0L) { + throw new IllegalArgumentException( + "Can't call setOverrideDeadline() on a periodic job."); + } + if (minLatencyMillis != 0L) { + throw new IllegalArgumentException( + "Can't call setMinimumLatency() on a periodic job"); + } + if (triggerContentUris != null) { + throw new IllegalArgumentException( + "Can't call addTriggerContentUri() on a periodic job"); + } + } + + if (isPersisted) { + // We can't serialize network specifiers + if (networkRequest != null + && networkRequest.networkCapabilities.getNetworkSpecifier() != null) { + throw new IllegalArgumentException( + "Network specifiers aren't supported for persistent jobs"); + } + if (triggerContentUris != null) { + throw new IllegalArgumentException( + "Can't call addTriggerContentUri() on a persisted job"); + } + if (!transientExtras.isEmpty()) { + throw new IllegalArgumentException( + "Can't call setTransientExtras() on a persisted job"); + } + if (clipData != null) { + throw new IllegalArgumentException( + "Can't call setClipData() on a persisted job"); + } + } + + if ((flags & FLAG_IMPORTANT_WHILE_FOREGROUND) != 0 && hasEarlyConstraint) { + throw new IllegalArgumentException( + "An important while foreground job cannot have a time delay"); + } + } + + /** * Convert a priority integer into a human readable string for debugging. * @hide */ diff --git a/apex/jobscheduler/framework/java/android/app/job/JobServiceEngine.java b/apex/jobscheduler/framework/java/android/app/job/JobServiceEngine.java index ab94da843635..3d43d20e7955 100644 --- a/apex/jobscheduler/framework/java/android/app/job/JobServiceEngine.java +++ b/apex/jobscheduler/framework/java/android/app/job/JobServiceEngine.java @@ -17,7 +17,6 @@ package android.app.job; import android.app.Service; -import android.content.Context; import android.content.Intent; import android.os.Handler; import android.os.IBinder; @@ -26,8 +25,6 @@ import android.os.Message; import android.os.RemoteException; import android.util.Log; -import com.android.internal.annotations.GuardedBy; - import java.lang.ref.WeakReference; /** @@ -206,8 +203,7 @@ public abstract class JobServiceEngine { /** * Call in to engine to report that a job has finished executing. See - * {@link JobService#jobFinished(JobParameters, boolean)} JobService.jobFinished} for more - * information. + * {@link JobService#jobFinished(JobParameters, boolean)} for more information. */ public void jobFinished(JobParameters params, boolean needsReschedule) { if (params == null) { diff --git a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java index 398ccb69fbe8..2ce85ee57205 100644 --- a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java +++ b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java @@ -3,7 +3,6 @@ package com.android.server.usage; import android.annotation.NonNull; import android.annotation.UserIdInt; import android.app.usage.AppStandbyInfo; -import android.app.usage.UsageEvents; import android.app.usage.UsageStatsManager.StandbyBuckets; import android.app.usage.UsageStatsManager.SystemForcedReasons; import android.content.Context; @@ -68,8 +67,6 @@ public interface AppStandbyInternal { */ void postOneTimeCheckIdleStates(); - void reportEvent(UsageEvents.Event event, int userId); - void setLastJobRunTime(String packageName, int userId, long elapsedRealtime); long getTimeSinceLastJobRun(String packageName, int userId); diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java index 9835e18d0bfd..99892527721a 100644 --- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java +++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java @@ -884,13 +884,12 @@ public class DeviceIdleController extends SystemService private static final String KEY_MAX_IDLE_TIMEOUT = "max_idle_to"; private static final String KEY_IDLE_FACTOR = "idle_factor"; private static final String KEY_MIN_TIME_TO_ALARM = "min_time_to_alarm"; - // TODO(166121524): update flag names - private static final String KEY_MAX_TEMP_APP_WHITELIST_DURATION = - "max_temp_app_whitelist_duration"; - private static final String KEY_MMS_TEMP_APP_WHITELIST_DURATION = - "mms_temp_app_whitelist_duration"; - private static final String KEY_SMS_TEMP_APP_WHITELIST_DURATION = - "sms_temp_app_whitelist_duration"; + private static final String KEY_MAX_TEMP_APP_ALLOWLIST_DURATION_MS = + "max_temp_app_allowlist_duration_ms"; + private static final String KEY_MMS_TEMP_APP_ALLOWLIST_DURATION_MS = + "mms_temp_app_allowlist_duration_ms"; + private static final String KEY_SMS_TEMP_APP_ALLOWLIST_DURATION_MS = + "sms_temp_app_allowlist_duration_ms"; private static final String KEY_NOTIFICATION_ALLOWLIST_DURATION_MS = "notification_allowlist_duration_ms"; /** @@ -949,9 +948,9 @@ public class DeviceIdleController extends SystemService private static final float DEFAULT_IDLE_FACTOR = 2f; private static final long DEFAULT_MIN_TIME_TO_ALARM = !COMPRESS_TIME ? 30 * 60 * 1000L : 6 * 60 * 1000L; - private static final long DEFAULT_MAX_TEMP_APP_WHITELIST_DURATION = 5 * 60 * 1000L; - private static final long DEFAULT_MMS_TEMP_APP_WHITELIST_DURATION = 60 * 1000L; - private static final long DEFAULT_SMS_TEMP_APP_WHITELIST_DURATION = 20 * 1000L; + private static final long DEFAULT_MAX_TEMP_APP_ALLOWLIST_DURATION_MS = 5 * 60 * 1000L; + private static final long DEFAULT_MMS_TEMP_APP_ALLOWLIST_DURATION_MS = 60 * 1000L; + private static final long DEFAULT_SMS_TEMP_APP_ALLOWLIST_DURATION_MS = 20 * 1000L; private static final long DEFAULT_NOTIFICATION_ALLOWLIST_DURATION_MS = 30 * 1000L; private static final boolean DEFAULT_WAIT_FOR_UNLOCK = true; private static final float DEFAULT_PRE_IDLE_FACTOR_LONG = 1.67f; @@ -1142,21 +1141,21 @@ public class DeviceIdleController extends SystemService * Max amount of time to temporarily whitelist an app when it receives a high priority * tickle. * - * @see #KEY_MAX_TEMP_APP_WHITELIST_DURATION + * @see #KEY_MAX_TEMP_APP_ALLOWLIST_DURATION_MS */ - public long MAX_TEMP_APP_WHITELIST_DURATION = DEFAULT_MAX_TEMP_APP_WHITELIST_DURATION; + public long MAX_TEMP_APP_ALLOWLIST_DURATION_MS = DEFAULT_MAX_TEMP_APP_ALLOWLIST_DURATION_MS; /** * Amount of time we would like to whitelist an app that is receiving an MMS. - * @see #KEY_MMS_TEMP_APP_WHITELIST_DURATION + * @see #KEY_MMS_TEMP_APP_ALLOWLIST_DURATION_MS */ - public long MMS_TEMP_APP_WHITELIST_DURATION = DEFAULT_MMS_TEMP_APP_WHITELIST_DURATION; + public long MMS_TEMP_APP_ALLOWLIST_DURATION_MS = DEFAULT_MMS_TEMP_APP_ALLOWLIST_DURATION_MS; /** * Amount of time we would like to whitelist an app that is receiving an SMS. - * @see #KEY_SMS_TEMP_APP_WHITELIST_DURATION + * @see #KEY_SMS_TEMP_APP_ALLOWLIST_DURATION_MS */ - public long SMS_TEMP_APP_WHITELIST_DURATION = DEFAULT_SMS_TEMP_APP_WHITELIST_DURATION; + public long SMS_TEMP_APP_ALLOWLIST_DURATION_MS = DEFAULT_SMS_TEMP_APP_ALLOWLIST_DURATION_MS; /** * Amount of time we would like to whitelist an app that is handling a @@ -1303,20 +1302,20 @@ public class DeviceIdleController extends SystemService MIN_TIME_TO_ALARM = properties.getLong( KEY_MIN_TIME_TO_ALARM, DEFAULT_MIN_TIME_TO_ALARM); break; - case KEY_MAX_TEMP_APP_WHITELIST_DURATION: - MAX_TEMP_APP_WHITELIST_DURATION = properties.getLong( - KEY_MAX_TEMP_APP_WHITELIST_DURATION, - DEFAULT_MAX_TEMP_APP_WHITELIST_DURATION); + case KEY_MAX_TEMP_APP_ALLOWLIST_DURATION_MS: + MAX_TEMP_APP_ALLOWLIST_DURATION_MS = properties.getLong( + KEY_MAX_TEMP_APP_ALLOWLIST_DURATION_MS, + DEFAULT_MAX_TEMP_APP_ALLOWLIST_DURATION_MS); break; - case KEY_MMS_TEMP_APP_WHITELIST_DURATION: - MMS_TEMP_APP_WHITELIST_DURATION = properties.getLong( - KEY_MMS_TEMP_APP_WHITELIST_DURATION, - DEFAULT_MMS_TEMP_APP_WHITELIST_DURATION); + case KEY_MMS_TEMP_APP_ALLOWLIST_DURATION_MS: + MMS_TEMP_APP_ALLOWLIST_DURATION_MS = properties.getLong( + KEY_MMS_TEMP_APP_ALLOWLIST_DURATION_MS, + DEFAULT_MMS_TEMP_APP_ALLOWLIST_DURATION_MS); break; - case KEY_SMS_TEMP_APP_WHITELIST_DURATION: - SMS_TEMP_APP_WHITELIST_DURATION = properties.getLong( - KEY_SMS_TEMP_APP_WHITELIST_DURATION, - DEFAULT_SMS_TEMP_APP_WHITELIST_DURATION); + case KEY_SMS_TEMP_APP_ALLOWLIST_DURATION_MS: + SMS_TEMP_APP_ALLOWLIST_DURATION_MS = properties.getLong( + KEY_SMS_TEMP_APP_ALLOWLIST_DURATION_MS, + DEFAULT_SMS_TEMP_APP_ALLOWLIST_DURATION_MS); break; case KEY_NOTIFICATION_ALLOWLIST_DURATION_MS: NOTIFICATION_ALLOWLIST_DURATION_MS = properties.getLong( @@ -1438,16 +1437,16 @@ public class DeviceIdleController extends SystemService TimeUtils.formatDuration(MIN_TIME_TO_ALARM, pw); pw.println(); - pw.print(" "); pw.print(KEY_MAX_TEMP_APP_WHITELIST_DURATION); pw.print("="); - TimeUtils.formatDuration(MAX_TEMP_APP_WHITELIST_DURATION, pw); + pw.print(" "); pw.print(KEY_MAX_TEMP_APP_ALLOWLIST_DURATION_MS); pw.print("="); + TimeUtils.formatDuration(MAX_TEMP_APP_ALLOWLIST_DURATION_MS, pw); pw.println(); - pw.print(" "); pw.print(KEY_MMS_TEMP_APP_WHITELIST_DURATION); pw.print("="); - TimeUtils.formatDuration(MMS_TEMP_APP_WHITELIST_DURATION, pw); + pw.print(" "); pw.print(KEY_MMS_TEMP_APP_ALLOWLIST_DURATION_MS); pw.print("="); + TimeUtils.formatDuration(MMS_TEMP_APP_ALLOWLIST_DURATION_MS, pw); pw.println(); - pw.print(" "); pw.print(KEY_SMS_TEMP_APP_WHITELIST_DURATION); pw.print("="); - TimeUtils.formatDuration(SMS_TEMP_APP_WHITELIST_DURATION, pw); + pw.print(" "); pw.print(KEY_SMS_TEMP_APP_ALLOWLIST_DURATION_MS); pw.print("="); + TimeUtils.formatDuration(SMS_TEMP_APP_ALLOWLIST_DURATION_MS, pw); pw.println(); pw.print(" "); pw.print(KEY_NOTIFICATION_ALLOWLIST_DURATION_MS); pw.print("="); @@ -1804,29 +1803,29 @@ public class DeviceIdleController extends SystemService public long whitelistAppTemporarily(String packageName, int userId, String reason) throws RemoteException { // At least 10 seconds. - long duration = Math.max(10_000L, mConstants.MAX_TEMP_APP_WHITELIST_DURATION / 2); - addPowerSaveTempWhitelistAppChecked(packageName, duration, userId, reason); - return duration; + long durationMs = Math.max(10_000L, mConstants.MAX_TEMP_APP_ALLOWLIST_DURATION_MS / 2); + addPowerSaveTempAllowlistAppChecked(packageName, durationMs, userId, reason); + return durationMs; } @Override public void addPowerSaveTempWhitelistApp(String packageName, long duration, int userId, String reason) throws RemoteException { - addPowerSaveTempWhitelistAppChecked(packageName, duration, userId, reason); + addPowerSaveTempAllowlistAppChecked(packageName, duration, userId, reason); } @Override public long addPowerSaveTempWhitelistAppForMms(String packageName, int userId, String reason) throws RemoteException { - long duration = mConstants.MMS_TEMP_APP_WHITELIST_DURATION; - addPowerSaveTempWhitelistAppChecked(packageName, duration, userId, reason); - return duration; + long durationMs = mConstants.MMS_TEMP_APP_ALLOWLIST_DURATION_MS; + addPowerSaveTempAllowlistAppChecked(packageName, durationMs, userId, reason); + return durationMs; } @Override public long addPowerSaveTempWhitelistAppForSms(String packageName, int userId, String reason) throws RemoteException { - long duration = mConstants.SMS_TEMP_APP_WHITELIST_DURATION; - addPowerSaveTempWhitelistAppChecked(packageName, duration, userId, reason); - return duration; + long durationMs = mConstants.SMS_TEMP_APP_ALLOWLIST_DURATION_MS; + addPowerSaveTempAllowlistAppChecked(packageName, durationMs, userId, reason); + return durationMs; } @Override public void exitIdle(String reason) { @@ -1900,7 +1899,7 @@ public class DeviceIdleController extends SystemService @Override public void addPowerSaveTempWhitelistApp(int callingUid, String packageName, long duration, int userId, boolean sync, String reason) { - addPowerSaveTempWhitelistAppInternal(callingUid, packageName, duration, + addPowerSaveTempAllowlistAppInternal(callingUid, packageName, duration, userId, sync, reason); } @@ -2617,7 +2616,7 @@ public class DeviceIdleController extends SystemService } } - void addPowerSaveTempWhitelistAppChecked(String packageName, long duration, + void addPowerSaveTempAllowlistAppChecked(String packageName, long duration, int userId, String reason) throws RemoteException { getContext().enforceCallingPermission( Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, @@ -2632,14 +2631,14 @@ public class DeviceIdleController extends SystemService "addPowerSaveTempWhitelistApp", null); final long token = Binder.clearCallingIdentity(); try { - addPowerSaveTempWhitelistAppInternal(callingUid, + addPowerSaveTempAllowlistAppInternal(callingUid, packageName, duration, userId, true, reason); } finally { Binder.restoreCallingIdentity(token); } } - void removePowerSaveTempWhitelistAppChecked(String packageName, int userId) + void removePowerSaveTempAllowlistAppChecked(String packageName, int userId) throws RemoteException { getContext().enforceCallingPermission( Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, @@ -2654,7 +2653,7 @@ public class DeviceIdleController extends SystemService "removePowerSaveTempWhitelistApp", null); final long token = Binder.clearCallingIdentity(); try { - removePowerSaveTempWhitelistAppInternal(packageName, userId); + removePowerSaveTempAllowlistAppInternal(packageName, userId); } finally { Binder.restoreCallingIdentity(token); } @@ -2664,7 +2663,7 @@ public class DeviceIdleController extends SystemService * Adds an app to the temporary whitelist and resets the endTime for granting the * app an exemption to access network and acquire wakelocks. */ - void addPowerSaveTempWhitelistAppInternal(int callingUid, String packageName, + void addPowerSaveTempAllowlistAppInternal(int callingUid, String packageName, long duration, int userId, boolean sync, String reason) { try { int uid = getContext().getPackageManager().getPackageUidAsUser(packageName, userId); @@ -2690,7 +2689,7 @@ public class DeviceIdleController extends SystemService + " is not on whitelist"); } } - duration = Math.min(duration, mConstants.MAX_TEMP_APP_WHITELIST_DURATION); + duration = Math.min(duration, mConstants.MAX_TEMP_APP_ALLOWLIST_DURATION_MS); Pair<MutableLong, String> entry = mTempWhitelistAppIdEndTimes.get(appId); final boolean newEntry = entry == null; // Set the new end time @@ -2728,7 +2727,7 @@ public class DeviceIdleController extends SystemService /** * Removes an app from the temporary whitelist and notifies the observers. */ - private void removePowerSaveTempWhitelistAppInternal(String packageName, int userId) { + private void removePowerSaveTempAllowlistAppInternal(String packageName, int userId) { try { final int uid = getContext().getPackageManager().getPackageUidAsUser( packageName, userId); @@ -4354,9 +4353,9 @@ public class DeviceIdleController extends SystemService if (arg != null) { try { if (removePkg) { - removePowerSaveTempWhitelistAppChecked(arg, shell.userId); + removePowerSaveTempAllowlistAppChecked(arg, shell.userId); } else { - addPowerSaveTempWhitelistAppChecked(arg, duration, shell.userId, "shell"); + addPowerSaveTempAllowlistAppChecked(arg, duration, shell.userId, "shell"); } } catch (Exception e) { pw.println("Failed: " + e); diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java index fe96952d83f9..255e2f6d8779 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -1054,6 +1054,7 @@ public class JobSchedulerService extends com.android.server.SystemService public int scheduleAsPackage(JobInfo job, JobWorkItem work, int uId, String packageName, int userId, String tag) { + // Rate limit excessive schedule() calls. final String servicePkg = job.getService().getPackageName(); if (job.isPersisted() && (packageName == null || packageName.equals(servicePkg))) { // Only limit schedule calls for persisted jobs scheduled by the app itself. @@ -1358,8 +1359,7 @@ public class JobSchedulerService extends com.android.server.SystemService for (int i=0; i<mActiveServices.size(); i++) { JobServiceContext jsc = mActiveServices.get(i); final JobStatus executing = jsc.getRunningJobLocked(); - if (executing != null - && (executing.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0) { + if (executing != null && !executing.canRunInDoze()) { jsc.cancelExecutingJobLocked(JobParameters.REASON_DEVICE_IDLE, "cancelled due to doze"); } @@ -1411,7 +1411,7 @@ public class JobSchedulerService extends com.android.server.SystemService final JobServiceContext jsc = mActiveServices.get(i); final JobStatus job = jsc.getRunningJobLocked(); if (job != null - && (job.getJob().getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0 + && !job.canRunInDoze() && !job.dozeWhitelisted && !job.uidActive) { // We will report active if we have a job running and it is not an exception @@ -2600,6 +2600,7 @@ public class JobSchedulerService extends com.android.server.SystemService // job that runs one of the app's services, as well as verifying that the // named service properly requires the BIND_JOB_SERVICE permission private void enforceValidJobRequest(int uid, JobInfo job) { + job.enforceValidity(); final PackageManager pm = getContext() .createContextAsUser(UserHandle.getUserHandleForUid(uid), 0) .getPackageManager(); diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java index 1e7206287566..cc202130ab07 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java @@ -20,10 +20,11 @@ import android.app.ActivityManager; import android.app.AppGlobals; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; -import android.os.BasicShellCommandHandler; import android.os.Binder; import android.os.UserHandle; +import com.android.modules.utils.BasicShellCommandHandler; + import java.io.PrintWriter; public final class JobSchedulerShellCommand extends BasicShellCommandHandler { diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java index 44c8fcff8c63..f647db901f48 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java @@ -189,8 +189,7 @@ public final class BackgroundJobsController extends StateController { final String packageName = jobStatus.getSourcePackageName(); final boolean canRun = !mAppStateTracker.areJobsRestricted(uid, packageName, - (jobStatus.getInternalFlags() & JobStatus.INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION) - != 0); + jobStatus.canRunInBatterySaver()); final boolean isActive; if (activeState == UNKNOWN) { diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java index 67997cf31501..6ddafadaa871 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java @@ -464,7 +464,7 @@ public final class ConnectivityController extends RestrictingController implemen NetworkCapabilities capabilities) { // TODO: consider matching against non-active networks - final boolean ignoreBlocked = (jobStatus.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0; + final boolean ignoreBlocked = jobStatus.shouldIgnoreNetworkBlocking(); final NetworkInfo info = mConnManager.getNetworkInfoForUid(network, jobStatus.getSourceUid(), ignoreBlocked); diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java index 00dbb8235d29..8f6c68dad36c 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java @@ -470,7 +470,7 @@ public final class JobStatus { } this.requiredConstraints = requiredConstraints; mRequiredConstraintsOfInterest = requiredConstraints & CONSTRAINTS_OF_INTEREST; - mReadyNotDozing = (job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0; + mReadyNotDozing = canRunInDoze(); if (standbyBucket == RESTRICTED_INDEX) { addDynamicConstraints(DYNAMIC_RESTRICTED_CONSTRAINTS); } else { @@ -1036,6 +1036,22 @@ public final class JobStatus { mPersistedUtcTimes = null; } + /** + * @return true if the job is exempted from Doze restrictions and therefore allowed to run + * in Doze. + */ + public boolean canRunInDoze() { + return (getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0; + } + + boolean canRunInBatterySaver() { + return (getInternalFlags() & INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION) != 0; + } + + boolean shouldIgnoreNetworkBlocking() { + return (getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0; + } + /** @return true if the constraint was changed, false otherwise. */ boolean setChargingConstraintSatisfied(boolean state) { return setConstraintSatisfied(CONSTRAINT_CHARGING, state); @@ -1086,7 +1102,7 @@ public final class JobStatus { dozeWhitelisted = whitelisted; if (setConstraintSatisfied(CONSTRAINT_DEVICE_NOT_DOZING, state)) { // The constraint was changed. Update the ready flag. - mReadyNotDozing = state || (job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0; + mReadyNotDozing = state || canRunInDoze(); return true; } return false; @@ -1771,9 +1787,11 @@ public final class JobStatus { pw.print(prefix); pw.print(" readyDeadlineSatisfied: "); pw.println(mReadyDeadlineSatisfied); } - pw.print(prefix); - pw.print(" readyDynamicSatisfied: "); - pw.println(mReadyDynamicSatisfied); + if (mDynamicConstraints != 0) { + pw.print(prefix); + pw.print(" readyDynamicSatisfied: "); + pw.println(mReadyDynamicSatisfied); + } pw.print(prefix); pw.print(" readyComponentEnabled: "); pw.println(serviceInfo != null); diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java index 7d7de3be8249..1d72b42d824d 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java @@ -1679,42 +1679,47 @@ public final class QuotaController extends StateController { // Update job bookkeeping out of band. JobSchedulerBackgroundThread.getHandler().post(() -> { final int bucketIndex = JobSchedulerService.standbyBucketToBucketIndex(bucket); - if (DEBUG) { - Slog.i(TAG, "Moving pkg " + string(userId, packageName) + " to bucketIndex " - + bucketIndex); - } - List<JobStatus> restrictedChanges = new ArrayList<>(); - synchronized (mLock) { - ArraySet<JobStatus> jobs = mTrackedJobs.get(userId, packageName); - if (jobs == null || jobs.size() == 0) { - return; - } - for (int i = jobs.size() - 1; i >= 0; i--) { - JobStatus js = jobs.valueAt(i); - // Effective standby bucket can change after this in some situations so - // use the real bucket so that the job is tracked by the controllers. - if ((bucketIndex == RESTRICTED_INDEX - || js.getStandbyBucket() == RESTRICTED_INDEX) - && bucketIndex != js.getStandbyBucket()) { - restrictedChanges.add(js); - } - js.setStandbyBucket(bucketIndex); - } - Timer timer = mPkgTimers.get(userId, packageName); - if (timer != null && timer.isActive()) { - timer.rescheduleCutoff(); - } - if (maybeUpdateConstraintForPkgLocked(userId, packageName)) { - mStateChangedListener.onControllerStateChanged(); - } - } - if (restrictedChanges.size() > 0) { - mStateChangedListener.onRestrictedBucketChanged(restrictedChanges); - } + updateStandbyBucket(userId, packageName, bucketIndex); }); } } + @VisibleForTesting + void updateStandbyBucket( + final int userId, final @NonNull String packageName, final int bucketIndex) { + if (DEBUG) { + Slog.i(TAG, "Moving pkg " + string(userId, packageName) + + " to bucketIndex " + bucketIndex); + } + List<JobStatus> restrictedChanges = new ArrayList<>(); + synchronized (mLock) { + ArraySet<JobStatus> jobs = mTrackedJobs.get(userId, packageName); + if (jobs == null || jobs.size() == 0) { + return; + } + for (int i = jobs.size() - 1; i >= 0; i--) { + JobStatus js = jobs.valueAt(i); + // Effective standby bucket can change after this in some situations so + // use the real bucket so that the job is tracked by the controllers. + if ((bucketIndex == RESTRICTED_INDEX || js.getStandbyBucket() == RESTRICTED_INDEX) + && bucketIndex != js.getStandbyBucket()) { + restrictedChanges.add(js); + } + js.setStandbyBucket(bucketIndex); + } + Timer timer = mPkgTimers.get(userId, packageName); + if (timer != null && timer.isActive()) { + timer.rescheduleCutoff(); + } + if (maybeUpdateConstraintForPkgLocked(userId, packageName)) { + mStateChangedListener.onControllerStateChanged(); + } + } + if (restrictedChanges.size() > 0) { + mStateChangedListener.onRestrictedBucketChanged(restrictedChanges); + } + } + private final class DeleteTimingSessionsFunctor implements Consumer<List<TimingSession>> { private final Predicate<TimingSession> mTooOld = new Predicate<TimingSession>() { public boolean test(TimingSession ts) { diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java index 1157ee905b86..0b0923a67de6 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -62,6 +62,7 @@ import android.app.usage.AppStandbyInfo; import android.app.usage.UsageEvents; import android.app.usage.UsageStatsManager.StandbyBuckets; import android.app.usage.UsageStatsManager.SystemForcedReasons; +import android.app.usage.UsageStatsManagerInternal; import android.appwidget.AppWidgetManager; import android.content.BroadcastReceiver; import android.content.ContentResolver; @@ -128,9 +129,10 @@ import java.util.concurrent.CountDownLatch; * Manages the standby state of an app, listening to various events. * * Unit test: - atest com.android.server.usage.AppStandbyControllerTests + * atest com.android.server.usage.AppStandbyControllerTests */ -public class AppStandbyController implements AppStandbyInternal { +public class AppStandbyController + implements AppStandbyInternal, UsageStatsManagerInternal.UsageEventListener { private static final String TAG = "AppStandbyController"; // Do not submit with true. @@ -468,10 +470,21 @@ public class AppStandbyController implements AppStandbyInternal { @VisibleForTesting void setAppIdleEnabled(boolean enabled) { + // Don't call out to USM with the lock held. Also, register the listener before we + // change our internal state so no events fall through the cracks. + final UsageStatsManagerInternal usmi = + LocalServices.getService(UsageStatsManagerInternal.class); + if (enabled) { + usmi.registerListener(this); + } else { + usmi.unregisterListener(this); + } + synchronized (mAppIdleLock) { if (mAppIdleEnabled != enabled) { final boolean oldParoleState = isInParole(); mAppIdleEnabled = enabled; + if (isInParole() != oldParoleState) { postParoleStateChanged(); } @@ -489,6 +502,11 @@ public class AppStandbyController implements AppStandbyInternal { mInjector.onBootPhase(phase); if (phase == PHASE_SYSTEM_SERVICES_READY) { Slog.d(TAG, "Setting app idle enabled state"); + + if (mAppIdleEnabled) { + LocalServices.getService(UsageStatsManagerInternal.class).registerListener(this); + } + // Observe changes to the threshold ConstantsObserver settingsObserver = new ConstantsObserver(mHandler); settingsObserver.start(); @@ -912,8 +930,10 @@ public class AppStandbyController implements AppStandbyInternal { } } - @Override - public void reportEvent(UsageEvents.Event event, int userId) { + /** + * Callback to inform listeners of a new event. + */ + public void onUsageEvent(int userId, @NonNull UsageEvents.Event event) { if (!mAppIdleEnabled) return; final int eventType = event.getEventType(); if ((eventType == UsageEvents.Event.ACTIVITY_RESUMED diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index 046145f90808..3b4823e799ce 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -605,6 +605,7 @@ bool BootAnimation::threadLoop() { result = movie(); } + mCallbacks->shutdown(); eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglDestroyContext(mDisplay, mContext); eglDestroySurface(mDisplay, mSurface); @@ -691,7 +692,6 @@ void BootAnimation::checkExit() { int exitnow = atoi(value); if (exitnow) { requestExit(); - mCallbacks->shutdown(); } } diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index d225f966c39d..6aad82fda915 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -14,34 +14,6 @@ // limitations under the License. // -// ========================================================== -// Build the library for use on the host -// ========================================================== -cc_library_host_shared { - name: "libstats_proto_host", - srcs: [ - "src/atoms.proto", - "src/atom_field_options.proto", - ], - - shared_libs: [ - "libplatformprotos", - ], - - proto: { - type: "full", - export_proto_headers: true, - include_dirs: [ - "external/protobuf/src", - ], - }, - - export_shared_lib_headers: [ - "libplatformprotos", - ] - -} - cc_defaults { name: "statsd_defaults", @@ -277,9 +249,8 @@ cc_test { // atom_field_options.proto needs field_options.proto, but that is // not included in libprotobuf-cpp-lite, so compile it here. ":libprotobuf-internal-protos", + ":libstats_internal_protos", - "src/atom_field_options.proto", - "src/atoms.proto", "src/shell/shell_data.proto", "src/stats_log.proto", "tests/AlarmMonitor_test.cpp", @@ -346,7 +317,10 @@ cc_test { proto: { type: "lite", - include_dirs: ["external/protobuf/src"], + include_dirs: [ + "external/protobuf/src", + "frameworks/proto_logging/stats", + ], }, } @@ -363,6 +337,7 @@ cc_benchmark { // atom_field_options.proto needs field_options.proto, but that is // not included in libprotobuf-cpp-lite, so compile it here. ":libprotobuf-internal-protos", + ":libstats_internal_protos", "benchmark/duration_metric_benchmark.cpp", "benchmark/filter_value_benchmark.cpp", @@ -372,14 +347,15 @@ cc_benchmark { "benchmark/main.cpp", "benchmark/metric_util.cpp", "benchmark/stats_write_benchmark.cpp", - "src/atom_field_options.proto", - "src/atoms.proto", "src/stats_log.proto", ], proto: { type: "lite", - include_dirs: ["external/protobuf/src"], + include_dirs: [ + "external/protobuf/src", + "frameworks/proto_logging/stats", + ], }, cflags: [ @@ -411,11 +387,14 @@ java_library { sdk_version: "core_current", proto: { type: "lite", - include_dirs: ["external/protobuf/src"], + include_dirs: [ + "external/protobuf/src", + "frameworks/proto_logging/stats", + ], }, srcs: [ - "src/atoms.proto", + ":libstats_atoms_proto", "src/shell/shell_config.proto", "src/shell/shell_data.proto", "src/stats_log.proto", @@ -437,10 +416,13 @@ java_library { proto: { type: "nano", output_params: ["store_unknown_fields=true"], - include_dirs: ["external/protobuf/src"], + include_dirs: [ + "external/protobuf/src", + "frameworks/proto_logging/stats", + ], }, srcs: [ - "src/atoms.proto", + ":libstats_atoms_proto", "src/shell/shell_config.proto", "src/shell/shell_data.proto", "src/stats_log.proto", diff --git a/cmds/statsd/src/OWNERS b/cmds/statsd/src/OWNERS new file mode 100644 index 000000000000..0f3ddf7388d9 --- /dev/null +++ b/cmds/statsd/src/OWNERS @@ -0,0 +1,6 @@ +# Temporary OWNERS Block to assist with migration +# bug: 167962588 +per-file *atoms.proto = set noparent +per-file *atom_field_options.proto = set noparent +per-file *atoms.proto = baligh@google.com, yro@google.com, singhtejinder@google.com, jeffreyhuang@google.com +per-file *atom_field_options.proto = baligh@google.com, yro@google.com, singhtejinder@google.com, jeffreyhuang@google.com diff --git a/cmds/statsd/src/atom_field_options.proto b/cmds/statsd/src/atom_field_options.proto deleted file mode 100644 index ff5717e4fa78..000000000000 --- a/cmds/statsd/src/atom_field_options.proto +++ /dev/null @@ -1,116 +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. - */ - -syntax = "proto2"; - -package android.os.statsd; -option java_package = "com.android.os"; -option java_multiple_files = true; -option java_outer_classname = "AtomFieldOptions"; - -import "google/protobuf/descriptor.proto"; - -// Used to annotate an atom that represents a state change. A state change atom must have exactly -// ONE exclusive state field, and any number of primary key fields. For example, message -// UidProcessStateChanged { -// optional int32 uid = 1 [(state_field_option).primary_field = true]; -// optional android.app.ProcessStateEnum state = -// 2 [(state_field_option).exclusive_state = true]; -// } -// Each UidProcessStateChanged atom event represents a state change for a specific uid. -// A new state automatically overrides the previous state. -// -// If the atom has 2 or more primary fields, it means the combination of the -// primary fields are the primary key. -// For example: -// message ThreadStateChanged { -// optional int32 pid = 1 [(state_field_option).primary_field = true]; -// optional int32 tid = 2 [(state_field_option).primary_field = true]; -// optional int32 state = 3 [(state_field_option).exclusive_state = true]; -// } -// -// Sometimes, there is no primary key field, when the state is GLOBAL. -// For example, -// message ScreenStateChanged { -// optional android.view.DisplayStateEnum state = -// 1 [(state_field_option).exclusive_state = true]; -// } -// -// For state atoms with attribution chain, sometimes the primary key is the first uid in the chain. -// For example: -// message AudioStateChanged { -// repeated AttributionNode attribution_node = 1 -// [(stateFieldOption).primary_field_first_uid = true]; -// -// enum State { -// OFF = 0; -// ON = 1; -// // RESET indicates all audio stopped. Used when it (re)starts (e.g. after it crashes). -// RESET = 2; -// } -// optional State state = 2 [(stateFieldOption).exclusive_state = true]; -// } -message StateAtomFieldOption { - // Fields that represent the key that the state belongs to. - // Used on simple proto fields. Do not use on attribution chains. - optional bool primary_field = 1 [default = false]; - - // The field that represents the state. It's an exclusive state. - optional bool exclusive_state = 2 [default = false]; - - // Used on an attribution chain field to indicate that the first uid is the - // primary field. - optional bool primary_field_first_uid = 3 [default = false]; - - // Note: We cannot annotate directly on the enums because many enums are imported from other - // proto files in the platform. proto-lite cc library does not support annotations unfortunately - - // Knowing the default state value allows state trackers to remove entries that become the - // default state. If there is no default value specified, the default value is unknown, and all - // states will be tracked in memory. - optional int32 default_state_value = 4; - - // A reset state signals all states go to default value. For example, BLE reset means all active - // BLE scans are to be turned off. - optional int32 trigger_state_reset_value = 5; - - // If the state change needs to count nesting. - optional bool nested = 6 [default = true]; -} - -// Used to generate StatsLog.write APIs. -enum LogMode { - MODE_UNSET = 0; - // Log fields as their actual types e.g., all primary data types. - // Or fields that are hardcoded in stats_log_api_gen tool e.g., AttributionNode - MODE_AUTOMATIC = 1; - // Log fields in their proto binary format. These fields will not be parsed in statsd - MODE_BYTES = 2; -} - -extend google.protobuf.FieldOptions { - // Flags to decorate an atom that presents a state change. - optional StateAtomFieldOption state_field_option = 50000; - - // Flags to decorate the uid fields in an atom. - optional bool is_uid = 50001 [default = false]; - - optional LogMode log_mode = 50002 [default = MODE_AUTOMATIC]; - - repeated string module = 50004; - - optional bool truncate_timestamp = 50005 [default = false]; -} diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto deleted file mode 100644 index 0f60eaebd59f..000000000000 --- a/cmds/statsd/src/atoms.proto +++ /dev/null @@ -1,12490 +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 android.os.statsd; -option java_package = "com.android.os"; -option java_outer_classname = "AtomsProto"; - -import "frameworks/base/cmds/statsd/src/atom_field_options.proto"; -import "frameworks/base/core/proto/android/app/enums.proto"; -import "frameworks/base/core/proto/android/app/job/enums.proto"; -import "frameworks/base/core/proto/android/app/settings_enums.proto"; -import "frameworks/base/core/proto/android/app/media_output_enum.proto"; -import "frameworks/base/core/proto/android/app/tvsettings_enums.proto"; -import "frameworks/base/core/proto/android/bluetooth/a2dp/enums.proto"; -import "frameworks/base/core/proto/android/bluetooth/enums.proto"; -import "frameworks/base/core/proto/android/bluetooth/hci/enums.proto"; -import "frameworks/base/core/proto/android/bluetooth/hfp/enums.proto"; -import "frameworks/base/core/proto/android/bluetooth/smp/enums.proto"; -import "frameworks/base/core/proto/android/debug/enums.proto"; -import "frameworks/base/core/proto/android/hardware/biometrics/enums.proto"; -import "frameworks/base/core/proto/android/hardware/sensor/assist/enums.proto"; -import "frameworks/base/core/proto/android/net/networkcapabilities.proto"; -import "frameworks/base/core/proto/android/os/enums.proto"; -import "frameworks/base/core/proto/android/server/connectivity/data_stall_event.proto"; -import "frameworks/base/core/proto/android/server/enums.proto"; -import "frameworks/base/core/proto/android/server/job/enums.proto"; -import "frameworks/base/core/proto/android/server/location/enums.proto"; -import "frameworks/base/core/proto/android/service/procstats_enum.proto"; -import "frameworks/base/core/proto/android/service/usb.proto"; -import "frameworks/base/core/proto/android/stats/connectivity/network_stack.proto"; -import "frameworks/base/core/proto/android/stats/connectivity/tethering.proto"; -import "frameworks/base/core/proto/android/stats/dnsresolver/dns_resolver.proto"; -import "frameworks/base/core/proto/android/stats/devicepolicy/device_policy.proto"; -import "frameworks/base/core/proto/android/stats/devicepolicy/device_policy_enums.proto"; -import "frameworks/base/core/proto/android/stats/docsui/docsui_enums.proto"; -import "frameworks/base/core/proto/android/stats/accessibility/accessibility_enums.proto"; -import "frameworks/base/core/proto/android/stats/enums.proto"; -import "frameworks/base/core/proto/android/stats/hdmi/enums.proto"; -import "frameworks/base/core/proto/android/stats/intelligence/enums.proto"; -import "frameworks/base/core/proto/android/stats/launcher/launcher.proto"; -import "frameworks/base/core/proto/android/stats/location/location_enums.proto"; -import "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.proto"; -import "frameworks/base/core/proto/android/stats/mediaprovider/mediaprovider_enums.proto"; -import "frameworks/base/core/proto/android/stats/storage/storage_enums.proto"; -import "frameworks/base/core/proto/android/stats/style/style_enums.proto"; -import "frameworks/base/core/proto/android/stats/sysui/notification_enums.proto"; -import "frameworks/base/core/proto/android/stats/tls/enums.proto"; -import "frameworks/base/core/proto/android/telecomm/enums.proto"; -import "frameworks/base/core/proto/android/telephony/enums.proto"; -import "frameworks/base/core/proto/android/view/enums.proto"; -import "frameworks/base/core/proto/android/wifi/enums.proto"; -import "frameworks/base/core/proto/android/stats/textclassifier/textclassifier_enums.proto"; -import "frameworks/base/core/proto/android/stats/otaupdate/updateengine_enums.proto"; - -/** - * The primary atom class. This message defines all of the available - * raw stats log events from the Android system, also known as "atoms." - * - * This field contains a single oneof with all of the available messages. - * The stats-log-api-gen tool runs as part of the Android build and - * generates the android.util.StatsLog class, which contains the constants - * and methods that Android uses to log. - * - * This Atom class is not actually built into the Android system. - * Instead, statsd on Android constructs these messages synthetically, - * in the format defined here and in stats_log.proto. - */ -message Atom { - // Pushed atoms start at 2. - oneof pushed { - // For StatsLog reasons, 1 is illegal and will not work. Must start at 2. - BleScanStateChanged ble_scan_state_changed = 2 - [(module) = "bluetooth", (module) = "statsdtest"]; - ProcessStateChanged process_state_changed = 3 [(module) = "framework"]; - BleScanResultReceived ble_scan_result_received = 4 [(module) = "bluetooth"]; - SensorStateChanged sensor_state_changed = - 5 [(module) = "framework", (module) = "statsdtest"]; - GpsScanStateChanged gps_scan_state_changed = 6 [(module) = "framework"]; - SyncStateChanged sync_state_changed = 7 [(module) = "framework", (module) = "statsdtest"]; - ScheduledJobStateChanged scheduled_job_state_changed = - 8 [(module) = "framework", (module) = "statsdtest"]; - ScreenBrightnessChanged screen_brightness_changed = - 9 [(module) = "framework", (module) = "statsdtest"]; - WakelockStateChanged wakelock_state_changed = - 10 [(module) = "framework", (module) = "statsdtest"]; - LongPartialWakelockStateChanged long_partial_wakelock_state_changed = - 11 [(module) = "framework"]; - MobileRadioPowerStateChanged mobile_radio_power_state_changed = - 12 [(module) = "framework", (truncate_timestamp) = true]; - WifiRadioPowerStateChanged wifi_radio_power_state_changed = 13 [(module) = "framework"]; - ActivityManagerSleepStateChanged activity_manager_sleep_state_changed = - 14 [(module) = "framework"]; - MemoryFactorStateChanged memory_factor_state_changed = 15 [(module) = "framework"]; - ExcessiveCpuUsageReported excessive_cpu_usage_reported = 16 [(module) = "framework"]; - CachedKillReported cached_kill_reported = 17 [(module) = "framework"]; - ProcessMemoryStatReported process_memory_stat_reported = 18 [(module) = "framework"]; - LauncherUIChanged launcher_event = 19 [(module) = "sysui"]; - BatterySaverModeStateChanged battery_saver_mode_state_changed = - 20 [(module) = "framework", (module) = "statsdtest"]; - DeviceIdleModeStateChanged device_idle_mode_state_changed = 21 [(module) = "framework"]; - DeviceIdlingModeStateChanged device_idling_mode_state_changed = 22 [(module) = "framework"]; - AudioStateChanged audio_state_changed = - 23 [(module) = "framework", (truncate_timestamp) = true]; - MediaCodecStateChanged media_codec_state_changed = 24 [(module) = "framework"]; - CameraStateChanged camera_state_changed = 25 [(module) = "framework"]; - FlashlightStateChanged flashlight_state_changed = 26 [(module) = "framework"]; - UidProcessStateChanged uid_process_state_changed = - 27 [(module) = "framework", (module) = "statsdtest"]; - ProcessLifeCycleStateChanged process_life_cycle_state_changed = - 28 [(module) = "framework", (module) = "statsdtest"]; - ScreenStateChanged screen_state_changed = - 29 [(module) = "framework", (module) = "statsdtest"]; - BatteryLevelChanged battery_level_changed = - 30 [(module) = "framework", (module) = "statsdtest"]; - ChargingStateChanged charging_state_changed = 31 [(module) = "framework"]; - PluggedStateChanged plugged_state_changed = 32 - [(module) = "framework", (module) = "statsdtest"]; - InteractiveStateChanged interactive_state_changed = 33 [(module) = "framework"]; - TouchEventReported touch_event_reported = 34; - WakeupAlarmOccurred wakeup_alarm_occurred = 35 [(module) = "framework"]; - KernelWakeupReported kernel_wakeup_reported = 36 [(module) = "framework"]; - WifiLockStateChanged wifi_lock_state_changed = 37 [(module) = "wifi"]; - WifiSignalStrengthChanged wifi_signal_strength_changed = 38 [(module) = "wifi"]; - WifiScanStateChanged wifi_scan_state_changed = 39 [(module) = "wifi"]; - PhoneSignalStrengthChanged phone_signal_strength_changed = - 40 [(module) = "framework", (truncate_timestamp) = true]; - SettingChanged setting_changed = 41 [(module) = "framework"]; - ActivityForegroundStateChanged activity_foreground_state_changed = - 42 [(module) = "framework", (module) = "statsdtest"]; - IsolatedUidChanged isolated_uid_changed = - 43 [(module) = "framework", (module) = "statsd", (module) = "statsdtest"]; - PacketWakeupOccurred packet_wakeup_occurred = 44 [(module) = "framework"]; - WallClockTimeShifted wall_clock_time_shifted = 45 [(module) = "framework"]; - AnomalyDetected anomaly_detected = 46 [(module) = "statsd"]; - AppBreadcrumbReported app_breadcrumb_reported = 47 [(module) = "statsd"]; - AppStartOccurred app_start_occurred = 48 [(module) = "framework", (module) = "statsdtest"]; - AppStartCanceled app_start_canceled = 49 [(module) = "framework"]; - AppStartFullyDrawn app_start_fully_drawn = 50 [(module) = "framework"]; - LmkKillOccurred lmk_kill_occurred = 51 [(module) = "lmkd"]; - PictureInPictureStateChanged picture_in_picture_state_changed = 52 [(module) = "framework"]; - WifiMulticastLockStateChanged wifi_multicast_lock_state_changed = 53 [(module) = "wifi"]; - LmkStateChanged lmk_state_changed = 54 [(module) = "lmkd"]; - AppStartMemoryStateCaptured app_start_memory_state_captured = 55 [(module) = "framework"]; - ShutdownSequenceReported shutdown_sequence_reported = 56 [(module) = "framework"]; - BootSequenceReported boot_sequence_reported = 57; - DaveyOccurred davey_occurred = 58 [(module) = "statsd", deprecated = true]; - OverlayStateChanged overlay_state_changed = - 59 [(module) = "framework", (module) = "statsdtest"]; - ForegroundServiceStateChanged foreground_service_state_changed - = 60 [(module) = "framework"]; - CallStateChanged call_state_changed = - 61 [(module) = "telecom", (truncate_timestamp) = true]; - KeyguardStateChanged keyguard_state_changed = 62 [(module) = "sysui"]; - KeyguardBouncerStateChanged keyguard_bouncer_state_changed = 63 [(module) = "sysui"]; - KeyguardBouncerPasswordEntered keyguard_bouncer_password_entered = 64 [(module) = "sysui"]; - AppDied app_died = 65 [(module) = "framework"]; - ResourceConfigurationChanged resource_configuration_changed = 66 [(module) = "framework"]; - BluetoothEnabledStateChanged bluetooth_enabled_state_changed = 67 [(module) = "framework"]; - BluetoothConnectionStateChanged bluetooth_connection_state_changed = - 68 [(module) = "bluetooth"]; - GpsSignalQualityChanged gps_signal_quality_changed = 69 [(module) = "framework"]; - UsbConnectorStateChanged usb_connector_state_changed = 70 [(module) = "framework"]; - SpeakerImpedanceReported speaker_impedance_reported = 71; - HardwareFailed hardware_failed = 72; - PhysicalDropDetected physical_drop_detected = 73; - ChargeCyclesReported charge_cycles_reported = 74; - MobileConnectionStateChanged mobile_connection_state_changed = 75 [(module) = "telephony"]; - MobileRadioTechnologyChanged mobile_radio_technology_changed = 76 [(module) = "telephony"]; - UsbDeviceAttached usb_device_attached = 77 [(module) = "framework"]; - AppCrashOccurred app_crash_occurred = 78 [(module) = "framework", (module) = "statsdtest"]; - ANROccurred anr_occurred = 79 [(module) = "framework"]; - WTFOccurred wtf_occurred = 80 [(module) = "framework"]; - LowMemReported low_mem_reported = 81 [(module) = "framework"]; - GenericAtom generic_atom = 82; - KeyValuePairsAtom key_value_pairs_atom = 83 [(module) = "framework", (module) = "statsd"]; - VibratorStateChanged vibrator_state_changed = 84 [(module) = "framework"]; - DeferredJobStatsReported deferred_job_stats_reported = 85 [(module) = "framework"]; - ThermalThrottlingStateChanged thermal_throttling = 86 [deprecated=true]; - BiometricAcquired biometric_acquired = 87 [(module) = "framework"]; - BiometricAuthenticated biometric_authenticated = 88 [(module) = "framework"]; - BiometricErrorOccurred biometric_error_occurred = 89 [(module) = "framework"]; - UiEventReported ui_event_reported = 90 [(module) = "framework", (module) = "sysui"]; - BatteryHealthSnapshot battery_health_snapshot = 91; - SlowIo slow_io = 92; - BatteryCausedShutdown battery_caused_shutdown = 93; - PhoneServiceStateChanged phone_service_state_changed = 94 [(module) = "framework"]; - PhoneStateChanged phone_state_changed = 95 [(module) = "framework"]; - UserRestrictionChanged user_restriction_changed = 96; - SettingsUIChanged settings_ui_changed = 97 [(module) = "settings"]; - ConnectivityStateChanged connectivity_state_changed = 98 [(module) = "framework"]; - // TODO: service state change is very noisy shortly after boot, as well - // as at other transitions - coming out of doze, device plugged in, etc. - // Consider removing this if it becomes a problem - ServiceStateChanged service_state_changed = 99 [(module) = "framework"]; - ServiceLaunchReported service_launch_reported = 100 [(module) = "framework"]; - FlagFlipUpdateOccurred flag_flip_update_occurred = 101 [(module) = "framework"]; - BinaryPushStateChanged binary_push_state_changed = 102 [(module) = "statsd"]; - DevicePolicyEvent device_policy_event = 103 [(module) = "framework"]; - DocsUIFileOperationCanceledReported docs_ui_file_op_canceled = 104 [(module) = "docsui"]; - DocsUIFileOperationCopyMoveModeReported docs_ui_file_op_copy_move_mode_reported = - 105 [(module) = "docsui"]; - DocsUIFileOperationFailureReported docs_ui_file_op_failure = 106 [(module) = "docsui"]; - DocsUIFileOperationReported docs_ui_provider_file_op = 107 [(module) = "docsui"]; - DocsUIInvalidScopedAccessRequestReported docs_ui_invalid_scoped_access_request = - 108 [(module) = "docsui"]; - DocsUILaunchReported docs_ui_launch_reported = 109 [(module) = "docsui"]; - DocsUIRootVisitedReported docs_ui_root_visited = 110 [(module) = "docsui"]; - DocsUIStartupMsReported docs_ui_startup_ms = 111 [(module) = "docsui"]; - DocsUIUserActionReported docs_ui_user_action_reported = 112 [(module) = "docsui"]; - WifiEnabledStateChanged wifi_enabled_state_changed = 113 [(module) = "framework"]; - WifiRunningStateChanged wifi_running_state_changed = 114 - [(module) = "framework", deprecated = true]; - AppCompacted app_compacted = 115 [(module) = "framework"]; - NetworkDnsEventReported network_dns_event_reported = 116 [(module) = "resolv"]; - DocsUIPickerLaunchedFromReported docs_ui_picker_launched_from_reported = - 117 [(module) = "docsui"]; - DocsUIPickResultReported docs_ui_pick_result_reported = 118 [(module) = "docsui"]; - DocsUISearchModeReported docs_ui_search_mode_reported = 119 [(module) = "docsui"]; - DocsUISearchTypeReported docs_ui_search_type_reported = 120 [(module) = "docsui"]; - DataStallEvent data_stall_event = 121 [(module) = "network_stack"]; - RescuePartyResetReported rescue_party_reset_reported = 122 [(module) = "framework"]; - SignedConfigReported signed_config_reported = 123 [(module) = "framework"]; - GnssNiEventReported gnss_ni_event_reported = 124 [(module) = "framework"]; - BluetoothLinkLayerConnectionEvent bluetooth_link_layer_connection_event = - 125 [(module) = "bluetooth"]; - BluetoothAclConnectionStateChanged bluetooth_acl_connection_state_changed = - 126 [(module) = "bluetooth"]; - BluetoothScoConnectionStateChanged bluetooth_sco_connection_state_changed = - 127 [(module) = "bluetooth"]; - AppDowngraded app_downgraded = 128 [(module) = "framework"]; - AppOptimizedAfterDowngraded app_optimized_after_downgraded = 129; - LowStorageStateChanged low_storage_state_changed = 130 [(module) = "framework"]; - GnssNfwNotificationReported gnss_nfw_notification_reported = 131 [(module) = "framework"]; - GnssConfigurationReported gnss_configuration_reported = 132 [(module) = "framework"]; - UsbPortOverheatEvent usb_port_overheat_event_reported = 133; - NfcErrorOccurred nfc_error_occurred = 134 [(module) = "nfc"]; - NfcStateChanged nfc_state_changed = 135 [(module) = "nfc"]; - NfcBeamOccurred nfc_beam_occurred = 136 [(module) = "nfc"]; - NfcCardemulationOccurred nfc_cardemulation_occurred = 137 [(module) = "nfc"]; - NfcTagOccurred nfc_tag_occurred = 138 [(module) = "nfc"]; - NfcHceTransactionOccurred nfc_hce_transaction_occurred = 139 [(module) = "nfc"]; - SeStateChanged se_state_changed = 140 [(module) = "secure_element"]; - SeOmapiReported se_omapi_reported = 141 [(module) = "secure_element"]; - BroadcastDispatchLatencyReported broadcast_dispatch_latency_reported = - 142 [(module) = "framework"]; - AttentionManagerServiceResultReported attention_manager_service_result_reported = - 143 [(module) = "framework"]; - AdbConnectionChanged adb_connection_changed = 144 [(module) = "framework"]; - SpeechDspStatReported speech_dsp_stat_reported = 145; - UsbContaminantReported usb_contaminant_reported = 146 [(module) = "framework"]; - WatchdogRollbackOccurred watchdog_rollback_occurred = - 147 [(module) = "framework", (module) = "statsd"]; - BiometricSystemHealthIssueDetected biometric_system_health_issue_detected = - 148 [(module) = "framework"]; - BubbleUIChanged bubble_ui_changed = 149 [(module) = "framework"]; - ScheduledJobConstraintChanged scheduled_job_constraint_changed = - 150 [(module) = "framework"]; - BluetoothActiveDeviceChanged bluetooth_active_device_changed = - 151 [(module) = "bluetooth"]; - BluetoothA2dpPlaybackStateChanged bluetooth_a2dp_playback_state_changed = - 152 [(module) = "bluetooth"]; - BluetoothA2dpCodecConfigChanged bluetooth_a2dp_codec_config_changed = - 153 [(module) = "bluetooth"]; - BluetoothA2dpCodecCapabilityChanged bluetooth_a2dp_codec_capability_changed = - 154 [(module) = "bluetooth"]; - BluetoothA2dpAudioUnderrunReported bluetooth_a2dp_audio_underrun_reported = - 155 [(module) = "bluetooth"]; - BluetoothA2dpAudioOverrunReported bluetooth_a2dp_audio_overrun_reported = - 156 [(module) = "bluetooth"]; - BluetoothDeviceRssiReported bluetooth_device_rssi_reported = - 157 [(module) = "bluetooth"]; - BluetoothDeviceFailedContactCounterReported - bluetooth_device_failed_contact_counter_reported = 158 [(module) = "bluetooth"]; - BluetoothDeviceTxPowerLevelReported bluetooth_device_tx_power_level_reported = - 159 [(module) = "bluetooth"]; - BluetoothHciTimeoutReported bluetooth_hci_timeout_reported = - 160 [(module) = "bluetooth"]; - BluetoothQualityReportReported bluetooth_quality_report_reported = - 161 [(module) = "bluetooth"]; - BluetoothDeviceInfoReported bluetooth_device_info_reported = - 162 [(module) = "bluetooth"]; - BluetoothRemoteVersionInfoReported bluetooth_remote_version_info_reported = - 163 [(module) = "bluetooth"]; - BluetoothSdpAttributeReported bluetooth_sdp_attribute_reported = - 164 [(module) = "bluetooth"]; - BluetoothBondStateChanged bluetooth_bond_state_changed = - 165 [(module) = "bluetooth"]; - BluetoothClassicPairingEventReported bluetooth_classic_pairing_event_reported = - 166 [(module) = "bluetooth"]; - BluetoothSmpPairingEventReported bluetooth_smp_pairing_event_reported = - 167 [(module) = "bluetooth"]; - ScreenTimeoutExtensionReported screen_timeout_extension_reported = - 168 [(module) = "framework"]; - ProcessStartTime process_start_time = 169 [(module) = "framework"]; - PermissionGrantRequestResultReported permission_grant_request_result_reported = - 170 [(module) = "permissioncontroller"]; - BluetoothSocketConnectionStateChanged bluetooth_socket_connection_state_changed = 171; - DeviceIdentifierAccessDenied device_identifier_access_denied = - 172 [(module) = "telephony_common"]; - BubbleDeveloperErrorReported bubble_developer_error_reported = 173 [(module) = "framework"]; - AssistGestureStageReported assist_gesture_stage_reported = 174 [(module) = "sysui"]; - AssistGestureFeedbackReported assist_gesture_feedback_reported = 175 [(module) = "sysui"]; - AssistGestureProgressReported assist_gesture_progress_reported = 176 [(module) = "sysui"]; - TouchGestureClassified touch_gesture_classified = 177 [(module) = "framework"]; - HiddenApiUsed hidden_api_used = 178 [(module) = "framework"]; - StyleUIChanged style_ui_changed = 179 [(module) = "sysui"]; - PrivacyIndicatorsInteracted privacy_indicators_interacted = - 180 [(module) = "permissioncontroller"]; - AppInstallOnExternalStorageReported app_install_on_external_storage_reported = - 181 [(module) = "framework"]; - NetworkStackReported network_stack_reported = 182 [(module) = "network_stack"]; - AppMovedStorageReported app_moved_storage_reported = 183 [(module) = "framework"]; - BiometricEnrolled biometric_enrolled = 184 [(module) = "framework"]; - SystemServerWatchdogOccurred system_server_watchdog_occurred = 185 [(module) = "framework"]; - TombStoneOccurred tomb_stone_occurred = 186 [(module) = "framework"]; - BluetoothClassOfDeviceReported bluetooth_class_of_device_reported = - 187 [(module) = "bluetooth"]; - IntelligenceEventReported intelligence_event_reported = - 188 [(module) = "intelligence"]; - ThermalThrottlingSeverityStateChanged thermal_throttling_severity_state_changed = - 189 [(module) = "framework"]; - RoleRequestResultReported role_request_result_reported = - 190 [(module) = "permissioncontroller"]; - MediametricsAudiopolicyReported mediametrics_audiopolicy_reported = 191; - MediametricsAudiorecordReported mediametrics_audiorecord_reported = 192; - MediametricsAudiothreadReported mediametrics_audiothread_reported = 193; - MediametricsAudiotrackReported mediametrics_audiotrack_reported = 194; - MediametricsCodecReported mediametrics_codec_reported = 195; - MediametricsDrmWidevineReported mediametrics_drm_widevine_reported = 196; - MediametricsExtractorReported mediametrics_extractor_reported = 197; - MediametricsMediadrmReported mediametrics_mediadrm_reported = 198; - MediametricsNuPlayerReported mediametrics_nuplayer_reported = 199; - MediametricsRecorderReported mediametrics_recorder_reported = 200; - MediametricsDrmManagerReported mediametrics_drmmanager_reported = 201; - CarPowerStateChanged car_power_state_changed = 203 [(module) = "car"]; - GarageModeInfo garage_mode_info = 204 [(module) = "car"]; - TestAtomReported test_atom_reported = 205 [(module) = "cts"]; - ContentCaptureCallerMismatchReported content_capture_caller_mismatch_reported = - 206 [(module) = "framework"]; - ContentCaptureServiceEvents content_capture_service_events = 207 [(module) = "framework"]; - ContentCaptureSessionEvents content_capture_session_events = 208 [(module) = "framework"]; - ContentCaptureFlushed content_capture_flushed = 209 [(module) = "framework"]; - LocationManagerApiUsageReported location_manager_api_usage_reported = - 210 [(module) = "framework"]; - ReviewPermissionsFragmentResultReported review_permissions_fragment_result_reported = - 211 [(module) = "permissioncontroller"]; - RuntimePermissionsUpgradeResult runtime_permissions_upgrade_result = - 212 [(module) = "permissioncontroller"]; - GrantPermissionsActivityButtonActions grant_permissions_activity_button_actions = - 213 [(module) = "permissioncontroller"]; - LocationAccessCheckNotificationAction location_access_check_notification_action = - 214 [(module) = "permissioncontroller"]; - AppPermissionFragmentActionReported app_permission_fragment_action_reported = - 215 [(module) = "permissioncontroller"]; - AppPermissionFragmentViewed app_permission_fragment_viewed = - 216 [(module) = "permissioncontroller"]; - AppPermissionsFragmentViewed app_permissions_fragment_viewed = - 217 [(module) = "permissioncontroller"]; - PermissionAppsFragmentViewed permission_apps_fragment_viewed = - 218 [(module) = "permissioncontroller"]; - TextSelectionEvent text_selection_event = 219 [(module) = "textclassifier"]; - TextLinkifyEvent text_linkify_event = 220 [(module) = "textclassifier"]; - ConversationActionsEvent conversation_actions_event = 221 [(module) = "textclassifier"]; - LanguageDetectionEvent language_detection_event = 222 [(module) = "textclassifier"]; - ExclusionRectStateChanged exclusion_rect_state_changed = 223 [(module) = "framework"]; - BackGesture back_gesture_reported_reported = 224 [(module) = "sysui"]; - UpdateEngineUpdateAttemptReported update_engine_update_attempt_reported = 225; - UpdateEngineSuccessfulUpdateReported update_engine_successful_update_reported = 226; - CameraActionEvent camera_action_event = 227 [(module) = "framework"]; - AppCompatibilityChangeReported app_compatibility_change_reported = - 228 [(module) = "framework"]; - PerfettoUploaded perfetto_uploaded = 229 [(module) = "perfetto"]; - VmsClientConnectionStateChanged vms_client_connection_state_changed = - 230 [(module) = "car"]; - MediaProviderScanOccurred media_provider_scan_occurred = 233 [(module) = "mediaprovider"]; - MediaContentDeleted media_content_deleted = 234 [(module) = "mediaprovider"]; - MediaProviderPermissionRequested media_provider_permission_requested = - 235 [(module) = "mediaprovider"]; - MediaProviderSchemaChanged media_provider_schema_changed = 236 [(module) = "mediaprovider"]; - MediaProviderIdleMaintenanceFinished media_provider_idle_maintenance_finished = - 237 [(module) = "mediaprovider"]; - RebootEscrowRecoveryReported reboot_escrow_recovery_reported = 238 [(module) = "framework"]; - BootTimeEventDuration boot_time_event_duration_reported = 239 [(module) = "framework"]; - BootTimeEventElapsedTime boot_time_event_elapsed_time_reported = - 240 [(module) = "framework"]; - BootTimeEventUtcTime boot_time_event_utc_time_reported = 241; - BootTimeEventErrorCode boot_time_event_error_code_reported = 242 [(module) = "framework"]; - UserspaceRebootReported userspace_reboot_reported = 243 [(module) = "framework"]; - NotificationReported notification_reported = 244 [(module) = "framework"]; - NotificationPanelReported notification_panel_reported = 245 [(module) = "sysui"]; - NotificationChannelModified notification_channel_modified = 246 [(module) = "framework"]; - IntegrityCheckResultReported integrity_check_result_reported = 247 [(module) = "framework"]; - IntegrityRulesPushed integrity_rules_pushed = 248 [(module) = "framework"]; - CellBroadcastMessageReported cb_message_reported = - 249 [(module) = "cellbroadcast"]; - CellBroadcastMessageError cb_message_error = - 250 [(module) = "cellbroadcast"]; - WifiHealthStatReported wifi_health_stat_reported = 251 [(module) = "wifi"]; - WifiFailureStatReported wifi_failure_stat_reported = 252 [(module) = "wifi"]; - WifiConnectionResultReported wifi_connection_result_reported = 253 [(module) = "wifi"]; - AppFreezeChanged app_freeze_changed = 254 [(module) = "framework"]; - SnapshotMergeReported snapshot_merge_reported = 255; - ForegroundServiceAppOpSessionEnded foreground_service_app_op_session_ended = - 256 [(module) = "framework"]; - DisplayJankReported display_jank_reported = 257; - AppStandbyBucketChanged app_standby_bucket_changed = 258 [(module) = "framework"]; - SharesheetStarted sharesheet_started = 259 [(module) = "framework"]; - RankingSelected ranking_selected = 260 [(module) = "framework", (module) = "sysui"]; - TvSettingsUIInteracted tvsettings_ui_interacted = 261 [(module) = "tv_settings"]; - LauncherStaticLayout launcher_snapshot = 262 [(module) = "sysui"]; - PackageInstallerV2Reported package_installer_v2_reported = 263 [(module) = "framework"]; - UserLifecycleJourneyReported user_lifecycle_journey_reported = 264 [(module) = "framework"]; - UserLifecycleEventOccurred user_lifecycle_event_occurred = 265 [(module) = "framework"]; - AccessibilityShortcutReported accessibility_shortcut_reported = - 266 [(module) = "framework"]; - AccessibilityServiceReported accessibility_service_reported = 267 [(module) = "settings"]; - DocsUIDragAndDropReported docs_ui_drag_and_drop_reported = 268 [(module) = "docsui"]; - AppUsageEventOccurred app_usage_event_occurred = 269 [(module) = "framework"]; - AutoRevokeNotificationClicked auto_revoke_notification_clicked = - 270 [(module) = "permissioncontroller"]; - AutoRevokeFragmentAppViewed auto_revoke_fragment_app_viewed = - 271 [(module) = "permissioncontroller"]; - AutoRevokedAppInteraction auto_revoked_app_interaction = - 272 [(module) = "permissioncontroller", (module) = "settings"]; - AppPermissionGroupsFragmentAutoRevokeAction - app_permission_groups_fragment_auto_revoke_action = - 273 [(module) = "permissioncontroller"]; - EvsUsageStatsReported evs_usage_stats_reported = 274 [(module) = "evs"]; - AudioPowerUsageDataReported audio_power_usage_data_reported = 275; - TvTunerStateChanged tv_tuner_state_changed = 276 [(module) = "framework"]; - MediaOutputOpSwitchReported mediaoutput_op_switch_reported = - 277 [(module) = "settings"]; - CellBroadcastMessageFiltered cb_message_filtered = - 278 [(module) = "cellbroadcast"]; - TvTunerDvrStatus tv_tuner_dvr_status = 279 [(module) = "framework"]; - TvCasSessionOpenStatus tv_cas_session_open_status = - 280 [(module) = "framework"]; - AssistantInvocationReported assistant_invocation_reported = 281 [(module) = "framework"]; - DisplayWakeReported display_wake_reported = 282 [(module) = "framework"]; - CarUserHalModifyUserRequestReported car_user_hal_modify_user_request_reported = - 283 [(module) = "car"]; - CarUserHalModifyUserResponseReported car_user_hal_modify_user_response_reported = - 284 [(module) = "car"]; - CarUserHalPostSwitchResponseReported car_user_hal_post_switch_response_reported = - 285 [(module) = "car"]; - CarUserHalInitialUserInfoRequestReported car_user_hal_initial_user_info_request_reported = - 286 [(module) = "car"]; - CarUserHalInitialUserInfoResponseReported car_user_hal_initial_user_info_response_reported = - 287 [(module) = "car"]; - CarUserHalUserAssociationRequestReported car_user_hal_user_association_request_reported = - 288 [(module) = "car"]; - CarUserHalSetUserAssociationResponseReported car_user_hal_set_user_association_response_reported = - 289 [(module) = "car"]; - NetworkIpProvisioningReported network_ip_provisioning_reported = - 290 [(module) = "network_stack"]; - NetworkDhcpRenewReported network_dhcp_renew_reported = 291 [(module) = "network_stack"]; - NetworkValidationReported network_validation_reported = 292 [(module) = "network_stack"]; - NetworkStackQuirkReported network_stack_quirk_reported = 293 [(module) = "network_stack"]; - MediametricsAudioRecordDeviceUsageReported mediametrics_audiorecorddeviceusage_reported = - 294; - MediametricsAudioThreadDeviceUsageReported mediametrics_audiothreaddeviceusage_reported = - 295; - MediametricsAudioTrackDeviceUsageReported mediametrics_audiotrackdeviceusage_reported = - 296; - MediametricsAudioDeviceConnectionReported mediametrics_audiodeviceconnection_reported = - 297; - BlobCommitted blob_committed = 298 [(module) = "framework"]; - BlobLeased blob_leased = 299 [(module) = "framework"]; - BlobOpened blob_opened = 300 [(module) = "framework"]; - ContactsProviderStatusReported contacts_provider_status_reported = 301; - KeystoreKeyEventReported keystore_key_event_reported = 302; - NetworkTetheringReported network_tethering_reported = - 303 [(module) = "network_tethering"]; - ImeTouchReported ime_touch_reported = 304 [(module) = "sysui"]; - UIInteractionFrameInfoReported ui_interaction_frame_info_reported = - 305 [(module) = "framework"]; - UIActionLatencyReported ui_action_latency_reported = 306 [(module) = "framework"]; - WifiDisconnectReported wifi_disconnect_reported = 307 [(module) = "wifi"]; - WifiConnectionStateChanged wifi_connection_state_changed = 308 [(module) = "wifi"]; - HdmiCecActiveSourceChanged hdmi_cec_active_source_changed = 309 [(module) = "framework"]; - HdmiCecMessageReported hdmi_cec_message_reported = 310 [(module) = "framework"]; - AirplaneMode airplane_mode = 311 [(module) = "telephony"]; - ModemRestart modem_restart = 312 [(module) = "telephony"]; - CarrierIdMismatchReported carrier_id_mismatch_reported = 313 [(module) = "telephony"]; - CarrierIdTableUpdated carrier_id_table_updated = 314 [(module) = "telephony"]; - DataStallRecoveryReported data_stall_recovery_reported = 315 [(module) = "telephony"]; - MediametricsMediaParserReported mediametrics_mediaparser_reported = 316; - TlsHandshakeReported tls_handshake_reported = 317 [(module) = "conscrypt"]; - TextClassifierApiUsageReported text_classifier_api_usage_reported = 318 [(module) = "textclassifier"]; - KilledAppStatsReported killed_app_stats_reported = 319 [(module) = "carwatchdogd"]; - MediametricsPlaybackReported mediametrics_playback_reported = 320; - MediaNetworkInfoChanged media_network_info_changed = 321; - MediaPlaybackStateChanged media_playback_state_changed = 322; - MediaPlaybackErrorReported media_playback_error_reported = 323; - MediaPlaybackTrackChanged media_playback_track_changed = 324; - WifiScanReported wifi_scan_reported = 325 [(module) = "wifi"]; - WifiPnoScanReported wifi_pno_scan_reported = 326 [(module) = "wifi"]; - - // StatsdStats tracks platform atoms with ids upto 500. - // Update StatsdStats::kMaxPushedAtomId when atom ids here approach that value. - } - - // Pulled events will start at field 10000. - // Next: 10092 - oneof pulled { - WifiBytesTransfer wifi_bytes_transfer = 10000 [(module) = "framework"]; - WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001 [(module) = "framework"]; - MobileBytesTransfer mobile_bytes_transfer = - 10002 [(module) = "framework", (truncate_timestamp) = true]; - MobileBytesTransferByFgBg mobile_bytes_transfer_by_fg_bg = - 10003 [(module) = "framework", (truncate_timestamp) = true]; - BluetoothBytesTransfer bluetooth_bytes_transfer = 10006 [(module) = "framework"]; - KernelWakelock kernel_wakelock = 10004 [(module) = "framework"]; - SubsystemSleepState subsystem_sleep_state = 10005 [(module) = "statsdtest"]; - CpuTimePerFreq cpu_time_per_freq = 10008 [(module) = "framework"]; - CpuTimePerUid cpu_time_per_uid = 10009 [(module) = "framework", (module) = "statsdtest"]; - CpuTimePerUidFreq cpu_time_per_uid_freq = - 10010 [(module) = "framework", (module) = "statsd"]; - WifiActivityInfo wifi_activity_info = 10011 [(module) = "framework"]; - ModemActivityInfo modem_activity_info = 10012 [(module) = "framework"]; - BluetoothActivityInfo bluetooth_activity_info = 10007 [(module) = "framework"]; - ProcessMemoryState process_memory_state = 10013 [(module) = "framework"]; - SystemElapsedRealtime system_elapsed_realtime = 10014 [(module) = "framework"]; - SystemUptime system_uptime = 10015 [(module) = "framework"]; - CpuActiveTime cpu_active_time = 10016 [(module) = "framework", (module) = "statsdtest"]; - CpuClusterTime cpu_cluster_time = 10017 [(module) = "framework"]; - DiskSpace disk_space = 10018 [deprecated=true, (module) = "statsdtest"]; - RemainingBatteryCapacity remaining_battery_capacity = 10019 [(module) = "framework"]; - FullBatteryCapacity full_battery_capacity = 10020 [(module) = "framework"]; - Temperature temperature = 10021 [(module) = "framework", (module) = "statsdtest"]; - BinderCalls binder_calls = 10022 [(module) = "framework", (module) = "statsd"]; - BinderCallsExceptions binder_calls_exceptions = 10023 [(module) = "framework"]; - LooperStats looper_stats = 10024 [(module) = "framework", (module) = "statsd"]; - DiskStats disk_stats = 10025 [(module) = "framework"]; - DirectoryUsage directory_usage = 10026 [(module) = "framework"]; - AppSize app_size = 10027 [(module) = "framework"]; - CategorySize category_size = 10028 [(module) = "framework"]; - ProcStats proc_stats = 10029 [(module) = "framework"]; - BatteryVoltage battery_voltage = 10030 [(module) = "framework"]; - NumFingerprintsEnrolled num_fingerprints_enrolled = 10031 [(module) = "framework"]; - DiskIo disk_io = 10032 [(module) = "framework"]; - PowerProfile power_profile = 10033 [(module) = "framework"]; - ProcStatsPkgProc proc_stats_pkg_proc = 10034 [(module) = "framework"]; - ProcessCpuTime process_cpu_time = 10035 [(module) = "framework"]; - CpuTimePerThreadFreq cpu_time_per_thread_freq = 10037 [(module) = "framework"]; - OnDevicePowerMeasurement on_device_power_measurement = 10038; - DeviceCalculatedPowerUse device_calculated_power_use = 10039 [(module) = "framework"]; - DeviceCalculatedPowerBlameUid device_calculated_power_blame_uid = - 10040 [(module) = "framework"]; - DeviceCalculatedPowerBlameOther device_calculated_power_blame_other = - 10041 [(module) = "framework"]; - ProcessMemoryHighWaterMark process_memory_high_water_mark = 10042 [(module) = "framework"]; - BatteryLevel battery_level = 10043 [(module) = "framework"]; - BuildInformation build_information = 10044 [(module) = "framework"]; - BatteryCycleCount battery_cycle_count = 10045 [(module) = "framework"]; - DebugElapsedClock debug_elapsed_clock = 10046 [(module) = "framework"]; - DebugFailingElapsedClock debug_failing_elapsed_clock = 10047 [(module) = "framework"]; - NumFacesEnrolled num_faces_enrolled = 10048 [(module) = "framework"]; - RoleHolder role_holder = 10049 [(module) = "framework"]; - DangerousPermissionState dangerous_permission_state = 10050 [(module) = "framework"]; - TrainInfo train_info = 10051 [(module) = "statsd"]; - TimeZoneDataInfo time_zone_data_info = 10052 [(module) = "framework"]; - ExternalStorageInfo external_storage_info = 10053 [(module) = "framework"]; - GpuStatsGlobalInfo gpu_stats_global_info = 10054; - GpuStatsAppInfo gpu_stats_app_info = 10055; - SystemIonHeapSize system_ion_heap_size = 10056 [deprecated = true, (module) = "framework"]; - AppsOnExternalStorageInfo apps_on_external_storage_info = 10057 [(module) = "framework"]; - FaceSettings face_settings = 10058 [(module) = "framework"]; - CoolingDevice cooling_device = 10059 [(module) = "framework"]; - AppOps app_ops = 10060 [(module) = "framework"]; - ProcessSystemIonHeapSize process_system_ion_heap_size = 10061 [(module) = "framework"]; - SurfaceflingerStatsGlobalInfo surfaceflinger_stats_global_info = 10062; - SurfaceflingerStatsLayerInfo surfaceflinger_stats_layer_info = 10063; - ProcessMemorySnapshot process_memory_snapshot = 10064 [(module) = "framework"]; - VmsClientStats vms_client_stats = 10065 [(module) = "car"]; - NotificationRemoteViews notification_remote_views = 10066 [(module) = "framework"]; - DangerousPermissionStateSampled dangerous_permission_state_sampled = - 10067 [(module) = "framework"]; - GraphicsStats graphics_stats = 10068; - RuntimeAppOpAccess runtime_app_op_access = 10069 [(module) = "framework"]; - IonHeapSize ion_heap_size = 10070 [(module) = "framework"]; - PackageNotificationPreferences package_notification_preferences = - 10071 [(module) = "framework"]; - PackageNotificationChannelPreferences package_notification_channel_preferences = - 10072 [(module) = "framework"]; - PackageNotificationChannelGroupPreferences package_notification_channel_group_preferences = - 10073 [(module) = "framework"]; - GnssStats gnss_stats = 10074 [(module) = "framework"]; - AttributedAppOps attributed_app_ops = 10075 [(module) = "framework"]; - VoiceCallSession voice_call_session = 10076 [(module) = "telephony"]; - VoiceCallRatUsage voice_call_rat_usage = 10077 [(module) = "telephony"]; - SimSlotState sim_slot_state = 10078 [(module) = "telephony"]; - SupportedRadioAccessFamily supported_radio_access_family = 10079 [(module) = "telephony"]; - SettingSnapshot setting_snapshot = 10080 [(module) = "framework"]; - BlobInfo blob_info = 10081 [(module) = "framework"]; - DataUsageBytesTransfer data_usage_bytes_transfer = - 10082 [(module) = "framework", (truncate_timestamp) = true]; - BytesTransferByTagAndMetered bytes_transfer_by_tag_and_metered = - 10083 [(module) = "framework", (truncate_timestamp) = true]; - DNDModeProto dnd_mode_rule = 10084 [(module) = "framework"]; - GeneralExternalStorageAccessStats general_external_storage_access_stats = - 10085 [(module) = "mediaprovider"]; - IncomingSms incoming_sms = 10086 [(module) = "telephony"]; - OutgoingSms outgoing_sms = 10087 [(module) = "telephony"]; - CarrierIdTableVersion carrier_id_table_version = 10088 [(module) = "telephony"]; - DataCallSession data_call_session = 10089 [(module) = "telephony"]; - CellularServiceState cellular_service_state = 10090 [(module) = "telephony"]; - CellularDataServiceSwitch cellular_data_service_switch = 10091 [(module) = "telephony"]; - } - - // DO NOT USE field numbers above 100,000 in AOSP. - // Field numbers 100,000 - 199,999 are reserved for non-AOSP (e.g. OEMs) to use. - // Field numbers 200,000 and above are reserved for future use; do not use them at all. - - reserved 10036; -} - -/** - * This proto represents a node of an attribution chain. - * Note: All attribution chains are represented as a repeated field of type - * AttributionNode. It is understood that in such arrays, the order is that - * of calls, that is [A, B, C] if A calls B that calls C. - */ -message AttributionNode { - // The uid for a given element in the attribution chain. - optional int32 uid = 1; - - // The (optional) string tag for an element in the attribution chain. If the - // element has no tag, it is encoded as an empty string. - optional string tag = 2; -} - -message KeyValuePair { - optional int32 key = 1; - oneof value { - int32 value_int = 2; - int64 value_long = 3; - string value_str = 4; - float value_float = 5; - } -} - -message KeyValuePairsAtom { - optional int32 uid = 1; - repeated KeyValuePair pairs = 2; -} - -/* - * ***************************************************************************** - * Below are all of the individual atoms that are logged by Android via statsd. - * - * RULES: - * - The field ids for each atom must start at 1, and count upwards by 1. - * Skipping field ids is not allowed. - * - These form an API, so renaming, renumbering or removing fields is - * not allowed between android releases. (This is not currently enforced, - * but there will be a tool to enforce this restriction). - * - The types must be built-in protocol buffer types, namely, no sub-messages - * are allowed (yet). The bytes type is also not allowed. - * - The CamelCase name of the message type should match the - * underscore_separated name as defined in Atom. - * - If an atom represents work that can be attributed to an app, there can - * be exactly one AttributionChain field. It must be field number 1. - * - A field that is a uid should be a string field, tagged with the [xxx] - * annotation. The generated code on android will be represented by UIDs, - * and those UIDs will be translated in xxx to those strings. - * - * CONVENTIONS: - * - Events are past tense. e.g. ScreenStateChanged, not ScreenStateChange. - * - If there is a UID, it goes first. Think in an object-oriented fashion. - * ***************************************************************************** - */ - -/** - * This atom is deprecated starting in Q. Please use ThermalThrottlingSeverityStateChanged. - * Logs when the Thermal service HAL notifies the throttling start/stop events. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/stats/StatsCompanionService.java - */ -message ThermalThrottlingStateChanged { - // The type of temperature being reported (CPU, GPU, SKIN, etc) - optional android.os.TemperatureTypeEnum sensor_type = 1; - - // Throttling state, this field is DEPRECATED - enum State { - UNKNOWN = 0; - START = 1; // START indicated that throttling was triggered. - STOP = 2; // STOP indicates that throttling was cleared. - } - optional State state = 2; - - optional float temperature = 3; -} - -/** - * Logs when the screen state changes. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java - */ -message ScreenStateChanged { - // New screen state, from frameworks/base/core/proto/android/view/enums.proto. - optional android.view.DisplayStateEnum state = 1 - [(state_field_option).exclusive_state = true, (state_field_option).nested = false]; -} - -/** - * Logs that the process state of the uid, as determined by ActivityManager - * (i.e. the highest process state of that uid's processes) has changed. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java - */ -message UidProcessStateChanged { - optional int32 uid = 1 [(state_field_option).primary_field = true, (is_uid) = true]; - - // The state, from frameworks/base/core/proto/android/app/enums.proto. - optional android.app.ProcessStateEnum state = 2 - [(state_field_option).exclusive_state = true, (state_field_option).nested = false]; -} - -/** - * Logs process state change of a process, as per the activity manager. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/am/ProcessRecord.java - */ -message ProcessStateChanged { - optional int32 uid = 1; - optional string process_name = 2; - optional string package_name = 3; - // TODO: remove this when validation is done - optional int64 version = 5; - // The state, from frameworks/base/core/proto/android/app/enums.proto. - optional android.app.ProcessStateEnum state = 4; -} - -/** - * Logs when ActivityManagerService sleep state is changed. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/am/ActivityTaskManagerService.java - */ -message ActivityManagerSleepStateChanged { - // TODO: import frameworks proto - enum State { - UNKNOWN = 0; - ASLEEP = 1; - AWAKE = 2; - } - optional State state = 1 - [(state_field_option).exclusive_state = true, (state_field_option).nested = false]; -} - -/** - * Logs when system memory state changes. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java - */ -message MemoryFactorStateChanged { - // TODO: import frameworks proto - enum State { - MEMORY_UNKNOWN = 0; - NORMAL = 1; // normal. - MODERATE = 2; // moderate memory pressure. - LOW = 3; // low memory. - CRITICAL = 4; // critical memory. - - } - optional State factor = 1 [(state_field_option).exclusive_state = true]; -} - -/** - * Logs when app is using too much cpu, according to ActivityManagerService. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java - */ -message ExcessiveCpuUsageReported { - optional int32 uid = 1; - optional string process_name = 2; - optional string package_name = 3; - // package version. TODO: remove this when validation is done - optional int64 version = 4; -} - -/** - * Logs when a cached process is killed, along with its pss. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java - */ -message CachedKillReported { - optional int32 uid = 1; - optional string process_name = 2; - optional string package_name = 3; - // TODO: remove this when validation is done - optional int64 version = 5; - optional int64 pss = 4; -} - -/** - * Logs the change in wifi health. - * - * Logged from: - * frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiDataStall.java - */ -message WifiHealthStatReported { - enum Band { - UNKNOWN = 0; - // All of 2.4GHz band - BAND_2G = 1; - // Frequencies in the range of [5150, 5250) GHz - BAND_5G_LOW = 2; - // Frequencies in the range of [5250, 5725) GHz - BAND_5G_MIDDLE = 3; - // Frequencies in the range of [5725, 5850) GHz - BAND_5G_HIGH = 4; - // Frequencies in the range of [5925, 6425) GHz - BAND_6G_LOW = 5; - // Frequencies in the range of [6425, 6875) GHz - BAND_6G_MIDDLE = 6; - // Frequencies in the range of [6875, 7125) GHz - BAND_6G_HIGH = 7; - } - // duration this stat is obtained over in milliseconds - optional int32 duration_millis = 1; - // whether wifi is classified as sufficient for the user's data traffic, determined - // by whether the calculated throughput exceeds the average demand within |duration_millis| - optional bool is_sufficient = 2; - // whether cellular data is available - optional bool is_cell_data_available = 3; - // the Band bucket the connected network is on - optional Band band = 4; -} - -/** - * Logged when wifi detects a significant change in connection failure rate. - * - * Logged from: frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiHealthMonitor.java - * - */ -message WifiFailureStatReported { - enum AbnormalityType { - UNKNOWN = 0; - SIGNIFICANT_INCREASE = 1; - SIGNIFICANT_DECREASE = 2; - SIMPLY_HIGH = 3; - } - enum FailureType { - FAILURE_UNKNOWN = 0; - FAILURE_CONNECTION = 1; - FAILURE_ASSOCIATION_REJECTION = 2; - FAILURE_ASSOCIATION_TIMEOUT = 3; - FAILURE_AUTHENTICATION = 4; - FAILURE_NON_LOCAL_DISCONNECTION = 5; - FAILURE_SHORT_CONNECTION_DUE_TO_NON_LOCAL_DISCONNECTION = 6; - } - // Reason for uploading this stat - optional AbnormalityType abnormality_type = 1; - // The particular type of failure - optional FailureType failure_type = 2; - // How many times we have encountered this combination of AbnormalityType and FailureType - optional int32 failure_count = 3; -} - -/** - * Logs whether a Wifi connection attempt was successful and reasons for failure if it wasn't. - * - * Logged from: - * frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiMetrics.java - */ -message WifiConnectionResultReported { - enum FailureCode { - FAILURE_UNKNOWN = 0; - FAILURE_ASSOCIATION_TIMEOUT = 1; - FAILURE_ASSOCIATION_REJECTION = 2; - FAILURE_AUTHENTICATION_GENERAL = 3; - FAILURE_AUTHENTICATION_EAP = 4; - FAILURE_DHCP = 5; - FAILURE_NETWORK_DISCONNECTION = 6; - FAILURE_ROAM_TIMEOUT = 7; - FAILURE_WRONG_PASSWORD = 8; - } - - enum Trigger { - UNKNOWN = 0; - // Connection attempt was initiated manually. - MANUAL = 1; - // Automatic reconnection to the same network as connected previously. - RECONNECT_SAME_NETWORK = 2; - // Automatic reconnection to a saved network, but not the previous one. - AUTOCONNECT_CONFIGURED_NETWORK = 3; - // Automatic first connection attempt after device boot. - AUTOCONNECT_BOOT = 4; - } - - // True represents a successful connection. - optional bool connection_result = 1; - // Reason for the connection failure. - optional FailureCode failure_code = 2; - // Scan RSSI before the connection attempt. - optional int32 rssi = 3; - // Time taken by this connection attempt. - optional int32 connection_attempt_duration_millis = 4; - // Band bucket the connected network is on. - optional android.net.wifi.WifiBandBucket band = 5; - // Authentication type. - optional android.net.wifi.WifiAuthType auth_type = 6; - // What triggered this connection attempt. - optional Trigger trigger = 7; - // Whether this network was used (successfully connected to) previously. - optional bool network_used = 8; - // Time taken from the last successful connection (or device boot if that's the first one). - optional int32 time_since_last_connection_seconds = 9; -} - -/** - * Logs when a Wifi connection drops. - * - * Logged from: - * frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiMetrics.java - */ -message WifiDisconnectReported { - enum FailureCode { - UNKNOWN = 0; - - // Wifi supplicant failure reason codes (IEEE Std 802.11-2016, 9.4.1.7, Table 9-45). - // See ISupplicantStaIfaceCallback.java:ReasonCode - UNSPECIFIED = 1; - PREV_AUTH_NOT_VALID = 2; - DEAUTH_LEAVING = 3; - DISASSOC_DUE_TO_INACTIVITY = 4; - DISASSOC_AP_BUSY = 5; - CLASS2_FRAME_FROM_NONAUTH_STA = 6; - CLASS3_FRAME_FROM_NONASSOC_STA = 7; - DISASSOC_STA_HAS_LEFT = 8; - STA_REQ_ASSOC_WITHOUT_AUTH = 9; - PWR_CAPABILITY_NOT_VALID = 10; - SUPPORTED_CHANNEL_NOT_VALID = 11; - BSS_TRANSITION_DISASSOC = 12; - INVALID_IE = 13; - MICHAEL_MIC_FAILURE = 14; - FOURWAY_HANDSHAKE_TIMEOUT = 15; - GROUP_KEY_UPDATE_TIMEOUT = 16; - IE_IN_4WAY_DIFFERS = 17; - GROUP_CIPHER_NOT_VALID = 18; - PAIRWISE_CIPHER_NOT_VALID = 19; - AKMP_NOT_VALID = 20; - UNSUPPORTED_RSN_IE_VERSION = 21; - INVALID_RSN_IE_CAPAB = 22; - IEEE_802_1X_AUTH_FAILED = 23; - CIPHER_SUITE_REJECTED = 24; - TDLS_TEARDOWN_UNREACHABLE = 25; - TDLS_TEARDOWN_UNSPECIFIED = 26; - SSP_REQUESTED_DISASSOC = 27; - NO_SSP_ROAMING_AGREEMENT = 28; - BAD_CIPHER_OR_AKM = 29; - NOT_AUTHORIZED_THIS_LOCATION = 30; - SERVICE_CHANGE_PRECLUDES_TS = 31; - UNSPECIFIED_QOS_REASON = 32; - NOT_ENOUGH_BANDWIDTH = 33; - DISASSOC_LOW_ACK = 34; - EXCEEDED_TXOP = 35; - STA_LEAVING = 36; - END_TS_BA_DLS = 37; - UNKNOWN_TS_BA = 38; - TIMEOUT = 39; - PEERKEY_MISMATCH = 45; - AUTHORIZED_ACCESS_LIMIT_REACHED = 46; - EXTERNAL_SERVICE_REQUIREMENTS = 47; - INVALID_FT_ACTION_FRAME_COUNT = 48; - INVALID_PMKID = 49; - INVALID_MDE = 50; - INVALID_FTE = 51; - MESH_PEERING_CANCELLED = 52; - MESH_MAX_PEERS = 53; - MESH_CONFIG_POLICY_VIOLATION = 54; - MESH_CLOSE_RCVD = 55; - MESH_MAX_RETRIES = 56; - MESH_CONFIRM_TIMEOUT = 57; - MESH_INVALID_GTK = 58; - MESH_INCONSISTENT_PARAMS = 59; - MESH_INVALID_SECURITY_CAP = 60; - MESH_PATH_ERROR_NO_PROXY_INFO = 61; - MESH_PATH_ERROR_NO_FORWARDING_INFO = 62; - MESH_PATH_ERROR_DEST_UNREACHABLE = 63; - MAC_ADDRESS_ALREADY_EXISTS_IN_MBSS = 64; - MESH_CHANNEL_SWITCH_REGULATORY_REQ = 65; - MESH_CHANNEL_SWITCH_UNSPECIFIED = 66; - - // ClientModeImpl error codes - // Defined in /frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiMetrics.java - IFACE_DESTROYED = 10000; - WIFI_DISABLED = 10001; - SUPPLICANT_DISCONNECTED = 10002; - CONNECTING_WATCHDOG_TIMER = 10003; - ROAM_WATCHDOG_TIMER = 10004; - } - - // How long the session lasted from successful connection to disconnect. - optional int32 connected_duration_seconds = 1; - - // Reason for the disconnect. - optional FailureCode failure_code = 2; - - // Band bucket the connected network was on. - optional android.net.wifi.WifiBandBucket band = 3; - - // Authentication type. - optional android.net.wifi.WifiAuthType auth_type = 4; - - // Last seen RSSI before the disconnect. - optional int32 last_rssi = 5; - - // Last seen link speed before the disconnect. - optional int32 last_link_speed = 6; -} - -/** - * Logs when Wifi connection is established or dropped. - * - * Logged from: - * frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiMetrics.java - */ -message WifiConnectionStateChanged { - optional bool is_connected = 1; - - // Band bucket the connected network was on. - // Filled for both connected and disconnected cases. - optional android.net.wifi.WifiBandBucket band = 2; - - // Authentication type. - // Filled for both connected and disconnected cases. - optional android.net.wifi.WifiAuthType auth_type = 3; -} - -/** - * Logs when memory stats of a process is reported. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/am/ProcessRecord.java - */ -message ProcessMemoryStatReported { - optional int32 uid = 1; - optional string process_name = 2; - optional string package_name = 3; - //TODO: remove this when validation is done - optional int64 version = 9; - optional int64 pss = 4; - optional int64 uss = 5; - optional int64 rss = 6; - enum Type { - ADD_PSS_INTERNAL_SINGLE = 0; - ADD_PSS_INTERNAL_ALL_MEM = 1; - ADD_PSS_INTERNAL_ALL_POLL = 2; - ADD_PSS_EXTERNAL = 3; - ADD_PSS_EXTERNAL_SLOW = 4; - } - optional Type type = 7; - optional int64 duration_millis = 8; -} - -/** - * Logs that a process started, finished, crashed, or ANRed. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java - */ -message ProcessLifeCycleStateChanged { - optional int32 uid = 1 [(is_uid) = true]; - - // The process name (usually same as the app name). - optional string process_name = 2; - - // What lifecycle state the process changed to. - // This enum is specific to atoms.proto. - enum State { - FINISHED = 0; - STARTED = 1; - CRASHED = 2; - } - optional State state = 3; -} - -/** - * Logs when the ble scan state changes. - * - * Logged from: - * packages/apps/Bluetooth/src/com/android/bluetooth/gatt/AppScanStats.java - */ -message BleScanStateChanged { - repeated AttributionNode attribution_node = 1 - [(state_field_option).primary_field_first_uid = true]; - - enum State { - OFF = 0; - ON = 1; - // RESET indicates all ble stopped. Used when it (re)starts (e.g. after it crashes). - RESET = 2; - } - optional State state = 2 [ - (state_field_option).exclusive_state = true, - (state_field_option).default_state_value = 0 /* State.OFF */, - (state_field_option).trigger_state_reset_value = 2 /* State.RESET */, - (state_field_option).nested = true - ]; - - // Does the scan have a filter. - optional bool is_filtered = 3 [(state_field_option).primary_field = true]; - // Whether the scan is a CALLBACK_TYPE_FIRST_MATCH scan. Called 'background' scan internally. - optional bool is_first_match = 4 [(state_field_option).primary_field = true]; - // Whether the scan set to piggy-back off the results of other scans (SCAN_MODE_OPPORTUNISTIC). - optional bool is_opportunistic = 5 [(state_field_option).primary_field = true]; -} - -/** - * Logs reporting of a ble scan finding results. - * - * Logged from: - * packages/apps/Bluetooth/src/com/android/bluetooth/gatt/AppScanStats.java - */ -// TODO: Consider also tracking per-scanner-id. -message BleScanResultReceived { - repeated AttributionNode attribution_node = 1; - - // Number of ble scan results returned. - optional int32 num_results = 2; -} - -/** - * Logs when a sensor state changes. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java - */ -message SensorStateChanged { - repeated AttributionNode attribution_node = 1; - - // The id (int) of the sensor. - optional int32 sensor_id = 2; - - enum State { - OFF = 0; - ON = 1; - } - optional State state = 3; -} - -/** - * Logs when GPS state changes. - * - * Logged from: - * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java - */ -message GpsScanStateChanged { - repeated AttributionNode attribution_node = 1; - - enum State { - OFF = 0; - ON = 1; - } - optional State state = 2; -} - -/** - * Logs when GPS signal quality. - * - * Logged from: - * /frameworks/base/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java - */ -message GpsSignalQualityChanged { - optional android.server.location.GpsSignalQualityEnum level = 1; -} - - -/** - * Logs when a sync manager sync state changes. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java - */ -message SyncStateChanged { - repeated AttributionNode attribution_node = 1; - - // Name of the sync (as named in the app). Can be chosen at run-time. - optional string sync_name = 2; - - enum State { - OFF = 0; - ON = 1; - } - optional State state = 3; -} - -/* - * Deferred job stats. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/job/JobSchedulerService.java -*/ -message DeferredJobStatsReported { - repeated AttributionNode attribution_node = 1; - - // Number of jobs deferred. - optional int32 num_jobs_deferred = 2; - - // Time since the last job runs. - optional int64 time_since_last_job_millis = 3; -} - -/** - * Logs when a job scheduler job state changes. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java - */ -message ScheduledJobStateChanged { - repeated AttributionNode attribution_node = 1; - - // Name of the job (as named in the app) - optional string job_name = 2; - - enum State { - FINISHED = 0; - STARTED = 1; - SCHEDULED = 2; - } - optional State state = 3; - - // The reason a job has stopped. - // This is only applicable when the state is FINISHED. - // The default value is STOP_REASON_UNKNOWN. - optional android.app.job.StopReasonEnum stop_reason = 4; - - // The standby bucket of the app that scheduled the job. These match the framework constants - // defined in JobSchedulerService.java with the addition of UNKNOWN using -1, as ACTIVE is - // already assigned 0. - enum Bucket { - UNKNOWN = -1; - ACTIVE = 0; - WORKING_SET = 1; - FREQUENT = 2; - RARE = 3; - NEVER = 4; - RESTRICTED = 5; - } - optional Bucket standby_bucket = 5 [default = UNKNOWN]; - - // The job id (as assigned by the app). - optional int32 job_id = 6; - - // One flag for each of the API constraints defined by Jobscheduler. Does not include implcit - // constraints as they are always assumed to be set. - optional bool has_charging_constraint = 7; - optional bool has_battery_not_low_constraint = 8; - optional bool has_storage_not_low_constraint = 9; - optional bool has_timing_delay_constraint = 10; - optional bool has_deadline_constraint = 11; - optional bool has_idle_constraint = 12; - optional bool has_connectivity_constraint = 13; - optional bool has_content_trigger_constraint = 14; -} - -/** - * Logs when the audio state changes. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java - */ -message AudioStateChanged { - repeated AttributionNode attribution_node = 1; - - enum State { - OFF = 0; - ON = 1; - // RESET indicates all audio stopped. Used when it (re)starts (e.g. after it crashes). - RESET = 2; - } - optional State state = 2; -} - -/** - * Logs when the video codec state changes. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java - */ -message MediaCodecStateChanged { - repeated AttributionNode attribution_node = 1; - - enum State { - OFF = 0; - ON = 1; - // RESET indicates all mediaCodec stopped. Used when it (re)starts (e.g. after it crashes). - RESET = 2; - } - optional State state = 2; -} - -/** - * Logs when the flashlight state changes. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java - */ -message FlashlightStateChanged { - repeated AttributionNode attribution_node = 1; - - enum State { - OFF = 0; - ON = 1; - // RESET indicates all flashlight stopped. Used when it (re)starts (e.g. after it crashes). - RESET = 2; - } - optional State state = 2; -} - -/** - * Logs when the camera state changes. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java - */ -message CameraStateChanged { - repeated AttributionNode attribution_node = 1; - - enum State { - OFF = 0; - ON = 1; - // RESET indicates all camera stopped. Used when it (re)starts (e.g. after it crashes). - RESET = 2; - } - optional State state = 2; -} - -/** - * Logs that the state of a wakelock (per app and per wakelock name) has changed. - * - * Logged from: - * TODO - */ -message WakelockStateChanged { - repeated AttributionNode attribution_node = 1 - [(state_field_option).primary_field_first_uid = true]; - - // The type (level) of the wakelock; e.g. a partial wakelock or a full wakelock. - // From frameworks/base/core/proto/android/os/enums.proto. - optional android.os.WakeLockLevelEnum type = 2 [(state_field_option).primary_field = true]; - - // The wakelock tag (Called tag in the Java API, sometimes name elsewhere). - optional string tag = 3 [(state_field_option).primary_field = true]; - - enum State { - RELEASE = 0; - ACQUIRE = 1; - CHANGE_RELEASE = 2; - CHANGE_ACQUIRE = 3; - } - optional State state = 4 [ - (state_field_option).exclusive_state = true, - (state_field_option).default_state_value = 0, - (state_field_option).nested = true - ]; -} - -/** - * Logs when a partial wakelock is considered 'long' (over 1 min). - * - * Logged from: - * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java - */ -message LongPartialWakelockStateChanged { - repeated AttributionNode attribution_node = 1; - - // The wakelock tag (Called tag in the Java API, sometimes name elsewhere). - optional string tag = 2; - - // TODO: I have no idea what this is. - optional string history_tag = 3; - - enum State { - OFF = 0; - ON = 1; - } - optional State state = 4; -} - -/** - * Logs when the device is interactive, according to the PowerManager Notifier. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/power/Notifier.java - */ -message InteractiveStateChanged { - enum State { - OFF = 0; - ON = 1; - } - optional State state = 1; -} - -/** - * Logs Battery Saver state change. - * - * Logged from: - * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java - */ -message BatterySaverModeStateChanged { - enum State { - OFF = 0; - ON = 1; - } - optional State state = 1 - [(state_field_option).exclusive_state = true, (state_field_option).nested = false]; -} - -/** - * Logs Doze mode state change. - * - * Logged from: - * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java - */ -message DeviceIdleModeStateChanged { - optional android.server.DeviceIdleModeEnum state = 1 - [(state_field_option).exclusive_state = true, (state_field_option).nested = false]; -} - - -/** - * Logs state change of Doze mode including maintenance windows. - * - * Logged from: - * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java - */ -message DeviceIdlingModeStateChanged { - optional android.server.DeviceIdleModeEnum state = 1 - [(state_field_option).exclusive_state = true, (state_field_option).nested = false]; -} - -/** - * Logs screen brightness level. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java - */ -message ScreenBrightnessChanged { - // Screen brightness level. Should be in [-1, 255] according to PowerManager.java. - optional int32 level = 1; -} - -/** - * Logs battery level (percent full, from 0 to 100). - * - * Logged from: - * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java - */ -message BatteryLevelChanged { - // Battery level. Should be in [0, 100]. - optional int32 battery_level = 1; -} - -/** - * Logs change in charging status of the device. - * - * Logged from: - * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java - */ -message ChargingStateChanged { - // State of the battery, from frameworks/base/core/proto/android/os/enums.proto. - optional android.os.BatteryStatusEnum state = 1 - [(state_field_option).exclusive_state = true, (state_field_option).nested = false]; -} - -/** - * Logs whether the device is plugged in, and what power source it is using. - * - * Logged from: - * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java - */ -message PluggedStateChanged { - // Whether the device is plugged in, from frameworks/base/core/proto/android/os/enums.proto. - optional android.os.BatteryPluggedStateEnum state = 1 - [(state_field_option).exclusive_state = true, (state_field_option).nested = false]; -} - -/** - * Logs when an app's wakeup alarm fires. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java - */ -message WakeupAlarmOccurred { - repeated AttributionNode attribution_node = 1; - - // Name of the wakeup alarm. - optional string tag = 2; - - // Name of source package (for historical reasons, since BatteryStats tracked it). - optional string package_name = 3; - - // The App Standby bucket of the app that scheduled the alarm at the time the alarm fired. - optional AppStandbyBucketChanged.Bucket app_standby_bucket = 4; -} - -/** - * Logs when an an app causes the mobile radio to change state. - * Changing from LOW to MEDIUM or HIGH can be considered the app waking the mobile radio. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/NetworkManagementService.java - */ -message MobileRadioPowerStateChanged { - repeated AttributionNode attribution_node = 1; - - // Power state, from frameworks/base/core/proto/android/telephony/enums.proto. - optional android.telephony.DataConnectionPowerStateEnum state = 2; -} - -/** - * Logs when an an app causes the wifi radio to change state. - * Changing from LOW to MEDIUM or HIGH can be considered the app waking the wifi radio. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/NetworkManagementService.java - */ -message WifiRadioPowerStateChanged { - repeated AttributionNode attribution_node = 1; - - // Power state, from frameworks/base/core/proto/android/telephony/enums.proto. - optional android.telephony.DataConnectionPowerStateEnum state = 2; -} - -/** - * Logs kernel wakeup reasons and aborts. - * - * Logged from: - * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java - */ -message KernelWakeupReported { - // Name of the kernel wakeup reason (or abort). - optional string wakeup_reason_name = 1; - - // Duration (in microseconds) for the wake-up interrupt to be serviced. - optional int64 duration_micros = 2; -} - -/** - * Logs when Wifi is toggled on/off. - * Note that Wifi may still perform certain functions (e.g. location scanning) even when disabled. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java - */ -message WifiEnabledStateChanged { - enum State { - OFF = 0; - ON = 1; - } - optional State state = 1; -} - -/** - * This atom is deprecated starting in R. - * - * Logs when an app causes Wifi to run. In this context, 'to run' means to use Wifi Client Mode. - * TODO: Include support for Hotspot, perhaps by using an extra field to denote 'mode'. - * Note that Wifi Scanning is monitored separately in WifiScanStateChanged. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java - */ -message WifiRunningStateChanged { - repeated AttributionNode attribution_node = 1; - - enum State { - OFF = 0; - ON = 1; - } - optional State state = 2; -} - -/** - * Logs wifi locks held by an app. - * - * Logged from: - * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java - */ -message WifiLockStateChanged { - repeated AttributionNode attribution_node = 1; - - enum State { - OFF = 0; - ON = 1; - } - optional State state = 2; - - // WifiLock type, from frameworks/base/core/proto/android/wifi/enums.proto. - optional android.net.wifi.WifiModeEnum mode = 3; -} - -/** - * Logs wifi signal strength changes. - * - * Logged from: - * frameworks/opt/net/wifi/service/java/com/android/server/wifi/ClientModeImpl.java - */ -message WifiSignalStrengthChanged { - // Signal strength, from frameworks/base/core/proto/android/telephony/enums.proto. - optional android.telephony.SignalStrengthEnum signal_strength = 1; -} - -/** - * Logs wifi scans performed by an app. - * - * Logged from: - * frameworks/opt/net/wifi/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java - */ -message WifiScanStateChanged { - repeated AttributionNode attribution_node = 1; - - enum State { - OFF = 0; - ON = 1; - } - optional State state = 2; -} - -/** - * Logs wifi multicast locks held by an app - * - * Logged from: - * frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiMulticastLockManager.java - */ -message WifiMulticastLockStateChanged { - repeated AttributionNode attribution_node = 1; - - enum State { - OFF = 0; - ON = 1; - } - optional State state = 2; - - optional string tag = 3; -} - -/** - * Logs shutdown reason and duration on next boot. - * - * Logged from: - * frameworks/base/core/java/com/android/server/BootReceiver.java - */ -message ShutdownSequenceReported { - // True if shutdown is for a reboot. Default: false if we do not know. - optional bool reboot = 1; - - // Reason for shutdown. Eg: userrequested. Default: "<EMPTY>". - optional string reason = 2; - - // Beginning of shutdown time in ms using wall clock time since unix epoch. - // Default: 0 if no start time received. - optional int64 start_time_millis = 3; - - // Duration of shutdown in ms. Default: 0 if no duration received. - optional int64 duration_millis = 4; -} - - -/** - * Logs boot reason and duration. - * - * Logged from: - * system/core/bootstat/bootstat.cpp - */ -message BootSequenceReported { - // Reason for bootloader boot. Eg. reboot. See bootstat.cpp for larger list - // Default: "<EMPTY>" if not available. - optional string bootloader_reason = 1; - - // Reason for system boot. Eg. bootloader, reboot,userrequested - // Default: "<EMPTY>" if not available. - optional string system_reason = 2; - - // End of boot time in ms from unix epoch using system wall clock. - optional int64 end_time_millis = 3; - - // Total boot duration in ms. - optional int64 total_duration_millis = 4; - - // Bootloader duration in ms. - optional int64 bootloader_duration_millis = 5; - - // Time since last boot in ms. Default: 0 if not available. - optional int64 time_since_last_boot = 6; -} - - -/** - * Logs call state and disconnect cause (if applicable). - * - * Logged from: - * packages/services/Telecomm/src/com/android/server/telecom/Call.java - */ -message CallStateChanged { - // The state of the call. Eg. DIALING, ACTIVE, ON_HOLD, DISCONNECTED. - // From frameworks/base/core/proto/android/telecomm/enums.proto. - optional android.telecom.CallStateEnum call_state = 1; - - // The reason the call disconnected. Eg. ERROR, MISSED, REJECTED, BUSY. - // This value is only applicable when the call_state is DISCONNECTED, and - // should always be UNKNOWN if the call_state is not DISCONNECTED. - // From frameworks/base/core/proto/android/telecomm/enums.proto. - optional android.telecom.DisconnectCauseEnum disconnect_cause = 2; - - // True if the call is self-managed, which are apps that use the - // telecom infrastructure to make their own calls. - optional bool self_managed = 3; - - // True if call is external. External calls are calls on connected Wear - // devices but show up in Telecom so the user can pull them onto the device. - optional bool external_call = 4; -} - -/** - * Logs keyguard state. The keyguard is the lock screen. - * - * Logged from: - * frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java - */ -message KeyguardStateChanged { - enum State { - UNKNOWN = 0; - // The keyguard is hidden when the phone is unlocked. - HIDDEN = 1; - // The keyguard is shown when the phone is locked (screen turns off). - SHOWN= 2; - // The keyguard is occluded when something is overlaying the keyguard. - // Eg. Opening the camera while on the lock screen. - OCCLUDED = 3; - } - optional State state = 1; -} - -/** - * Logs keyguard bouncer state. The bouncer is a part of the keyguard, and - * prompts the user to enter a password (pattern, pin, etc). - * - * Logged from: - * frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java - */ - -message KeyguardBouncerStateChanged { - enum State { - UNKNOWN = 0; - // Bouncer is hidden, either as a result of successfully entering the - // password, screen timing out, or user going back to lock screen. - HIDDEN = 1; - // This is when the user is being prompted to enter the password. - SHOWN = 2; - } - optional State state = 1; -} - -/** - * Logs the result of entering a password into the keyguard bouncer. - * - * Logged from: - * frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java - */ -message KeyguardBouncerPasswordEntered { - enum BouncerResult { - UNKNOWN = 0; - // The password entered was incorrect. - FAILURE = 1; - // The password entered was correct. - SUCCESS = 2; - } - optional BouncerResult result = 1; -} - -/* - * Logs changes to the configuration of the device. The configuration is defined - * in frameworks/base/core/java/android/content/res/Configuration.java - * More documentation is at https://d.android.com/reference/android/content/res/Configuration.html - * Please go there to interpret the possible values each field can be. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java - */ -message ResourceConfigurationChanged { - // Bit mask of color capabilities of the screen. - // Contains information about the color gamut and hdr mode of the screen. - // See: https://d.android.com/reference/android/content/res/Configuration.html#colorMode - optional int32 color_mode = 1; - - // The target screen density being rendered to. - // See: https://d.android.com/reference/android/content/res/Configuration.html#densityDpi - optional int32 density_dpi = 2; - - // Current user preference for the scaling factor for fonts, - // relative to the base density scaling. - // See: https://d.android.com/reference/android/content/res/Configuration.html#fontScale - optional float font_scale = 3; - - // Flag indicating whether the hard keyboard is hidden. - // See: https://d.android.com/reference/android/content/res/Configuration.html#hardKeyboardHidden - optional int32 hard_keyboard_hidden = 4; - - // The type of keyboard attached to the device. - // See: https://d.android.com/reference/android/content/res/Configuration.html#keyboard - optional int32 keyboard = 5; - - // Flag indicating whether any keyboard is available. Takes soft keyboards into account. - // See: https://d.android.com/reference/android/content/res/Configuration.html#keyboardHidden - optional int32 keyboard_hidden = 6; - - // IMSI MCC (Mobile Country Code), corresponding to mcc resource qualifier. - // 0 if undefined. - // See: https://d.android.com/reference/android/content/res/Configuration.html#mcc - optional int32 mcc = 7; - - // IMSI MNC (Mobile Network Code), corresponding to mnc resource qualifier. - // 0 if undefined. Note: the actual MNC may be 0, to check for this use the - // MNC_ZERO symbol defined in Configuration.java. - // See: https://d.android.com/reference/android/content/res/Configuration.html#mnc - optional int32 mnc = 8; - - // The kind of navigation available on the device. - // See: https://developer.android.com/reference/android/content/res/Configuration.html#navigation - optional int32 navigation = 9; - - // Flag indicating whether the navigation is available. - // See: https://d.android.com/reference/android/content/res/Configuration.html#navigationHidden - optional int32 navigation_hidden = 10; - - // Overall orientation of the screen. - // See: https://d.android.com/reference/android/content/res/Configuration.html#orientation - optional int32 orientation = 11; - - // The current height of the available screen space, in dp units. - // See: https://d.android.com/reference/android/content/res/Configuration.html#screenHeightDp - optional int32 screen_height_dp = 12; - - // Bit mask of overall layout of the screen. - // Contains information about screen size, whether the screen is wider/taller - // than normal, whether the screen layout is right-tl-left or left-to-right, - // and whether the screen has a rounded shape. - // See: https://d.android.com/reference/android/content/res/Configuration.html#screenLayout - optional int32 screen_layout = 13; - - // Current width of the available screen space, in dp units. - // See: https://d.android.com/reference/android/content/res/Configuration.html#screenWidthDp - optional int32 screen_width_dp = 14; - - // The smallest screen size an application will see in normal operation. - // This is the smallest value of both screenWidthDp and screenHeightDp - // in portrait and landscape. - // See: https://d.android.com/reference/android/content/res/Configuration.html#smallestScreenWidthDp - optional int32 smallest_screen_width_dp = 15; - - // The type of touch screen attached to the device. - // See: https://d.android.com/reference/android/content/res/Configuration.html#touchscreen - optional int32 touchscreen = 16; - - // Bit mask of the ui mode. - // Contains information about the overall ui mode of the device. - // Eg: NORMAL, DESK, CAR, TELEVISION, WATCH, VR_HEADSET - // Also contains information about whether the device is in night mode. - // See: https://d.android.com/reference/android/content/res/Configuration.html#uiMode - optional int32 ui_mode = 17; -} - - -/** - * Logs changes in the connection state of the mobile radio. - * - * Logged from: - * frameworks/opt/telephony/src/java/com/android/internal/telephony/dataconnection/DataConnection.java - */ -message MobileConnectionStateChanged { - // States are from the state machine DataConnection.java. - enum State { - UNKNOWN = 0; - // The connection is inactive, or disconnected. - INACTIVE = 1; - // The connection is being activated, or connecting. - ACTIVATING = 2; - // The connection is active, or connected. - ACTIVE = 3; - // The connection is disconnecting. - DISCONNECTING = 4; - // The connection is disconnecting after creating a connection. - DISCONNECTION_ERROR_CREATING_CONNECTION = 5; - } - optional State state = 1; - // For multi-sim phones, this distinguishes between the sim cards. - optional int32 sim_slot_index = 2; - // Used to identify the connection. Starts at 0 and increments by 1 for - // every new network created. Resets whenever the device reboots. - optional int32 data_connection_id = 3; - // A bitmask for the capabilities of this connection. - // Eg. DEFAULT (internet), MMS, SUPL, DUN, IMS. - // Default value (if we have no information): 0 - optional int64 capabilities = 4; - // If this connection has internet. - // This just checks if the DEFAULT bit of capabilities is set. - optional bool has_internet = 5; -} - -/** - * Logs changes in mobile radio technology. eg: LTE, EDGE, CDMA. - * - * Logged from: - * frameworks/opt/telephony/src/java/com/android/internal/telephony/ServiceStateTracker.java - */ -message MobileRadioTechnologyChanged { - optional android.telephony.NetworkTypeEnum state = 1; - // For multi-sim phones, this distinguishes between the sim cards. - optional int32 sim_slot_index = 2; -} - -/** - * Logs the VID and PID of any connected USB devices. - * - * Notes if any Audio, HID (input buttons/mouse/keyboard), or Storage interfaces are present. - * - * Logged by Vendor. - */ -message UsbDeviceAttached { - optional int32 vid = 1; - optional int32 pid = 2; - optional bool has_audio = 3; - optional bool has_hid = 4; - optional bool has_storage = 5; - enum State { - STATE_DISCONNECTED = 0; - STATE_CONNECTED = 1; - } - optional State state = 6; - optional int64 last_connect_duration_millis = 7; -} - - -/** - * Logs when Bluetooth is enabled and disabled. - * - * Logged from: - * services/core/java/com/android/server/BluetoothManagerService.java - */ -message BluetoothEnabledStateChanged { - repeated AttributionNode attribution_node = 1; - // Whether or not bluetooth is enabled on the device. - enum State { - UNKNOWN = 0; - ENABLED = 1; - DISABLED = 2; - } - optional State state = 2; - // The reason for being enabled/disabled. - // Eg. Airplane mode, crash, application request. - optional android.bluetooth.EnableDisableReasonEnum reason = 3; - // If the reason is an application request, this will be the package name. - optional string pkg_name = 4; -} - -/** - * Logs when profiles on a Bluetooth device connects and disconnects. - * - * Logged from: - * packages/apps/Bluetooth/src/com/android/bluetooth/btservice/RemoteDevices.java - * - * Next Tag: 6 - */ -message BluetoothConnectionStateChanged { - // The state of the connection. - // Eg: CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED. - optional android.bluetooth.ConnectionStateEnum state = 1; - // An identifier that can be used to match connect and disconnect events. - // Currently is last two bytes of a hash of a device level ID and - // the mac address of the bluetooth device that is connected. - // Deprecated: use obfuscated_id instead, this one is always 0 for Q+ - optional int32 obfuscated_id = 2 [deprecated = true]; - // The profile that is connected. Eg. GATT, A2DP, HEADSET. - // From android.bluetooth.BluetoothAdapter.java - // Default: 0 when not used - optional int32 bt_profile = 3; - // An identifier that can be used to match events for this device. - // Currently, this is a salted hash of the MAC address of this Bluetooth device. - // Salt: Randomly generated 256 bit value - // Hash algorithm: HMAC-SHA256 - // Size: 32 byte - // Default: null or empty if the device identifier is not known - optional bytes new_obfuscated_id = 4 [(android.os.statsd.log_mode) = MODE_BYTES]; - // An identifier that can be used to match events for this device. - // The incremental identifier is locally generated and guaranteed not derived - // from any globally unique hardware id. - // For paired devices, it stays consistent between Bluetooth toggling for the - // same remote device. - // For unpaired devices, it stays consistent within the same Bluetooth adapter - // session for the same remote device. - // Default: 0 if the device's metric id is unknown. - optional int32 metric_id = 5; -} - -/** - * Logs when a Bluetooth device connects and disconnects over ACL - * - * Logged from: - * packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterProperties.java - * - * Next Tag: 4 - */ -message BluetoothAclConnectionStateChanged { - // An identifier that can be used to match events for this device. - // Currently, this is a salted hash of the MAC address of this Bluetooth device. - // Salt: Randomly generated 256 bit value - // Hash algorithm: HMAC-SHA256 - // Size: 32 byte - // Default: null or empty if the device identifier is not known - optional bytes obfuscated_id = 1 [(android.os.statsd.log_mode) = MODE_BYTES]; - // The state of the connection. - // Eg: CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED. - optional android.bluetooth.ConnectionStateEnum state = 2; - // An identifier that can be used to match events for this device. - // The incremental identifier is locally generated and guaranteed not derived - // from any globally unique hardware id. - // For paired devices, it stays consistent between Bluetooth toggling for the - // same remote device. - // For unpaired devices, it stays consistent within the same Bluetooth adapter - // session for the same remote device. - // Default: 0 if the device's metric id is unknown. - optional int32 metric_id = 3; -} - -/** - * Logs when a Bluetooth device connects and disconnects over SCO - * - * Logged from: - * packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetStateMachine.java - * packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetClientStateMachine.java - * - * Next Tag: 5 - */ -message BluetoothScoConnectionStateChanged { - // An identifier that can be used to match events for this device. - // Currently, this is a salted hash of the MAC address of this Bluetooth device. - // Salt: Randomly generated 256 bit value - // Hash algorithm: HMAC-SHA256 - // Size: 32 byte - // Default: null or empty if the device identifier is not known - optional bytes obfuscated_id = 1 [(android.os.statsd.log_mode) = MODE_BYTES]; - // The state of the connection. - // Eg: CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED. - optional android.bluetooth.ConnectionStateEnum state = 2; - // Codec used for this SCO connection - // Default: UNKNOWN - optional android.bluetooth.hfp.ScoCodec codec = 3; - // An identifier that can be used to match events for this device. - // The incremental identifier is locally generated and guaranteed not derived - // from any globally unique hardware id. - // For paired devices, it stays consistent between Bluetooth toggling for the - // same remote device. - // For unpaired devices, it stays consistent within the same Bluetooth adapter - // session for the same remote device. - // Default: 0 if the device's metric id is unknown. - optional int32 metric_id = 4; -} - -/** - * Logged when active device of a profile changes - * - * Logged from: - * packages/apps/Bluetooth/src/com/android/bluetooth/a2dp/A2dpService.java - * packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetService.java - * packages/apps/Bluetooth/src/com/android/bluetooth/hearingaid/HearingAidService.java - */ -message BluetoothActiveDeviceChanged { - // The profile whose active device has changed. Eg. A2DP, HEADSET, HEARING_AID - // From android.bluetooth.BluetoothProfile - optional int32 bt_profile = 1; - // An identifier that can be used to match events for this new active device. - // Currently, this is a salted hash of the MAC address of this Bluetooth device. - // Salt: Randomly generated 256 bit value - // Hash algorithm: HMAC-SHA256 - // Size: 32 byte - // Default: null or empty if there is no active device for this profile - optional bytes obfuscated_id = 2 [(android.os.statsd.log_mode) = MODE_BYTES]; - // An identifier that can be used to match events for this device. - // The incremental identifier is locally generated and guaranteed not derived - // from any globally unique hardware id. - // For paired devices, it stays consistent between Bluetooth toggling for the - // same remote device. - // For unpaired devices, it stays consistent within the same Bluetooth adapter - // session for the same remote device. - // Default: 0 if the device's metric id is unknown. - optional int32 metric_id = 3; -} - -// Logs when there is an event affecting Bluetooth device's link layer connection. -// - This event is triggered when there is a related HCI command or event -// - Users of this metrics can deduce Bluetooth device's connection state from these events -// - HCI commands are logged before the command is sent, after receiving command status, and after -// receiving command complete -// - HCI events are logged when they arrive -// -// Low level log from system/bt -// -// Bluetooth classic commands: -// - CMD_CREATE_CONNECTION -// - CMD_DISCONNECT -// - CMD_CREATE_CONNECTION_CANCEL -// - CMD_ACCEPT_CONNECTION_REQUEST -// - CMD_REJECT_CONNECTION_REQUEST -// - CMD_SETUP_ESCO_CONNECTION -// - CMD_ACCEPT_ESCO_CONNECTION -// - CMD_REJECT_ESCO_CONNECTION -// - CMD_ENH_SETUP_ESCO_CONNECTION -// - CMD_ENH_ACCEPT_ESCO_CONNECTION -// -// Bluetooth low energy commands: -// - CMD_BLE_CREATE_LL_CONN [Only logged on error or when initiator filter policy is 0x00] -// - CMD_BLE_CREATE_CONN_CANCEL [Only logged when there is an error] -// - CMD_BLE_EXTENDED_CREATE_CONNECTION [Only logged on error or when initiator filter policy is 0x00] -// - CMD_BLE_CLEAR_WHITE_LIST -// - CMD_BLE_ADD_WHITE_LIST -// - CMD_BLE_REMOVE_WHITE_LIST -// -// Bluetooth classic events: -// - EVT_CONNECTION_COMP -// - EVT_CONNECTION_REQUEST -// - EVT_DISCONNECTION_COMP -// - EVT_ESCO_CONNECTION_COMP -// - EVT_ESCO_CONNECTION_CHANGED -// -// Bluetooth low energy meta events: -// - BLE_EVT_CONN_COMPLETE_EVT -// - BLE_EVT_ENHANCED_CONN_COMPLETE_EVT -// -// Next tag: 10 -message BluetoothLinkLayerConnectionEvent { - // An identifier that can be used to match events for this device. - // Currently, this is a salted hash of the MAC address of this Bluetooth device. - // Salt: Randomly generated 256 bit value - // Hash algorithm: HMAC-SHA256 - // Size: 32 byte - // Default: null or empty if the device identifier is not known - optional bytes obfuscated_id = 1 [(android.os.statsd.log_mode) = MODE_BYTES]; - // Connection handle of this connection if available - // Range: 0x0000 - 0x0EFF (12 bits) - // Default: 0xFFFF if the handle is unknown - optional int32 connection_handle = 2; - // Direction of the link - // Default: DIRECTION_UNKNOWN - optional android.bluetooth.DirectionEnum direction = 3; - // Type of this link - // Default: LINK_TYPE_UNKNOWN - optional android.bluetooth.LinkTypeEnum type = 4; - - // Reason metadata for this link layer connection event, rules for interpretation: - // 1. If hci_cmd is set and valid, hci_event can be either EVT_COMMAND_STATUS or - // EVT_COMMAND_COMPLETE, ignore hci_ble_event in this case - // 2. If hci_event is set to EVT_BLE_META, look at hci_ble_event; otherwise, if hci_event is - // set and valid, ignore hci_ble_event - - // HCI command associated with this event - // Default: CMD_UNKNOWN - optional android.bluetooth.hci.CommandEnum hci_cmd = 5; - // HCI event associated with this event - // Default: EVT_UNKNOWN - optional android.bluetooth.hci.EventEnum hci_event = 6; - // HCI BLE meta event associated with this event - // Default: BLE_EVT_UNKNOWN - optional android.bluetooth.hci.BleMetaEventEnum hci_ble_event = 7; - // HCI command status code if this is triggerred by hci_cmd - // Default: STATUS_UNKNOWN - optional android.bluetooth.hci.StatusEnum cmd_status = 8; - // HCI reason code associated with this event - // Default: STATUS_UNKNOWN - optional android.bluetooth.hci.StatusEnum reason_code = 9; - // An identifier that can be used to match events for this device. - // The incremental identifier is locally generated and guaranteed not derived - // from any globally unique hardware id. - // For paired devices, it stays consistent between Bluetooth toggling for the - // same remote device. - // For unpaired devices, it stays consistent within the same Bluetooth adapter - // session for the same remote device. - // Default: 0 if the device's metric id is unknown. - optional int32 metric_id = 10; -} - -/** - * Logs when a module is rolled back by Watchdog. - * - * Logged from: Rollback Manager - */ -message WatchdogRollbackOccurred { - enum RollbackType { - UNKNOWN = 0; - ROLLBACK_INITIATE = 1; - ROLLBACK_SUCCESS = 2; - ROLLBACK_FAILURE = 3; - ROLLBACK_BOOT_TRIGGERED = 4; - } - optional RollbackType rollback_type = 1; - - optional string package_name = 2; - - optional int32 package_version_code = 3; - - enum RollbackReasonType { - REASON_UNKNOWN = 0; - REASON_NATIVE_CRASH = 1; - REASON_EXPLICIT_HEALTH_CHECK = 2; - REASON_APP_CRASH = 3; - REASON_APP_NOT_RESPONDING = 4; - REASON_NATIVE_CRASH_DURING_BOOT = 5; - } - optional RollbackReasonType rollback_reason = 4; - - // Set by RollbackPackageHealthObserver to be the package that is failing when a rollback - // is initiated. Empty if the package is unknown. - optional string failing_package_name = 5; - - optional TrainExperimentIds experiment_ids = 6 [(log_mode) = MODE_BYTES]; -} - -/** - * Logs when there is a change in Bluetooth A2DP playback state - * - * Logged from: - * packages/apps/Bluetooth/src/com/android/bluetooth/a2dp/A2dpService.java - */ -message BluetoothA2dpPlaybackStateChanged { - // An identifier that can be used to match events for this device. - // Currently, this is a salted hash of the MAC address of this Bluetooth device. - // Salt: Randomly generated 256 bit value - // Hash algorithm: HMAC-SHA256 - // Size: 32 byte - // Default: null or empty if the device identifier is not known - optional bytes obfuscated_id = 1 [(android.os.statsd.log_mode) = MODE_BYTES]; - // Current playback state - // Default: PLAYBACK_STATE_UNKNOWN - optional android.bluetooth.a2dp.PlaybackStateEnum playback_state = 2; - // Current audio coding mode - // Default: AUDIO_CODING_MODE_UNKNOWN - optional android.bluetooth.a2dp.AudioCodingModeEnum audio_coding_mode = 3; - // An identifier that can be used to match events for this device. - // The incremental identifier is locally generated and guaranteed not derived - // from any globally unique hardware id. - // For paired devices, it stays consistent between Bluetooth toggling for the - // same remote device. - // For unpaired devices, it stays consistent within the same Bluetooth adapter - // session for the same remote device. - // Default: 0 if the device's metric id is unknown. - optional int32 metric_id = 4; -} - -/** - * Logs when there is a change in A2DP codec config for a particular remote device - * - * Logged from: - * frameworks/base/core/java/android/bluetooth/BluetoothCodecConfig.java - * packages/apps/Bluetooth/src/com/android/bluetooth/a2dp/A2dpService.java - */ -message BluetoothA2dpCodecConfigChanged { - // An identifier that can be used to match events for this device. - // Currently, this is a salted hash of the MAC address of this Bluetooth device. - // Salt: Randomly generated 256 bit value - // Hash algorithm: HMAC-SHA256 - // Size: 32 byte - // Default: null or empty if the device identifier is not known - optional bytes obfuscated_id = 1 [(android.os.statsd.log_mode) = MODE_BYTES]; - // Type of codec as defined by various SOURCE_CODEC_TYPE_* constants in BluetoothCodecConfig - // Default SOURCE_CODEC_TYPE_INVALID - optional int32 codec_type = 2; - // Codec priroity, the higher the more preferred, -1 for disabled - // Default: CODEC_PRIORITY_DEFAULT - optional int32 codec_priority = 3; - // Sample rate in Hz as defined by various SAMPLE_RATE_* constants in BluetoothCodecConfig - // Default: SAMPLE_RATE_NONE - optional int32 sample_rate = 4; - // Bits per sample as defined by various BITS_PER_SAMPLE_* constants in BluetoothCodecConfig - // Default: BITS_PER_SAMPLE_NONE - optional int32 bits_per_sample = 5; - // Channel mode as defined by various CHANNEL_MODE_* constants in BluetoothCodecConfig - // Default: CHANNEL_MODE_NONE - optional int32 channel_mode = 6; - // Codec specific values - // Default 0 - optional int64 codec_specific_1 = 7; - optional int64 codec_specific_2 = 8; - optional int64 codec_specific_3 = 9; - optional int64 codec_specific_4 = 10; - // An identifier that can be used to match events for this device. - // The incremental identifier is locally generated and guaranteed not derived - // from any globally unique hardware id. - // For paired devices, it stays consistent between Bluetooth toggling for the - // same remote device. - // For unpaired devices, it stays consistent within the same Bluetooth adapter - // session for the same remote device. - // Default: 0 if the device's metric id is unknown. - optional int32 metric_id = 11; -} - -/** - * Logs when there is a change in selectable A2DP codec capability for a paricular remote device - * Each codec's capability is logged separately due to statsd restriction - * - * Logged from: - * frameworks/base/core/java/android/bluetooth/BluetoothCodecConfig.java - * packages/apps/Bluetooth/src/com/android/bluetooth/a2dp/A2dpService.java - */ -message BluetoothA2dpCodecCapabilityChanged { - // An identifier that can be used to match events for this device. - // Currently, this is a salted hash of the MAC address of this Bluetooth device. - // Salt: Randomly generated 256 bit value - // Hash algorithm: HMAC-SHA256 - // Size: 32 byte - // Default: null or empty if the device identifier is not known - optional bytes obfuscated_id = 1 [(android.os.statsd.log_mode) = MODE_BYTES]; - // Type of codec as defined by various SOURCE_CODEC_TYPE_* constants in BluetoothCodecConfig - // Default SOURCE_CODEC_TYPE_INVALID - optional int32 codec_type = 2; - // Codec priroity, the higher the more preferred, -1 for disabled - // Default: CODEC_PRIORITY_DEFAULT - optional int32 codec_priority = 3; - // A bit field of supported sample rates as defined by various SAMPLE_RATE_* constants - // in BluetoothCodecConfig - // Default: empty and SAMPLE_RATE_NONE for individual item - optional int32 sample_rate = 4; - // A bit field of supported bits per sample as defined by various BITS_PER_SAMPLE_* constants - // in BluetoothCodecConfig - // Default: empty and BITS_PER_SAMPLE_NONE for individual item - optional int32 bits_per_sample = 5; - // A bit field of supported channel mode as defined by various CHANNEL_MODE_* constants in - // BluetoothCodecConfig - // Default: empty and CHANNEL_MODE_NONE for individual item - optional int32 channel_mode = 6; - // Codec specific values - // Default 0 - optional int64 codec_specific_1 = 7; - optional int64 codec_specific_2 = 8; - optional int64 codec_specific_3 = 9; - optional int64 codec_specific_4 = 10; - // An identifier that can be used to match events for this device. - // The incremental identifier is locally generated and guaranteed not derived - // from any globally unique hardware id. - // For paired devices, it stays consistent between Bluetooth toggling for the - // same remote device. - // For unpaired devices, it stays consistent within the same Bluetooth adapter - // session for the same remote device. - // Default: 0 if the device's metric id is unknown. - optional int32 metric_id = 11; -} - -/** - * Logs when A2DP failed to read from PCM source. - * This typically happens when audio HAL cannot supply A2DP with data fast enough for encoding. - * - * Logged from: - * system/bt - */ -message BluetoothA2dpAudioUnderrunReported { - // An identifier that can be used to match events for this device. - // Currently, this is a salted hash of the MAC address of this Bluetooth device. - // Salt: Randomly generated 256 bit value - // Hash algorithm: HMAC-SHA256 - // Size: 32 byte - // Default: null or empty if the device identifier is not known - optional bytes obfuscated_id = 1 [(android.os.statsd.log_mode) = MODE_BYTES]; - // Encoding interval in nanoseconds - // Default: 0 - optional int64 encoding_interval_nanos = 2; - // Number of bytes of PCM data that could not be read from the source - // Default: 0 - optional int32 num_missing_pcm_bytes = 3; - // An identifier that can be used to match events for this device. - // The incremental identifier is locally generated and guaranteed not derived - // from any globally unique hardware id. - // For paired devices, it stays consistent between Bluetooth toggling for the - // same remote device. - // For unpaired devices, it stays consistent within the same Bluetooth adapter - // session for the same remote device. - // Default: 0 if the device's metric id is unknown. - optional int32 metric_id = 4; -} - -/** - * Logs when A2DP failed send encoded data to the remote device fast enough such that the transmit - * buffer queue is full and we have to drop data - * - * Logged from: - * system/bt - */ -message BluetoothA2dpAudioOverrunReported { - // An identifier that can be used to match events for this device. - // Currently, this is a salted hash of the MAC address of this Bluetooth device. - // Salt: Randomly generated 256 bit value - // Hash algorithm: HMAC-SHA256 - // Size: 32 byte - // Default: null or empty if the device identifier is not known - optional bytes obfuscated_id = 1 [(android.os.statsd.log_mode) = MODE_BYTES]; - // Encoding interval in nanoseconds - // Default: 0 - optional int64 encoding_interval_nanos = 2; - // Number of buffers dropped in this event - // Each buffer is encoded in one encoding interval and consists of multiple encoded frames - // Default: 0 - optional int32 num_dropped_buffers = 3; - // Number of encoded buffers dropped in this event - // Default 0 - optional int32 num_dropped_encoded_frames = 4; - // Number of encoded bytes dropped in this event - // Default: 0 - optional int32 num_dropped_encoded_bytes = 5; - // An identifier that can be used to match events for this device. - // The incremental identifier is locally generated and guaranteed not derived - // from any globally unique hardware id. - // For paired devices, it stays consistent between Bluetooth toggling for the - // same remote device. - // For unpaired devices, it stays consistent within the same Bluetooth adapter - // session for the same remote device. - // Default: 0 if the device's metric id is unknown. - optional int32 metric_id = 6; -} - -/** - * Logs when we receive reports regarding a device's RSSI value - * - * Logged from: - * system/bt - */ -message BluetoothDeviceRssiReported { - // An identifier that can be used to match events for this device. - // Currently, this is a salted hash of the MAC address of this Bluetooth device. - // Salt: Randomly generated 256 bit value - // Hash algorithm: HMAC-SHA256 - // Size: 32 byte - // Default: null or empty if the device identifier is not known - optional bytes obfuscated_id = 1 [(android.os.statsd.log_mode) = MODE_BYTES]; - // Connection handle of this connection if available - // Range: 0x0000 - 0x0EFF (12 bits) - // Default: 0xFFFF if the handle is unknown - optional int32 connection_handle = 2; - // HCI command status code if this is triggerred by hci_cmd - // Default: STATUS_UNKNOWN - optional android.bluetooth.hci.StatusEnum hci_status = 3; - // BR/EDR - // Range: -128 ≤ N ≤ 127 (signed integer) - // Units: dB - // LE: - // Range: -127 to 20, 127 (signed integer) - // Units: dBm - // Invalid when an out of range value is reported - optional int32 rssi = 4; - // An identifier that can be used to match events for this device. - // The incremental identifier is locally generated and guaranteed not derived - // from any globally unique hardware id. - // For paired devices, it stays consistent between Bluetooth toggling for the - // same remote device. - // For unpaired devices, it stays consistent within the same Bluetooth adapter - // session for the same remote device. - // Default: 0 if the device's metric id is unknown. - optional int32 metric_id = 5; -} - -/** - * Logs when we receive reports regarding how many consecutive failed contacts for a connection - * - * Logged from: - * system/bt - */ -message BluetoothDeviceFailedContactCounterReported { - // An identifier that can be used to match events for this device. - // Currently, this is a salted hash of the MAC address of this Bluetooth device. - // Salt: Randomly generated 256 bit value - // Hash algorithm: HMAC-SHA256 - // Size: 32 byte - // Default: null or empty if the device identifier is not known - optional bytes obfuscated_id = 1 [(android.os.statsd.log_mode) = MODE_BYTES]; - // Connection handle of this connection if available - // Range: 0x0000 - 0x0EFF (12 bits) - // Default: 0xFFFF if the handle is unknown - optional int32 connection_handle = 2; - // HCI command status code if this is triggerred by hci_cmd - // Default: STATUS_UNKNOWN - optional android.bluetooth.hci.StatusEnum cmd_status = 3; - // Number of consecutive failed contacts for a connection corresponding to the Handle - // Range: uint16_t, 0-0xFFFF - // Default: 0xFFFFF - optional int32 failed_contact_counter = 4; - // An identifier that can be used to match events for this device. - // The incremental identifier is locally generated and guaranteed not derived - // from any globally unique hardware id. - // For paired devices, it stays consistent between Bluetooth toggling for the - // same remote device. - // For unpaired devices, it stays consistent within the same Bluetooth adapter - // session for the same remote device. - // Default: 0 if the device's metric id is unknown. - optional int32 metric_id = 5; -} - -/** - * Logs when we receive reports regarding the tranmit power level used for a specific connection - * - * Logged from: - * system/bt - */ -message BluetoothDeviceTxPowerLevelReported { - // An identifier that can be used to match events for this device. - // Currently, this is a salted hash of the MAC address of this Bluetooth device. - // Salt: Randomly generated 256 bit value - // Hash algorithm: HMAC-SHA256 - // Size: 32 byte - // Default: null or empty if the device identifier is not known - optional bytes obfuscated_id = 1 [(android.os.statsd.log_mode) = MODE_BYTES]; - // Connection handle of this connection if available - // Range: 0x0000 - 0x0EFF (12 bits) - // Default: 0xFFFF if the handle is unknown - optional int32 connection_handle = 2; - // HCI command status code if this is triggered by hci_cmd - // Default: STATUS_UNKNOWN - optional android.bluetooth.hci.StatusEnum hci_status = 3; - // Range: -30 ≤ N ≤ 20 - // Units: dBm - // Invalid when an out of range value is reported - optional int32 transmit_power_level = 4; - // An identifier that can be used to match events for this device. - // The incremental identifier is locally generated and guaranteed not derived - // from any globally unique hardware id. - // For paired devices, it stays consistent between Bluetooth toggling for the - // same remote device. - // For unpaired devices, it stays consistent within the same Bluetooth adapter - // session for the same remote device. - // Default: 0 if the device's metric id is unknown. - optional int32 metric_id = 5; -} - -/** - * Logs when Bluetooth controller failed to reply with command status within a timeout period after - * receiving an HCI command from the host - * - * Logged from: system/bt - */ -message BluetoothHciTimeoutReported { - // HCI command associated with this event - // Default: CMD_UNKNOWN - optional android.bluetooth.hci.CommandEnum hci_command = 1; -} - -/** - * Logs when we receive Bluetooth Link Quality Report event from the controller - * See Android Bluetooth HCI specification for more details - * - * Note: all count and bytes field are counted since last event - * - * Logged from: system/bt - */ -message BluetoothQualityReportReported { - // Quality report ID - // Original type: uint8_t - // Default: BQR_ID_UNKNOWN - optional android.bluetooth.hci.BqrIdEnum quality_report_id = 1; - // Packet type of the connection - // Original type: uint8_t - // Default: BQR_PACKET_TYPE_UNKNOWN - optional android.bluetooth.hci.BqrPacketTypeEnum packet_types = 2; - // Connection handle of the connection - // Original type: uint16_t - optional int32 connection_handle = 3; - // Performing Role for the connection - // Original type: uint8_t - optional int32 connection_role = 4; - // Current Transmit Power Level for the connection. This value is the same as the controller's - // response to the HCI_Read_Transmit_Power_Level HCI command - // Original type: uint8_t - optional int32 tx_power_level = 5; - // Received Signal Strength Indication (RSSI) value for the connection. This value is an - // absolute receiver signal strength value - // Original type: int8_t - optional int32 rssi = 6; - // Signal-to-Noise Ratio (SNR) value for the connection. It is the average SNR of all the - // channels used by the link currently - // Original type: uint8_t - optional int32 snr = 7; - // Indicates the number of unused channels in AFH_channel_map - // Original type: uint8_t - optional int32 unused_afh_channel_count = 8; - // Indicates the number of the channels which are interfered and quality is bad but are still - // selected for AFH - // Original type: uint8_t - optional int32 afh_select_unideal_channel_count = 9; - // Current Link Supervision Timeout Setting - // Unit: N * 0.3125 ms (1 Bluetooth Clock) - // Original type: uint16_t - optional int32 lsto = 10; - // Piconet Clock for the specified Connection_Handle. This value is the same as the controller's - // response to HCI_Read_Clock HCI command with the parameter "Which_Clock" of - // 0x01 (Piconet Clock) - // Unit: N * 0.3125 ms (1 Bluetooth Clock) - // Original type: uint32_t - optional int64 connection_piconet_clock = 11; - // The count of retransmission - // Original type: uint32_t - optional int64 retransmission_count = 12; - // The count of no RX - // Original type: uint32_t - optional int64 no_rx_count = 13; - // The count of NAK (Negative Acknowledge) - // Original type: uint32_t - optional int64 nak_count = 14; - // Controller timestamp of last TX ACK - // Unit: N * 0.3125 ms (1 Bluetooth Clock) - // Original type: uint32_t - optional int64 last_tx_ack_timestamp = 15; - // The count of Flow-off (STOP) - // Original type: uint32_t - optional int64 flow_off_count = 16; - // Controller timestamp of last Flow-on (GO) - // Unit: N * 0.3125 ms (1 Bluetooth Clock) - // Original type: uint32_t - optional int64 last_flow_on_timestamp = 17; - // Buffer overflow count (how many bytes of TX data are dropped) since the last event - // Original type: uint32_t - optional int64 buffer_overflow_bytes = 18; - // Buffer underflow count (in byte) since last event - // Original type: uint32_t - optional int64 buffer_underflow_bytes = 19; -} - -/** - * Logs when a Bluetooth device's manufacturer information is learnt by the Bluetooth stack - * - * Notes: - * - Each event can be partially filled as we might learn different pieces of device - * information at different time - * - Multiple device info events can be combined to give more complete picture - * - When multiple device info events tries to describe the same information, the - * later one wins - * - * Logged from: - * packages/apps/Bluetooth - */ -message BluetoothDeviceInfoReported { - // An identifier that can be used to match events for this device. - // Currently, this is a salted hash of the MAC address of this Bluetooth device. - // Salt: Randomly generated 256 bit value - // Hash algorithm: HMAC-SHA256 - // Size: 32 byte - // Default: null or empty if the device identifier is not known - optional bytes obfuscated_id = 1 [(android.os.statsd.log_mode) = MODE_BYTES]; - // Where is this device info obtained from - optional android.bluetooth.DeviceInfoSrcEnum source_type = 2; - // Name of the data source - // For EXTERNAL: package name of the data source - // For INTERNAL: null for general case, component name otherwise - optional string source_name = 3; - // Name of the manufacturer of this device - optional string manufacturer = 4; - // Model of this device - optional string model = 5; - // Hardware version of this device - optional string hardware_version = 6; - // Software version of this device - optional string software_version = 7; - // An identifier that can be used to match events for this device. - // The incremental identifier is locally generated and guaranteed not derived - // from any globally unique hardware id. - // For paired devices, it stays consistent between Bluetooth toggling for the - // same remote device. - // For unpaired devices, it stays consistent within the same Bluetooth adapter - // session for the same remote device. - // Default: 0 if the device's metric id is unknown. - optional int32 metric_id = 8; -} - -/** - * Logs when we receive Bluetooth Read Remote Version Information Complete Event from the remote - * device, as documented by the Bluetooth Core HCI specification - * Reference: https://www.bluetooth.com/specifications/bluetooth-core-specification - * Vol 2, Part E, Page 1118 - * - * Logged from: - * system/bt - */ -message BluetoothRemoteVersionInfoReported { - // Connection handle of the connection - // Original type: uint16_t - optional int32 connection_handle = 1; - // HCI command status code - // Default: STATUS_UNKNOWN - optional android.bluetooth.hci.StatusEnum hci_status = 2; - // 1 byte Version of current LMP in the remote controller - optional int32 lmp_version = 3; - // 2 bytes LMP manufacturer code of the remote controller - // https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers - optional int32 lmp_manufacturer_code = 4; - // 4 bytes subversion of the LMP in the remote controller - optional int32 lmp_subversion = 5; -} - -/** - * Logs when certain Bluetooth SDP attributes are discovered - * Constant definitions are from: - * https://www.bluetooth.com/specifications/assigned-numbers/service-discovery - * - * Current logged attributes: - * - BluetoothProfileDescriptorList - * - Supported Features Bitmask - * - * Logged from: - * system/bt - */ -message BluetoothSdpAttributeReported { - // An identifier that can be used to match events for this device. - // Currently, this is a salted hash of the MAC address of this Bluetooth device. - // Salt: Randomly generated 256 bit value - // Hash algorithm: HMAC-SHA256 - optional bytes obfuscated_id = 1 [(android.os.statsd.log_mode) = MODE_BYTES]; - // Short form UUIDs used to identify Bluetooth protocols, profiles, and service classes - // Original type: uint16_t - optional int32 protocol_uuid = 2; - // Short form UUIDs used to identify Bluetooth SDP attribute types - // Original type: uint16_t - optional int32 attribute_id = 3; - // Attribute value for the particular attribute - optional bytes attribute_value = 4 [(android.os.statsd.log_mode) = MODE_BYTES]; - // An identifier that can be used to match events for this device. - // The incremental identifier is locally generated and guaranteed not derived - // from any globally unique hardware id. - // For paired devices, it stays consistent between Bluetooth toggling for the - // same remote device. - // For unpaired devices, it stays consistent within the same Bluetooth adapter - // session for the same remote device. - // Default: 0 if the device's metric id is unknown. - optional int32 metric_id = 5; -} - -/** - * Logs when bond state of a Bluetooth device changes - * - * Logged from: - * frameworks/base/core/java/android/bluetooth/BluetoothDevice.java - * packages/apps/Bluetooth/src/com/android/bluetooth/btservice/BondStateMachine.java - */ -message BluetoothBondStateChanged { - // An identifier that can be used to match events for this device. - // Currently, this is a salted hash of the MAC address of this Bluetooth device. - // Salt: Randomly generated 256 bit value - // Hash algorithm: HMAC-SHA256 - // Size: 32 byte - // Default: null or empty if the device identifier is not known - optional bytes obfuscated_id = 1 [(android.os.statsd.log_mode) = MODE_BYTES]; - // Preferred transport type to remote dual mode device - // Default: TRANSPORT_AUTO means no preference - optional android.bluetooth.TransportTypeEnum transport = 2; - // The type of this Bluetooth device (Classic, LE, or Dual mode) - // Default: UNKNOWN - optional android.bluetooth.DeviceTypeEnum type = 3; - // Current bond state (NONE, BONDING, BONDED) - // Default: BOND_STATE_UNKNOWN - optional android.bluetooth.BondStateEnum bond_state = 4; - // Bonding sub state - // Default: BOND_SUB_STATE_UNKNOWN - optional android.bluetooth.BondSubStateEnum bonding_sub_state = 5; - // Unbond Reason - // Default: UNBOND_REASON_UNKNOWN - optional android.bluetooth.UnbondReasonEnum unbond_reason = 6; - // An identifier that can be used to match events for this device. - // The incremental identifier is locally generated and guaranteed not derived - // from any globally unique hardware id. - // For paired devices, it stays consistent between Bluetooth toggling for the - // same remote device. - // For unpaired devices, it stays consistent within the same Bluetooth adapter - // session for the same remote device. - // Default: 0 if the device's metric id is unknown. - optional int32 metric_id = 7; -} - -/** - * Logs there is an event related Bluetooth classic pairing - * - * Logged from: - * system/bt - */ -message BluetoothClassicPairingEventReported { - // An identifier that can be used to match events for this device. - // Currently, this is a salted hash of the MAC address of this Bluetooth device. - // Salt: Randomly generated 256 bit value - // Hash algorithm: HMAC-SHA256 - // Size: 32 byte - // Default: null or empty if the device identifier is not known - optional bytes obfuscated_id = 1 [(android.os.statsd.log_mode) = MODE_BYTES]; - // Connection handle of this connection if available - // Range: 0x0000 - 0x0EFF (12 bits) - // Default: 0xFFFF if the handle is unknown - optional int32 connection_handle = 2; - // HCI command associated with this event - // Default: CMD_UNKNOWN - optional android.bluetooth.hci.CommandEnum hci_cmd = 3; - // HCI event associated with this event - // Default: EVT_UNKNOWN - optional android.bluetooth.hci.EventEnum hci_event = 4; - // HCI command status code if this is triggerred by hci_cmd - // Default: STATUS_UNKNOWN - optional android.bluetooth.hci.StatusEnum cmd_status = 5; - // HCI reason code associated with this event - // Default: STATUS_UNKNOWN - optional android.bluetooth.hci.StatusEnum reason_code = 6; - // A status value related to this specific event - // Default: 0 - optional int64 event_value = 7; - // An identifier that can be used to match events for this device. - // The incremental identifier is locally generated and guaranteed not derived - // from any globally unique hardware id. - // For paired devices, it stays consistent between Bluetooth toggling for the - // same remote device. - // For unpaired devices, it stays consistent within the same Bluetooth adapter - // session for the same remote device. - // Default: 0 if the device's metric id is unknown. - optional int32 metric_id = 8; -} - -/** - * Logs when there is an event related to Bluetooth Security Manager Protocol (SMP) - * - * Logged from: - * system/bt - */ -message BluetoothSmpPairingEventReported { - // An identifier that can be used to match events for this device. - // Currently, this is a salted hash of the MAC address of this Bluetooth device. - // Salt: Randomly generated 256 bit value - // Hash algorithm: HMAC-SHA256 - // Size: 32 byte - // Default: null or empty if the device identifier is not known - optional bytes obfuscated_id = 1 [(android.os.statsd.log_mode) = MODE_BYTES]; - // SMP command sent or received over L2CAP - // Default: CMD_UNKNOWN - optional android.bluetooth.smp.CommandEnum smp_command = 2; - // Whether this command is sent or received - // Default: DIRECTION_UNKNOWN - optional android.bluetooth.DirectionEnum direction = 3; - // SMP failure reason code - // Default: PAIRING_FAIL_REASON_DEFAULT - optional android.bluetooth.smp.PairingFailReasonEnum smp_fail_reason = 4; - // An identifier that can be used to match events for this device. - // The incremental identifier is locally generated and guaranteed not derived - // from any globally unique hardware id. - // For paired devices, it stays consistent between Bluetooth toggling for the - // same remote device. - // For unpaired devices, it stays consistent within the same Bluetooth adapter - // session for the same remote device. - // Default: 0 if the device's metric id is unknown. - optional int32 metric_id = 5; -} - -/** - * Logs when a Bluetooth socket’s connection state changed - * - * Logged from: - * system/bt - */ -message BluetoothSocketConnectionStateChanged { - // An identifier that can be used to match events for this device. - // Currently, this is a salted hash of the MAC address of this Bluetooth device. - // Salt: Randomly generated 256 bit value - // Hash algorithm: HMAC-SHA256 - // Size: 32 byte - // Default: null or empty if this is a server listener socket - optional bytes obfuscated_id = 1 [(android.os.statsd.log_mode) = MODE_BYTES]; - // Temporary port of this socket for the current connection or session only - // Default 0 when unknown or don't care - optional int32 port = 2; - // Socket type as mentioned in - // frameworks/base/core/java/android/bluetooth/BluetoothSocket.java - // Default: SOCKET_TYPE_UNKNOWN - optional android.bluetooth.SocketTypeEnum type = 3; - // Socket connection state - // Default: SOCKET_CONNECTION_STATE_UNKNOWN - optional android.bluetooth.SocketConnectionstateEnum state = 4; - // Number of bytes sent to remote device during this connection - optional int64 tx_bytes = 5; - // Number of bytes received from remote device during this connection - optional int64 rx_bytes = 6; - // Socket owner's UID - optional int32 uid = 7 [(is_uid) = true]; - // Server port of this socket, if any. When both |server_port| and |port| fields are populated, - // |port| must be spawned by |server_port| - // Default 0 when unknown or don't care - optional int32 server_port = 8; - // Whether this is a server listener socket - optional android.bluetooth.SocketRoleEnum is_server = 9; - // An identifier that can be used to match events for this device. - // The incremental identifier is locally generated and guaranteed not derived - // from any globally unique hardware id. - // For paired devices, it stays consistent between Bluetooth toggling for the - // same remote device. - // For unpaired devices, it stays consistent within the same Bluetooth adapter - // session for the same remote device. - // Default: 0 if the device's metric id is unknown. - optional int32 metric_id = 10; -} - -/** - * Logs when Class of Device (CoD) value is learnt for a device during pairing or connection - * - * Logged from: - * packages/apps/Bluetooth/src/com/android/bluetooth/btservice/BondStateMachine.java - * packages/apps/Bluetooth/src/com/android/bluetooth/btservice/RemoteDevices.java - * - */ -message BluetoothClassOfDeviceReported { - // An identifier that can be used to match events for this device. - // Currently, this is a salted hash of the MAC address of this Bluetooth device. - // Salt: Randomly generated 256 bit value - // Hash algorithm: HMAC-SHA256 - // Size: 32 byte - // Default: null or empty if this is a server listener socket - optional bytes obfuscated_id = 1 [(android.os.statsd.log_mode) = MODE_BYTES]; - // Class of Device (CoD) value including both Major, Minor device class and service class - // Defined in: https://www.bluetooth.com/specifications/assigned-numbers/baseband - // Also defined in: https://developer.android.com/reference/android/bluetooth/BluetoothClass - // Default: 0 - optional int32 class_of_device = 2; - // An identifier that can be used to match events for this device. - // The incremental identifier is locally generated and guaranteed not derived - // from any globally unique hardware id. - // For paired devices, it stays consistent between Bluetooth toggling for the - // same remote device. - // For unpaired devices, it stays consistent within the same Bluetooth adapter - // session for the same remote device. - // Default: 0 if the device's metric id is unknown. - optional int32 metric_id = 3; -} - -/** - * Logs when something is plugged into or removed from the USB-C connector. - * - * Logged from: - * UsbService - */ -message UsbConnectorStateChanged { - enum State { - STATE_DISCONNECTED = 0; - STATE_CONNECTED = 1; - } - optional State state = 1 - [(state_field_option).exclusive_state = true, (state_field_option).nested = false]; - optional string id = 2 [(state_field_option).primary_field = true]; - // Last active session in ms. - // 0 when the port is in connected state. - optional int64 last_connect_duration_millis = 3; -} - -/** - * Logs the reported speaker impedance. - * - * Logged from: - * Vendor audio implementation. - */ -message SpeakerImpedanceReported { - optional int32 speaker_location = 1; - optional int32 impedance = 2; -} - -/** - * Logs the report of a failed hardware. - * - * Logged from: - * Vendor HALs. - * - */ -message HardwareFailed { - enum HardwareType { - HARDWARE_FAILED_UNKNOWN = 0; - HARDWARE_FAILED_MICROPHONE = 1; - HARDWARE_FAILED_CODEC = 2; - HARDWARE_FAILED_SPEAKER = 3; - HARDWARE_FAILED_FINGERPRINT = 4; - } - optional HardwareType hardware_type = 1; - - /** - * hardware_location allows vendors to differentiate between multiple instances of - * the same hardware_type. The specific locations are vendor defined integers, - * referring to board-specific numbering schemes. - */ - optional int32 hardware_location = 2; - - /** - * failure_code is specific to the HardwareType of the failed hardware. - * It should use one of the enum values defined below. - */ - enum HardwareErrorCode { - UNKNOWN = 0; - COMPLETE = 1; - SPEAKER_HIGH_Z = 2; - SPEAKER_SHORT = 3; - FINGERPRINT_SENSOR_BROKEN = 4; - FINGERPRINT_TOO_MANY_DEAD_PIXELS = 5; - DEGRADE = 6; - } - optional int32 failure_code = 3; -} - -/** - * Log an event when the device has been physically dropped. - * Reported from the /vendor partition. - */ -message PhysicalDropDetected { - // Confidence that the event was actually a drop, 0 -> 100 - optional int32 confidence_pctg = 1; - // Peak acceleration of the drop, in 1/1000s of a g. - optional int32 accel_peak_thousandths_g = 2; - // Duration of freefall in ms - optional int32 freefall_time_millis = 3; -} - -/** - * Log bucketed battery charge cycles. - * - * Each bucket represents cycles of the battery past - * a given charge point. For example, if 10 cycle buckets are - * initialized, bucket 1 is the lowest 1/10th of the battery, - * and bucket 10 is 100%. - * - * Logged from: - * /sys/class/power_supply/bms/cycle_count, via Vendor. - */ -message ChargeCyclesReported { - optional int32 cycle_bucket_1 = 1; - optional int32 cycle_bucket_2 = 2; - optional int32 cycle_bucket_3 = 3; - optional int32 cycle_bucket_4 = 4; - optional int32 cycle_bucket_5 = 5; - optional int32 cycle_bucket_6 = 6; - optional int32 cycle_bucket_7 = 7; - optional int32 cycle_bucket_8 = 8; - optional int32 cycle_bucket_9 = 9; - optional int32 cycle_bucket_10 = 10; -} - -/** - * Log battery health snapshot. - * - * Resistance, Voltage, Open Circuit Voltage, Temperature, and Charge Level - * are snapshotted periodically over 24hrs. - */ -message BatteryHealthSnapshot { - enum BatterySnapshotType { - UNKNOWN = 0; - MIN_TEMP = 1; // Snapshot at min batt temp over 24hrs. - MAX_TEMP = 2; // Snapshot at max batt temp over 24hrs. - MIN_RESISTANCE = 3; // Snapshot at min batt resistance over 24hrs. - MAX_RESISTANCE = 4; // Snapshot at max batt resistance over 24hrs. - MIN_VOLTAGE = 5; // Snapshot at min batt voltage over 24hrs. - MAX_VOLTAGE = 6; // Snapshot at max batt voltage over 24hrs. - MIN_CURRENT = 7; // Snapshot at min batt current over 24hrs. - MAX_CURRENT = 8; // Snapshot at max batt current over 24hrs. - MIN_BATT_LEVEL = 9; // Snapshot at min battery level (SoC) over 24hrs. - MAX_BATT_LEVEL = 10; // Snapshot at max battery level (SoC) over 24hrs. - AVG_RESISTANCE = 11; // Snapshot at average battery resistance over 24hrs. - } - optional BatterySnapshotType type = 1; - // Temperature, in 1/10ths of degree C. - optional int32 temperature_deci_celsius = 2; - // Voltage Battery Voltage, in microVolts. - optional int32 voltage_micro_volt = 3; - // Current Battery current, in microAmps. - optional int32 current_micro_amps = 4; - // OpenCircuitVoltage Battery Open Circuit Voltage, in microVolts. - optional int32 open_circuit_micro_volt = 5; - // Resistance Battery Resistance, in microOhms. - optional int32 resistance_micro_ohm = 6; - // Level Battery Level, as % of full. - optional int32 level_percent = 7; -} - -/** - * Log slow I/O operations on the primary storage. - */ -message SlowIo { - // Classifications of IO Operations. - enum IoOperation { - UNKNOWN = 0; - READ = 1; - WRITE = 2; - UNMAP = 3; - SYNC = 4; - } - optional IoOperation operation = 1; - - // The number of slow IO operations of this type over 24 hours. - optional int32 count = 2; -} - -/** - * Log battery caused shutdown with the last recorded voltage. - */ -message BatteryCausedShutdown { - // The last recorded battery voltage prior to shutdown. - optional int32 last_recorded_micro_volt = 1; -} - -/** - * Logs when ThermalService receives throttling events. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/stats/StatsCompanionService.java - */ -message ThermalThrottlingSeverityStateChanged { - // The type of temperature being reported (CPU, GPU, SKIN, etc) - optional android.os.TemperatureTypeEnum sensor_type = 1; - - // The name of the temperature source. Eg. CPU0 - optional string sensor_name = 2; - - // Temperature in tenths of a degree C. - // For BCL, it is decimillivolt, decimilliamps, and percentage * 10. - optional int32 temperature_deci_celsius = 3; - - // Relative severity of the throttling, see enum definition. - optional android.os.ThrottlingSeverityEnum severity = 4; -} - -/** - * Logs the duration of a davey (jank of >=700ms) when it occurs - * - * Logged from: - * frameworks/base/libs/hwui/JankTracker.cpp - */ -message DaveyOccurred { - // The UID that logged this atom. - optional int32 uid = 1 [(is_uid) = true]; - - // Amount of time it took to render the frame. Should be >=700ms. - optional int64 jank_duration_millis = 2; -} - -/** - * Logs phone signal strength changes. - * - * Logged from: - * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java - */ -message PhoneSignalStrengthChanged { - // Signal strength, from frameworks/base/core/proto/android/telephony/enums.proto. - optional android.telephony.SignalStrengthEnum signal_strength = 1; -} - - -/** - * Logs when the phone state, sim state or signal strength changes - * - * Logged from: - * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java - */ -message PhoneServiceStateChanged { - optional android.telephony.ServiceStateEnum state = 1; - optional android.telephony.SimStateEnum sim_state = 2; - optional android.telephony.SignalStrengthEnum signal_strength = 3; -} - -/** - * Logs when the phone becomes on or off. - * - * Logged from: - * frameworks/base/core/java/com/android/internal/os/TelephonyRegistry.java - */ -message PhoneStateChanged { - enum State { - OFF = 0; - ON = 1; - } - optional State state = 1; -} - -message BackGesture { - enum BackType { - DEFAULT_BACK_TYPE = 0; - COMPLETED = 1; - COMPLETED_REJECTED = 2; // successful because coming from rejected area - INCOMPLETE_EXCLUDED = 3; // would have been successful but in the exclusion area - INCOMPLETE = 4; // Unsuccessful, for reasons other than below. - INCOMPLETE_FAR_FROM_EDGE = 5; // Unsuccessful, far from the edge. - INCOMPLETE_MULTI_TOUCH = 6; // Unsuccessful, multi touch. - INCOMPLETE_LONG_PRESS = 7; // Unsuccessful, long press. - INCOMPLETE_VERTICAL_MOVE = 8; // Unsuccessful, move vertically. - } - optional BackType type = 1; - - optional int32 y_coordinate = 2 [deprecated = true]; // y coordinate for ACTION_DOWN event - optional int32 start_x = 4; // X coordinate for ACTION_DOWN event. - optional int32 start_y = 5; // Y coordinate for ACTION_DOWN event. - optional int32 end_x = 6; // X coordinate for ACTION_MOVE event. - optional int32 end_y = 7; // Y coordinate for ACTION_MOVE event. - optional int32 left_boundary = 8; // left edge width + left inset - optional int32 right_boundary = 9; // screen width - (right edge width + right inset) - // The score between 0 and 1 which is the prediction output for the Back Gesture model. - optional float ml_model_score = 10; - optional string package_name = 11; // The name of the top 100 most used package by all users. - - enum WindowHorizontalLocation { - DEFAULT_LOCATION = 0; - LEFT = 1; - RIGHT = 2; - } - optional WindowHorizontalLocation x_location = 3 [deprecated = true]; -} - -message ExclusionRectStateChanged { - optional string component_name = 1; // if not available, simply packageName - optional int32 requested_height = 2; // px - optional int32 rejected_height = 3; // px - - enum WindowHorizontalLocation { - DEFAULT_LOCATION = 0; - LEFT = 1; - RIGHT = 2; - } - optional WindowHorizontalLocation x_location = 4; - optional bool landscape = 5; - optional bool splitscreen = 6; - optional int32 duration_millis = 7; -} - -/** - * Logs when IME is on. - * - * Logged from: /packages/SystemUI/src/com/android/systemui/ - statusbar/phone/NavigationBarView.java - * - */ -message ImeTouchReported { - optional int32 x_coordinate = 1; // X coordinate for ACTION_DOWN event. - optional int32 y_coordinate = 2; // Y coordinate for ACTION_DOWN event. -} - -/** - * Logs when Launcher (HomeScreen) UI has changed or was interacted. - * - * Logged from: - * packages/apps/Launcher3 - */ -message LauncherUIChanged { - optional android.stats.launcher.LauncherAction action = 1 [deprecated = true]; - optional android.stats.launcher.LauncherState src_state = 2; - optional android.stats.launcher.LauncherState dst_state = 3; - optional android.stats.launcher.LauncherExtension extension = 4 [(log_mode) = MODE_BYTES, deprecated = true]; - optional bool is_swipe_up_enabled = 5 [deprecated = true]; - - // The event id (e.g., app launch, drag and drop, long press) - optional int32 event_id = 6; - // The event's source or target id (e.g., icon, task, button) - optional int32 target_id = 7; - // If the target needs to be tracked, use this id field - optional int32 instance_id = 8; - optional int32 uid = 9 [(is_uid) = true]; - optional string package_name = 10; - optional string component_name = 11; - - // (x, y) coordinate and the index information of the target on the container - optional int32 grid_x = 12 [default = -1]; - optional int32 grid_y = 13 [default = -1]; - optional int32 page_id = 14 [default = -2]; - - // e.g., folder icon's (x, y) location and index information on the workspace - optional int32 grid_x_parent = 15 [default = -1]; - optional int32 grid_y_parent = 16 [default = -1]; - optional int32 page_id_parent = 17 [default = -2]; - - // e.g., SEARCHBOX_ALLAPPS, FOLDER_WORKSPACE - optional int32 hierarchy = 18; - - optional bool is_work_profile = 19; - - // Used to store the predicted rank of the target - optional int32 rank = 20 [default = -1]; - - // e.g., folderLabelState can be captured in the following two fields - optional int32 from_state = 21; - optional int32 to_state = 22; - - // e.g., autofilled or suggested texts that are not user entered - optional string edittext = 23; - - // e.g., number of contents inside a container (e.g., icons inside a folder) - optional int32 cardinality = 24; -} - -/** - * Used for snapshot of the HomeScreen UI elements - * - * Logged from: - * packages/apps/Launcher3 - */ -message LauncherStaticLayout { - // The event id (e.g., snapshot, drag and drop) - optional int32 event_id = 1; - // The event's source or target id (e.g., icon, shortcut, widget) - optional int32 target_id = 2; - // If the target needs to be tracked, use this id field - optional int32 instance_id = 3; - optional int32 uid = 4 [(is_uid) = true]; - optional string package_name = 5; - optional string component_name = 6; - - // (x, y) coordinate and the index information of the target on the container - optional int32 grid_x = 7 [default = -1]; - optional int32 grid_y = 8 [default = -1]; - optional int32 page_id = 9 [default = -2]; - - // e.g., folder icon's (x, y) location and index information on the workspace - // e.g., when used with widgets target, use these values for (span_x, span_y) - optional int32 grid_x_parent = 10 [default = -1]; - optional int32 grid_y_parent = 11 [default = -1]; - optional int32 page_id_parent = 12 [default = -2]; - - // UNKNOWN = 0 - // HOTSEAT = 1 - // WORKSPACE = 2 - // FOLDER_HOTSEAT = 3 - // FOLDER_WORKSPACE = 4 - optional int32 hierarchy = 13; - - optional bool is_work_profile = 14; - - // e.g., PIN, WIDGET TRAY, APPS TRAY, PREDICTION - optional int32 origin = 15; - - // e.g., number of icons inside a folder - optional int32 cardinality = 16; - - // e.g., (x, y) span of the widget inside homescreen grid system - optional int32 span_x = 17 [default = 1]; - optional int32 span_y = 18 [default = 1]; -} - -/** - * Logs when Wallpaper or ThemePicker UI has changed. - * - * Logged from: - * packages/apps/ThemePicker - * packages/apps/WallpaperPicker2 - */ -message StyleUIChanged { - optional android.stats.style.Action action = 1; - optional int32 color_package_hash = 2; - optional int32 font_package_hash = 3; - optional int32 shape_package_hash = 4; - optional int32 clock_package_hash = 5; - optional int32 launcher_grid = 6; - optional int32 wallpaper_category_hash = 7; - optional int32 wallpaper_id_hash = 8; - optional int32 color_preference = 9; - optional android.stats.style.LocationPreference location_preference = 10; - optional android.stats.style.DatePreference date_preference = 11; - optional android.stats.style.LaunchedPreference launched_preference = 12; -} - -/** - * Logs when Settings UI has changed. - * - * Logged from: - * packages/apps/Settings - */ -message SettingsUIChanged { - /** - * Where this SettingsUIChange event comes from. For example, if - * it's a PAGE_VISIBLE event, where the page is opened from. - */ - optional android.app.settings.PageId attribution = 1; - - /** - * What the UI action is. - */ - optional android.app.settings.Action action = 2; - - /** - * Where the action is happening - */ - optional android.app.settings.PageId page_id = 3; - - /** - * What preference changed in this event. - */ - optional string changed_preference_key = 4; - - /** - * The new value of the changed preference. - */ - optional int64 changed_preference_int_value = 5; -} - -/** - * Logs basic timing information about touch events. - * Reported at most every 5 minutes while device is being interacted with. - * - * Logged from: - * frameworks/native/services/inputflinger - */ -message TouchEventReported { - /** - * The fields latency_{min|max|mean|stdev} represent minimum, maximum, mean, - * and the standard deviation of the time spent processing touchscreen events - * in the kernel and inputflinger. The units are microseconds. - * - * On supported devices, the starting point is taken during the hard interrupt inside the - * kernel touch driver. On all other devices, the starting point is taken inside - * the kernel's input event subsystem upon receipt of the input event. - * The ending point is taken inside InputDispatcher, just after the input event - * is sent to the app. - */ - // Minimum value - optional float latency_min_micros = 1; - // Maximum value - optional float latency_max_micros = 2; - // Average value - optional float latency_mean_micros = 3; - // Standard deviation - optional float latency_stdev_micros = 4; - // Number of touch events (input_event) in this report - optional int32 count = 5; -} - -/** - * Logs gesture classification and timing information for touch events. - * - * Logged from: - * frameworks/base/core/java/android/view/GestureDetector.java - * frameworks/base/core/java/android/view/View.java - */ -message TouchGestureClassified { - // The source of the classification (e.g. Java class name). - optional string source = 1; - - enum Classification { - UNKNOWN_CLASSIFICATION = 0; - SINGLE_TAP = 1; - DOUBLE_TAP = 2; - LONG_PRESS = 3; - DEEP_PRESS = 4; - SCROLL = 5; - } - // The classification of the gesture. - optional Classification classification = 2; - - // The interval from the start of a touch event stream until the - // classification was made. - optional int32 latency_millis = 3; - - // The distance from the location of the first touch event to the - // location of the touch event when the classification was made. - optional float displacement_px = 4; -} - -/** - * Logs that a setting was updated. - * Logged from: - * frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java - * The tag and is_default allow resetting of settings to default values based on the specified - * tag. See Settings#putString(ContentResolver, String, String, String, boolean) for more details. - */ -message SettingChanged { - // The name of the setting. - optional string setting = 1; - - // The change being imposed on this setting. May represent a number, eg "3". - optional string value = 2; - - // The new value of this setting. For most settings, this is same as value. For some settings, - // value is +X or -X where X represents an element in a set. For example, if the previous value - // is A,B,C and value is -B, then new_value is A,C and prev_value is A,B,C. - // The +/- feature is currently only used for location_providers_allowed. - optional string new_value = 3; - - // The previous value of this setting. - optional string prev_value = 4; - - // The tag used with the is_default for resetting sets of settings. This is generally null. - optional string tag = 5; - - // True if this setting with tag should be resettable. - optional bool is_default = 6; - - // The associated user (for multi-user feature). Defined in android/os/UserHandle.java - optional int32 user = 7; - - enum ChangeReason { - UPDATED = 1; // Updated can be an insertion or an update. - DELETED = 2; - } - optional ChangeReason reason = 8; -} - -/** - * Logs activity going to foreground or background - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/am/ActivityRecord.java - */ -message ActivityForegroundStateChanged { - optional int32 uid = 1 [(is_uid) = true]; - optional string pkg_name = 2; - optional string class_name = 3; - - enum State { - BACKGROUND = 0; - FOREGROUND = 1; - } - optional State state = 4; -} - -/** - * Logs when a volume entered low Storage state. - * Logged from: - * frameworks/base/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java - */ -message LowStorageStateChanged { - // Volume that ran out of storage. - optional string volume_description = 1; - - enum State { - UNKNOWN = 0; - OFF = 1; - ON = 2; - } - optional State state = 2; -} - -/** - * Logs when an app is downgraded. - * Logged from: - * frameworks/base/services/core/java/com/android/server/pm/BackgroundDexOptService.java - */ -message AppDowngraded { - optional string package_name = 1; - // Size of the package (all data) before being downgraded. - optional int64 size_in_bytes_before = 2; - // Size of the package (all data) after being downgraded. - optional int64 size_in_bytes_after = 3; - - optional bool aggressive = 4; -} - -/** - * Logs when an app is optimized after being downgraded. - * Logged from: - * frameworks/base/services/core/java/com/android/server/pm/BackgroundDexOptService.java - */ -message AppOptimizedAfterDowngraded { - optional string package_name = 1; -} - -/** - * Logs whenever an app is installed on external storage. - * Logged from: - frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java - */ -message AppInstallOnExternalStorageReported { - // The type of external storage. - optional android.stats.storage.ExternalStorageType storage_type = 1; - // The name of the package that is installed on the sd card. - optional string package_name = 2; -} - -/** - * Logs when an app crashes. - * Logged from: - * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java - */ -message AppCrashOccurred { - optional int32 uid = 1 [(is_uid) = true]; - - optional string event_type = 2; - - // The name of the process. - // system_server if it is not by an app - optional string process_name = 3; - - // The pid if available. -1 means not available. - optional int32 pid = 4; - - optional string package_name = 5; - - enum InstantApp { - UNAVAILABLE = 0; - FALSE = 1; - TRUE = 2; - } - optional InstantApp is_instant_app = 6; - - enum ForegroundState { - UNKNOWN = 0; - BACKGROUND = 1; - FOREGROUND = 2; - } - optional ForegroundState foreground_state = 7; - - optional android.server.ErrorSource error_source = 8; - - optional bool is_package_loading = 9; -} - -/** - * Logs when a WTF (What a Terrible Failure) happened. - * Logged from: - * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java - */ -message WTFOccurred { - optional int32 uid = 1 [(is_uid) = true]; - - optional string tag = 2; - - // The name of the process. - // system_server if it is not by an app - optional string process_name = 3; - - // The pid if available. -1 means not available. - optional int32 pid = 4; - - optional android.server.ErrorSource error_source = 5; -} - -/** - * Logs when system server reports low memory. - * Logged from: - * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java - */ -message LowMemReported { -} - -/** - * Logs when an app ANR (App Not Responding) occurs. - * Logged from: - * frameworks/base/services/core/java/com/android/server/am/AppErrors.java - */ -message ANROccurred { - optional int32 uid = 1 [(is_uid) = true]; - - optional string process_name = 2; - - optional string short_component_name = 3; - - optional string reason = 4; - - enum InstantApp { - UNAVAILABLE = 0; - FALSE = 1; - TRUE = 2; - } - optional InstantApp is_instant_app = 5; - - enum ForegroundState { - UNKNOWN = 0; - BACKGROUND = 1; - FOREGROUND = 2; - } - optional ForegroundState foreground_state = 6; - - optional android.server.ErrorSource error_source = 7; - - optional string package_name = 8; - - optional bool is_package_loading = 9; -} - -/** - * Logs when the vibrator state changes. - * Logged from: - * frameworks/base/services/core/java/com/android/server/VibratorService.java - */ -message VibratorStateChanged { - repeated AttributionNode attribution_node = 1; - - enum State { - OFF = 0; - ON = 1; - } - optional State state = 2; - - // Duration (in milliseconds) requested to keep the vibrator on. - // Only applicable for State == ON. - optional int64 duration_millis = 3; -} - -/* - * Allows other apps to push events into statsd. - * Logged from: - * frameworks/base/core/java/android/util/StatsLog.java - */ -message AppBreadcrumbReported { - // The uid of the application that sent this custom atom. - optional int32 uid = 1 [(is_uid) = true]; - - // An arbitrary label chosen by the developer. For Android P, the label should be in [0, 16). - optional int32 label = 2; - - // Allows applications to easily use a custom event as start/stop boundaries (ie, define custom - // predicates for the metrics). - enum State { - UNKNOWN = 0; - UNSPECIFIED = 1; // For events that are known to not represent START/STOP. - STOP = 2; - START = 3; - } - optional State state = 3; -} - -/** - * Logs the wall-clock time when a significant wall-clock time shift occurs. - * For example, this could be due to the user manually changing the time. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/AlarmManagerService.java - */ -message WallClockTimeShifted { - // New wall-clock time in milliseconds, according to System.currentTimeMillis(). - optional int64 wall_clock_timestamp_millis = 1; -} - -/** - * Logs when statsd detects an anomaly. - * - * Logged from: - * frameworks/base/cmds/statsd/src/anomaly/AnomalyTracker.cpp - */ -message AnomalyDetected { - // Uid that owns the config whose anomaly detection alert fired. - optional int32 config_uid = 1 [(is_uid) = true]; - - // Id of the config whose anomaly detection alert fired. - optional int64 config_id = 2; - - // Id of the alert (i.e. name of the anomaly that was detected). - optional int64 alert_id = 3; -} - -message AppStartOccurred { - // The uid if available. -1 means not available. - optional int32 uid = 1 [(is_uid) = true]; - - // The app package name. - optional string pkg_name = 2; - - enum TransitionType { - UNKNOWN = 0; - WARM = 1; - HOT = 2; - COLD = 3; - } - // The transition type. - optional TransitionType type = 3; - - // The activity name. - optional string activity_name = 4; - - // The name of the calling app. Empty if not set. - optional string calling_pkg_name = 5; - - // Whether the app is an instant app. - optional bool is_instant_app = 6; - - // Device uptime when activity started. - optional int64 activity_start_millis = 7; - - optional android.app.AppTransitionReasonEnum reason = 8; - - optional int32 transition_delay_millis = 9; - // -1 if not set. - optional int32 starting_window_delay_millis = 10; - // -1 if not set. - optional int32 bind_application_delay_millis = 11; - optional int32 windows_drawn_delay_millis = 12; - - // Empty if not set. - optional string launch_token = 13; - - // The compiler filter used when when the package was optimized. - optional int32 package_optimization_compilation_filter = 14; - - // The reason why the package was optimized. - optional int32 package_optimization_compilation_reason = 15; - - enum SourceType { - UNAVAILABLE = 0; - LAUNCHER = 1; - NOTIFICATION = 2; - LOCKSCREEN = 3; - RECENTS_ANIMATION = 4; - } - // The type of the startup source. - optional SourceType source_type = 16; - - // The time from the startup source to the beginning of handling the startup event. - // -1 means not available. - optional int32 source_event_delay_millis = 17; -} - -message AppStartCanceled { - // The uid if available. -1 means not available. - optional int32 uid = 1 [(is_uid) = true]; - - // The app package name. - optional string pkg_name = 2; - - enum TransitionType { - UNKNOWN = 0; - WARM = 1; - HOT = 2; - COLD = 3; - } - // The transition type. - optional TransitionType type = 3; - - // The activity name. - optional string activity_name = 4; -} - -message AppStartFullyDrawn { - // The uid if available. -1 means not available. - optional int32 uid = 1 [(is_uid) = true]; - - // The app package name. - optional string pkg_name = 2; - - enum TransitionType { - UNKNOWN = 0; - WITH_BUNDLE = 1; - WITHOUT_BUNDLE = 2; - } - // The transition type. - optional TransitionType type = 3; - - // The activity name. - optional string activity_name = 4; - - optional bool transition_process_running = 5; - - // App startup time (until call to Activity#reportFullyDrawn()). - optional int64 app_startup_time_millis = 6; - - // The compiler filter used when when the package was optimized. - optional int32 package_optimization_compilation_filter = 7; - - // The reason why the package was optimized. - optional int32 package_optimization_compilation_reason = 8; - - enum SourceType { - UNAVAILABLE = 0; - LAUNCHER = 1; - NOTIFICATION = 2; - LOCKSCREEN = 3; - } - // The type of the startup source. - optional SourceType source_type = 9; - - // The time from the startup source to the beginning of handling the startup event. - // -1 means not available. - optional int32 source_event_delay_millis = 10; -} - -/** - * Logs a picture-in-picture action - * Logged from: - * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java - * frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java - * frameworks/base/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java - */ -message PictureInPictureStateChanged { - // -1 if it is not available - optional int32 uid = 1 [(is_uid) = true]; - - optional string short_name = 2; - - enum State { - ENTERED = 1; - EXPANDED_TO_FULL_SCREEN = 2; - MINIMIZED = 3; - DISMISSED = 4; - } - optional State state = 3; -} - -/** - * Logs overlay action - * Logged from: - * services/core/java/com/android/server/wm/Session.java - */ -message OverlayStateChanged { - optional int32 uid = 1 [(state_field_option).primary_field = true, (is_uid) = true]; - - optional string package_name = 2 [(state_field_option).primary_field = true]; - - optional bool using_alert_window = 3; - - enum State { - ENTERED = 1; - EXITED = 2; - } - optional State state = 4 - [(state_field_option).exclusive_state = true, (state_field_option).nested = false]; -} - -/** - * Logs foreground service starts and stops. - * Note that this is not when a service starts or stops, but when it is - * considered foreground. - * Logged from - * frameworks/base/services/core/java/com/android/server/am/ActiveServices.java - */ -message ForegroundServiceStateChanged { - optional int32 uid = 1 [(is_uid) = true]; - // package_name + "/" + class_name - optional string short_name = 2; - - enum State { - ENTER = 1; - EXIT = 2; - } - optional State state = 3; - - // Whether the fgs is allowed while-in-use permissions, i.e. is considered 'in-use' to the user. - // (If the fgs was started while the app wasn't TOP it usually will be denied these permissions) - optional bool allow_while_in_use_permission = 4; -} - -/** - * Logs the number of times a uid accesses a sensitive AppOp during a foreground service session. - * A foreground service session is any continuous period during which the uid holds at least one - * foreground service; the atom will be pushed when the uid no longer holds any foreground services. - * Accesses initiated while the uid is in the TOP state are ignored. - * Sessions with no attempted accesses are not logged. - * Logged from - * frameworks/base/services/core/java/com/android/server/am/ActiveServices.java - */ -message ForegroundServiceAppOpSessionEnded { - optional int32 uid = 1 [(is_uid) = true]; - - // The operation's name. - // Only following four ops are logged - // COARSE_LOCATION = 0 - // FINE_LOCATION = 1 - // CAMERA = 26 - // RECORD_AUDIO = 27 - optional android.app.AppOpEnum app_op_name = 2 [default = APP_OP_NONE]; - - // The uid's permission mode for accessing the AppOp during this fgs session. - enum Mode { - MODE_UNKNOWN = 0; - MODE_ALLOWED = 1; // Always allowed - MODE_IGNORED = 2; // Denied - MODE_FOREGROUND = 3; // Allow-while-in-use (or allowed-one-time) - } - optional Mode app_op_mode = 3; - - // Number of times this AppOp was requested and allowed. - optional int32 count_ops_accepted = 4; - // Number of times this AppOp was requested but denied. - optional int32 count_ops_rejected = 5; -} - -/** - * Logs creation or removal of an isolated uid. Isolated uid's are temporary uid's to sandbox risky - * behavior in its own uid. However, the metrics of these isolated uid's almost always should be - * attributed back to the parent (host) uid. One example is Chrome. - * - * Logged from: - * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java - */ -message IsolatedUidChanged { - // The host UID. Generally, we should attribute metrics from the isolated uid to the host uid. - // NOTE: DO NOT annotate uid field in this atom. This atom is specially handled in statsd. - // This field is ignored when event == REMOVED. - optional int32 parent_uid = 1; - - optional int32 isolated_uid = 2; - - // We expect an isolated uid to be removed before if it's used for another parent uid. - enum Event { - REMOVED = 0; - CREATED = 1; - } - optional Event event = 3; -} - -/* - * Logs the reception of an incoming network packet causing the main system to wake up for - * processing that packet. These events are notified by the kernel via Netlink NFLOG to Netd - * and processed by WakeupController.cpp. - */ -message PacketWakeupOccurred { - // The uid owning the socket into which the packet was delivered, or -1 if the packet was - // delivered nowhere. - optional int32 uid = 1 [(is_uid) = true]; - // The interface name on which the packet was received. - optional string iface = 2; - // The ethertype value of the packet. - optional int32 ethertype = 3; - // String representation of the destination MAC address of the packet. - optional string destination_hardware_address = 4; - // String representation of the source address of the packet if this was an IP packet. - optional string source_ip = 5; - // String representation of the destination address of the packet if this was an IP packet. - optional string destination_ip = 6; - // The value of the protocol field if this was an IPv4 packet or the value of the Next Header - // field if this was an IPv6 packet. The range of possible values is the same for both IP - // families. - optional int32 ip_next_header = 7; - // The source port if this was a TCP or UDP packet. - optional int32 source_port = 8; - // The destination port if this was a TCP or UDP packet. - optional int32 destination_port = 9; -} - -/* - * Logs the memory stats for an app on startup. - * Logged from: - * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java - */ -message AppStartMemoryStateCaptured { - // The uid if available. -1 means not available. - optional int32 uid = 1 [(is_uid) = true]; - - // The process name. - optional string process_name = 2; - - // The activity name. - optional string activity_name = 3; - - // # of page-faults - optional int64 page_fault = 4; - - // # of major page-faults - optional int64 page_major_fault = 5; - - // RSS - optional int64 rss_in_bytes = 6; - - // CACHE - optional int64 cache_in_bytes = 7; - - // SWAP - optional int64 swap_in_bytes = 8; -} - -/* - * Logs the change in Low Memory Killer Daemon (LMKD) state which is used as start/stop boundaries - * for LMK event. - * Logged from: - * system/core/lmkd/lmkd.c - */ -message LmkStateChanged { - enum State { - UNKNOWN = 0; - START = 1; - STOP = 2; - } - optional State state = 1; -} - -/* - * Logs the event when Low Memory Killer Daemon (LMKD) kills a process to reduce memory pressure. - * Logged from: - * system/core/lmkd/lmkd.c - */ -message LmkKillOccurred { - enum Reason { - UNKNOWN = 0; - PRESSURE_AFTER_KILL = 1; - NOT_RESPONDING = 2; - LOW_SWAP_AND_THRASHING = 3; - LOW_MEM_AND_SWAP = 4; - LOW_MEM_AND_THRASHING = 5; - DIRECT_RECL_AND_THRASHING = 6; - LOW_MEM_AND_SWAP_UTIL = 7; - } - - // The uid if available. -1 means not available. - optional int32 uid = 1 [(is_uid) = true]; - - // The process name. - optional string process_name = 2; - - // oom adj score. - optional int32 oom_adj_score = 3; - - // # of page-faults - optional int64 page_fault = 4; - - // # of major page-faults - optional int64 page_major_fault = 5; - - // RSS - optional int64 rss_in_bytes = 6; - - // CACHE - optional int64 cache_in_bytes = 7; - - // SWAP - optional int64 swap_in_bytes = 8; - - // The elapsed real time of start of the process. - optional int64 process_start_time_nanos = 9; - - // Min oom adj score considered by lmkd. - optional int32 min_oom_score = 10; - - // Free physical memory on device at LMK time. - optional int32 free_mem_kb = 11; - - // Free swap on device at LMK time. - optional int32 free_swap_kb = 12; - - // What triggered the LMK event. - optional Reason reason = 13; -} - -/* - * Logs when the ActivityManagerService detects that an app died. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java - */ -message AppDied { - // timestamp(elapsedRealtime) of record creation - optional uint64 timestamp_millis = 1 [(state_field_option).exclusive_state = true]; -} - -/** - * An atom for generic metrics logging. Available from Android Q. - */ -message GenericAtom { - // The uid of the application that sent this custom atom. - optional int32 uid = 1 [(is_uid) = true]; - - // An event_id indicates the type of event. - optional android.stats.EventType event_id = 2; -} - -/** - * Atom for simple logging of user interaction and impression events, such as "the user touched - * this button" or "this dialog was displayed". - * Keep the UI event stream clean: don't use for system or background events. - * Log using the UiEventLogger wrapper - don't write with the StatsLog API directly. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/ - * frameworks/base/packages/SystemUI/src/com/android/systemui/ - */ -message UiEventReported { - // The event_id. - optional int32 event_id = 1; - // The event's source or target uid and package, if applicable. - // For example, the package posting a notification, or the destination package of a share. - optional int32 uid = 2 [(is_uid) = true]; - optional string package_name = 3; - // An identifier used to disambiguate which logs refer to a particular instance of some - // UI element. Useful when there might be multiple instances simultaneously active. - optional int32 instance_id = 4; -} - -/** - * Reports a notification was created or updated. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/notification/ - */ -message NotificationReported { - // The event_id (as for UiEventReported). - optional int32 event_id = 1; - // The notifying app's uid and package. - optional int32 uid = 2 [(is_uid) = true]; - optional string package_name = 3; - // A small system-assigned identifier for the notification. - // Locally probably-unique, but expect collisions across users and/or days. - optional int32 instance_id = 4; - optional int32 notification_id_hash = 5; // Small hash of the app-assigned notif ID + tag - optional int32 channel_id_hash = 6; // Small hash of app-assigned channel ID - - // Grouping information - optional int32 group_id_hash = 7; // Small hash of the group ID of the notification - optional int32 group_instance_id = 8; // Instance_id of the group-summary notification - optional bool is_group_summary = 9; // Tags the group-summary notification - - // Attributes - optional string category = 10; // App-assigned notification category (API-defined strings) - optional int32 style = 11; // App-assigned notification style - optional int32 num_people = 12; // Number of Person records attached to the notification - - // Ordering, importance and interruptiveness - - optional int32 position = 13; // Position in NotificationManager's list - - optional android.stats.sysui.NotificationImportance importance = 14; - optional int32 alerting = 15; // Bitfield, 1=buzz 2=beep 4=blink - - enum NotificationImportanceExplanation { - IMPORTANCE_EXPLANATION_UNKNOWN = 0; - IMPORTANCE_EXPLANATION_APP = 1; // App-specified channel importance. - IMPORTANCE_EXPLANATION_USER = 2; // User-specified channel importance. - IMPORTANCE_EXPLANATION_ASST = 3; // Notification Assistant override. - IMPORTANCE_EXPLANATION_SYSTEM = 4; // System override. - // Like _APP, but based on pre-channels priority signal. - IMPORTANCE_EXPLANATION_APP_PRE_CHANNELS = 5; - } - - optional NotificationImportanceExplanation importance_source = 16; - optional android.stats.sysui.NotificationImportance importance_initial = 17; - optional NotificationImportanceExplanation importance_initial_source = 18; - optional android.stats.sysui.NotificationImportance importance_asst = 19; - optional int32 assistant_hash = 20; - optional float assistant_ranking_score = 21; -} - -message Notification { - // The notifying app's uid and package. - optional int32 uid = 1 [(is_uid) = true]; - optional string package_name = 2; - // A small system-assigned identifier for the notification. - optional int32 instance_id = 3; - - // Grouping information. - optional int32 group_instance_id = 4; - optional bool is_group_summary = 5; - - // The section of the shade that the notification is in. - // See SystemUI Notifications.proto. - enum NotificationSection { - SECTION_UNKNOWN = 0; - SECTION_HEADS_UP = 1; - SECTION_MEDIA_CONTROLS = 2; - SECTION_PEOPLE = 3; - SECTION_ALERTING = 4; - SECTION_SILENT = 5; - SECTION_FOREGROUND_SERVICE = 6; - } - optional NotificationSection section = 6; -} - -message NotificationList { - repeated Notification notifications = 1; // An ordered sequence of notifications. -} - -/** - * Reports a notification panel was displayed, e.g. from the lockscreen or status bar. - * - * Logged from: - * frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/ - */ -message NotificationPanelReported { - // The event_id (as for UiEventReported). - optional int32 event_id = 1; - optional int32 num_notifications = 2; - // The notifications in the panel, in the order that they appear there. - optional NotificationList notifications = 3 [(log_mode) = MODE_BYTES]; -} - -/** - * Reports a notification channel, or channel group, was created, updated, or deleted. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/notification/ - */ -message NotificationChannelModified { - // The event_id (as for UiEventReported). - optional int32 event_id = 1; - // The notifying app's uid and package. - optional int32 uid = 2 [(is_uid) = true]; - optional string package_name = 3; - // Hash of app-assigned notification channel ID or channel-group ID - optional int32 channel_id_hash = 4; - // Previous importance setting, if applicable - optional android.stats.sysui.NotificationImportance old_importance = 5; - // New importance setting - optional android.stats.sysui.NotificationImportance importance = 6; - // whether or not this channel represents a conversation - optional bool is_conversation = 7; - // Hash of app-assigned notification conversation id - optional int32 conversation_id_hash = 8; - // whether or not the user demoted this channel out of the conversation space - optional bool is_conversation_demoted = 9; - // whether this conversation is marked as being a priority - optional bool is_conversation_priority = 10; -} - -/** - * Logs when a biometric acquire event occurs. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/biometrics - */ -message BiometricAcquired { - // Biometric modality that was acquired. - optional android.hardware.biometrics.ModalityEnum modality = 1; - // The associated user. Eg: 0 for owners, 10+ for others. Defined in android/os/UserHandle.java. - optional int32 user = 2; - // If this acquire is for a crypto operation. e.g. Secure purchases, unlock password storage. - optional bool is_crypto = 3; - // Action that the device is performing. Acquired messages are only expected for enroll and - // authenticate. Other actions may indicate an error. - optional android.hardware.biometrics.ActionEnum action = 4; - // The client that this acquisition was received for. - optional android.hardware.biometrics.ClientEnum client = 5; - // Acquired constants, e.g. ACQUIRED_GOOD. See constants defined by <Biometric>Manager. - optional int32 acquire_info = 6; - // Vendor-specific acquire info. Valid only if acquire_info == ACQUIRED_VENDOR. - optional int32 acquire_info_vendor = 7; - // Dictates if this message should trigger additional debugging. - optional bool debug = 8; -} - -/** - * Logs when a biometric authentication event occurs. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/biometrics - */ -message BiometricAuthenticated { - // Biometric modality that was used. - optional android.hardware.biometrics.ModalityEnum modality = 1; - // The associated user. Eg: 0 for owners, 10+ for others. Defined in android/os/UserHandle.java - optional int32 user = 2; - // If this authentication is for a crypto operation. e.g. Secure purchases, unlock password - // storage. - optional bool is_crypto = 3; - // The client that this acquisition was received for. - optional android.hardware.biometrics.ClientEnum client = 4; - // If authentication requires user confirmation. See BiometricPrompt's - // setRequireConfirmation(bool) method. - optional bool require_confirmation = 5; - - enum State { - UNKNOWN = 0; - REJECTED = 1; - PENDING_CONFIRMATION = 2; - CONFIRMED = 3; - } - - // State of the current auth attempt. - optional State state = 6; - // Time it took to authenticate. For BiometricPrompt where setRequireConfirmation(false) is - // specified and supported by the biometric modality, this is from the first ACQUIRED_GOOD to - // AUTHENTICATED. for setRequireConfirmation(true), this is from PENDING_CONFIRMATION to - // CONFIRMED. - optional int64 latency_millis = 7; - // Dictates if this message should trigger additional debugging. - optional bool debug = 8; -} - -/** - * Logs when a biometric error occurs. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/biometrics - */ -message BiometricErrorOccurred { - // Biometric modality that was used. - optional android.hardware.biometrics.ModalityEnum modality = 1; - // The associated user. Eg: 0 for owners, 10+ for others. Defined in android/os/UserHandle.java - optional int32 user = 2; - // If this error is for a crypto operation. e.g. Secure purchases, unlock password storage. - optional bool is_crypto = 3; - // Action that the device is performing. - optional android.hardware.biometrics.ActionEnum action = 4; - // The client that this acquisition was received for. - optional android.hardware.biometrics.ClientEnum client = 5; - // Error constants. See constants defined by <Biometric>Manager. Enums won't work since errors - // are unique to modality. - optional int32 error_info = 6; - // Vendor-specific error info. Valid only if acquire_info == ACQUIRED_VENDOR. These are defined - // by the vendor and not specified by the HIDL interface. - optional int32 error_info_vendor = 7; - // Dictates if this message should trigger additional debugging. - optional bool debug = 8; - // Time spent during the authentication attempt. - optional int64 latency_millis = 9; -} - -/** - * Logs when a system health issue is detected. - * Logged from: - * frameworks/base/services/core/java/com/android/server/biometrics - */ -message BiometricSystemHealthIssueDetected { - // Biometric modality. - optional android.hardware.biometrics.ModalityEnum modality = 1; - // Type of issue detected. - optional android.hardware.biometrics.IssueEnum issue = 2; - // Dictates if this message should trigger additional debugging. - optional bool debug = 3; -} - -/** - * Logs when a biometric enrollment occurs. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/biometrics - */ -message BiometricEnrolled { - // Biometric modality that was used. - optional android.hardware.biometrics.ModalityEnum modality = 1; - // The associated user. Eg: 0 for owners, 10+ for others. Defined in android/os/UserHandle.java - optional int32 user = 2; - // The amount of time the enrollment took in milliseconds. - optional int64 latency_millis = 3; - // Whether or not the enrollment was successful. - optional bool success = 4; -} - -/* - * Logs when a flag flip update occurrs. Used for mainline modules that update via flag flips. - */ -message FlagFlipUpdateOccurred { - // If the event is from a flag config package, specify the package name. - optional string flag_flip_package_name = 1; - - // The order id of the package - optional int64 order_id = 2; -} - -/** - * Potential experiment ids that goes with a train install. - * Should be kept in sync with experiment_ids.proto. - */ -message TrainExperimentIds { - repeated int64 experiment_id = 1; -} - -/* - * Logs when a binary push state changes. - * Logged by the installer via public api. - */ -message BinaryPushStateChanged { - // Name of the train. - optional string train_name = 1; - // Version code for a "train" of packages that need to be installed atomically - optional int64 train_version_code = 2; - // After installation of this package, device requires a restart. - optional bool requires_staging = 3; - // Rollback should be enabled for this install. - optional bool rollback_enabled = 4; - // Requires low latency monitoring if possible. - optional bool requires_low_latency_monitor = 5; - - enum State { - UNKNOWN = 0; - INSTALL_REQUESTED = 1; - INSTALL_STARTED = 2; - INSTALL_STAGED_NOT_READY = 3; - INSTALL_STAGED_READY = 4; - INSTALL_SUCCESS = 5; - // Replaced by INSTALL_FAILURE_DOWNLOAD, INSTALL_FAILURE_STATE_MISMATCH, - // and INSTALL_FAILURE_COMMIT. - INSTALL_FAILURE = 6 [deprecated = true]; - // This enum is for installs that are manually cancelled via the Manual Update UI. - INSTALL_CANCELLED = 7; - INSTALLER_ROLLBACK_REQUESTED = 8; - INSTALLER_ROLLBACK_INITIATED = 9; - INSTALLER_ROLLBACK_INITIATED_FAILURE = 10; - INSTALLER_ROLLBACK_STAGED = 11; - INSTALLER_ROLLBACK_STAGED_FAILURE = 12; - INSTALLER_ROLLBACK_BOOT_TRIGGERED = 13; - INSTALLER_ROLLBACK_BOOT_TRIGGERED_FAILURE = 14; - INSTALLER_ROLLBACK_SUCCESS = 15; - INSTALLER_ROLLBACK_FAILURE = 16; - INSTALLER_ROLLBACK_STAGED_CANCEL_REQUESTED = 17; - INSTALLER_ROLLBACK_STAGED_CANCEL_SUCCESS = 18; - INSTALLER_ROLLBACK_STAGED_CANCEL_FAILURE = 19; - INSTALL_STAGED_CANCEL_REQUESTED = 20; - INSTALL_STAGED_CANCEL_SUCCESS = 21; - INSTALL_STAGED_CANCEL_FAILURE = 22; - INSTALL_FAILURE_DOWNLOAD = 23; - INSTALL_FAILURE_STATE_MISMATCH = 24; - INSTALL_FAILURE_COMMIT = 25; - REBOOT_TRIGGERED = 26; - // Logged after INSTALL_REQUESTED for devices installing a train that - // contains no module requiring reboot. - REBOOT_NOT_REQUIRED = 27; - // Logged after INSTALL_REQUESTED for devices that are installing a train - // which requires reboot and eligible for soft restart. - SOFT_RESTART_ELIGIBLE = 28; - // Logged after INSTALL_REQUESTED for devices that are installing a train - // which requires reboot and eligible for notification restart. - NOTIFICATION_RESTART_ELIGIBLE = 29; - // Logged after INSTALL_REQUESTED for devices that are installing a train - // which requires reboot and not eligible for any reboot promotion strategy - // (e.g. soft restart, notification restart). - NO_REBOOT_PROMOTION_STRATEGY_ELIGIBLE = 30; - REBOOT_TRIGGER_FAILURE = 31; - } - optional State state = 6; - // Possible experiment ids for monitoring this push. - optional TrainExperimentIds experiment_ids = 7 [(log_mode) = MODE_BYTES]; - // user id - optional int32 user_id = 8; - optional int32 reason = 9; - // Whether or not this is a rollback event - optional bool is_rollback = 10; -} - -/* Test atom, is not logged anywhere */ -message TestAtomReported { - repeated AttributionNode attribution_node = 1; - optional int32 int_field = 2; - optional int64 long_field = 3; - optional float float_field = 4; - optional string string_field = 5; - optional bool boolean_field = 6; - enum State { - UNKNOWN = 0; - OFF = 1; - ON = 2; - } - optional State state = 7; - optional TrainExperimentIds bytes_field = 8 [(android.os.statsd.log_mode) = MODE_BYTES]; -} - -/** Represents USB port overheat event. */ -message UsbPortOverheatEvent { - /* Temperature of USB port at USB plug event, in 1/10ths of degree C. */ - optional int32 plug_temperature_deci_c = 1; - - /* Maximum temperature of USB port during overheat event, in 1/10ths of degree C. */ - optional int32 max_temperature_deci_c = 2; - - /* Time between USB plug event and overheat threshold trip, in seconds. */ - optional int32 time_to_overheat_secs = 3; - - /* Time between overheat threshold trip and hysteresis, in seconds. */ - optional int32 time_to_hysteresis_secs = 4; - - /* Time between hysteresis and active mitigation ending, in seconds. */ - optional int32 time_to_inactive_secs = 5; -}; - -/** - * Logs total effective full charge and discharge cycles on a battery. - * Here are some examples of one effective cycle: - * 1) the battery charges from 0% to 100% and drains back to 0%, - * 2) charging from 50% to 100% and draining back to 50% twice. - * Pulled from: - * frameworks/base/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp - */ -message BatteryCycleCount { - /* Number of total charge and discharge cycles on the system battery. */ - optional int32 cycle_count = 1; -} - -/** - * Logs that external storage is mounted and information about it, the storage type (sd card/usb/ - * others), its type (public or private) and the size in bytes. - * Pulled from: - * StatsCompanionService - */ - -message ExternalStorageInfo { - - enum VolumeType { - UNKNOWN = 0; - PUBLIC = 1; - PRIVATE = 2; - OTHER = 3; - } - - // The type of external storage. - optional android.stats.storage.ExternalStorageType storage_type = 1; - // Type of the volume: TYPE_PUBLIC if portable and TYPE_PRIVATE if internal. - optional VolumeType volume_type = 2; - // Total size of the sd card in bytes. - optional int64 size_bytes = 3; -} - -/* - * Logs when a connection becomes available and lost. - * Logged in StatsCompanionService.java - */ -message ConnectivityStateChanged { - // Id of the network. - optional int32 net_id = 1; - - enum State { - UNKNOWN = 0; - CONNECTED = 1; - DISCONNECTED = 2; - } - // Connected state of a network. - optional State state = 2; -} - -/** - * Logs when a service starts and stops. - * Logged from: - * services/core/java/com/android/server/am/ActiveServices.java - */ -message ServiceStateChanged { - - optional int32 uid = 1 [(is_uid) = true]; - - optional string package_name = 2; - - optional string service_name = 3; - - enum State { - START = 1; - STOP = 2; - } - - optional State state = 4; -} - -/** - * Logs when a service is launched. - * Logged from: - * services/core/java/com/android/server/am/ActiveServices.java - */ -message ServiceLaunchReported { - - optional int32 uid = 1 [(is_uid) = true]; - - optional string package_name = 2; - - optional string service_name = 3; -} - -/** - * Logs when a hidden API is used. - * - * Logged from: - * libcore/libart/src/main/java/dalvik/system/VMRuntime.java - */ -message HiddenApiUsed { - // The uid of the app making the hidden access. - optional int32 uid = 1 [(is_uid) = true]; - - // Signature of the method or field accessed. - optional string signature = 2; - - enum AccessMethod { - NONE = 0; - REFLECTION = 1; - JNI = 2; - LINKING = 3; - } - - // Type of access. - optional AccessMethod access_method = 3; - - // Whether the access was prevented or not. - optional bool access_denied = 4; -} - -/** - * Logs user interaction with the Privacy Indicators added in Q. In particular: - * - When user sees privacy chip - * - When user clicks privacy chip - * - How does the user exit the Privacy Dialog - * Logged from: - * packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java - */ -message PrivacyIndicatorsInteracted { - - enum Type { - UNKNOWN = 0; - CHIP_VIEWED = 1; - CHIP_CLICKED = 2; - reserved 3; // Used only in beta builds, never shipped - DIALOG_DISMISS = 4; - DIALOG_LINE_ITEM = 5; - } - - optional Type type = 1 [(state_field_option).exclusive_state = true]; -} - -/** - * Logs information about a package that is moved from the internal to external storage and vice - * versa. - * It logs the package name, the type of the external storage where the package is installed - * (if moved to external storage, or UNKNOWN if moved to internal storage), - * and the move type: if it's from internal to external or the other way around. - * - * Logged from: - frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java - */ -message AppMovedStorageReported { - enum MoveType { - UNKNOWN = 0; - TO_EXTERNAL = 1; - TO_INTERNAL = 2; - } - // The type of the external storage. - optional android.stats.storage.ExternalStorageType external_storage_type = 1; - // The type of move. - optional MoveType move_type = 2; - // The name of the package that was moved. - optional string package_name = 3; -} - -/** - * Logs when system server watchdog occurs. - * Logged from: - * frameworks/base/services/core/java/com/android/server/Watchdog.java - */ -message SystemServerWatchdogOccurred { - optional string subject = 1; -} - -/** - * Logs when new file added to tombstones. - * Logged from: - * frameworks/base/core/java/com/android/server/BootReceiver.java - */ -message TombStoneOccurred { -} - -/* - * Information about a role request - * - * Logged from: - * packages/apps/PermissionController/src/com/android/packageinstaller/role/ui/RequestRoleFragment.java - */ -message RoleRequestResultReported { - // UID of application requesting the role - optional int32 requesting_uid = 1; - - // Package name of application requesting the role - optional string requesting_package_name = 2; - - // The role to be granted - optional string role_name = 3; - - // The count of applications qualifying for the role - optional int32 qualifying_count = 4; - - // UID of application current granted the role - optional int32 current_uid = 5; - - // Package name of application current granted the role - optional string current_package_name = 6; - - // UID of another application that user chose to grant the role to, instead of the requesting - // application - optional int32 granted_another_uid = 7; - - // Package name of another application that user chose to grant the role to, instead of the - // requesting application - optional string granted_another_package_name = 8; - - enum Result { - UNDEFINED = 0; - // role request was ignored - IGNORED = 1; - // role request was ignored because it's already granted - IGNORED_ALREADY_GRANTED = 2; - // role request was ignored because the application isn't qualified - IGNORED_NOT_QUALIFIED = 3; - // role request was ignored because user said it should be always denied - IGNORED_USER_ALWAYS_DENIED = 4; - // role was granted by user action - USER_GRANTED = 5; - // role was denied by user action - USER_DENIED = 6; - // role was denied by user granting another application the role - USER_DENIED_GRANTED_ANOTHER = 7; - // role was denied and set to be always denied by the user - USER_DENIED_WITH_ALWAYS = 8; - } - // The result of the role request - optional Result result = 9; -} - -/** - * Logs when a Vehicle Maps Service client's connection state has changed - * - * Logged from: - * packages/services/Car/service/src/com/android/car/stats/VmsClientLog.java - */ -message VmsClientConnectionStateChanged { - // The UID of the VMS client app - optional int32 uid = 1 [(is_uid) = true]; - - enum State { - UNKNOWN = 0; - // Attempting to connect to the client - CONNECTING = 1; - // Client connection established - CONNECTED = 2; - // Client connection closed unexpectedly - DISCONNECTED = 3; - // Client connection closed by VMS - TERMINATED = 4; - // Error establishing the client connection - CONNECTION_ERROR = 5; - } - - optional State state = 2; -} - -message MimeTypes { - repeated string mime_types = 1; -} - -/** - * Logs statistics regarding accesses to external storage. - * All stats are normalized for one day period. - * - * Logged from: - * packages/providers/MediaProvider/src/com/android/providers/media/MediaProvider.java - */ -message GeneralExternalStorageAccessStats { - optional int32 uid = 1 [(is_uid) = true]; - // Total number of accesses like creation, open, delete and rename/update. - // Includes file path and ContentResolver accesses - optional uint32 total_accesses = 2; - // Number of file path accesses, as opposed to file path and ContentResolver. - optional uint32 file_path_accesses = 3; - // Number of accesses on secondary volumes like SD cards. - // Includes file path and ContentResolver accesses - optional uint32 secondary_storage_accesses = 4; - // Comma-separated list of mime types that were accessed. - optional MimeTypes mime_types_accessed = 5 [(log_mode) = MODE_BYTES]; -} - -/** - * Logs when MediaProvider has successfully finished scanning a storage volume. - * - * Logged from: - * packages/providers/MediaProvider/src/com/android/providers/media/scan/ModernMediaScanner.java - */ -message MediaProviderScanOccurred { - enum Reason { - // Scan triggered due to unknown reason - UNKNOWN = 0; - // Scan triggered due to storage volume being mounted - MOUNTED = 1; - // Scan triggered due to explicit user action or app request - DEMAND = 2; - // Scan triggered due to idle maintenance - IDLE = 3; - } - - // Volume type that this event pertains to - optional android.stats.mediaprovider.VolumeType volume_type = 1; - // Reason why this scan was triggered - optional Reason reason = 2; - // Total number of files scanned - optional int64 item_count = 3; - // Duration of scan, normalized per file - optional float normalized_duration_millis = 4; - // Number of database inserts, normalized per file - optional float normalized_insert_count = 5; - // Number of database updates, normalized per file - optional float normalized_update_count = 6; - // Number of database deletes, normalized per file - optional float normalized_delete_count = 7; -} - -/** - * Logs when an app has asked MediaProvider to delete media belonging to the user. - * - * Logged from: - * packages/providers/MediaProvider/src/com/android/providers/media/MediaProvider.java - */ -message MediaContentDeleted { - // Volume type that this event pertains to - optional android.stats.mediaprovider.VolumeType volume_type = 1; - // UID of app that requested deletion - optional int32 uid = 2 [(is_uid) = true]; - // Number of items that were deleted - optional int32 item_count = 3; -} - -/** - * Logs when an app has asked MediaProvider to grant them access to media belonging to the user. - * - * Logged from: - * packages/providers/MediaProvider/src/com/android/providers/media/PermissionActivity.java - */ -message MediaProviderPermissionRequested { - enum Result { - UNKNOWN = 0; - USER_GRANTED = 1; - AUTO_GRANTED = 2; - USER_DENIED = 3; - USER_DENIED_WITH_PREJUDICE = 4; - AUTO_DENIED = 5; - } - - // Volume type that this event pertains to - optional android.stats.mediaprovider.VolumeType volume_type = 1; - // UID of app that requested permission - optional int32 uid = 2 [(is_uid) = true]; - // Number of items that were requested - optional int32 item_count = 3; - // Result of this request - optional Result result = 4; -} - -/** - * Logs when MediaProvider has finished upgrading or downgrading its database schema. - * - * Logged from: - * packages/providers/MediaProvider/src/com/android/providers/media/DatabaseHelper.java - */ -message MediaProviderSchemaChanged { - // Volume type that this event pertains to - optional android.stats.mediaprovider.VolumeType volume_type = 1; - // Old database version code - optional int32 version_from = 2; - // New database version code - optional int32 version_to = 3; - // Total number of files in database - optional int64 item_count = 4; - // Duration of schema change, normalized per file - optional float normalized_duration_millis = 5; -} - -/** - * Logs when MediaProvider has finished an idle maintenance job. - * - * Logged from: - * packages/providers/MediaProvider/src/com/android/providers/media/MediaProvider.java - */ -message MediaProviderIdleMaintenanceFinished { - // Volume type that this event pertains to - optional android.stats.mediaprovider.VolumeType volume_type = 1; - - // Total number of files in database - optional int64 item_count = 2; - // Duration of idle maintenance, normalized per file - optional float normalized_duration_millis = 3; - // Number of thumbnails found to be stale, normalized per file - optional float normalized_stale_thumbnails = 4; - // Number of items found to be expired, normalized per file - optional float normalized_expired_media = 5; -} - -/** - * Represents boot time event with duration in ms. - * - * Logged from: bootstat and various system server components. Check each enums for details. - */ -message BootTimeEventDuration { - enum DurationEvent { - UNKNOWN = 0; - // Bootloader time excluding BOOTLOADER_UI_WAIT + boot complete time. Logged from bootstat. - ABSOLUTE_BOOT_TIME = 1; - // Bootloader's 1st stage execution time. - // Logged from bootstat. - BOOTLOADER_FIRST_STAGE_EXEC = 2; - // Bootloader's 1st stage loading time. - // Logged from bootstat. - BOOTLOADER_FIRST_STAGE_LOAD = 3; - // Bootloader's kernel loading time. - // Logged from bootstat. - BOOTLOADER_KERNEL_LOAD = 4; - // Bootloader's 2nd stage execution time. - // Logged from bootstat. - BOOTLOADER_SECOND_STAGE_EXEC = 5; - // Bootloader's 2nd stage loading time. - // Logged from bootstat. - BOOTLOADER_SECOND_STAGE_LOAD = 6; - // Duration for Bootloader to show unlocked device's warning UI. This should not happen - // for locked device. - // Logged from bootstat. - BOOTLOADER_UI_WAIT = 7; - // Total time spend in bootloader. This is the sum of all BOOTLOADER_* listed above. - // Logged from bootstat. - BOOTLOADER_TOTAL = 8; - // Shutdown duration inside init for the reboot before the current boot up. - // Logged from f/b/services/.../BootReceiver.java. - SHUTDOWN_DURATION = 9; - // Total time for mounting of disk devices during bootup. - // Logged from f/b/services/.../BootReceiver.java. - MOUNT_DEFAULT_DURATION = 10; - // Total time for early stage mounting of disk devices during bootup. - // Logged from f/b/services/.../BootReceiver.java. - MOUNT_EARLY_DURATION = 11; - // Total time for late stage mounting of disk devices during bootup. - // Logged from f/b/services/.../BootReceiver.java. - MOUNT_LATE_DURATION = 12; - // Average time to scan non-system app after OTA - // Logged from f/b/services/.../PackageManagerService.java - OTA_PACKAGE_MANAGER_INIT_TIME = 13; - // Time to initialize Package manager after OTA - // Logged from f/b/services/.../PackageManagerService.java - OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME = 14; - // Time to scan all system app from Package manager after OTA - // Logged from f/b/services/.../PackageManagerService.java - OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME = 15; - // Init's total time for cold boot stage. - // Logged from bootstat. - COLDBOOT_WAIT = 16; - // Init's total time for initializing selinux. - // Logged from bootstat. - SELINUX_INIT = 17; - // Time since last factory reset. - // Logged from bootstat. - FACTORY_RESET_TIME_SINCE_RESET = 18; - // Init's total time spent for completing the 1st stage. - // Logged from bootstat. - ANDROID_INIT_STAGE_1 = 19; - } - - // Type of the event. - optional DurationEvent event = 1; - // Duration of the event in ms. - optional int64 duration_millis = 2; -} - -/** - * Represents the start of specific boot time event during bootup in ms. This is usually a time - * since boot-up. - * - * Logged from: bootstat and various system server components. Check each enums for details. - */ -message BootTimeEventElapsedTime { - enum ElapsedTimeEvent { - UNKNOWN = 0; - // Time when init starts 1st stage. Logged from bootstat. - ANDROID_INIT_STAGE_1 = 1; - // Time when sys.boot_completed prop is set. - // Logged from bootstat. - BOOT_COMPLETE = 2; - // BOOT_COMPLETE for encrypted device. - BOOT_COMPLETE_ENCRYPTION = 3; - // BOOT_COMPLETE for device with no encryption. - BOOT_COMPLETE_NO_ENCRYPTION = 4; - // Adjusted BOOT_COMPLETE for encrypted device extracting decryption time. - BOOT_COMPLETE_POST_DECRYPT = 5; - // BOOT_COMPLETE after factory reset. - FACTORY_RESET_BOOT_COMPLETE = 6; - // BOOT_COMPLETE_NO_ENCRYPTION after factory reset. - FACTORY_RESET_BOOT_COMPLETE_NO_ENCRYPTION = 7; - // BOOT_COMPLETE_POST_DECRYPT after factory reset. - FACTORY_RESET_BOOT_COMPLETE_POST_DECRYPT = 8; - // BOOT_COMPLETE after OTA. - OTA_BOOT_COMPLETE = 9; - // BOOT_COMPLETE_NO_ENCRYPTION after OTA. - OTA_BOOT_COMPLETE_NO_ENCRYPTION = 10; - // BOOT_COMPLETE_POST_DECRYPT after OTA. - OTA_BOOT_COMPLETE_POST_DECRYPT = 11; - // Time when the system starts sending LOCKED_BOOT_COMPLETED broadcast. - // Logged from f/b/services/.../UserController.java - FRAMEWORK_LOCKED_BOOT_COMPLETED = 12; - // Time when the system starts sending BOOT_COMPLETED broadcast. - // Logged from f/b/services/.../UserController.java - FRAMEWORK_BOOT_COMPLETED = 13; - // Time when the package manager starts init. - // Logged from f/b/services/.../SystemServer.java - PACKAGE_MANAGER_INIT_START = 14; - // Time when package manager is ready - // Logged from f/b/services/.../SystemServer.java - PACKAGE_MANAGER_INIT_READY = 15; - // Represents the time when user has entered unlock credential for system with user pin. - // Logged from bootstat. - POST_DECRYPT = 16; - // Represents the start of zygote's init. - // Logged from zygote itself. - ZYGOTE_INIT_START = 17; - // Represents the start of secondary zygote's init. - // TODO: add logging to zygote - SECONDARY_ZYGOTE_INIT_START = 18; - // Represents the start of system server's init. - // Logged from f/b/services/.../SystemServer.java - SYSTEM_SERVER_INIT_START = 19; - // Represents the completion of system server's init. - // Logged from f/b/services/.../SystemServer.java - SYSTEM_SERVER_READY = 20; - // Represents the start of launcher during boot-up. - // TODO: add logging - LAUNCHER_START = 21; - // Represents the completion of launcher's initial rendering. User can use other apps from - // launcher from this point. - // TODO: add logging - LAUNCHER_SHOWN = 22; - } - - // Type of the event. - optional ElapsedTimeEvent event = 1; - // Time since bootup for the event. - // It should be acquired from SystemClock elapsedRealtime() call or equivalent. - optional int64 time_millis = 2; -} - -/** - * Boot time events with UTC time. - * - * Logged from: bootstat and various system server components. Check each enums for details. - */ -message BootTimeEventUtcTime { - enum UtcTimeEvent { - UNKNOWN = 0; - // Time of the bootstat's marking of 1st boot after the last factory reset. - // Logged from bootstat. - FACTORY_RESET_RESET_TIME = 1; - // The time when bootstat records FACTORY_RESET_* events. This is close to - // BOOT_COMPLETE time for the current bootup. - // Logged from bootstat. - FACTORY_RESET_CURRENT_TIME = 2; - // DUplicate of FACTORY_RESET_RESET_TIME added for debugging purpose. - // Logged from bootstat. - FACTORY_RESET_RECORD_VALUE = 3; - } - - // Type of the event. - optional UtcTimeEvent event = 1; - // UTC time for the event. - optional int64 utc_time_secs = 2; -} - -/** - * Boot time events representing specific error code during bootup. - * Meaning of error code can be different per each event type. - * - * Logged from: bootstat and various system server components. Check each enums for details. - */ -message BootTimeEventErrorCode { - enum ErrorCodeEvent { - UNKNOWN = 0; - // Linux error code for time() call to get the current UTC time. - // Logged from bootstat. - FACTORY_RESET_CURRENT_TIME_FAILURE = 1; - // Represents UmountStat before the reboot for the current boot up. Error codes defined - // as UMOUNT_STAT_* from init/reboot.cpp. - // Logged from f/b/services/.../BootReceiver.java. - SHUTDOWN_UMOUNT_STAT = 2; - // Reprepsents fie system mounting error code of /data partition for the current boot. - // Error codes defined as combination of FsStatFlags from system/core/fs_mgr/fs_mgr.cpp. - // Logged from f/b/services/.../BootReceiver.java. - FS_MGR_FS_STAT_DATA_PARTITION = 3; - } - - // Type of the event. - optional ErrorCodeEvent event = 1; - // error code defined per each event type. - // For example, this can have a value of FsStatFlags.FS_STAT_FULL_MOUNT_FAILED for the event of - // FS_MGR_FS_STAT. - optional int32 error_code = 2; -} - -/** - * Collects Virtual A/B statistics related to the use of dm-snapshot performed - * after an OTA. - * - * Logged from: - * - system/update_engine/cleanup_previous_update_action.cc - */ -message SnapshotMergeReported { - // Keep in sync with - // system/core/fs_mgr/libsnapshot/android/snapshot/snapshot.proto - enum UpdateState { - // No update or merge is in progress. - NONE = 0; - // An update is applying; snapshots may already exist. - INITIATED = 1; - // An update is pending, but has not been successfully booted yet. - UNVERIFIED = 2; - // The kernel is merging in the background. - MERGING = 3; - // Post-merge cleanup steps could not be completed due to a transient - // error, but the next reboot will finish any pending operations. - MERGE_NEEDS_REBOOT = 4; - // Merging is complete, and needs to be acknowledged. - MERGE_COMPLETED = 5; - // Merging failed due to an unrecoverable error. - MERGE_FAILED = 6; - // The update was implicitly cancelled, either by a rollback or a flash - // operation via fastboot. This state can only be returned by WaitForMerge. - CANCELLED = 7; - }; - - // Status of the update after the merge attempts. - optional UpdateState final_state = 1; - - // Time to complete a merge operation in milliseconds. - // A negative value corresponds to the case in which the merge operation - // was interrupted and resumed (e.g. in case of a system reboot during the - // merge). - optional int64 duration_millis = 2; - - // Number of reboots that occurred after issuing and before completing the - // merge of all the snapshot devices. - optional int32 intermediate_reboots = 3; - - // The device has been upgraded to Virtual A/B. - optional bool is_vab_retrofit = 4; - - // Space that has been temporarily allocated in the /data partition - // containing the dm-snapshot's copy-on-write data generated during a - // Virtual A/B update. - optional int64 cow_file_size_bytes = 5; -} - -/** - * Event representing when BlobStoreManager.Session#commit() is called - * - * Logged from: - * frameworks/base/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java - */ -message BlobCommitted { - // Uid of the Blob committer - optional int32 uid = 1 [(is_uid) = true]; - - // Id of the Blob committed - optional int64 blob_id = 2; - - // Size of the Blob - optional int64 size = 3; - - enum Result { - UNKNOWN = 0; - // Commit Succeeded - SUCCESS = 1; - // Commit Failed: Error occurred during commit - ERROR_DURING_COMMIT = 2; - // Commit Failed: Digest of the data did not match Blob digest - DIGEST_MISMATCH = 3; - // Commit Failed: Allowed count limit exceeded - COUNT_LIMIT_EXCEEDED = 4; - } - optional Result result = 4; -} - -/** - * Event representing when BlobStoreManager#acquireLease() is called - * - * Logged from: - * frameworks/base/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java - */ -message BlobLeased{ - // Uid of the Blob leasee - optional int32 uid = 1 [(is_uid) = true]; - - // Id of the Blob leased or 0 if the Blob does not exist - optional int64 blob_id = 2; - - // Size of the Blob or 0 if the Blob does not exist - optional int64 size = 3; - - enum Result { - UNKNOWN = 0; - // Lease Succeeded - SUCCESS = 1; - // Lease Failed: Blob does not exist - BLOB_DNE = 2; - // Lease Failed: Leasee does not have access to the Blob - ACCESS_NOT_ALLOWED = 3; - // Lease Failed: Leasee requested an invalid expiry duration - LEASE_EXPIRY_INVALID = 4; - // Lease Failed: Leasee has exceeded the total data lease limit - DATA_SIZE_LIMIT_EXCEEDED = 5; - // Leasee Failed: Allowed count limit exceeded - COUNT_LIMIT_EXCEEDED = 6; - } - optional Result result = 4; -} - -/** - * Event representing when BlobStoreManager#openBlob() is called - * - * Logged from: - * frameworks/base/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java - */ -message BlobOpened{ - // Uid of the Blob opener - optional int32 uid = 1 [(is_uid) = true]; - - // Id of the Blob opened or 0 if the Blob does not exist - optional int64 blob_id = 2; - - // Size of the Blob or 0 if the Blob does not exist - optional int64 size = 3; - - enum Result { - UNKNOWN = 0; - // Open Succeeded - SUCCESS = 1; - // Open Failed: Blob does not exist - BLOB_DNE = 2; - // Open Failed: Opener does not have access to the Blob - ACCESS_NOT_ALLOWED = 3; - } - optional Result result = 4; -} - -/** - * Event to track Jank for various system interactions. - * - * Logged from: - * frameworks/base/core/java/com/android/internal/jank/FrameTracker.java - */ -message UIInteractionFrameInfoReported { - enum InteractionType { - UNKNOWN = 0; - NOTIFICATION_SHADE_SWIPE = 1; - SHADE_EXPAND_COLLAPSE_LOCK = 2; - SHADE_SCROLL_FLING = 3; - SHADE_ROW_EXPAND = 4; - SHADE_ROW_SWIPE = 5; - SHADE_QS_EXPAND_COLLAPSE = 6; - SHADE_QS_SCROLL_SWIPE = 7; - LAUNCHER_APP_LAUNCH_FROM_RECENTS = 8; - LAUNCHER_APP_LAUNCH_FROM_ICON = 9; - LAUNCHER_APP_CLOSE_TO_HOME = 10; - LAUNCHER_APP_CLOSE_TO_PIP = 11; - LAUNCHER_QUICK_SWITCH = 12; - SHADE_HEADS_UP_APPEAR = 13; - SHADE_HEADS_UP_DISAPPEAR = 14; - SHADE_NOTIFICATION_ADD = 15; - SHADE_NOTIFICATION_REMOVE = 16; - SHADE_APP_LAUNCH = 17; - } - - optional InteractionType interaction_type = 1; - - // Number of frames rendered during the interaction. - optional int64 total_frames = 2; - - // Number of frames that were skipped in rendering during the interaction. - optional int64 missed_frames = 3; - - // Maximum time it took to render a single frame during the interaction. - optional int64 max_frame_time_nanos = 4; -} - -/** - * Event to track various latencies in SystemUI. - * - * Logged from: - * frameworks/base/core/java/com/android/internal/util/LatencyTracker.java - */ -message UIActionLatencyReported { - enum ActionType { - UNKNOWN = 0; - ACTION_EXPAND_PANEL = 1; - ACTION_TOGGLE_RECENTS = 2; - ACTION_FINGERPRINT_WAKE_AND_UNLOCK = 3; - ACTION_CHECK_CREDENTIAL = 4; - ACTION_CHECK_CREDENTIAL_UNLOCKED = 5; - ACTION_TURN_ON_SCREEN = 6; - ACTION_ROTATE_SCREEN = 7; - ACTION_FACE_WAKE_AND_UNLOCK = 8; - } - - optional ActionType action = 1; - - optional int64 latency_millis = 2; -} - -////////////////////////////////////////////////////////////////////// -// Pulled atoms below this line // -////////////////////////////////////////////////////////////////////// - -/** - * Pulls bytes transferred via wifi (Sum of foreground and background usage). - * - * Pulled from: - * StatsCompanionService (using BatteryStats to get which interfaces are wifi) - */ -message WifiBytesTransfer { - optional int32 uid = 1 [(is_uid) = true]; - - optional int64 rx_bytes = 2; - - optional int64 rx_packets = 3; - - optional int64 tx_bytes = 4; - - optional int64 tx_packets = 5; -} - -/** - * Pulls bytes transferred via wifi (separated by foreground and background usage). - * - * Pulled from: - * StatsCompanionService (using BatteryStats to get which interfaces are wifi) - */ -message WifiBytesTransferByFgBg { - optional int32 uid = 1 [(is_uid) = true]; - - // 1 denotes foreground and 0 denotes background. This is called Set in NetworkStats. - optional bool is_foreground = 2; - - optional int64 rx_bytes = 3; - - optional int64 rx_packets = 4; - - optional int64 tx_bytes = 5; - - optional int64 tx_packets = 6; -} - -/** - * Pulls bytes transferred via mobile networks (Sum of foreground and background usage). - * - * Pulled from: - * StatsCompanionService (using BatteryStats to get which interfaces are mobile data) - */ -message MobileBytesTransfer { - optional int32 uid = 1 [(is_uid) = true]; - - optional int64 rx_bytes = 2; - - optional int64 rx_packets = 3; - - optional int64 tx_bytes = 4; - - optional int64 tx_packets = 5; -} - -/** - * Pulls bytes transferred via mobile networks (separated by foreground and background usage). - * - * Pulled from: - * StatsCompanionService (using BatteryStats to get which interfaces are mobile data) - */ -message MobileBytesTransferByFgBg { - optional int32 uid = 1 [(is_uid) = true]; - - // 1 denotes foreground and 0 denotes background. This is called Set in - // NetworkStats. - optional bool is_foreground = 2; - - optional int64 rx_bytes = 3; - - optional int64 rx_packets = 4; - - optional int64 tx_bytes = 5; - - optional int64 tx_packets = 6; -} - -/** - * Used for pull network statistics via mobile|wifi networks, and sliced by interesting dimensions. - * Note that the data is expected to be sliced into more dimensions in future. In other words, - * the caller must not assume any row of data is one full report when filtering with a set of - * matching conditions, because future data may represent with multiple rows what is currently - * represented by one. - * To avoid being broken by future slicing, callers must take care to aggregate rows even if they - * query all the existing columns. - * - * Pulled from: - * StatsPullAtomService (using NetworkStatsService to get NetworkStats) - */ -message DataUsageBytesTransfer { - // State of this record. Should be NetworkStats#SET_DEFAULT or NetworkStats#SET_FOREGROUND to - // indicate the foreground state, or NetworkStats#SET_ALL to indicate the record is for all - // states combined, not including debug states. See NetworkStats#SET_*. - optional int32 state = 1; - - optional int64 rx_bytes = 2; - - optional int64 rx_packets = 3; - - optional int64 tx_bytes = 4; - - optional int64 tx_packets = 5; - - // Radio Access Technology (RAT) type of this record, should be one of - // TelephonyManager#NETWORK_TYPE_* constants, or NetworkTemplate#NETWORK_TYPE_ALL to indicate - // the record is for all rat types combined. - optional int32 rat_type = 6; - - // Mcc/Mnc read from sim if the record is for a specific subscription, null indicates the - // record is combined across subscriptions. - optional string sim_mcc = 7; - optional string sim_mnc = 8; - - // Allows mobile virtual network operators (MVNOs) to be identified with individual IDs. - // See TelephonyManager#getSimCarrierId. - optional int32 carrier_id = 9; - - // Enumeration of opportunistic states with an additional ALL state indicates the record is - // combined regardless of the boolean value in its field. - enum DataSubscriptionState { - UNKNOWN = 0; // For server side backward compatibility. - ALL = 1; - OPPORTUNISTIC = 2; - NOT_OPPORTUNISTIC = 3; - } - // Mark whether the subscription is an opportunistic data subscription, and ALL indicates the - // record is combined across opportunistic data subscriptions. - // See {@link SubscriptionManager#setOpportunistic}. - optional DataSubscriptionState opportunistic_data_sub = 10; - - // Indicate whether NR is connected, server side could use this with RAT type to determine if - // the record is for 5G NSA (Non Stand Alone) mode, where the primary cell is still LTE and - // network allocates a secondary 5G cell so telephony reports RAT = LTE along with NR state as - // connected. - optional bool is_nr_connected = 11; -} - -/** - * Pulls bytes transferred via bluetooth. It is pulled from Bluetooth controller. - * - * Pulled from: - * StatsCompanionService - */ -message BluetoothBytesTransfer { - optional int32 uid = 1 [(is_uid) = true]; - - optional int64 rx_bytes = 2; - - optional int64 tx_bytes = 3; -} - -/** - * Pulls the kernel wakelock durations. This atom is adapted from - * android/internal/os/KernelWakelockStats.java - * - * Pulled from: - * StatsCompanionService using KernelWakelockReader. - */ -message KernelWakelock { - optional string name = 1; - - optional int32 count = 2; - - optional int32 version = 3; - - optional int64 time_micros = 4; -} - -/** - * Pulls low power state information. If power.stats HAL is not available, this - * includes platform and subsystem sleep state information, - * PowerStatePlatformSleepState, PowerStateVoter or PowerStateSubsystemSleepState - * as defined in: - * hardware/interfaces/power/1.0/types.hal - * hardware/interfaces/power/1.1/types.hal - * If power.stats HAL is available, this includes PowerEntityStateResidencyResult - * as defined in: - * hardware/interfaces/power/stats/1.0/types.hal - */ -message SubsystemSleepState { - // Subsystem name - optional string subsystem_name = 1; - // For PlatformLowPowerStats (hal 1.0), this is the voter name, which could be empty. - // For SubsystemLowPowerStats (hal 1.1), this is the sleep state name. - // For PowerEntityStateResidencyResult (hal power/stats/1.0) this is the - // powerEntityStateName from the corresponding PowerEntityStateInfo. - optional string subname = 2; - // The number of times it entered, or voted for entering the sleep state - optional uint64 count = 3; - // The length of time spent in, or spent voting for, the sleep state - optional uint64 time_millis = 4; -} - -/** - * Pulls on-device power measurement information. - * Data defined by hardware/interfaces/power/stats/1.0/types.hal. - * Pulled from: - * frameworks/base/cmds/statsd/src/external/PowerStatsPuller.cpp - */ -message OnDevicePowerMeasurement { - // Name of the subsystem (to which the rail belongs). - optional string subsystem_name = 1; - - // Rail name. The rail lies within the subsystem. - optional string rail_name = 2; - - // Time (in ms since boot) at which the rail energy value was measured. - // This may differ slightly from the time that statsd logs this information. - optional uint64 measurement_timestamp_millis = 3; - - // Accumulated energy used via the rail since device boot in uWs. - optional uint64 energy_microwatt_secs = 4; -} - -/** - * Pulls Cpu time per frequency. - * Pulls the time the cpu spend on the frequency index. Frequency index - * starts from highest to lowest. The value should be monotonically - * increasing since boot. However, if there is a cpu - * hotplug event, the value would be reset as well. - */ -message CpuTimePerFreq { - optional uint32 cluster = 1; - optional uint32 freq_index = 2; - optional uint64 time_millis = 3; -} - -/** - * Pulls Cpu Time Per Uid. - * Note that isolated process uid time should be attributed to host uids. - */ -message CpuTimePerUid { - optional int32 uid = 1 [(is_uid) = true]; - optional uint64 user_time_micros = 2; - optional uint64 sys_time_micros = 3; -} - -/** - * Pulls Cpu Time Per Uid per frequency. - * Note that isolated process uid time should be attributed to host uids. - * For each uid, we order the time by descending frequencies. - */ -message CpuTimePerUidFreq { - optional int32 uid = 1 [(is_uid) = true]; - optional uint32 freq_index = 2; - optional uint64 time_millis = 3; -} - -/** - * Pulls Wifi Controller Activity Energy Info - */ -message WifiActivityInfo { - // timestamp(wall clock) of record creation - optional uint64 timestamp_millis = 1; - // stack reported state - // TODO: replace this with proto enum - optional int32 stack_state = 2; - // tx time in millis - optional uint64 controller_tx_time_millis = 3; - // rx time in millis - optional uint64 controller_rx_time_millis = 4; - // idle time in millis - optional uint64 controller_idle_time_millis = 5; - // product of current(mA), voltage(V) and time(ms) - optional uint64 controller_energy_used = 6; -} - -/** - * Pulls Modem Activity Energy Info - */ -message ModemActivityInfo { - // timestamp(wall clock) of record creation - optional uint64 timestamp_millis = 1; - // sleep time in millis. - optional uint64 sleep_time_millis = 2; - // idle time in millis - optional uint64 controller_idle_time_millis = 3; - /** - * Tx power index - * index 0 = tx_power < 0dBm - * index 1 = 0dBm < tx_power < 5dBm - * index 2 = 5dBm < tx_power < 15dBm - * index 3 = 15dBm < tx_power < 20dBm - * index 4 = tx_power > 20dBm - */ - // tx time in ms at power level 0 - optional uint64 controller_tx_time_pl0_millis = 4; - // tx time in ms at power level 1 - optional uint64 controller_tx_time_pl1_millis = 5; - // tx time in ms at power level 2 - optional uint64 controller_tx_time_pl2_millis = 6; - // tx time in ms at power level 3 - optional uint64 controller_tx_time_pl3_millis = 7; - // tx time in ms at power level 4 - optional uint64 controller_tx_time_pl4_millis = 8; - // rx time in ms at power level 5 - optional uint64 controller_rx_time_millis = 9; - // product of current(mA), voltage(V) and time(ms) - optional uint64 energy_used = 10 [deprecated=true]; -} - -/** - * Pulls Bluetooth Activity Energy Info - * Note: BluetoothBytesTransfer is pulled at the same time from the controller. - */ -message BluetoothActivityInfo { - // timestamp(wall clock) of record creation - optional uint64 timestamp_millis = 1; - // bluetooth stack state - optional int32 bluetooth_stack_state = 2; - // tx time in millis - optional uint64 controller_tx_time_millis = 3; - // rx time in millis - optional uint64 controller_rx_time_millis = 4; - // idle time in millis - optional uint64 controller_idle_time_millis = 5; - // product of current(mA), voltage(V) and time(ms) - optional uint64 energy_used = 6; -} - -/* - * Logs the memory stats for a process. - * - * Pulled from StatsCompanionService for all managed processes (from ActivityManagerService). - */ -message ProcessMemoryState { - // The uid if available. -1 means not available. - optional int32 uid = 1 [(is_uid) = true]; - - // The process name. - // Usually package name, "system" for system server. - // Provided by ActivityManagerService. - optional string process_name = 2; - - // Current OOM score adjustment. Value read from ProcessRecord. - optional int32 oom_adj_score = 3; - - // # of page-faults - optional int64 page_fault = 4; - - // # of major page-faults - optional int64 page_major_fault = 5; - - // RSS - // Value is read from memory.stat, field total_rss if per-app memory - // cgroups are enabled. Otherwise, value from /proc/pid/stat. - optional int64 rss_in_bytes = 6; - - // CACHE - // Value is read from memory.stat, field total_cache if per-app memory - // cgroups are enabled. Otherwise, 0. - optional int64 cache_in_bytes = 7; - - // SWAP - // Value is read from memory.stat, field total_swap if per-app memory - // cgroups are enabled. Otherwise, 0. - optional int64 swap_in_bytes = 8; - - // Deprecated: use ProcessMemoryHighWaterMark atom instead. Always -1. - optional int64 rss_high_watermark_in_bytes = 9 [deprecated = true]; - - // Deprecated: use ProcessMemorySnapshot atom instead. Always -1. - optional int64 start_time_nanos = 10 [deprecated = true]; - - // Deprecated: use ProcessMemorySnapshot atom instead. Always -1. - optional int32 anon_rss_and_swap_in_kilobytes = 11 [deprecated = true]; -} - -/* - * Logs the memory high-water mark for a process. - * - * Pulled from StatsCompanionService for all managed processes (from ActivityManagerServie) - * and for selected native processes. - * - * Pulling this atom resets high-water mark counters for all processes. - */ -message ProcessMemoryHighWaterMark { - // The uid if available. -1 means not available. - optional int32 uid = 1 [(is_uid) = true]; - - // The process name. - // Usually package name or process cmdline. - // Provided by ActivityManagerService or read from /proc/PID/cmdline. - optional string process_name = 2; - - // Deprecated: use rss_high_water_mark_in_kilobytes instead. This field is - // computed by converting kilobytes to bytes. - optional int64 rss_high_water_mark_in_bytes = 3 [deprecated = true]; - - // RSS high-water mark. Peak RSS usage of the process. Read from the VmHWM field in - // /proc/PID/status. - optional int32 rss_high_water_mark_in_kilobytes = 4; -} - -/* - * Logs the memory stats for a process. - * - * Pulled from StatsCompanionService for all managed processes (from ActivityManagerService) - * and for selected native processes. - */ -message ProcessMemorySnapshot { - // The uid if available. -1 means not available. - optional int32 uid = 1 [(is_uid) = true]; - - // The process name. - // Usually package name or process cmdline. - // Provided by ActivityManagerService or read from /proc/PID/cmdline. - optional string process_name = 2; - - // The pid of the process. - // Allows to disambiguate instances of the process. - optional int32 pid = 3; - - // The current OOM score adjustment value. - // Read from ProcessRecord for managed processes. - // Placeholder -1001 (OOM_SCORE_ADJ_MIN - 1, outside of allowed range) for native ones. - optional int32 oom_score_adj = 4; - - // The current RSS of the process. - // VmRSS from /proc/pid/status. - optional int32 rss_in_kilobytes = 5; - - // The current anon RSS of the process. - // RssAnon from /proc/pid/status. - optional int32 anon_rss_in_kilobytes = 6; - - // The current swap size of the process. - // VmSwap from /proc/pid/status. - optional int32 swap_in_kilobytes = 7; - - // The sum of rss_in_kilobytes and swap_in_kilobytes. - optional int32 anon_rss_and_swap_in_kilobytes = 8; -} - -/* - * Elapsed real time from SystemClock. - */ -message SystemElapsedRealtime { - optional uint64 time_millis = 1; -} - -/* - * Up time from SystemClock. - */ -message SystemUptime { - // Milliseconds since the system was booted. - // This clock stops when the system enters deep sleep (CPU off, display dark, device waiting - // for external input). - // It is not affected by clock scaling, idle, or other power saving mechanisms. - optional uint64 uptime_millis = 1; -} - -/* - * Reads from /proc/uid_concurrent_active_time which has the format: - * active: X (X is # cores) - * [uid0]: [time-0] [time-1] [time-2] ... (# entries = # cores) - * [uid1]: [time-0] [time-1] [time-2] ... ... - * ... - * Time-N means the CPU time a UID spent running concurrently with N other processes. - * The file contains a monotonically increasing count of time for a single boot. - */ -message CpuActiveTime { - optional int32 uid = 1 [(is_uid) = true]; - optional uint64 time_millis = 2; -} - -/** - * Reads from /proc/uid_concurrent_policy_time which has the format: - * policy0: X policy4: Y (there are X cores on policy0, Y cores on policy4) - * [uid0]: [time-0-0] [time-0-1] ... [time-1-0] [time-1-1] ... - * [uid1]: [time-0-0] [time-0-1] ... [time-1-0] [time-1-1] ... - * ... - * Time-X-Y means the time a UID spent on clusterX running concurrently with Y other processes. - * The file contains a monotonically increasing count of time for a single boot. - */ -message CpuClusterTime { - optional int32 uid = 1 [(is_uid) = true]; - optional int32 cluster_index = 2; - optional uint64 time_millis = 3; -} - -/* - * Pulls free disk space, for data, system partition and temporary directory. - */ -message DiskSpace { - // available bytes in data partition - optional uint64 data_available_bytes = 1; - // available bytes in system partition - optional uint64 system_available_bytes = 2; - // available bytes in download cache or temp directories - optional uint64 temp_available_bytes = 3; -} - -/** - * Pulls battery coulomb counter, which is the remaining battery charge in uAh. - * - * Pulled from StatsCompanionService.java - */ -message RemainingBatteryCapacity { - optional int32 charge_micro_ampere_hour = 1; -} - -/** - * Pulls battery capacity, which is the battery capacity when full in uAh. - * Pulled from: - * frameworks/base/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp - */ -message FullBatteryCapacity { - optional int32 capacity_micro_ampere_hour = 1; -} - -/** - * Pulls battery voltage. - * Pulled from: - * frameworks/base/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp - */ -message BatteryVoltage { - // The voltage of the battery, in millivolts. - optional int32 voltage_millivolt = 1; -} - -/** - * Pulls battery level (percent full, from 0 to 100). - * - * Pulled from: - * frameworks/base/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp - */ -message BatteryLevel { - // Battery level. Should be in [0, 100]. - optional int32 battery_level = 1; -} - -/** - * Pulls the temperature of various parts of the device. - * The units are tenths of a degree Celsius. Eg: 30.3C is reported as 303. - * - * Pulled from StatsCompanionService.java - */ -message Temperature { - // The type of temperature being reported. Eg. CPU, GPU, SKIN, BATTERY, BCL_. - optional android.os.TemperatureTypeEnum sensor_location = 1; - - // The name of the temperature source. Eg. CPU0 - optional string sensor_name = 2; - - // Temperature in tenths of a degree C. - // For BCL, it is decimillivolt, decimilliamps, and percentage * 10. - optional int32 temperature_deci_celsius = 3; - - // Relative severity of the throttling, see enum definition. - optional android.os.ThrottlingSeverityEnum severity = 4; -} - -/** - * Pulls the statistics of calls to Binder. - * - * Binder stats will be reset every time the data is pulled. It means it can only be pulled by one - * config on the device. - * - * Next tag: 15 - */ -message BinderCalls { - // UID of the process responsible for the binder transaction. It will be set if the process - // executing the binder transaction attribute the transaction to another uid using - // Binder.setThreadWorkSource(). - // - // If not set, the value will be -1. - optional int32 uid = 1 [(is_uid) = true]; - // UID of the process executing the binder transaction. - optional int32 direct_caller_uid = 14; - // Fully qualified class name of the API call. - // - // This is a system server class name. - // - // TODO(gaillard): figure out if binder call stats includes data from isolated uids, if a uid - // gets recycled and we have isolated uids, we might attribute the data incorrectly. - // TODO(gaillard): there is a high dimensions cardinality, figure out if we should drop the less - // commonly used APIs. - optional string service_class_name = 2; - // Method name of the API call. It can also be a transaction code if we cannot - // resolve it to a name. See Binder#getTransactionName. - // - // This is a system server method name. - optional string service_method_name = 3; - // Total number of API calls. - optional int64 call_count = 4; - // True if the screen was interactive PowerManager#isInteractive at the end of the call. - optional bool screen_interactive = 13; - // Total number of API calls we have data recorded for. If we collected data for all the calls, - // call_count will be equal to recorded_call_count. - // - // If recorded_call_count is different than call_count, it means data collection has been - // sampled. All the fields below will be sampled in this case. - optional int64 recorded_call_count = 12; - // Number of exceptions thrown by the API. - optional int64 recorded_exception_count = 5; - // Total latency of all API calls. - // Average can be computed using total_latency_micros / recorded_call_count. - optional int64 recorded_total_latency_micros = 6; - // Maximum latency of one API call. - optional int64 recorded_max_latency_micros = 7; - // Total CPU usage of all API calls. - // Average can be computed using total_cpu_micros / recorded_call_count. - // Total can be computed using total_cpu_micros / recorded_call_count * call_count. - optional int64 recorded_total_cpu_micros = 8; - // Maximum CPU usage of one API call. - optional int64 recorded_max_cpu_micros = 9; - // Maximum parcel reply size of one API call. - optional int64 recorded_max_reply_size_bytes = 10; - // Maximum parcel request size of one API call. - optional int64 recorded_max_request_size_bytes = 11; -} - -/** - * Pulls the statistics of exceptions during calls to Binder. - * - * Binder stats are cumulative from boot unless somebody reset the data using - * > adb shell dumpsys binder_calls_stats --reset - */ -message BinderCallsExceptions { - // Exception class name, e.g. java.lang.IllegalArgumentException. - // - // This is an exception class name thrown by the system server. - optional string exception_class_name = 1; - // Total number of exceptions. - optional int64 exception_count = 2; -} - -/** - * Pulls the statistics of message dispatching on HandlerThreads. - * - * Looper stats will be reset every time the data is pulled. It means it can only be pulled by one - * config on the device. - * - * Next tag: 11 - */ -message LooperStats { - // The uid that made a call to the System Server and caused the message to be enqueued. - optional int32 uid = 1 [(is_uid) = true]; - - // Fully qualified class name of the handler target class. - // - // This field does not contain PII. This is a system server class name. - optional string handler_class_name = 2; - - // The name of the thread that runs the Looper. - // - // This field does not contain PII. This is a system server thread name. - optional string looper_thread_name = 3; - - // The name of the dispatched message. - // - // This field does not contain PII. This is a system server constant or class - // name. - optional string message_name = 4; - - // Total number of successfully dispatched messages. - optional int64 message_count = 5; - - // Total number of messages that failed dispatching. - optional int64 exception_count = 6; - - // Total number of processed messages we have data recorded for. If we - // collected data for all the messages, message_count will be equal to - // recorded_message_count. - // - // If recorded_message_count is different than message_count, it means data - // collection has been sampled. The fields below will be sampled in this case. - optional int64 recorded_message_count = 7; - - // Total latency of all processed messages. - // Average can be computed using recorded_total_latency_micros / - // recorded_message_count. - optional int64 recorded_total_latency_micros = 8; - - // Total CPU usage of all processed message. - // Average can be computed using recorded_total_cpu_micros / - // recorded_message_count. Total can be computed using - // recorded_total_cpu_micros / recorded_message_count * message_count. - optional int64 recorded_total_cpu_micros = 9; - - // True if the screen was interactive PowerManager#isInteractive at the end of the call. - optional bool screen_interactive = 10; - - // Max recorded CPU usage of all processed messages. - optional int64 recorded_max_cpu_micros = 11; - - // Max recorded latency of all processed messages. - optional int64 recorded_max_latency_micros = 12; - - // Total number of messages we tracked the dispatching delay for. If we - // collected data for all the messages, message_count will be equal to - // recorded_delay_message_count. - // - // If recorded_delay_message_count is different than message_count, it means data - // collection has been sampled or/and not all messages specified the target dispatch time. - // The fields below will be sampled in this case. - optional int64 recorded_delay_message_count = 13; - - // Total dispatching delay of all processed messages. - // Calculated as a difference between the target dispatching time (Message.when) - // and the actual dispatching time. - // Average can be computed using recorded_total_delay_millis / recorded_delay_message_count. - optional int64 recorded_total_delay_millis = 14; - - // Max dispatching delay of all processed messages. - // Calculated as a difference between the target dispatching time (Message.when) - // and the actual dispatching time. - optional int64 recorded_max_delay_millis = 15; -} - -/** - * Pulls disk information, such as write speed and latency. - */ -message DiskStats { - // Time taken to open, write 512B to, and close a file. - // -1 if error performing the check. - optional int64 data_write_latency_millis = 1; - - optional bool file_based_encryption = 2; - - // Recent disk write speed in kB/s. - // -1 if error querying storageed. - // 0 if data is unavailable. - optional int32 recent_disk_write_speed = 3; -} - - -/** - * Free and total bytes of the Data, Cache, and System partition. - */ -message DirectoryUsage { - enum Directory { - UNKNOWN = 0; - DATA = 1; - CACHE = 2; - SYSTEM = 3; - } - optional Directory directory = 1; - optional int64 free_bytes = 2; - optional int64 total_bytes = 3; -} - - -/** - * Size of an application: apk size, data size, and cache size. - * Reads from a cached file produced daily by DiskStatsLoggingService.java. - * Information is only reported for apps with the primary user (user 0). - * Sizes are aggregated by package name. - */ -message AppSize { - // Including uids will involve modifying diskstats logic. - optional string package_name = 1; - // App size in bytes. -1 if unavailable. - optional int64 app_size_bytes = 2; - // App data size in bytes. -1 if unavailable. - optional int64 app_data_size_bytes = 3; - // App cache size in bytes. -1 if unavailable. - optional int64 app_cache_size_bytes = 4; - // Time that the cache file was produced. - // Uses System.currentTimeMillis(), which is wall clock time. - optional int64 cache_time_millis = 5; -} - - -/** - * Size of a particular category. Eg: photos, videos. - * Reads from a cached file produced daily by DiskStatsLoggingService.java. - */ -message CategorySize { - enum Category { - UNKNOWN = 0; - APP_SIZE = 1; - APP_DATA_SIZE = 2; - APP_CACHE_SIZE = 3; - PHOTOS = 4; - VIDEOS = 5; - AUDIO = 6; - DOWNLOADS = 7; - SYSTEM = 8; - OTHER = 9; - } - optional Category category = 1; - // Category size in bytes. - optional int64 size_bytes = 2; - // Time that the cache file was produced. - // Uses System.currentTimeMillis(), which is wall clock time. - optional int64 cache_time_millis = 3; -} - -/** - * Pulls per uid I/O stats. The stats are cumulative since boot. - * - * Read/write bytes are I/O events from a storage device - * Read/write chars are data requested by read/write syscalls, and can be - * satisfied by caching. - * - * Pulled from StatsCompanionService, which reads proc/uid_io/stats. - */ -message DiskIo { - optional int32 uid = 1 [(is_uid) = true]; - optional int64 fg_chars_read = 2; - optional int64 fg_chars_write = 3; - optional int64 fg_bytes_read = 4; - optional int64 fg_bytes_write = 5; - optional int64 bg_chars_read = 6; - optional int64 bg_chars_write = 7; - optional int64 bg_bytes_read = 8; - optional int64 bg_bytes_write = 9; - optional int64 fg_fsync = 10; - optional int64 bg_fsync= 11; -} - - -/** - * Pulls the number of fingerprints for each user. - * - * Pulled from StatsCompanionService, which queries <Biometric>Manager. - */ -message NumFingerprintsEnrolled { - // The associated user. Eg: 0 for owners, 10+ for others. - // Defined in android/os/UserHandle.java - optional int32 user = 1; - // Number of fingerprints registered to that user. - optional int32 num_fingerprints_enrolled = 2; -} - -/** - * Pulls the number of faces for each user. - * - * Pulled from StatsCompanionService, which queries <Biometric>Manager. - */ -message NumFacesEnrolled { - // The associated user. Eg: 0 for owners, 10+ for others. - // Defined in android/os/UserHandle.java - optional int32 user = 1; - // Number of faces registered to that user. - optional int32 num_faces_enrolled = 2; -} -/** - * A mapping of role holder -> role - */ -message RoleHolder { - // uid of the role holder - optional int32 uid = 1 [(is_uid) = true]; - - // package name of the role holder - optional string package_name = 2; - - // the role held - optional string role = 3; -} - -message AggStats { - // These are all in byte resolution. - optional int64 min = 1 [deprecated = true]; - optional int64 average = 2 [deprecated = true]; - optional int64 max = 3 [deprecated = true]; - - // These are all in kilobyte resolution. Can fit in int32, so smaller on the wire than the above - // int64 fields. - optional int32 mean_kb = 4; - optional int32 max_kb = 5; -} - -// A reduced subset of process states; reducing the number of possible states allows more -// aggressive device-side aggregation of statistics and hence reduces metric upload size. -enum ProcessStateAggregated { - PROCESS_STATE_UNKNOWN = 0; - // Persistent system process. - PROCESS_STATE_PERSISTENT = 1; - // Top activity; actually any visible activity. - PROCESS_STATE_TOP = 2; - // Process binding to top or a foreground service. - PROCESS_STATE_BOUND_TOP_OR_FGS = 3; - // Processing running a foreground service. - PROCESS_STATE_FGS = 4; - // Important foreground process (ime, wallpaper, etc). - PROCESS_STATE_IMPORTANT_FOREGROUND = 5; - // Important background process. - PROCESS_STATE_BACKGROUND = 6; - // Process running a receiver. - PROCESS_STATE_RECEIVER = 7; - // All kinds of cached processes. - PROCESS_STATE_CACHED = 8; -} - -// Next tag: 13 -message ProcessStatsStateProto { - optional android.service.procstats.ScreenState screen_state = 1; - - optional android.service.procstats.MemoryState memory_state = 2 [deprecated = true]; - - // this enum list is from frameworks/base/core/java/com/android/internal/app/procstats/ProcessStats.java - // and not frameworks/base/core/java/android/app/ActivityManager.java - optional android.service.procstats.ProcessState process_state = 3 [deprecated = true]; - - optional ProcessStateAggregated process_state_aggregated = 10; - - // Millisecond uptime duration spent in this state - optional int64 duration_millis = 4 [deprecated = true]; - // Same as above, but with minute resolution so it fits into an int32. - optional int32 duration_minutes = 11; - - // Millisecond elapsed realtime duration spent in this state - optional int64 realtime_duration_millis = 9 [deprecated = true]; - // Same as above, but with minute resolution so it fits into an int32. - optional int32 realtime_duration_minutes = 12; - - // # of samples taken - optional int32 sample_size = 5; - - // PSS is memory reserved for this process - optional AggStats pss = 6 [deprecated = true]; - - // USS is memory shared between processes, divided evenly for accounting - optional AggStats uss = 7 [deprecated = true]; - - // RSS is memory resident for this process - optional AggStats rss = 8; -} - -// Next Tag: 8 -message ProcessStatsProto { - // Name of process. - optional string process = 1; - - // Uid of the process. - optional int32 uid = 2 [(is_uid) = true]; - - // Information about how often kills occurred - message Kill { - // Count of excessive CPU kills - optional int32 cpu = 1; - - // Count of kills when cached - optional int32 cached = 2; - - // PSS stats during cached kill - optional AggStats cached_pss = 3; - } - optional Kill kill = 3 [deprecated = true]; - - // Time and memory spent in various states. - repeated ProcessStatsStateProto states = 5; - - // Total time process has been running... screen_state, memory_state, and process_state - // will not be set. - optional ProcessStatsStateProto total_running_state = 6; - - // Association data for this process in this state; - // each entry here is one association. - repeated ProcessStatsAssociationProto assocs = 7; -} - -// Next Tag: 6 -message ProcessStatsAssociationProto { - // Procss Name of the associated process (client process of service binding) - optional string assoc_process_name = 1; - - // Package Name of the associated package (client package of service binding) - optional string assoc_package_name = 2 [deprecated = true]; - - // UID of the associated process/package (client package of service binding) - optional int32 assoc_uid = 5 [(is_uid) = true]; - - // Total count of the times this association (service binding) appeared. - optional int32 total_count = 3; - - // Uptime total duration in seconds this association (service binding) was around. - optional int32 total_duration_secs = 4; -} - -message PackageServiceOperationStatsProto { - // Operate enum: Started, Foreground, Bound, Executing - optional android.service.procstats.ServiceOperationState operation = 1; - - // Number of times the service was in this operation. - optional int32 count = 2; - - // Information about a state the service can be in. - message StateStats { - // Screen state enum. - optional android.service.procstats.ScreenState screen_state = 1; - // Memory state enum. - optional android.service.procstats.MemoryState memory_state = 2; - - // duration in milliseconds. - optional int64 duration_millis = 3; - // Millisecond elapsed realtime duration spent in this state - optional int64 realtime_duration_millis = 4; - } - repeated StateStats state_stats = 3; -} - -message PackageServiceStatsProto { - // Name of service component. - optional string service_name = 1; - - // The operation stats. - // The package_name, package_uid, package_version, service_name will not be set to save space. - repeated PackageServiceOperationStatsProto operation_stats = 2; -} - -message PackageAssociationSourceProcessStatsProto { - // Uid of the process. - optional int32 process_uid = 1; - // Process name. - optional string process_name = 2; - // Package name. - optional string package_name = 7; - // Total count of the times this association appeared. - optional int32 total_count = 3; - - // Millisecond uptime total duration this association was around. - optional int64 total_duration_millis = 4; - - // Total count of the times this association became actively impacting its target process. - optional int32 active_count = 5; - - // Information on one source in this association. - message StateStats { - // Process state enum. - optional android.service.procstats.ProcessState process_state = 1; - // Millisecond uptime duration spent in this state - optional int64 duration_millis = 2; - // Millisecond elapsed realtime duration spent in this state - optional int64 realtime_duration_mmillis = 3; - } - repeated StateStats active_state_stats = 6; -} - -message PackageAssociationProcessStatsProto { - // Name of the target component. - optional string component_name = 1; - // Information on one source in this association. - repeated PackageAssociationSourceProcessStatsProto sources = 2; -} - - -message ProcessStatsPackageProto { - // Name of package. - optional string package = 1; - - // Uid of the package. - optional int32 uid = 2; - - // Version of the package. - optional int64 version = 3; - - // Stats for each process running with the package loaded in to it. - repeated ProcessStatsProto process_stats = 4; - - // Stats for each of the package's services. - repeated PackageServiceStatsProto service_stats = 5; - - // Stats for each association with the package. - repeated PackageAssociationProcessStatsProto association_stats = 6; -} - -message ProcessStatsSectionProto { - // Elapsed realtime at start of report. - optional int64 start_realtime_millis = 1; - - // Elapsed realtime at end of report. - optional int64 end_realtime_millis = 2; - - // CPU uptime at start of report. - optional int64 start_uptime_millis = 3; - - // CPU uptime at end of report. - optional int64 end_uptime_millis = 4; - - // System runtime library. e.g. "libdvm.so", "libart.so". - optional string runtime = 5; - - // whether kernel reports swapped pss. - optional bool has_swapped_pss = 6; - - // Data completeness. e.g. "complete", "partial", shutdown", or "sysprops". - enum Status { - STATUS_UNKNOWN = 0; - STATUS_COMPLETE = 1; - STATUS_PARTIAL = 2; - STATUS_SHUTDOWN = 3; - STATUS_SYSPROPS = 4; - } - repeated Status status = 7; - - // Number of pages available of various types and sizes, representation fragmentation. - repeated ProcessStatsAvailablePagesProto available_pages = 10; - - // Stats for each process. - repeated ProcessStatsProto process_stats = 8; - - // Stats for each package. - repeated ProcessStatsPackageProto package_stats = 9; -} - -message ProcessStatsAvailablePagesProto { - // Node these pages are in (as per /proc/pagetypeinfo) - optional int32 node = 1; - - // Zone these pages are in (as per /proc/pagetypeinfo) - optional string zone = 2; - - // Label for the type of these pages (as per /proc/pagetypeinfo) - optional string label = 3; - - // Distribution of number of pages available by order size. First entry in array is - // order 0, second is order 1, etc. Each order increase is a doubling of page size. - repeated int32 pages_per_order = 4; -} - -/** - * Pulled from ProcessStatsService.java - */ -message ProcStats { - optional ProcessStatsSectionProto proc_stats_section = 1 [(log_mode) = MODE_BYTES]; - // Data pulled from device into this is sometimes sharded across multiple atoms to work around - // a size limit. When this happens, this shard ID will contain an increasing 1-indexed integer - // with the number of this shard. - optional int32 shard_id = 2; -} - -/** - * Pulled from ProcessStatsService.java - */ -message ProcStatsPkgProc { - optional ProcessStatsSectionProto proc_stats_section = 1 [(log_mode) = MODE_BYTES]; -} - -// Next Tag: 2 -message PackageRemoteViewInfoProto { - optional string package_name = 1; - // add per-package additional info here (like channels) -} - -// Next Tag: 2 -message NotificationRemoteViewsProto { - repeated PackageRemoteViewInfoProto package_remote_view_info = 1; -} - -/** - * Pulled from NotificationManagerService.java - */ -message NotificationRemoteViews { - optional NotificationRemoteViewsProto notification_remote_views = 1 [(log_mode) = MODE_BYTES]; -} - -/** - * Atom that contains a list of a package's preferences, pulled from NotificationManagerService.java - */ -message PackageNotificationPreferences { - // Uid under which the package is installed. - optional int32 uid = 1 [(is_uid) = true]; - // Notification importance, which specifies when and how a notification is displayed. - // Specified under core/java/android/app/NotificationManager.java. - optional int32 importance = 2; - // Lockscreen visibility as set by the user. - optional int32 visibility = 3; - // Bitfield mask indicating what fields were locked by the user (see LockableAppfields in - // PreferencesHelper.java) - optional int32 user_locked_fields = 4; -} - -/** - * Atom that contains a list of a package's channel preferences, pulled from - * NotificationManagerService.java. - */ -message PackageNotificationChannelPreferences { - // Uid under which the package is installed. - optional int32 uid = 1 [(is_uid) = true]; - // Channel's ID. Should always be available. - optional string channel_id = 2; - // Channel's name. Should always be available. - optional string channel_name = 3; - // Channel's description. Optionally set by the channel creator. - optional string description = 4; - // Notification importance, which specifies when and how a notification is displayed. Specified - // under core/java/android/app/NotificationManager.java. - optional int32 importance = 5; - // Bitmask representing which fields have been set by the user. See field bitmask descriptions - // at core/java/android/app/NotificationChannel.java - optional int32 user_locked_fields = 6; - // Indicates if the channel was deleted by the app. - optional bool is_deleted = 7; - // Indicates if the channel was marked as a conversation by the app. - optional bool is_conversation = 8; - // Indicates if the channel is a conversation that was demoted by the user. - optional bool is_demoted_conversation = 9; - // Indicates if the channel is a conversation that was marked as important by the user. - optional bool is_important_conversation = 10; -} - -/** - * Atom that represents an item in the list of Do Not Disturb rules, pulled from - * NotificationManagerService.java. - */ -message DNDModeProto { - enum Mode { - ROOT_CONFIG = -1; // Used to distinguish the config (one per user) from the rules. - ZEN_MODE_OFF = 0; - ZEN_MODE_IMPORTANT_INTERRUPTIONS = 1; - ZEN_MODE_NO_INTERRUPTIONS = 2; - ZEN_MODE_ALARMS = 3; - } - optional int32 user = 1; // Android user ID (0, 1, 10, ...) - optional bool enabled = 2; // true for ROOT_CONFIG if a manualRule is enabled - optional bool channels_bypassing = 3; // only valid for ROOT_CONFIG - optional Mode zen_mode = 4; - // id is one of the system default rule IDs, or empty - // May also be "MANUAL_RULE" to indicate app-activation of the manual rule. - optional string id = 5; - optional int32 uid = 6 [(is_uid) = true]; // currently only SYSTEM_UID or 0 for other - optional DNDPolicyProto policy = 7 [(log_mode) = MODE_BYTES]; -} - -/** - * Atom that represents a Do Not Disturb policy, an optional detail proto for DNDModeProto. - */ -message DNDPolicyProto { - enum State { - STATE_UNSET = 0; - STATE_ALLOW = 1; - STATE_DISALLOW = 2; - } - optional State calls = 1; - optional State repeat_callers = 2; - optional State messages = 3; - optional State conversations = 4; - optional State reminders = 5; - optional State events = 6; - optional State alarms = 7; - optional State media = 8; - optional State system = 9; - optional State fullscreen = 10; - optional State lights = 11; - optional State peek = 12; - optional State status_bar = 13; - optional State badge = 14; - optional State ambient = 15; - optional State notification_list = 16; - - enum PeopleType { - PEOPLE_UNSET = 0; - PEOPLE_ANYONE = 1; - PEOPLE_CONTACTS = 2; - PEOPLE_STARRED = 3; - PEOPLE_NONE = 4; - } - - optional PeopleType allow_calls_from = 17; - optional PeopleType allow_messages_from = 18; - - enum ConversationType { - CONV_UNSET = 0; - CONV_ANYONE = 1; - CONV_IMPORTANT = 2; - CONV_NONE = 3; - } - - optional ConversationType allow_conversations_from = 19; -} - -/** - * Atom that contains a list of a package's channel group preferences, pulled from - * NotificationManagerService.java. - */ -message PackageNotificationChannelGroupPreferences { - // Uid under which the package is installed. - optional int32 uid = 1 [(is_uid) = true]; - // Channel Group's ID. Should always be available. - optional string group_id = 2; - // Channel Group's name. Should always be available. - optional string group_name = 3; - // Channel Group's description. Optionally set by group creator. - optional string description = 4; - // Indicates if notifications from this channel group are blocked. - optional bool is_blocked = 5; - // Bitmask representing which fields have been set by the user. See field bitmask descriptions - // at core/java/android/app/NotificationChannelGroup.java - optional int32 user_locked_fields = 6; -} - -message PowerProfileProto { - optional double cpu_suspend = 1; - - optional double cpu_idle = 2; - - optional double cpu_active = 3; - - message CpuCluster { - optional int32 id = 1; - optional double cluster_power = 2; - optional int32 cores = 3; - repeated int64 speed = 4; - repeated double core_power = 5; - } - - repeated CpuCluster cpu_cluster = 40; - - optional double wifi_scan = 4; - - optional double wifi_on = 5; - - optional double wifi_active = 6; - - optional double wifi_controller_idle = 7; - - optional double wifi_controller_rx = 8; - - optional double wifi_controller_tx = 9; - - repeated double wifi_controller_tx_levels = 10; - - optional double wifi_controller_operating_voltage = 11; - - optional double bluetooth_controller_idle = 12; - - optional double bluetooth_controller_rx = 13; - - optional double bluetooth_controller_tx = 14; - - optional double bluetooth_controller_operating_voltage = 15; - - optional double modem_controller_sleep = 16; - - optional double modem_controller_idle = 17; - - optional double modem_controller_rx = 18; - - repeated double modem_controller_tx = 19; - - optional double modem_controller_operating_voltage = 20; - - optional double gps_on = 21; - - repeated double gps_signal_quality_based = 22; - - optional double gps_operating_voltage = 23; - - optional double bluetooth_on = 24; - - optional double bluetooth_active = 25; - - optional double bluetooth_at_cmd = 26; - - optional double ambient_display = 27; - - optional double screen_on = 28; - - optional double radio_on = 29; - - optional double radio_scanning = 30; - - optional double radio_active = 31; - - optional double screen_full = 32; - - optional double audio = 33; - - optional double video = 34; - - optional double flashlight = 35; - - optional double memory = 36; - - optional double camera = 37; - - optional double wifi_batched_scan = 38; - - optional double battery_capacity = 39; -} - -/** - * power_profile.xml and other constants for power model calculations. - * Pulled from PowerProfile.java - */ -message PowerProfile { - optional PowerProfileProto power_profile = 1 [(log_mode) = MODE_BYTES]; -} - -/** - * Logs when a user restriction was added or removed. - * - * Logged from: - * frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java - */ -message UserRestrictionChanged { - // The raw string of the user restriction as defined in UserManager. - // Allowed values are defined in UserRestrictionsUtils#USER_RESTRICTIONS. - optional string restriction = 1; - // Whether the restriction is enabled or disabled. - optional bool enabled = 2; -} - -/** - * Pulls process user time and system time. Puller takes a snapshot of all pids - * in the system and returns cpu stats for those that are working at the time. - * Dead pids will be dropped. Kernel processes are excluded. - * Min cool-down is 5 sec. - */ -message ProcessCpuTime { - optional int32 uid = 1 [(is_uid) = true]; - - optional string process_name = 2; - // Process cpu time in user space, cumulative from boot/process start - optional int64 user_time_millis = 3; - // Process cpu time in system space, cumulative from boot/process start - optional int64 system_time_millis = 4; -} - -/** - * Pulls the CPU usage for each thread. - * - * Read from /proc/$PID/task/$TID/time_in_state files. - * - * TODO(mishaw): This is an experimental atom. Issues with big/little CPU frequencies, and - * time_in_state files not being present on some phones, have not been addressed. These should be - * considered before a public release. - */ -message CpuTimePerThreadFreq { - // UID that owns the process. - optional int32 uid = 1 [(is_uid) = true]; - // ID of the process. - optional int32 process_id = 2; - // ID of the thread. - optional int32 thread_id = 3; - // Name of the process taken from `/proc/$PID/cmdline`. - optional string process_name = 4; - // Name of the thread taken from `/proc/$PID/task/$TID/comm` - optional string thread_name = 5; - - // Report eight different frequencies, and how much time is spent in each frequency. Frequencies - // are given in KHz, and time is given in milliseconds since the thread started. All eight - // frequencies are given here as the alternative is sending eight separate atoms. This method - // significantly reduces the amount of data created - optional int32 frequency1_khz = 6; - optional int32 time1_millis = 7; - optional int32 frequency2_khz = 8; - optional int32 time2_millis = 9; - optional int32 frequency3_khz = 10; - optional int32 time3_millis = 11; - optional int32 frequency4_khz = 12; - optional int32 time4_millis = 13; - optional int32 frequency5_khz = 14; - optional int32 time5_millis = 15; - optional int32 frequency6_khz = 16; - optional int32 time6_millis = 17; - optional int32 frequency7_khz = 18; - optional int32 time7_millis = 19; - optional int32 frequency8_khz = 20; - optional int32 time8_millis = 21; -} - -/** - * Pulls information about the device's build. - */ -message BuildInformation { - // Build.FINGERPRINT. A string that uniquely identifies this build. Do not parse. - // E.g. may be composed of the brand, product, device, release, id, incremental, type, and tags. - optional string fingerprint = 1; - - // Build.BRAND. The consumer-visible brand with which the product/hardware will be associated. - optional string brand = 2; - - // Build.PRODUCT. The name of the overall product. - optional string product = 3; - - // Build.DEVICE. The name of the industrial design. - optional string device = 4; - - // Build.VERSION.RELEASE. The user-visible version string. E.g., "1.0" or "3.4b5" or "bananas". - optional string version_release = 5; - - // Build.ID. E.g. a label like "M4-rc20". - optional string id = 6; - - // Build.VERSION.INCREMENTAL. The internal value used by the underlying source control to - // represent this build. - optional string version_incremental = 7; - - // Build.TYPE. The type of build, like "user" or "eng". - optional string type = 8; - - // Build.TAGS. Comma-separated tags describing the build, like "unsigned,debug". - optional string tags = 9; -} - -/** - * Logs information about mismatched caller for content capture. - * - * Logged from: - * frameworks/base/core/java/android/service/contentcapture/ContentCaptureService.java - */ -message ContentCaptureCallerMismatchReported { - optional string intended_package = 1; - optional string calling_package = 2; -} - -/** - * Logs information about content capture service events. - * - * Logged from: - * frameworks/base/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureMetricsLogger.java - */ -message ContentCaptureServiceEvents { - // The type of event. - enum Event { - UNKNOWN = 0; - ON_CONNECTED = 1; - ON_DISCONNECTED = 2; - SET_WHITELIST = 3; - SET_DISABLED = 4; - ON_USER_DATA_REMOVED = 5; - ON_DATA_SHARE_REQUEST = 6; - ACCEPT_DATA_SHARE_REQUEST = 7; - REJECT_DATA_SHARE_REQUEST = 8; - DATA_SHARE_WRITE_FINISHED = 9; - DATA_SHARE_ERROR_IOEXCEPTION = 10; - DATA_SHARE_ERROR_EMPTY_DATA = 11; - DATA_SHARE_ERROR_CLIENT_PIPE_FAIL = 12; - DATA_SHARE_ERROR_SERVICE_PIPE_FAIL = 13; - DATA_SHARE_ERROR_CONCURRENT_REQUEST = 14; - DATA_SHARE_ERROR_TIMEOUT_INTERRUPTED = 15; - } - optional Event event = 1; - // component/package of content capture service. - optional string service_info = 2; - // component/package of target. - // it's a concatenated list of component/package for SET_WHITELIST event - // separated by " ". - optional string target_info = 3; -} - -/** - * Logs information about content capture session events. - * - * Logged from: - * frameworks/base/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureMetricsLogger.java - */ -message ContentCaptureSessionEvents { - // The type of event. - enum Event { - UNKNOWN = 0; - ON_SESSION_STARTED = 1; - ON_SESSION_FINISHED = 2; - SESSION_NOT_CREATED = 3; - } - optional int32 session_id = 1; - optional Event event = 2; - // (n/a on session finished) - optional int32 state_flags = 3; - // component/package of content capture service. - optional string service_info = 4; - // component/package of app. - // (n/a on session finished) - optional string app_info = 5; - optional bool is_child_session = 6; -} - -/** - * Logs information about session being flushed. - * - * Logged from: - * frameworks/base/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureMetricsLogger.java - */ -message ContentCaptureFlushed { - optional int32 session_id = 1; - // component/package of content capture service. - optional string service_info = 2; - // component/package of app. - optional string app_info = 3; - // session start/finish events - optional int32 child_session_started = 4; - optional int32 child_session_finished = 5; - // count of view events. - optional int32 view_appeared_count = 6; - optional int32 view_disappeared_count = 7; - optional int32 view_text_changed_count = 8; - - // Flush stats. - optional int32 max_events = 9; - optional int32 idle_flush_freq = 10; - optional int32 text_flush_freq = 11; - optional int32 flush_reason = 12; -} - -/** - * Pulls on-device BatteryStats power use calculations for the overall device. - */ -message DeviceCalculatedPowerUse { - // Power used by the device in nAs (i.e. nanocoulombs (nC)), as computed by BatteryStats, since - // BatteryStats last reset (i.e. roughly since device was last significantly charged). - // Currently, this is from BatteryStatsHelper.getComputedPower() (not getTotalPower()). - optional int64 computed_power_nano_amp_secs = 1; -} - -/** - * Pulls on-device BatteryStats power use calculations broken down by uid. - * This atom should be complemented by DeviceCalculatedPowerBlameOther, which contains the power use - * that is attributed to non-uid items. They must all be included to get the total power use. - */ -message DeviceCalculatedPowerBlameUid { - // Uid being blamed. Note: isolated uids have already been mapped to host uid. - optional int32 uid = 1 [(is_uid) = true]; - - // Power used by this uid in nAs (i.e. nanocoulombs (nC)), as computed by BatteryStats, since - // BatteryStats last reset (i.e. roughly since device was last significantly charged). - optional int64 power_nano_amp_secs = 2; -} - -/** - * Pulls on-device BatteryStats power use calculations that are not due to a uid, broken down by - * drain type. - * This atom should be complemented by DeviceCalculatedPowerBlameUid, which contains the blame that - * is attributed uids. They must all be included to get the total power use. - */ -message DeviceCalculatedPowerBlameOther { - // The type of item whose power use is being reported. - enum DrainType { - AMBIENT_DISPLAY = 0; - // reserved 1; reserved "APP"; // Logged instead in DeviceCalculatedPowerBlameUid. - BLUETOOTH = 2; - CAMERA = 3; - // Cell-standby - CELL = 4; - FLASHLIGHT = 5; - IDLE = 6; - MEMORY = 7; - // Amount that total computed drain exceeded the drain estimated using the - // battery level changes and capacity. - OVERCOUNTED = 8; - PHONE = 9; - SCREEN = 10; - // Amount that total computed drain was below the drain estimated using the - // battery level changes and capacity. - UNACCOUNTED = 11; - // reserved 12; reserved "USER"; // Entire drain for a user. This is NOT supported. - WIFI = 13; - } - optional DrainType drain_type = 1; - - // Power used by this item in nAs (i.e. nanocoulombs (nC)), as computed by BatteryStats, since - // BatteryStats last reset (i.e. roughly since device was last significantly charged). - optional int64 power_nano_amp_secs = 2; -} - -/** - * Logs device policy features. - * - * Logged from: - * frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java - * packages/apps/ManagedProvisioning/src/com/android/managedprovisioning/ - */ -message DevicePolicyEvent { - // The event id - unique for each event. - optional android.stats.devicepolicy.EventId event_id = 1; - // The admin package name. - optional string admin_package_name = 2; - // A generic integer parameter. - optional int32 integer_value = 3; - // A generic boolean parameter. - optional bool boolean_value = 4; - // A parameter specifying a time period in milliseconds. - optional uint64 time_period_millis = 5; - // A parameter specifying a list of package names, bundle extras or string parameters. - optional android.stats.devicepolicy.StringList string_list_value = 6 [(log_mode) = MODE_BYTES]; -} - -/** - * Logs when DocumentsUI is started, and how. Call this when DocumentsUI first starts up. - * - * Logged from: - * package/app/DocumentsUI/src/com/android/documentsui/Metrics.java - */ -message DocsUILaunchReported { - optional android.stats.docsui.LaunchAction launch_action = 1; - optional bool has_initial_uri = 2; - optional android.stats.docsui.MimeType mime_type = 3; - optional android.stats.docsui.Root initial_root = 4; -} - -/** - * Logs root/app visited event in file managers/picker. Call this when the user - * taps on root/app in hamburger menu. - * - * Logged from: - * package/app/DocumentsUI/src/com/android/documentsui/Metrics.java - */ -message DocsUIRootVisitedReported { - optional android.stats.docsui.ContextScope scope = 1; - optional android.stats.docsui.Root root = 2; -} - -/** - * Logs file operation stats. Call this when a file operation has completed. - * - * Logged from: - * package/app/DocumentsUI/src/com/android/documentsui/Metrics.java - */ -message DocsUIFileOperationReported { - optional android.stats.docsui.Provider provider = 1; - optional android.stats.docsui.FileOperation file_op = 2; -} - -/** - * Logs file operation stats. Call this when a copy/move operation has completed with a specific - * mode. - * - * Logged from: - * package/app/DocumentsUI/src/com/android/documentsui/Metrics.java - */ -message DocsUIFileOperationCopyMoveModeReported { - optional android.stats.docsui.FileOperation file_op = 1; - optional android.stats.docsui.CopyMoveOpMode mode = 2; -} - - -/** - * Logs file sub operation stats. Call this when a file operation has failed. - * - * Logged from: - * package/app/DocumentsUI/src/com/android/documentsui/Metrics.java - */ -message DocsUIFileOperationFailureReported { - optional android.stats.docsui.Authority authority = 1; - optional android.stats.docsui.SubFileOperation sub_op = 2; -} - -/** -* Logs the cancellation of a file operation. Call this when a job is canceled -* -* Logged from: -* package/app/DocumentsUI/src/com/android/documentsui/Metrics.java -*/ -message DocsUIFileOperationCanceledReported { - optional android.stats.docsui.FileOperation file_op = 1; -} - -/** - * Logs startup time in milliseconds. - * - * Logged from: - * package/app/DocumentsUI/src/com/android/documentsui/Metrics.java - */ -message DocsUIStartupMsReported { - optional int32 startup_millis = 1; -} - -/** - * Logs the action that was started by user. - * - * Logged from: - * package/app/DocumentsUI/src/com/android/documentsui/Metrics.java - */ -message DocsUIUserActionReported { - optional android.stats.docsui.UserAction action = 1; -} - -/** - * Logs the invalid type when invalid scoped access is requested. - * - * Logged from: - * package/app/DocumentsUI/src/com/android/documentsui/ScopedAccessMetrics.java - */ -message DocsUIInvalidScopedAccessRequestReported { - optional android.stats.docsui.InvalidScopedAccess type = 1; -} - -/** - * Logs the package name that launches docsui picker mode. - * - * Logged from: - * package/app/DocumentsUI/src/com/android/documentsui/Metrics.java - */ -message DocsUIPickerLaunchedFromReported { - optional string package_name = 1; -} - -/** - * Logs the search type. - * - * Logged from: - * package/app/DocumentsUI/src/com/android/documentsui/Metrics.java - */ -message DocsUISearchTypeReported { - optional android.stats.docsui.SearchType search_type = 1; -} - -/** - * Logs the search mode. - * - * Logged from: - * package/app/DocumentsUI/src/com/android/documentsui/Metrics.java - */ -message DocsUISearchModeReported { - optional android.stats.docsui.SearchMode search_mode = 1; -} - -/** - * Logs the pick result information. - * - * Logged from: - * package/app/DocumentsUI/src/com/android/documentsui/Metrics.java - */ -message DocsUIPickResultReported { - optional int32 total_action_count = 1; - optional int64 duration_millis = 2; - optional int32 file_count= 3; - optional bool is_searching = 4; - optional android.stats.docsui.Root picked_from = 5; - optional android.stats.docsui.MimeType mime_type = 6; - optional int32 repeatedly_pick_times = 7; -} - -/** Logs the drag and drop of files. - - * Logged from: - * package/app/DocumentsUI/src/com/android/documentsui/Metrics.java - */ -message DocsUIDragAndDropReported { - optional bool drag_initiated_from_docsui = 1; -} - -/** - * Logs when an app's memory is compacted. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java - */ -message AppCompacted { - // The pid of the process being compacted. - optional int32 pid = 1; - - // The name of the process being compacted. - optional string process_name = 2; - - // The type of compaction. - enum Action { - UNKNOWN = 0; - SOME = 1; - FULL = 2; - PERSISTENT = 3; - BFGS = 4; - } - optional Action action = 3; - - // Total RSS in kilobytes consumed by the process prior to compaction. - optional int64 before_rss_total_kilobytes = 4; - - // File RSS in kilobytes consumed by the process prior to compaction. - optional int64 before_rss_file_kilobytes = 5; - - // Anonymous RSS in kilobytes consumed by the process prior to compaction. - optional int64 before_rss_anon_kilobytes = 6; - - // Swap in kilobytes consumed by the process prior to compaction. - optional int64 before_swap_kilobytes = 7; - - // Total RSS in kilobytes consumed by the process after compaction. - optional int64 after_rss_total_kilobytes = 8; - - // File RSS in kilobytes consumed by the process after compaction. - optional int64 after_rss_file_kilobytes = 9; - - // Anonymous RSS in kilobytes consumed by the process after compaction. - optional int64 after_rss_anon_kilobytes = 10; - - // Swap in kilobytes consumed by the process after compaction. - optional int64 after_swap_kilobytes = 11; - - // The time taken to perform compaction in milliseconds. - optional int64 time_to_compact_millis = 12; - - // The last compaction action performed for this app. - optional Action last_action = 13; - - // The last time that compaction was attempted on this process in milliseconds - // since boot, not including sleep (see SystemClock.uptimeMillis()). - optional int64 last_compact_timestamp_ms_since_boot = 14; - - // The "setAdj" (i.e. previous) oom_score_adj at the time of compaction. - optional int32 oom_score_adj = 15; - - // The process state at the time of compaction. - optional android.app.ProcessStateEnum process_state = 16 [default = PROCESS_STATE_UNKNOWN]; - - // Free ZRAM in kilobytes before compaction. - optional int64 before_zram_free_kilobytes = 17; - - // Free ZRAM in kilobytes after compaction. - optional int64 after_zram_free_kilobytes = 18; -} - -/** - * Logs when a Tethering event occurs. - * - */ -message NetworkTetheringReported { - // tethering error code - optional android.stats.connectivity.ErrorCode error_code = 1; - - // tethering downstream type - optional android.stats.connectivity.DownstreamType downstream_type = 2; - - // transport type of upstream network - optional android.stats.connectivity.UpstreamType upstream_type = 3; - - // The user type of Tethering - optional android.stats.connectivity.UserType user_type= 4; -} - -/** - * Logs a DNS lookup operation initiated by the system resolver on behalf of an application - * invoking native APIs such as getaddrinfo() or Java APIs such as Network#getAllByName(). - * - * The NetworkDnsEventReported message represents the entire lookup operation, which may - * result one or more queries to the recursive DNS resolvers. Those are individually logged - * in DnsQueryEvents to enable computing error rates and network latency and timeouts - * broken up by query type, transport, network interface, etc. - */ -message NetworkDnsEventReported { - optional android.stats.dnsresolver.EventType event_type = 1; - - optional android.stats.dnsresolver.ReturnCode return_code = 2; - - // The latency in microseconds of the entire DNS lookup operation. - optional int32 latency_micros = 3; - - // Only valid for event_type = EVENT_GETADDRINFO. - optional int32 hints_ai_flags = 4; - - // Flags passed to android_res_nsend() defined in multinetwork.h - // Only valid for event_type = EVENT_RESNSEND. - optional int32 res_nsend_flags = 5; - - optional android.stats.dnsresolver.NetworkType network_type = 6; - - // The DNS over TLS mode on a specific netId. - optional android.stats.dnsresolver.PrivateDnsModes private_dns_modes = 7; - - // Additional pass-through fields opaque to statsd. - // The DNS resolver Mainline module can add new fields here without requiring an OS update. - optional android.stats.dnsresolver.DnsQueryEvents dns_query_events = 8 [(log_mode) = MODE_BYTES]; - - // The sample rate of DNS stats (to statsd) is 1/sampling_rate_denom. - optional int32 sampling_rate_denom = 9; -} - -/** - * logs the CapportApiData info - * Logged from: - * packages/modules/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java - */ -message CapportApiData { - // The TTL of the network connection provided by captive portal - optional int32 remaining_ttl_secs = 1; - - // The limit traffic data of the network connection provided by captive portal - optional int32 remaining_bytes = 2; - - // Is portal url option included in the DHCP packet (Yes, No) - optional bool has_portal_url = 3; - - // Is venue info (e.g. store info, maps, flight status) included (Yes, No) - optional bool has_venue_info = 4; -} - -/** - * logs a network Probe Event - * Logged from: - * packages/modules/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java - */ -message ProbeEvent { - // The probe type (http or https, or captive portal API...) - optional android.stats.connectivity.ProbeType probe_type = 1; - - // The latency in microseconds of the probe event - optional int32 latency_micros = 2; - - // The result of the probe event - optional android.stats.connectivity.ProbeResult probe_result = 3; - - // The CaptivePortal API info - optional CapportApiData capport_api_data = 4; -} - -/** - * log each ProbeEvent in ProbeEvents - * Logged from: - * packages/modules/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java - */ -message ProbeEvents { - // Record probe event during the validation - repeated ProbeEvent probe_event = 1; -} - -/** - * The DHCP (Dynamic Host Configuration Protocol) session info - * Logged from: - * packages/modules/NetworkStack/src/android/net/dhcp/DhcpClient.java - */ -message DhcpSession { - // The DHCP Feature(s) enabled in this session - repeated android.stats.connectivity.DhcpFeature used_features = 1; - - // The discover packet (re)transmit count - optional int32 discover_count = 2; - - // The request packet (re)transmit count - optional int32 request_count = 3; - - // The IPv4 address conflict count - // (only be meaningful when duplicate address detection is enabled) - optional int32 conflict_count = 4; - - // The DHCP packet parsing error code in this session - // (defined in android.net.metrics.DhcpErrorEvent) - repeated android.stats.connectivity.DhcpErrorCode error_code = 5; - - // The result of DHCP hostname transliteration - optional android.stats.connectivity.HostnameTransResult ht_result = 6; -} - -/** - * Logs Network IP provisioning event - * Logged from: - * packages/modules/NetworkStack/src/com/android/networkstack/metrics/NetworkIpProvisioningMetrics.java - */ -message NetworkIpProvisioningReported { - // Transport type (WIFI, CELLULAR, BLUETOOTH, ..) - optional android.stats.connectivity.TransportType transport_type = 1; - - // The latency in microseconds of IP Provisioning over IPV4 - optional int32 ipv4_latency_micros = 2; - - // The latency in microseconds of IP Provisioning over IPV6 - optional int32 ipv6_latency_micros = 3; - - // The time duration between provisioning start and end (success or failure) - optional int64 provisioning_duration_micros = 4; - - // The specific disconnect reason for this IP provisioning - optional android.stats.connectivity.DisconnectCode disconnect_code = 5; - - // Log DHCP session info (Only valid for IPv4) - optional DhcpSession dhcp_session = 6 [(log_mode) = MODE_BYTES]; - - // The random number between 0 ~ 999 for sampling - optional int32 random_number = 7; -} - -/** - * Logs Network DHCP Renew event - * Logged from: - * packages/modules/NetworkStack/src/android/net/dhcp/DhcpClient.java - */ -message NetworkDhcpRenewReported { - // Transport type (WIFI, CELLULAR, BLUETOOTH, ..) - optional android.stats.connectivity.TransportType transport_type = 1; - - // The request packet (re)transmit count - optional int32 request_count = 2; - - // The latency in microseconds of DHCP Renew - optional int32 latency_micros = 3; - - // The DHCP error code is defined in android.net.metrics.DhcpErrorEvent - optional android.stats.connectivity.DhcpErrorCode error_code = 4; - - // The result of DHCP renew - optional android.stats.connectivity.DhcpRenewResult renew_result = 5; - - // The random number between 0 ~ 999 for sampling - optional int32 random_number = 6; -} - -/** - * Logs Network Validation event - * Logged from: - * packages/modules/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java - */ -message NetworkValidationReported { - // Transport type (WIFI, CELLULAR, BLUETOOTH, ..) - optional android.stats.connectivity.TransportType transport_type = 1; - - // Record each probe event - optional ProbeEvents probe_events = 2 [(log_mode) = MODE_BYTES]; - - // The result of the network validation - optional android.stats.connectivity.ValidationResult validation_result = 3; - - // The latency in microseconds of network validation - optional int32 latency_micros = 4; - - // The validation index (the first validation attempt or second, third...) - optional int32 validation_index = 5; - - // The random number between 0 ~ 999 for sampling - optional int32 random_number = 6; -} - -/** - * Logs NetworkStack Quirk event - * Logged from: - * packages/modules/NetworkStack/src/com/android/networkstack/ - */ -message NetworkStackQuirkReported { - // Transport type (WIFI, CELLULAR, BLUETOOTH, ..) - optional android.stats.connectivity.TransportType transport_type = 1; - - // Record each Quirk event - optional android.stats.connectivity.NetworkQuirkEvent event = 2; -} - -/** - * Logs when a data stall event occurs. - * - * Log from: - * packages/modules/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java - */ -message DataStallEvent { - // Data stall evaluation type. - // See packages/modules/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java - // Refer to the definition of DATA_STALL_EVALUATION_TYPE_*. - optional int32 evaluation_type = 1; - // See definition in data_stall_event.proto. - optional com.android.server.connectivity.ProbeResult validation_result = 2; - // See definition in data_stall_event.proto. - optional android.net.NetworkCapabilitiesProto.Transport network_type = 3; - // See definition in data_stall_event.proto. - optional com.android.server.connectivity.WifiData wifi_info = 4 [(log_mode) = MODE_BYTES]; - // See definition in data_stall_event.proto. - optional com.android.server.connectivity.CellularData cell_info = 5 [(log_mode) = MODE_BYTES]; - // See definition in data_stall_event.proto. - optional com.android.server.connectivity.DnsEvent dns_event = 6 [(log_mode) = MODE_BYTES]; - // The tcp packets fail rate from the latest tcp polling. - optional int32 tcp_fail_rate = 7; - // Number of packets sent since the last received packet. - optional int32 tcp_sent_since_last_recv = 8; -} - -/* - * Logs when RescueParty resets some set of experiment flags. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/RescueParty.java - */ -message RescuePartyResetReported { - // The rescue level of this reset. A value of 0 indicates missing or unknown level information. - optional int32 rescue_level = 1; -} - -/** - * Logs when signed config is received from an APK, and if that config was applied successfully. - * Logged from: - * frameworks/base/services/core/java/com/android/server/signedconfig/SignedConfigService.java - */ -message SignedConfigReported { - enum Type { - UNKNOWN_TYPE = 0; - GLOBAL_SETTINGS = 1; - } - optional Type type = 1; - - // The final status of the signed config received. - enum Status { - UNKNOWN_STATUS = 0; - APPLIED = 1; - BASE64_FAILURE_CONFIG = 2; - BASE64_FAILURE_SIGNATURE = 3; - SECURITY_EXCEPTION = 4; - INVALID_CONFIG = 5; - OLD_CONFIG = 6; - SIGNATURE_CHECK_FAILED = 7; - NOT_APPLICABLE = 8; - SIGNATURE_CHECK_FAILED_PROD_KEY_ABSENT = 9; - } - optional Status status = 2; - - // The version of the signed config processed. - optional int32 version = 3; - - // The package name that the config was extracted from. - optional string from_package = 4; - - enum Key { - NO_KEY = 0; - DEBUG = 1; - PRODUCTION = 2; - } - // Which key was used to verify the config. - optional Key verified_with = 5; -} - -/* - * Logs GNSS Network-Initiated (NI) location events. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/location/GnssLocationProvider.java - */ -message GnssNiEventReported { - // The type of GnssNiEvent. - enum EventType { - UNKNOWN = 0; - NI_REQUEST = 1; - NI_RESPONSE = 2; - } - optional EventType event_type = 1; - - // An ID generated by HAL to associate NI notifications and UI responses. - optional int32 notification_id = 2; - - // A type which distinguishes different categories of NI request, such as VOICE, UMTS_SUPL etc. - optional android.server.location.GnssNiType ni_type = 3; - - // NI requires notification. - optional bool need_notify = 4; - - // NI requires verification. - optional bool need_verify = 5; - - // NI requires privacy override, no notification/minimal trace. - optional bool privacy_override = 6; - - // Timeout period to wait for user response. Set to 0 for no timeout limit. Specified in - // seconds. - optional int32 timeout = 7; - - // Default response when timeout. - optional android.server.location.GnssUserResponseType default_response = 8; - - // String representing the requester of the network inititated location request. - optional string requestor_id = 9; - - // Notification message text string representing the service(for eg. SUPL-service) who sent the - // network initiated location request. - optional string text = 10; - - // requestorId decoding scheme. - optional android.server.location.GnssNiEncodingType requestor_id_encoding = 11; - - // Notification message text decoding scheme. - optional android.server.location.GnssNiEncodingType text_encoding = 12; - - // True if SUPL ES is enabled. - optional bool is_supl_es_enabled = 13; - - // True if GNSS location is enabled. - optional bool is_location_enabled = 14; - - // GNSS NI responses which define the response in NI structures. - optional android.server.location.GnssUserResponseType user_response = 15; -} - -/** - * Logs GNSS non-framework (NFW) location notification. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/location/GnssLocationProvider.java - */ -message GnssNfwNotificationReported { - // Package name of the Android proxy application representing the non-framework entity that - // requested location. Set to empty string if unknown. - optional string proxy_app_package_name = 1; - - // Protocol stack that initiated the non-framework location request. - optional android.server.location.NfwProtocolStack protocol_stack = 2; - - // Name of the protocol stack if protocol_stack field is set to OTHER_PROTOCOL_STACK. Otherwise, - // set to empty string. This field is opaque to the framework and used for logging purposes. - optional string other_protocol_stack_name = 3; - - // Source initiating/receiving the location information. - optional android.server.location.NfwRequestor requestor = 4; - - // Identity of the endpoint receiving the location information. For example, carrier name, OEM - // name, SUPL SLP/E-SLP FQDN, chipset vendor name, etc. This field is opaque to the framework - // and used for logging purposes. - optional string requestor_id = 5; - - // Indicates whether location information was provided for this request. - optional android.server.location.NfwResponseType response_type = 6; - - // True if the device is in user initiated emergency session. - optional bool in_emergency_mode = 7; - - // True if cached location is provided. - optional bool is_cached_location = 8; - - // True if proxy app permission mismatch between framework and GNSS HAL. - optional bool is_permission_mismatched = 9; -} - -/** - * Logs GNSS configuration as defined in IGnssConfiguration.hal. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/location/GnssConfiguration.java - */ -message GnssConfigurationReported { - // SUPL host name. - optional string supl_host = 1; - - // SUPL port number. - optional int32 supl_port = 2; - - // C2K host name. - optional string c2k_host = 3; - - // C2K port number. - optional int32 c2k_port = 4; - - // The SUPL version requested by Carrier. - optional int32 supl_ver = 5; - - // The SUPL mode. - optional android.server.location.SuplMode supl_mode = 6; - - // True if NI emergency SUPL restrictions is enabled. - optional bool supl_es = 7; - - // LTE Positioning Profile settings - optional android.server.location.LppProfile lpp_profile = 8; - - // Positioning protocol on A-Glonass system. - optional android.server.location.GlonassPosProtocol a_glonass_pos_protocol_select = 9; - - // True if emergency PDN is used. Otherwise, regular PDN is used. - optional bool use_emergency_pdn_for_emergency_supl= 10; - - // Configurations of how GPS functionalities should be locked when user turns off GPS On setting. - optional android.server.location.GpsLock gps_lock = 11; - - // Number of seconds to extend the emergency session duration post emergency call. - optional int32 es_extension_sec = 12; - - // The full list of package names of proxy Android applications representing the non-framework - // location access entities (on/off the device) for which the framework user has granted - // non-framework location access permission. The package names are concatenated in one string - // with spaces as separators. - optional string enabled_proxy_app_package_name_list = 13; -} - -/** - * Logs when a NFC device's error occurred. - * Logged from: - * system/nfc/src/nfc/nfc/nfc_ncif.cc - * packages/apps/Nfc/src/com/android/nfc/cardemulation/AidRoutingManager.java - */ -message NfcErrorOccurred { - enum Type { - UNKNOWN = 0; - CMD_TIMEOUT = 1; - ERROR_NOTIFICATION = 2; - AID_OVERFLOW = 3; - } - optional Type type = 1; - // If it's nci cmd timeout, log the timeout command. - optional uint32 nci_cmd = 2; - - optional uint32 error_ntf_status_code = 3; -} - -/** - * Logs when a NFC device's state changed event - * Logged from: - * packages/apps/Nfc/src/com/android/nfc/NfcService.java - */ -message NfcStateChanged { - enum State { - UNKNOWN = 0; - OFF = 1; - ON = 2; - ON_LOCKED = 3; // Secure Nfc enabled. - CRASH_RESTART = 4; // NfcService watchdog timeout restart. - } - optional State state = 1; -} - -/** - * Logs when a NFC Beam Transaction occurred. - * Logged from: - * packages/apps/Nfc/src/com/android/nfc/P2pLinkManager.java - */ -message NfcBeamOccurred { - enum Operation { - UNKNOWN = 0; - SEND = 1; - RECEIVE = 2; - } - optional Operation operation = 1; -} - -/** - * Logs when a NFC Card Emulation Transaction occurred. - * Logged from: - * packages/apps/Nfc/src/com/android/nfc/cardemulation/HostEmulationManager.java - * packages/apps/Nfc/src/com/android/nfc/cardemulation/HostNfcFEmulationManager.java - */ -message NfcCardemulationOccurred { - enum Category { - UNKNOWN = 0; - HCE_PAYMENT = 1; - HCE_OTHER = 2; - OFFHOST = 3; - } - // Transaction belongs to HCE payment or HCE other category, or offhost. - optional Category category = 1; - // SeName from transaction: SIMx, eSEx, HCE, HCEF. - optional string se_name = 2; -} - -/** - * Logs when a NFC Tag event occurred. - * Logged from: - * packages/apps/Nfc/src/com/android/nfc/NfcDispatcher.java - */ -message NfcTagOccurred { - enum Type { - UNKNOWN = 0; - URL = 1; - BT_PAIRING = 2; - PROVISION = 3; - WIFI_CONNECT = 4; - APP_LAUNCH = 5; - OTHERS = 6; - } - optional Type type = 1; -} - -/** - * Logs when Hce transaction triggered - * Logged from: - * system/nfc/src/nfc/nfc/nfc_ncif.cc - */ -message NfcHceTransactionOccurred { - // The latency period(in microseconds) it took for the first HCE data - // exchange. - optional uint32 latency_micros = 1; -} - -/** - * Logs when SecureElement state event changed - * Logged from: - * packages/apps/SecureElement/src/com/android/se/Terminal.java - */ -message SeStateChanged { - enum State { - UNKNOWN = 0; - INITIALIZED = 1; - DISCONNECTED = 2; - CONNECTED = 3; - HALCRASH = 4; - } - optional State state = 1; - - optional string state_change_reason = 2; - // SIMx or eSEx. - optional string terminal = 3; -} - -/** - * Information about a permission grant request - */ -message PermissionGrantRequestResultReported { - // unique value identifying an API call. A API call might result in multiple of these atoms - optional int64 request_id = 1; - - // UID of package requesting the permission grant - optional int32 uid = 2 [(is_uid) = true]; - - // Name of package requesting the permission grant - optional string package_name = 3; - - // The permission to be granted - optional string permission_name = 4; - - // If the permission was explicitly requested via the API or added by the system - optional bool is_implicit = 5; - - enum Result { - UNDEFINED = 0; - // permission request was ignored - IGNORED = 1; - // permission request was ignored because it was user fixed - IGNORED_USER_FIXED = 2; - // permission request was ignored because it was policy fixed - IGNORED_POLICY_FIXED = 3; - // permission was granted by user action - USER_GRANTED = 4; - // permission was automatically granted - AUTO_GRANTED = 5; - // permission was denied by user action - USER_DENIED = 6; - // permission was denied with prejudice by the user - USER_DENIED_WITH_PREJUDICE = 7; - // permission was automatically denied - AUTO_DENIED = 8; - // permission request was ignored because permission is restricted - IGNORED_RESTRICTED_PERMISSION = 9; - // one time permission was granted by user action - USER_GRANTED_ONE_TIME = 10; - // user ignored request by leaving the request screen without choosing any option - USER_IGNORED = 11; - // user granted the permission after being linked to settings - USER_GRANTED_IN_SETTINGS = 12; - // user denied the permission after being linked to settings - USER_DENIED_IN_SETTINGS = 13; - // user denied the permission with prejudice after being linked to settings - USER_DENIED_WITH_PREJUDICE_IN_SETTINGS = 14; - // permission was automatically revoked after one-time permission expired - AUTO_ONE_TIME_PERMISSION_REVOKED = 15; - // permission was automatically revoked for unused app - AUTO_UNUSED_APP_PERMISSION_REVOKED = 16; - } - // The result of the permission grant - optional Result result = 6; -} - -/** - * Logs when Omapi API used - * Logged from: - * packages/apps/SecureElement/src/com/android/se/Terminal.java - */ -message SeOmapiReported { - enum Operation { - UNKNOWN = 0; - OPEN_CHANNEL = 1; - } - optional Operation operation = 1; - // SIMx or eSEx. - optional string terminal = 2; - - optional string package_name = 3; -} - -/** - * Logs the dispatch latency of a broadcast during processing of BOOT_COMPLETED. - * The dispatch latency is the dispatchClockTime - enqueueClockTime. - * Logged from: - * frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java - */ -message BroadcastDispatchLatencyReported { - optional int64 dispatch_latency_millis = 1; -} - -/** - * Logs AttentionManagerService attention check result. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/attention/AttentionManagerService.java - */ -message AttentionManagerServiceResultReported { - // See core/java/android/service/attention/AttentionService.java - enum AttentionCheckResult { - UNKNOWN = 20; - ATTENTION_SUCCESS_ABSENT = 0; - ATTENTION_SUCCESS_PRESENT = 1; - ATTENTION_FAILURE_UNKNOWN = 2; - ATTENTION_FAILURE_CANCELLED = 3; - ATTENTION_FAILURE_PREEMPTED = 4; - ATTENTION_FAILURE_TIMED_OUT = 5; - ATTENTION_FAILURE_CAMERA_PERMISSION_ABSENT = 6; - } - optional AttentionCheckResult attention_check_result = 1 [default = UNKNOWN]; -} - -/** - * Logs when an adb connection changes state. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/adb/AdbDebuggingManager.java - */ -message AdbConnectionChanged { - // The last time this system connected via adb, or 0 if the 'always allow' option was not - // previously selected for this system. - optional int64 last_connection_time_millis = 1; - - // The time in ms within which a subsequent connection from an 'always allow' system is allowed - // to reconnect via adb without user interaction. - optional int64 auth_window_millis = 2; - - // The state of the adb connection from frameworks/base/core/proto/android/debug/enums.proto. - optional android.debug.AdbConnectionStateEnum state = 3; - - // True if the 'always allow' option was selected for this system. - optional bool always_allow = 4; -} - -/* - * Logs the reported speech DSP status. - * - * Logged from: - * Vendor audio implementation. - */ -message SpeechDspStatReported { - // The total Speech DSP uptime in milliseconds. - optional int32 total_uptime_millis = 1; - // The total Speech DSP downtime in milliseconds. - optional int32 total_downtime_millis = 2; - optional int32 total_crash_count = 3; - optional int32 total_recover_count = 4; -} - -/** - * Logs USB connector contaminant status. - * - * Logged from: USB Service. - */ -message UsbContaminantReported { - optional string id = 1; - optional android.service.usb.ContaminantPresenceStatus status = 2; -} - -/** - * This atom is for debugging purpose. - */ -message DebugElapsedClock { - // Monotically increasing value for each pull. - optional int64 pull_count = 1; - // Time from System.elapsedRealtime. - optional int64 elapsed_clock_millis = 2; - // Time from System.elapsedRealtime. - optional int64 same_elapsed_clock_millis = 3; - // Diff between current elapsed time and elapsed time from previous pull. - optional int64 elapsed_clock_diff_millis = 4; - - enum Type { - TYPE_UNKNOWN = 0; - ALWAYS_PRESENT = 1; - PRESENT_ON_ODD_PULLS = 2; - } - // Type of behavior for the pulled data. - optional Type type = 5; -} - -/** - * This atom is for debugging purpose. - */ -message DebugFailingElapsedClock { - // Monotically increasing value for each pull. - optional int64 pull_count = 1; - // Time from System.elapsedRealtime. - optional int64 elapsed_clock_millis = 2; - // Time from System.elapsedRealtime. - optional int64 same_elapsed_clock_millis = 3; - // Diff between current elapsed time and elapsed time from previous pull. - optional int64 elapsed_clock_diff_millis = 4; -} - -/** Logs System UI bubbles event changed. - * - * Logged from: - * frameworks/base/packages/SystemUI/src/com/android/systemui/bubbles - */ -message BubbleUIChanged { - - // The app package that is posting the bubble. - optional string package_name = 1; - - // The notification channel that is posting the bubble. - optional string notification_channel = 2; - - // The notification id associated with the posted bubble. - optional int32 notification_id = 3; - - // The position of the bubble within the bubble stack. - optional int32 position = 4; - - // The total number of bubbles within the bubble stack. - optional int32 total_number = 5; - - // User interactions with the bubble. - enum Action { - UNKNOWN = 0; - POSTED = 1; - UPDATED = 2; - EXPANDED = 3; - COLLAPSED = 4; - DISMISSED = 5; - STACK_DISMISSED = 6; - STACK_MOVED = 7; - HEADER_GO_TO_APP = 8; - HEADER_GO_TO_SETTINGS = 9; - PERMISSION_OPT_IN = 10; - PERMISSION_OPT_OUT = 11; - PERMISSION_DIALOG_SHOWN = 12; - SWIPE_LEFT = 13; - SWIPE_RIGHT = 14; - STACK_EXPANDED = 15; - FLYOUT = 16; - } - optional Action action = 6; - - // Normalized screen position of the bubble stack. The range is between 0 and 1. - optional float normalized_x_position = 7; - optional float normalized_y_position = 8; - - // Whether the bubble is unread. If it is unread, a dot is shown in the bubble stack icon. - optional bool is_unread = 9; - - // Whether the bubble is an on-going one. - optional bool is_ongoing = 10; - - // Whether the bubble is produced by an app running in foreground. - // This is deprecated and the value should be ignored. - optional bool is_foreground = 11 [deprecated = true]; -} - -/** - * Logs System UI bubbles developer errors. - * - * Logged from: - * frameworks/base/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java - */ -message BubbleDeveloperErrorReported { - - // The app package that is posting the bubble. - optional string package_name = 1; - - // Bubble developer error type enums. - enum Error { - UNKNOWN = 0; - ACTIVITY_INFO_MISSING = 1; - ACTIVITY_INFO_NOT_RESIZABLE = 2; - DOCUMENT_LAUNCH_NOT_ALWAYS = 3; - } - optional Error error = 2 [default = UNKNOWN]; -} - -/** - * Logs that a constraint for a scheduled job has changed. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/job/controllers/JobStatus.java - */ -message ScheduledJobConstraintChanged { - repeated AttributionNode attribution_node = 1; - - // Name of the job. - optional string job_name = 2; - - optional com.android.server.job.ConstraintEnum constraint = 3; - - enum State { - UNKNOWN = 0; - UNSATISFIED = 1; - SATISFIED = 2; - } - optional State state = 4; -} - -/** - * Logs PowerManagerService screen timeout resets (extensions) that happen when an attention check - * returns true. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java - */ -message ScreenTimeoutExtensionReported { - // Describes how many times in a row did the power manager reset the screen off timeout. - optional uint32 consecutive_timeout_extended_count = 1; -} - -/* -* Logs number of milliseconds it takes to start a process. -* The definition of app process start time is from the app launch time to -* the time that Zygote finished forking the app process and loaded the -* application package's java classes. - -* This metric is different from AppStartOccurred which is for foreground -* activity only. - -* ProcessStartTime can report all processes (both foreground and background) -* start time. -* -* Logged from: -* frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java -*/ -message ProcessStartTime { - // The uid of the ProcessRecord. - optional int32 uid = 1 [(is_uid) = true]; - - // The process pid. - optional int32 pid = 2; - - // The process name. - // Usually package name, "system" for system server. - // Provided by ActivityManagerService. - optional string process_name = 3; - - enum StartType { - UNKNOWN = 0; - WARM = 1; - HOT = 2; - COLD = 3; - } - - // The start type. - optional StartType type = 4; - - // The elapsed realtime at the start of the process. - optional int64 process_start_time_millis = 5; - - // Number of milliseconds it takes to reach bind application. - optional int32 bind_application_delay_millis = 6; - - // Number of milliseconds it takes to finish start of the process. - optional int32 process_start_delay_millis = 7; - - // hostingType field in ProcessRecord, the component type such as "activity", - // "service", "content provider", "broadcast" or other strings. - optional string hosting_type = 8; - - // hostingNameStr field in ProcessRecord. The component class name that runs - // in this process. - optional string hosting_name = 9; -} - -/** - * Track Media Codec usage - * Logged from: - * frameworks/av/media/libstagefright/MediaCodec.cpp - * frameworks/av/services/mediaanalytics/statsd_codec.cpp - */ -message MediametricsCodecReported { - optional int64 timestamp_nanos = 1; - optional string package_name = 2; - optional int64 package_version_code = 3; - optional int64 media_apex_version = 4; - - optional android.stats.mediametrics.CodecData codec_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES]; -} - -/** - * Track Media Extractor (pulling video/audio streams out of containers) usage - * Logged from: - * frameworks/av/media/libstagefright/RemoteMediaExtractor.cpp - * frameworks/av/services/mediaanalytics/statsd_extractor.cpp - */ -message MediametricsExtractorReported { - optional int64 timestamp_nanos = 1; - optional string package_name = 2; - optional int64 package_version_code = 3; - optional int64 media_apex_version = 4; - - optional android.stats.mediametrics.ExtractorData extractor_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES]; -} - -/** - * Track MediaParser (parsing video/audio streams from containers) usage - * Logged from: - * - * frameworks/av/services/mediametrics/statsd_mediaparser.cpp - * frameworks/base/apex/media/framework/jni/android_media_MediaParserJNI.cpp - */ -message MediametricsMediaParserReported { - optional int64 timestamp_nanos = 1; - optional string package_name = 2; - optional int64 package_version_code = 3; - - // MediaParser specific data. - /** - * The name of the parser selected for parsing the media, or an empty string - * if no parser was selected. - */ - optional string parser_name = 4; - /** - * Whether the parser was created by name. 1 represents true, and 0 - * represents false. - */ - optional int32 created_by_name = 5; - /** - * The parser names in the sniffing pool separated by "|". - */ - optional string parser_pool = 6; - /** - * The fully qualified name of the last encountered exception, or an empty - * string if no exception was encountered. - */ - optional string last_exception = 7; - /** - * The size of the parsed media in bytes, or -1 if unknown. Note this value - * contains intentional random error to prevent media content - * identification. - */ - optional int64 resource_byte_count = 8; - /** - * The duration of the media in milliseconds, or -1 if unknown. Note this - * value contains intentional random error to prevent media content - * identification. - */ - optional int64 duration_millis = 9; - /** - * The MIME types of the tracks separated by "|". - */ - optional string track_mime_types = 10; - /** - * The tracks' RFC 6381 codec strings separated by "|". - */ - optional string track_codecs = 11; - /** - * Concatenation of the parameters altered by the client, separated by "|". - */ - optional string altered_parameters = 12; - /** - * The video width in pixels, or -1 if unknown or not applicable. - */ - optional int32 video_width = 13; - /** - * The video height in pixels, or -1 if unknown or not applicable. - */ - optional int32 video_height = 14; -} - -/** - * Track how we arbitrate between microphone/input requests. - * Logged from - * frameworks/av/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp - * frameworks/av/services/mediaanalytics/statsd_audiopolicy.cpp - */ -message MediametricsAudiopolicyReported { - optional int64 timestamp_nanos = 1; - optional string package_name = 2; - optional int64 package_version_code = 3; - optional int64 media_apex_version = 4; - - optional android.stats.mediametrics.AudioPolicyData audiopolicy_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES]; -} - -/** - * Track how we arbitrate between microphone requests. - * Logged from - * frameworks/av/media/libaudioclient/AudioRecord.cpp - * frameworks/av/services/mediaanalytics/statsd_audiorecord.cpp - */ -message MediametricsAudiorecordReported { - optional int64 timestamp_nanos = 1; - optional string package_name = 2; - optional int64 package_version_code = 3; - optional int64 media_apex_version = 4; - - optional android.stats.mediametrics.AudioRecordData audiorecord_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES]; -} - -/** - * Track how we arbitrate between microphone/input requests. - * Logged from - * frameworks/av/media/libnblog/ReportPerformance.cpp - * frameworks/av/services/mediaanalytics/statsd_audiothread.cpp - */ -message MediametricsAudiothreadReported { - optional int64 timestamp_nanos = 1; - optional string package_name = 2; - optional int64 package_version_code = 3; - optional int64 media_apex_version = 4; - - optional android.stats.mediametrics.AudioThreadData audiothread_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES]; -} - -/** - * Track how we arbitrate between microphone/input requests. - * Logged from - * frameworks/av/media/libaudioclient/AudioTrack.cpp - * frameworks/av/services/mediaanalytics/statsd_audiotrack.cpp - */ -message MediametricsAudiotrackReported { - optional int64 timestamp_nanos = 1; - optional string package_name = 2; - optional int64 package_version_code = 3; - optional int64 media_apex_version = 4; - - optional android.stats.mediametrics.AudioTrackData audiotrack_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES]; -} - -/** - * Track information about DRM framework performance - * Logged from - * frameworks/av/drm/libmediadrm/DrmHal.cpp - * frameworks/av/services/mediaanalytics/statsd_drm.cpp - */ -message MediametricsMediadrmReported { - optional int64 timestamp_nanos = 1; - optional string package_name = 2; - optional int64 package_version_code = 3; - optional int64 media_apex_version = 4; - - // vendor+description tell about which DRM plugin is in use on this device - optional string vendor = 5; - optional string description = 6; - // from frameworks/av/drm/libmediadrm/protos/metrics.proto - optional bytes framework_stats = 7 [(android.os.statsd.log_mode) = MODE_BYTES]; -} - -/** - * Track information about the widevine DRM plugin performance - * Logged from - * vendor/widevine/libwvdrmengine/cdm/metrics - * frameworks/av/services/mediaanalytics/statsd_drm.cpp - */ -message MediametricsDrmWidevineReported { - optional int64 timestamp_nanos = 1; - optional string package_name = 2; - optional int64 package_version_code = 3; - optional int64 media_apex_version = 4; - - optional bytes vendor_specific_stats = 5 [(android.os.statsd.log_mode) = MODE_BYTES]; -} - -/** - * Track information about recordings (e.g. camcorder) - * Logged from - * frameworks/av/media/libmediaplayerservice/StagefrightRecorder.cpp - * frameworks/av/services/mediaanalytics/statsd_recorder.cpp - */ -message MediametricsRecorderReported { - optional int64 timestamp_nanos = 1; - optional string package_name = 2; - optional int64 package_version_code = 3; - optional int64 media_apex_version = 4; - - optional android.stats.mediametrics.RecorderData recorder_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES]; -} - -/** - * Track Media Player usage - * Logged from: - * frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp - * frameworks/av/services/mediaanalytics/statsd_nuplayer.cpp - */ -message MediametricsNuPlayerReported { - optional int64 timestamp_nanos = 1; - optional string package_name = 2; - optional int64 package_version_code = 3; - optional int64 media_apex_version = 4; - - optional android.stats.mediametrics.NuPlayerData nuplayer_data = 5 [(android.os.statsd.log_mode) = MODE_BYTES]; -} - -/** - * Track Legacy DRM usage - * Logged from - * frameworks/av/drm/drmserver/DrmManager.cpp - */ -message MediametricsDrmManagerReported { - optional int64 timestamp_nanos = 1; - optional string package_name = 2; - optional int64 package_version_code = 3; - optional int64 media_apex_version = 4; - - enum Method { - METHOD_NOT_FOUND = -1; - GET_CONSTRAINTS = 0; - GET_METADATA = 1; - CAN_HANDLE = 2; - PROCESS_DRM_INFO = 3; - ACQUIRE_DRM_INFO = 4; - SAVE_RIGHTS = 5; - GET_ORIGINAL_MIME_TYPE = 6; - GET_DRM_OBJECT_TYPE = 7; - CHECK_RIGHTS_STATUS = 8; - REMOVE_RIGHTS = 9; - REMOVE_ALL_RIGHTS = 10; - OPEN_CONVERT_SESSION = 11; - OPEN_DECRYPT_SESSION = 12; - } - - // plugin_id+description inform which Legacy DRM plugins are still in use on device - optional string plugin_id = 5; - optional string description = 6; - optional Method method = 7; - optional string mime_types = 8; - - optional int64 get_constraints_count = 9; - optional int64 get_metadata_count = 10; - optional int64 can_handle_count = 11; - optional int64 process_drm_info_count = 12; - optional int64 acquire_drm_info_count = 13; - optional int64 save_rights_count = 14; - optional int64 get_original_mime_type_count = 15; - optional int64 get_drm_object_type_count = 16; - optional int64 check_rights_status_count = 17; - optional int64 remove_rights_count = 18; - optional int64 remove_all_rights_count = 19; - optional int64 open_convert_session_count = 20; - optional int64 open_decrypt_session_count = 21; -} - -/** - * State of a dangerous permission requested by a package - * Pulled from: StatsCompanionService -*/ -message DangerousPermissionState { - // Name of the permission - optional string permission_name = 1; - - // Uid of the package - optional int32 uid = 2 [(is_uid) = true]; - - // Package requesting the permission - optional string package_name = 3; - - // If the permission is granted to the uid - optional bool is_granted = 4; - - // Permission flags as per android.content.pm.PermissionFlags - optional int32 permission_flags = 5; -} - -/** - * Logs when a package is denied access to a device identifier based on the new access requirements. - * - * Logged from: - * frameworks/base/telephony/java/com/android/internal/telephony/TelephonyPermissions.java - */ -message DeviceIdentifierAccessDenied { - // The name of the package denied access to the requested device identifier. - optional string package_name = 1; - - // The name of the device identifier method the package attempted to invoke. - optional string method_name = 2; - - // True if the package is preinstalled. - // Starting from Android 11, this boolean is not set and will always be false. - optional bool is_preinstalled = 3 [deprecated = true]; - - // True if the package is privileged. - // Starting from Android 11, this boolean is not set and will always be false. - optional bool is_priv_app = 4 [deprecated = true]; -} - -/** - * Pulls the ongoing mainline install train version code. - * Pulled from StatsCompanionService - */ -message TrainInfo { - optional int64 train_version_code = 1; - - optional TrainExperimentIds train_experiment_id = 2 [(log_mode) = MODE_BYTES]; - - optional string train_name = 3; - - enum Status { - UNKNOWN = 0; - INSTALL_REQUESTED = 1; - INSTALL_STARTED = 2; - INSTALL_STAGED_NOT_READY = 3; - INSTALL_STAGED_READY = 4; - INSTALL_SUCCESS = 5; - // Replaced by INSTALL_FAILURE_DOWNLOAD, INSTALL_FAILURE_STATE_MISMATCH, - // and INSTALL_FAILURE_COMMIT. - INSTALL_FAILURE = 6 [deprecated = true]; - // This enum is for installs that are manually cancelled via the Manual Update UI. - INSTALL_CANCELLED = 7; - INSTALLER_ROLLBACK_REQUESTED = 8; - INSTALLER_ROLLBACK_INITIATED = 9; - INSTALLER_ROLLBACK_INITIATED_FAILURE = 10; - INSTALLER_ROLLBACK_STAGED = 11; - INSTALLER_ROLLBACK_STAGED_FAILURE = 12; - INSTALLER_ROLLBACK_BOOT_TRIGGERED = 13; - INSTALLER_ROLLBACK_BOOT_TRIGGERED_FAILURE = 14; - INSTALLER_ROLLBACK_SUCCESS = 15; - INSTALLER_ROLLBACK_FAILURE = 16; - INSTALLER_ROLLBACK_STAGED_CANCEL_REQUESTED = 17; - INSTALLER_ROLLBACK_STAGED_CANCEL_SUCCESS = 18; - INSTALLER_ROLLBACK_STAGED_CANCEL_FAILURE = 19; - INSTALL_STAGED_CANCEL_REQUESTED = 20; - INSTALL_STAGED_CANCEL_SUCCESS = 21; - INSTALL_STAGED_CANCEL_FAILURE = 22; - INSTALL_FAILURE_DOWNLOAD = 23; - INSTALL_FAILURE_STATE_MISMATCH = 24; - INSTALL_FAILURE_COMMIT = 25; - REBOOT_TRIGGERED = 26; - } - optional Status status = 4; -} - -/** - * Logs the gesture stage changed event. - * - * Logged from: - * frameworks/base/packages/SystemUI/ - */ -message AssistGestureStageReported { - optional android.hardware.sensor.assist.AssistGestureStageEnum gesture_stage = 1; -} - -/** - * Logs the feedback type. - * - * Logged from: - * frameworks/base/packages/SystemUI/ - */ -message AssistGestureFeedbackReported { - // Whether or not the gesture was used. - optional android.hardware.sensor.assist.AssistGestureFeedbackEnum feedback_type = 1; -} - -/** - * Logs the progress. - * - * Logged from: - * frameworks/base/packages/SystemUI/ - */ -message AssistGestureProgressReported { - // [0,100] progress for the assist gesture. - optional int32 progress = 1; -} - -/* - * Information about the time zone data on a device. - */ -message TimeZoneDataInfo { - // A version identifier for the data set on device. e.g. "2018i" - optional string tzdb_version = 1; -} - -/** - * Logs the GPU stats global health information. - * - * Logged from: - * frameworks/native/services/gpuservice/gpustats/ - */ -message GpuStatsGlobalInfo { - // Package name of the gpu driver. - optional string driver_package_name = 1; - - // Version name of the gpu driver. - optional string driver_version_name = 2; - - // Version code of the gpu driver. - optional int64 driver_version_code = 3; - - // Build time of the gpu driver in UTC as seconds since January 1, 1970. - optional int64 driver_build_time = 4; - - // Total count of the gl driver gets loaded. - optional int64 gl_loading_count = 5; - - // Total count of the gl driver fails to be loaded. - optional int64 gl_loading_failure_count = 6; - - // Total count of the Vulkan driver gets loaded. - optional int64 vk_loading_count = 7; - - // Total count of the Vulkan driver fails to be loaded. - optional int64 vk_loading_failure_count = 8; - - // Api version of the system Vulkan driver. - optional int32 vulkan_version = 9; - - // Api version of the system CPU Vulkan driver. - optional int32 cpu_vulkan_version = 10; - - // Api version of the system GLES driver. - optional int32 gles_version = 11; - - // Total count of the angle driver gets loaded. - optional int64 angle_loading_count = 12; - - // Total count of the angle driver fails to be loaded. - optional int64 angle_loading_failure_count = 13; -} - -/** - * GPU driver loading time info. - */ -message GpuDriverLoadingTime { - // List of all the driver loading times for this app. The list size is - // capped at 50. - repeated int64 driver_loading_time = 1; -} - -/** - * Logs the GPU stats per app health information. - * - * Logged from: - * frameworks/native/services/gpuservice/gpustats/ - */ -message GpuStatsAppInfo { - // Package name of the application that loads the gpu driver. Total number - // of different packages is capped at 100. - optional string app_package_name = 1; - - // Version code of the gpu driver this app loads. - optional int64 driver_version_code = 2; - - // gl driver loading time info. - optional GpuDriverLoadingTime gl_driver_loading_time = 3 - [(android.os.statsd.log_mode) = MODE_BYTES]; - - // Vulkan driver loading time info. - optional GpuDriverLoadingTime vk_driver_loading_time = 4 - [(android.os.statsd.log_mode) = MODE_BYTES]; - - // Angle driver loading time info. - optional GpuDriverLoadingTime angle_driver_loading_time = 5 - [(android.os.statsd.log_mode) = MODE_BYTES]; - - // CPU Vulkan implementation is in use. - optional bool cpu_vulkan_in_use = 6; - - // App is not doing pre-rotation correctly. - optional bool false_prerotation = 7; - - // App creates GLESv1 context. - optional bool gles_1_in_use = 8; -} - -/* - * Logs the size of the system ion heap. - * - * Pulled from StatsCompanionService. - */ -message SystemIonHeapSize { - // Deprecated due to limited support of ion stats in debugfs. - // Use `IonHeapSize` instead. - option deprecated = true; - - // Size of the system ion heap in bytes. - // Read from debugfs. - optional int64 size_in_bytes = 1; -} - -/* - * Logs the total size of the ion heap. - * - * Pulled from StatsCompanionService. - */ -message IonHeapSize { - // Total size of all ion heaps in kilobytes. - // Read from: /sys/kernel/ion/total_heaps_kb. - optional int32 total_size_kb = 1; -} - -/* - * Logs the per-process size of the system ion heap. - * - * Pulled from StatsCompanionService. - */ -message ProcessSystemIonHeapSize { - // The uid if available. -1 means not available. - optional int32 uid = 1 [(is_uid) = true]; - - // The process name (from /proc/PID/cmdline). - optional string process_name = 2; - - // Sum of sizes of all allocations. - optional int32 total_size_in_kilobytes = 3; - - // Number of allocations. - optional int32 allocation_count = 4; - - // Size of the largest allocation. - optional int32 max_size_in_kilobytes = 5; -} - -/** - * Push network stack events. - * - * Log from: - * frameworks/base/packages/NetworkStack/ - */ -message NetworkStackReported { - // The id that indicates the event reported from NetworkStack. - optional int32 event_id = 1; - // The data for the reported events. - optional android.stats.connectivity.NetworkStackEventData network_stack_event = 2 [(log_mode) = MODE_BYTES]; -} - -/** - * Logs the apps that are installed on the external storage. - * Pulled from: - * StatsCompanionService - */ -message AppsOnExternalStorageInfo { - // The type of the external storage. - optional android.stats.storage.ExternalStorageType external_storage_type = 1; - // The name of the package that is installed on the external storage. - optional string package_name = 2; -} - -/** - * Logs the settings related to Face. - * Logged from: - * frameworks/base/services/core/java/com/android/server/stats - */ -message FaceSettings { - // Whether or not face unlock is allowed on Keyguard. - optional bool unlock_keyguard_enabled = 1; - // Whether or not face unlock dismisses the Keyguard. - optional bool unlock_dismisses_keyguard = 2; - // Whether or not face unlock requires attention. - optional bool unlock_attention_required = 3; - // Whether or not face unlock is allowed for apps (through BiometricPrompt). - optional bool unlock_app_enabled = 4; - // Whether or not face unlock always requires user confirmation. - optional bool unlock_always_require_confirmation = 5; - // Whether or not a diverse set of poses are required during enrollment. - optional bool unlock_diversity_required = 6; -} - -/** - * Logs cooling devices maintained by the kernel. - * - * Pulled from StatsCompanionService.java - */ -message CoolingDevice { - // The type of cooling device being reported. Eg. CPU, GPU... - optional android.os.CoolingTypeEnum device_location = 1; - // The name of the cooling device source. Eg. CPU0 - optional string device_name = 2; - // Current throttle state of the cooling device. The value can any unsigned - // integer between 0 and max_state defined in its driver. 0 means device is - // not in throttling, higher value means deeper throttling. - optional int32 state = 3; -} - -/** - * Intelligence has several counter-type events that don't warrant a - * full separate atom. These are primarily API call counters but also include - * counters for feature usage and specific failure modes. - * - * Logged from the Intelligence mainline module. - */ -message IntelligenceEventReported { - // The event type. - optional android.stats.intelligence.EventType event_id = 1; - // Success, failure. - optional android.stats.intelligence.Status status = 2; - // How many times the event occured (to report a batch of high frequency events). - optional int32 count = 3; - // How long the event took (sum of durations if count > 1) - optional int64 duration_millis = 4; -} - -/** - * Logs when Car Power state changed. - * - * Logged from: - * packages/services/Car/service/src/com/android/car/CarStatsLog.java - */ -message CarPowerStateChanged { - // States come from CpmsState in CarPowerManagementService.java. - enum State { - WAIT_FOR_VHAL = 0; - ON = 1; - SHUTDOWN_PREPARE = 2; - WAIT_FOR_FINISH = 3; - SUSPEND = 4; - SIMULATE_SLEEP = 5; - } - optional State state = 1; -} - -/** - * Logs when Car User Hal is requested to switch/create/remove user. - * - * Logged from: - * packages/services/Car/service/src/com/android/car/hal/UserHalService.java - */ -message CarUserHalModifyUserRequestReported { - // Request id for the request. - optional int32 request_id = 1; - // Request type. - enum RequestType { - UNKNOWN = 0; - // Car user manager requested user switch. - SWITCH_REQUEST_ANDROID = 1; - // OEM requested User switch. - SWITCH_REQUEST_OEM = 2; - // Hal switch requested after android switch using activity manager. - SWITCH_REQUEST_LEGACY = 3; - // Create User - CREATE_REQUEST = 4; - // Remove User - REMOVE_REQUEST = 5; - } - optional RequestType request_type = 2; - // Android User id of the current user which can only be 0, 10, 11 and so on. - // -1 if not available. - optional int32 user_id = 3; - // VHAL flags of the current user. (-1 if not available) - optional int32 user_flags = 4; - // Android User id of the target user for switch/create/remove. It can only - // be 0, 10, 11 and so on. -1 if not available. - optional int32 target_user_id = 5; - // VHAL flags of the target user for switch/create/remove. (-1 if not available) - optional int32 target_user_flags = 6; - // Request timeout Milliseconds (-1 if not available) - optional int32 timeout_millis = 7; -} - -/** - * Logs when Car User Hal responds to switch/create user request. - * - * Logged from: - * packages/services/Car/service/src/com/android/car/hal/UserHalService.java - */ -message CarUserHalModifyUserResponseReported { - // Request id of the request associated with the response. - optional int32 request_id = 1; - // Car user hal callback status. - enum CallbackStatus { - UNKNOWN = 0; - // Hal response was invalid. - INVALID = 1; - // Hal response was ok. - OK = 2; - // Hal timeout during set call. - HAL_SET_TIMEOUT = 3; - // Hal response timeout. - HAL_RESPONSE_TIMEOUT = 4; - // Hal responded with wrong info. - WRONG_HAL_RESPONSE = 5; - // Hal is processing multiple requests simultaneously. - CONCURRENT_OPERATION = 6; - } - optional CallbackStatus callback_status = 2; - - // Hal request status for user switch/create/remove. - enum HalRequestStatus { - UNSPECIFIED = 0; - // Hal request for user switch/create is successful. - SUCCESS = 1; - // Hal request for user switch/create failed. - FAILURE = 2; - } - optional HalRequestStatus request_status = 3; -} - -/** - * Logs when post switch response is posted to Car User Hal. - * - * Logged from: - * packages/services/Car/service/src/com/android/car/hal/UserHalService.java - */ -message CarUserHalPostSwitchResponseReported { - // Request id. - optional int32 request_id = 1; - - // Android user switch status. - enum UserSwitchStatus { - UNKNOWN = 0; - // Android user switch is successful. - SUCCESS = 1; - // Android user switch failed. - FAILURE = 2; - } - optional UserSwitchStatus switch_status = 2; -} - -/** - * Logs when initial user information is requested from Car User Hal. - * - * Logged from: - * packages/services/Car/service/src/com/android/car/hal/UserHalService.java - */ -message CarUserHalInitialUserInfoRequestReported { - // Request id for the request. - optional int32 request_id = 1; - - // Request type for initial user information. - enum InitialUserInfoRequestType { - UNKNOWN = 0; - // At the first time Android was booted (or after a factory reset). - FIRST_BOOT = 1; - // At the first time Android was booted after the system was updated. - FIRST_BOOT_AFTER_OTA = 2; - // When Android was booted "from scratch". - COLD_BOOT = 3; - // When Android was resumed after the system was suspended to memory. - RESUME = 4; - } - optional InitialUserInfoRequestType request_type = 2; - // Request timeout Milliseconds (-1 if not available) - optional int32 timeout_millis = 3; -} - -/** - * Logs when Car User Hal responds to initial user information requests. - * - * Logged from: - * packages/services/Car/service/src/com/android/car/hal/UserHalService.java - */ -message CarUserHalInitialUserInfoResponseReported { - // Request id of the request associated with the response. - optional int32 request_id = 1; - // Car user hal callback status. - enum CallbackStatus { - UNKNOWN = 0; - // Hal response was invalid. - INVALID = 1; - // Hal response was ok. - OK = 2; - // Hal timeout during set call. - HAL_SET_TIMEOUT = 3; - // Hal response timeout. - HAL_RESPONSE_TIMEOUT = 4; - // Hal responded with wrong info. - WRONG_HAL_RESPONSE = 5; - // Hal is processing multiple requests simultaneously. - CONCURRENT_OPERATION = 6; - } - optional CallbackStatus callback_status = 2; - // Response for initial user information request. - enum InitialUserInfoResponseAction { - UNSPECIFIED = 0; - // Let the Android System decide what to do. - DEFAULT = 1; - // Switch to an existing Android user. - SWITCH = 2; - // Create a new Android user (and switch to it). - CREATE = 3; - } - optional InitialUserInfoResponseAction response_action = 3; - // Android User id of the target user which can only be 0, 10, 11 and so on. - // -1 if not available. - optional int32 target_user = 4; - // VHAL flags of the current user. (-1 if not available) - optional int32 target_user_flags = 5; - // User locales - optional string user_locales = 6; -} - -/** - * Logs when set user association is requested from Car User Hal. - * - * Logged from: - * packages/services/Car/service/src/com/android/car/hal/UserHalService.java - */ -message CarUserHalUserAssociationRequestReported { - // Request id for the request. - optional int32 request_id = 1; - // Request type. - enum RequestType { - UNKNOWN = 0; - // For setting user association information. - SET = 1; - // For getting user association information. - GET = 2; - } - optional RequestType request_type = 2; - // Android User id of the current user which can only be 0, 10, 11 and so on. - // -1 if not available. - optional int32 current_user_id = 3; - // VHAL flags of the current user. (-1 if not available) - optional int32 current_user_flags = 4; - // Number of the set associations requested. - optional int32 number_associations = 5; - // Concatenated string for the types from set associations request. - // This is a string converted from an array of integers. - optional string user_identification_association_types = 6; - // Concatenated string for the values from set associations request. - // This is a string converted from an array of integers. - optional string user_identification_association_values = 7; -} - -/** - * Logs when Car User Hal responds to set user association requests. - * - * Logged from: - * packages/services/Car/service/src/com/android/car/hal/UserHalService.java - */ -message CarUserHalSetUserAssociationResponseReported { - // Request id of the request associated with the response. - optional int32 request_id = 1; - // Car user hal callback status. - enum CallbackStatus { - UNKNOWN = 0; - // Hal response was invalid. - INVALID = 1; - // Hal response was ok. - OK = 2; - // Hal timeout during set call. - HAL_SET_TIMEOUT = 3; - // Hal response timeout. - HAL_RESPONSE_TIMEOUT = 4; - // Hal responded with wrong info. - WRONG_HAL_RESPONSE = 5; - // Hal is processing multiple requests simultaneously. - CONCURRENT_OPERATION = 6; - } - optional CallbackStatus callback_status = 2; - // Number of the set associations in the response. - optional int32 number_associations = 3; - // Concatenated string for the types from set associations request. - // This is a string converted from an array of integers. - optional string user_identification_association_types = 4; - // Concatenated string for the values from set associations request. - // This is a string converted from an array of integers. - optional string user_identification_association_values = 5; -} - -/** - * Logs whether GarageMode is entered. - * - * Logged from: - * packages/services/Car/service/src/com/android/car/CarStatsLog.java - */ -message GarageModeInfo { - // Whether GarageMode is entered. - optional bool is_garage_mode = 1; -} - -/** - * Historical app ops data per package. - */ -message AppOps { - // Uid of the package requesting the op - optional int32 uid = 1 [(is_uid) = true]; - - // Name of the package performing the op - optional string package_name = 2; - - // operation id - optional android.app.AppOpEnum op_id = 3 [default = APP_OP_NONE]; - - // The number of times the op was granted while the app was in the - // foreground (only for trusted requests) - optional int64 trusted_foreground_granted_count = 4; - - // The number of times the op was granted while the app was in the - // background (only for trusted requests) - optional int64 trusted_background_granted_count = 5; - - // The number of times the op was rejected while the app was in the - // foreground (only for trusted requests) - optional int64 trusted_foreground_rejected_count = 6; - - // The number of times the op was rejected while the app was in the - // background (only for trusted requests) - optional int64 trusted_background_rejected_count = 7; - - // For long-running operations, total duration of the operation - // while the app was in the foreground (only for trusted requests) - optional int64 trusted_foreground_duration_millis = 8; - - // For long-running operations, total duration of the operation - // while the app was in the background (only for trusted requests) - optional int64 trusted_background_duration_millis = 9; - - // Whether AppOps is guarded by Runtime permission - optional bool is_runtime_permission = 10; -} - -/** - * Historical app ops data per package and attribution tag. - */ -message AttributedAppOps { - // Uid of the package requesting the op - optional int32 uid = 1 [(is_uid) = true]; - - // Name of the package performing the op - optional string package_name = 2; - - // tag; provided by developer when accessing related API, limited at 50 chars by API. - // Attributions must be provided through manifest using <attribution> tag available in R and - // above. - optional string tag = 3; - - // operation id - optional android.app.AppOpEnum op = 4 [default = APP_OP_NONE]; - - // The number of times the op was granted while the app was in the - // foreground (only for trusted requests) - optional int64 trusted_foreground_granted_count = 5; - - // The number of times the op was granted while the app was in the - // background (only for trusted requests) - optional int64 trusted_background_granted_count = 6; - - // The number of times the op was rejected while the app was in the - // foreground (only for trusted requests) - optional int64 trusted_foreground_rejected_count = 7; - - // The number of times the op was rejected while the app was in the - // background (only for trusted requests) - optional int64 trusted_background_rejected_count = 8; - - // For long-running operations, total duration of the operation - // while the app was in the foreground (only for trusted requests) - optional int64 trusted_foreground_duration_millis = 9; - - // For long-running operations, total duration of the operation - // while the app was in the background (only for trusted requests) - optional int64 trusted_background_duration_millis = 10; - - // Whether AppOps is guarded by Runtime permission - optional bool is_runtime_permission = 11; - - // Sampling rate used on device, from 0 to 100 - optional int32 sampling_rate = 12; -} - -/** - * Location Manager API Usage information(e.g. API under usage, - * API call's parameters). - * Logged from: - * frameworks/base/services/core/java/com/android/server/LocationManagerService.java - */ -message LocationManagerApiUsageReported { - - // Indicating if usage starts or usage ends. - optional android.stats.location.UsageState state = 1; - - // LocationManagerService's API in use. - // We can identify which API from LocationManager is - // invoking current LMS API by the combination of - // API parameter(e.g. is_listener_null, is_intent_null, - // is_location_request_null) - optional android.stats.location.LocationManagerServiceApi api_in_use = 2; - - // Name of the package calling the API. - optional string calling_package_name = 3; - - // Type of the location provider. - optional android.stats.location.ProviderType provider = 4; - - // Quality of the location request - optional android.stats.location.LocationRequestQuality quality = 5; - - // The desired interval for active location updates, in milliseconds. - // Bucketized to reduce cardinality. - optional android.stats.location.LocationRequestIntervalBucket bucketized_interval = 6; - - // Minimum distance between location updates, in meters. - // Bucketized to reduce cardinality. - optional android.stats.location.SmallestDisplacementBucket - bucketized_smallest_displacement = 7; - - // The number of location updates. - optional int64 num_updates = 8; - - // The request expiration time, in millisecond since boot. - // Bucketized to reduce cardinality. - optional android.stats.location.ExpirationBucket - bucketized_expire_in = 9; - - // Type of Callback passed in for this API. - optional android.stats.location.CallbackType callback_type = 10; - - // The radius of the central point of the alert - // region, in meters. Only for API REQUEST_GEOFENCE. - // Bucketized to reduce cardinality. - optional android.stats.location.GeofenceRadiusBucket bucketized_radius = 11; - - // Activity Importance of API caller. - // Categorized to 3 types that are interesting from location's perspective. - optional android.stats.location.ActivityImportance activiy_importance = 12; -} - -/** - * Information about a permission grant or denial made by user inside ReviewPermissionsFragment - */ -message ReviewPermissionsFragmentResultReported { - // unique value identifying a permission group change. A permission group change might result - // in multiple of these atoms - optional int64 change_id = 1; - - // UID of package the permission belongs to - optional int32 uid = 2 [(is_uid) = true]; - - // Name of package the permission belongs to - optional string package_name = 3; - - // The permission to be granted - optional string permission_name = 4; - - // The result of the permission grant - optional bool permission_granted = 5; -} - -/** -* Information about results of permission upgrade by RuntimePermissionsUpgradeController -* Logged from: RuntimePermissionUpdgradeController -*/ -message RuntimePermissionsUpgradeResult { - // Permission granted as result of upgrade - optional string permission_name = 1; - - // UID of package granted permission - optional int32 uid = 2 [(is_uid) = true]; - - // Name of package granted permission - optional string package_name = 3; -} - -/** -* Information about a buttons presented in GrantPermissionsActivty and choice made by user -*/ -message GrantPermissionsActivityButtonActions { - // Permission granted as result of upgrade - optional string permission_group_name = 1; - - // UID of package granted permission - optional int32 uid = 2 [(is_uid) = true]; - - // Name of package requesting permission - optional string package_name = 3; - - // Buttons presented in the dialog - bit flags, bit numbers are in accordance with - // LABEL_ constants in GrantPermissionActivity.java - optional int32 buttons_presented = 4; - - // Button clicked by user - same as bit flags in buttons_presented with only single bit set - optional int32 button_clicked = 5; - - // id which identifies single session of user interacting with permission controller - optional int64 session_id = 6; -} - -/** - * Information about LocationAccessCheck notification presented to user - */ -message LocationAccessCheckNotificationAction { - - // id which identifies single session of user interacting with permission controller - optional int64 session_id = 1; - - // Uid of package for which location access check is presented - optional int32 package_uid = 2; - - // Name of package for which location access check is presented - optional string package_name = 3; - - enum Result { - UNDEFINED = 0; - // notification was presented to the user - NOTIFICATION_PRESENTED = 1; - // notification was declined by the user - NOTIFICATION_DECLINED = 2; - // notification was clicked by the user - NOTIFICATION_CLICKED = 3; - } - - // View / interaction recorded - optional Result result = 4; -} - -/** - * Information about a permission grant or revoke made by user inside AppPermissionFragment - */ -message AppPermissionFragmentActionReported { - // id which identifies single session of user interacting with permission controller - optional int64 session_id = 1; - - // unique value identifying a permission group change. A permission group change might result - // in multiple of these atoms - optional int64 change_id = 2; - - // UID of package the permission belongs to - optional int32 uid = 3 [(is_uid) = true]; - - // Name of package the permission belongs to - optional string package_name = 4; - - // The permission to be granted - optional string permission_name = 5; - - // The result of the permission grant - optional bool permission_granted = 6; - - // State of Permission Flags after grant as per android.content.pm.PermissionFlags - optional int32 permission_flags = 7; - - enum Button { - UNDEFINED = 0; - // Allow button - ALLOW = 1; - // Deny button - DENY = 2; - // Ask every time button - ASK_EVERY_TIME = 3; - // Allow all the time button - ALLOW_ALWAYS = 4; - // Allow only while using the app button - ALLOW_FOREGROUND = 5; - // Same is Deny button but shown in while in use dialog - DENY_FOREGROUND = 6; - } - - // Button pressed in the dialog - optional Button button_pressed = 8; -} - -/** -* Information about a AppPermissionFragment viewed by user -*/ -message AppPermissionFragmentViewed { - // id which identifies single session of user interacting with permission controller - optional int64 session_id = 1; - - // UID of package for which permissions are viewed - optional int32 uid = 2 [(is_uid) = true]; - - // Name of package for which permissions are viewed - optional string package_name = 3; - - // Permission group viewed - optional string permission_group_name = 4; -} - -/** -* Information about a AppPermissionGroupsFragment viewed by user. Fragment has been renamed, but -* the log retains the old fragment name. -*/ -message AppPermissionsFragmentViewed { - // id which identifies single session of user interacting with permission controller - optional int64 session_id = 1; - - // id which identifies single view as every view might have several logging records - // with different package information attached - optional int64 view_id = 2; - - // Permission group viewed - optional string permission_group_name = 3; - - // UID of package for which permissions are viewed - optional int32 uid = 4 [(is_uid) = true]; - - // Name of package for which permissions are viewed - optional string package_name = 5; - - // Category in which permission is included - enum Category { - UNDEFINED = 0; - ALLOWED = 1; - ALLOWED_FOREGROUND = 2; - DENIED = 3; - } - optional Category category = 6; -} -/** -* Information about a PermissionAppsFragment viewed by user. -* Logged from ui/handheld/PermissionAppsFragment.java -*/ -message PermissionAppsFragmentViewed { - // id which identifies single session of user interacting with permission controller - optional int64 session_id = 1; - - // id which identifies single view as every view might have several logging records - // with different package information attached - optional int64 view_id = 2; - - // Permission group viewed - optional string permission_group_name = 3; - - // UID of package for which permissions are viewed - optional int32 uid = 4 [(is_uid) = true]; - - // Name of package for which permissions are viewed - optional string package_name = 5; - - // Category in which app is included - enum Category { - UNDEFINED = 0; - ALLOWED = 1; - ALLOWED_FOREGROUND = 2; - DENIED = 3; - } - optional Category category = 6; -} - -/** -* Log that the Auto Revoke notification has been clicked -* Logged from ui/ManagePermissionsActivity -*/ -message AutoRevokeNotificationClicked { - // id which identifies single session of user interacting with permission controller - optional int64 session_id = 1; -} - -/** -* Log that an app has been displayed on the auto revoke page, and lists one permission that was -* auto revoked for it. -* Logged from ui/handheld/AutoRevokeFragment -*/ -message AutoRevokeFragmentAppViewed { - // id which identifies single session of user interacting with permission controller - optional int64 session_id = 1; - - // UID of package for which permissions are viewed - optional int32 uid = 2 [(is_uid) = true]; - - // Name of package for which permissions are viewed - optional string package_name = 3; - - // The name of a permission group that has been revoked - optional string permission_group_name = 4; - - // The age of the app- more than three months old, or more than six months - enum Age { - UNDEFINED = 0; - NEWER_BUCKET = 1; - OLDER_BUCKET = 2; - } - - // How long the app has been unused. Currently, newer bucket is 3 months, older is 6 months - optional Age age = 5; -} - -/** -* Log that the user has interacted with an app on the auto revoke fragment -* Logged from ui/handheld/AutoRevokeFragment -*/ -message AutoRevokedAppInteraction { - // id which identifies single session of user interacting with permission controller - optional int64 session_id = 1; - - // UID of package for which permissions are viewed - optional int32 uid = 2 [(is_uid) = true]; - - // Name of package for which permissions are viewed - optional string package_name = 3; - - enum Action { - UNDEFINED = 0; - REMOVE = 1; - OPEN = 2; - APP_INFO = 3; - PERMISSIONS = 4; - REMOVE_IN_SETTINGS = 5; - OPEN_IN_SETTINGS = 6; - } - - // The action the user took to interact with the app - optional Action action = 4; -} - -/** -* Log that the AppPermissionGroupsFragment has been interacted with for the possible purposes of -* auto revoke, or that the auto revoke switch has been changed -* Logged from ui/handheld/AppPermissionGroupsFragment - */ -message AppPermissionGroupsFragmentAutoRevokeAction { - // id which identifies single session of user interacting with permission controller - optional int64 session_id = 1; - - // UID of package for which permissions are viewed - optional int32 uid = 2 [(is_uid) = true]; - - // Name of package for which permissions are viewed - optional string package_name = 3; - - enum Action { - UNDEFINED = 0; - OPENED_FOR_AUTO_REVOKE = 1; - OPENED_FROM_INTENT = 2; - SWITCH_ENABLED = 3; - SWITCH_DISABLED = 4; - } - - // The action the user took to interact with the fragment - optional Action action = 4; -} - -/** - * Logs when there is a smart selection related event. - * See frameworks/base/core/java/android/view/textclassifier/TextClassifierEvent.java - * Logged from: TextClassifierEventLogger.java - */ -message TextSelectionEvent { - // A session ID. - optional string session_id = 1; - - // Event type of this event. - optional android.stats.textclassifier.EventType event_type = 2; - - // Name of the annotator model that is involved in this event. - optional string model_name = 3; - - // Type of widget that was involved in triggering this event. - optional android.stats.textclassifier.WidgetType widget_type = 4; - - // Index of this event in a session. - optional int32 event_index = 5; - - // Entity type that is involved. - optional string entity_type = 6; - - // Relative word index of the start of the selection. - optional int32 relative_word_start_index = 7; - - // Relative word (exclusive) index of the end of the selection. - optional int32 relative_word_end_index = 8; - - // Relative word index of the start of the smart selection. - optional int32 relative_suggested_word_start_index = 9; - - // Relative word (exclusive) index of the end of the smart selection. - optional int32 relative_suggested_word_end_index = 10; - - // Name of source package. - optional string package_name = 11; - - // Name of the LangID model that is involved in this event. - optional string langid_model_name = 12; -} - -/** - * Logs when there is a smart linkify related event. - * See frameworks/base/core/java/android/view/textclassifier/TextClassifierEvent.java - * Logged from: TextClassifierEventLogger.java - */ -message TextLinkifyEvent { - // A session ID. - optional string session_id = 1; - - // Event type of this event. - optional android.stats.textclassifier.EventType event_type = 2; - - // Name of the annotator model that is involved in this event. - optional string model_name = 3; - - // Type of widget that was involved in triggering this event. - optional android.stats.textclassifier.WidgetType widget_type = 4; - - // Index of this event in a session. - optional int32 event_index = 5; - - // Entity type that is involved. - optional string entity_type = 6; - - // Number of links detected. - optional int32 num_links = 7; - - // The total length of all links. - optional int32 linked_text_length = 8; - - // Length of input text. - optional int32 text_length = 9; - - // Time spent on generating links in ms. - optional int64 latency_millis = 10; - - // Name of source package. - optional string package_name = 11; - - // Name of the LangID model that is involved in this event. - optional string langid_model_name = 12; -} - -/** - * Logs when there is a conversation actions related event. - * See frameworks/base/core/java/android/view/textclassifier/TextClassifierEvent.java - * Logged from: TextClassifierEventLogger.java - */ -message ConversationActionsEvent { - // A session ID. - optional string session_id = 1; - - // Event type of this event. - optional android.stats.textclassifier.EventType event_type = 2; - - // Name of the actions model that is involved in this event. - optional string model_name = 3; - - // Type of widget that was involved in triggering this event. - optional android.stats.textclassifier.WidgetType widget_type = 4; - - // The first entity type that is involved. - optional string first_entity_type = 5; - - // The second entity type that is involved. - optional string second_entity_type = 6; - - // The third entity type that is involved. - optional string third_entity_type = 7; - - // The score of the first entity type. - optional float score = 8; - - // Name of source package. - optional string package_name = 9; - - // Name of the annotator model that is involved in this event. - optional string annotator_model_name = 10; - - // Name of the LangID model that is involved in this event. - optional string langid_model_name = 11; -} - -/** - * Logs when there is a language detection related event. - * See frameworks/base/core/java/android/view/textclassifier/TextClassifierEvent.java - * Logged from: TextClassifierEventLogger.java - */ -message LanguageDetectionEvent { - // A session ID. - optional string session_id = 1; - - // Event type of this event. - optional android.stats.textclassifier.EventType event_type = 2; - - // Name of the language detection model that is involved in this event. - optional string model_name = 3; - - // Type of widget that was involved in triggering this event. - optional android.stats.textclassifier.WidgetType widget_type = 4; - - // Detected language. - optional string language_tag = 5; - - // Score of the detected language. - optional float score = 6; - - // Position of this action. - optional int32 action_index = 7; - - // Name of source package. - optional string package_name = 8; -} - -/** - * Information about an OTA update attempt by update_engine. - * Logged from platform/system/update_engine/metrics_reporter_android.cc - */ -message UpdateEngineUpdateAttemptReported { - // The number of attempts for the update engine to apply a given payload. - optional int32 attempt_number = 1; - - optional android.stats.otaupdate.PayloadType payload_type = 2; - - // The total time in minutes for the update engine to apply a given payload. - // The time is calculated by calling clock_gettime() / CLOCK_BOOTTIME; and - // it's increased when the system is sleeping. - optional int32 duration_boottime_in_minutes = 3; - - // The total time in minutes for the update engine to apply a given payload. - // The time is calculated by calling clock_gettime() / CLOCK_MONOTONIC_RAW; - // and it's not increased when the system is sleeping. - optional int32 duration_monotonic_in_minutes = 4; - - // The size of the payload in MiBs. - optional int32 payload_size_mib = 5; - - // The attempt result reported by the update engine for an OTA update. - optional android.stats.otaupdate.AttemptResult attempt_result = 6; - - // The error code reported by the update engine after an OTA update attempt - // on A/B devices. - optional android.stats.otaupdate.ErrorCode error_code = 7; - - // The build fingerprint of the source system. The value is read from a - // system property when the device takes the update. e.g. - // Android/aosp_sailfish/sailfish:10/QP1A.190425.004/5507117:userdebug/test-keys - optional string source_fingerprint = 8; - - // Size of super partition. - optional int64 super_partition_size_bytes = 9; - - // Size of current slot within the super partition. - optional int64 slot_size_bytes = 10; - - // Free space available in the super partition. - optional int64 super_free_space_bytes = 11; -} - -/** - * Information about all the attempts the device make before finishing the - * successful update. - * Logged from platform/system/update_engine/metrics_reporter_android.cc - */ -message UpdateEngineSuccessfulUpdateReported { - // The number of attempts for the update engine to apply the payload for a - // successful update. - optional int32 attempt_count = 1; - - optional android.stats.otaupdate.PayloadType payload_type = 2; - - optional int32 payload_size_mib = 3; - - // The total number of bytes downloaded by update_engine since the last - // successful update. - optional int32 total_bytes_downloaded_mib = 4; - - // The ratio in percentage of the over-downloaded bytes compared to the - // total bytes needed to successfully install the update. e.g. 200 if we - // download 200MiB in total for a 100MiB package. - optional int32 download_overhead_percentage = 5; - - // The total time in minutes for the update engine to apply the payload for a - // successful update. - optional int32 total_duration_minutes = 6; - - // The number of reboot of the device during a successful update. - optional int32 reboot_count = 7; -} - -/** - * Reported when the RebootEscrow HAL has attempted to recover the escrowed - * key to indicate whether it was successful or not. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/locksettings/RebootEscrowManager.java - */ -message RebootEscrowRecoveryReported { - optional bool successful = 1; -} - -/** - * Global display pipeline metrics reported by SurfaceFlinger. - * Metrics exist beginning in Android 11. - * Pulled from: - * frameworks/native/services/surfaceflinger/TimeStats/TimeStats.cpp - */ -message SurfaceflingerStatsGlobalInfo { - // Total number of frames presented during the tracing period - optional int64 total_frames = 1; - // Total number of frames missed - optional int64 missed_frames = 2; - // Total number of frames that fell back to client composition - optional int64 client_composition_frames = 3; - // Total time the display was turned on - optional int64 display_on_millis = 4; - // Total time that was spent performing animations. - // This is derived from the present-to-present layer histogram - optional int64 animation_millis = 5; - // Total number of event connections tracked by SurfaceFlinger at the time - // of this pull. If this number grows prohibitively large, then this can - // cause jank due to resource contention. - optional int32 event_connection_count = 6; - // Set of timings measured from when SurfaceFlinger began compositing a - // frame, until the frame was requested to be presented to the display. This - // measures SurfaceFlinger's total CPU walltime on the critical path per - // frame. - optional FrameTimingHistogram frame_duration = 7 - [(android.os.statsd.log_mode) = MODE_BYTES]; - // Set of timings measured from when SurfaceFlinger first began using the - // GPU to composite a frame, until the GPU has finished compositing that - // frame. This measures the total additional time SurfaceFlinger needed to - // perform due to falling back into GPU composition. - optional FrameTimingHistogram render_engine_timing = 8 - [(android.os.statsd.log_mode) = MODE_BYTES]; - // Number of frames where SF saw a frame, based on its frame timeline. - // Frame timelines may include transactions without updating buffer contents. - // Introduced in Android 12. - optional int32 total_timeline_frames = 9; - // Number of frames where SF saw a janky frame. - // Introduced in Android 12. - optional int32 total_janky_frames = 10; - // Number of janky frames where SF spent a long time on the CPU. - // Introduced in Android 12. - optional int32 total_janky_frames_with_long_cpu = 11; - // Number of janky frames where SF spent a long time on the GPU. - // Introduced in Android 12. - optional int32 total_janky_frames_with_long_gpu = 12; - // Number of janky frames where SF missed the frame deadline, but there - // was not an attributed reason (e.g., maybe HWC missed?) - // Introduced in Android 12. - optional int32 total_janky_frames_sf_unattributed = 13; - // Number of janky frames where the app missed the frame deadline, but - // there was not an attributed reason - // Introduced in Android 12. - optional int32 total_janky_frames_app_unattributed = 14; - - // Next ID: 15 -} - -/** - * Per-layer display pipeline metrics reported by SurfaceFlinger. - * Metrics exist beginning in Android 11. - * The number of layers uploaded may be restricted due to size limitations. - * Pulled from: - * frameworks/native/services/surfaceflinger/TimeStats/TimeStats.cpp - */ -message SurfaceflingerStatsLayerInfo { - // UID of the application who submitted this layer for presentation - // This is intended to be used as a dimension for surfacing rendering - // statistics to applications. - // Introduced in Android 12. - optional int32 uid = 12 [(is_uid) = true]; - // The layer for this set of metrics - // In many scenarios the package name is included in the layer name, e.g., - // layers created by Window Manager. But this is not a guarantee - in the - // general case layer names are arbitrary debug names. - optional string layer_name = 1; - // Total number of frames presented - optional int64 total_frames = 2; - // Total number of dropped frames while latching a buffer for this layer. - optional int64 dropped_frames = 3; - // Set of timings measured between successive presentation timestamps. - optional FrameTimingHistogram present_to_present = 4 - [(android.os.statsd.log_mode) = MODE_BYTES]; - // Set of timings measured from when an app queued a buffer for - // presentation, until the buffer was actually presented to the - // display. - optional FrameTimingHistogram post_to_present = 5 - [(android.os.statsd.log_mode) = MODE_BYTES]; - // Set of timings measured from when a buffer is ready to be presented, - // until the buffer was actually presented to the display. - optional FrameTimingHistogram acquire_to_present = 6 - [(android.os.statsd.log_mode) = MODE_BYTES]; - // Set of timings measured from when a buffer was latched by - // SurfaceFlinger, until the buffer was presented to the display - optional FrameTimingHistogram latch_to_present = 7 - [(android.os.statsd.log_mode) = MODE_BYTES]; - // Set of timings measured from the desired presentation to the actual - // presentation time - optional FrameTimingHistogram desired_to_present = 8 - [(android.os.statsd.log_mode) = MODE_BYTES]; - // Set of timings measured from when an app queued a buffer for - // presentation, until the buffer was ready to be presented. - optional FrameTimingHistogram post_to_acquire = 9 - [(android.os.statsd.log_mode) = MODE_BYTES]; - // Frames missed latch because the acquire fence didn't fire - optional int64 late_acquire_frames = 10; - // Frames latched early because the desired present time was bad - optional int64 bad_desired_present_frames = 11; - // Number of frames where SF saw a frame, based on its frame timeline. - // Frame timelines may include transactions without updating buffer contents. - // Introduced in Android 12. - optional int32 total_timeline_frames = 13; - // Number of frames where SF saw a janky frame. - // Introduced in Android 12. - optional int32 total_janky_frames = 14; - // Number of janky frames where SF spent a long time on the CPU. - // Introduced in Android 12. - optional int32 total_janky_frames_with_long_cpu = 15; - // Number of janky frames where SF spent a long time on the GPU. - // Introduced in Android 12. - optional int32 total_janky_frames_with_long_gpu = 16; - // Number of janky frames where SF missed the frame deadline, but there - // was not an attributed reason (e.g., maybe HWC missed?) - // Introduced in Android 12. - optional int32 total_janky_frames_sf_unattributed = 17; - // Number of janky frames where the app missed the frame deadline, but - // there was not an attributed reason - // Introduced in Android 12. - optional int32 total_janky_frames_app_unattributed = 18; - - // Next ID: 19 -} - -/** - * Histogram of frame counts bucketed by time in milliseconds. - * Because of size limitations, we hard-cap the number of buckets, with - * buckets for corresponding to larger milliseconds being less precise. - */ -message FrameTimingHistogram { - // Timings in milliseconds that describes a set of histogram buckets - repeated int32 time_millis_buckets = 1; - // Number of frames that match to each time_millis, i.e. the bucket - // contents - // It's required that len(time_millis) == len(frame_count) - repeated int64 frame_counts = 2; -} - -/** - * Janky event as reported by SurfaceFlinger. - * This event is intended to be consumed by a Perfetto subscriber for - * automated trace collection. - * - * Logged from: - * frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp - */ -message DisplayJankReported { - // Informational field for how long the janky event lasted in milliseconds - optional int64 event_duration_millis = 1; - // Number of frame deadlines missed, where SurfaceFlinger failed to update - // the display on time. - optional int32 present_deadlines_missed = 2; -} - -/** - * Information about camera facing and API level usage. - * Logged from: - * frameworks/base/services/core/java/com/android/server/camera/CameraServiceProxy.java - */ -message CameraActionEvent { - // Camera session duration - optional int64 duration_millis = 1; - - // Camera API level used - optional int32 api_level = 2; - - // Name of client package - optional string package_name = 3; - - // Camera facing - enum Facing { - UNKNOWN = 0; - BACK = 1; - FRONT = 2; - EXTERNAL = 3; - } - optional Facing facing = 4; -} - -/** - * Logs when a compatibility change is affecting an app. - * - * Logged from: - * frameworks/base/core/java/android/app/AppCompatCallbacks.java and - * frameworks/base/services/core/java/com/android/server/compat/PlatformCompat.java - */ -message AppCompatibilityChangeReported { - // The UID of the app being affected by the compatibilty change. - optional int32 uid = 1 [(is_uid) = true]; - - // The ID of the change affecting the app. - optional int64 change_id = 2; - - enum State { - UNKNOWN_STATE = 0; - ENABLED = 1; - DISABLED = 2; - LOGGED = 3; - } - - // The state of the change - if logged from gating whether it was enabled or disabled, or just - // logged otherwise. - optional State state = 3; - - enum Source { - UNKNOWN_SOURCE = 0; - APP_PROCESS = 1; - SYSTEM_SERVER = 2; - } - - // Where it was logged from. - optional Source source = 4; - -} - -/** - * Logged from - * external/perfetto/src/perfetto_cmd/perfetto_cmd.cc - */ -message PerfettoUploaded { - enum Event { - PERFETTO_UNDEFINED = 0; - PERFETTO_TRACE_BEGIN = 1; - PERFETTO_BACKGROUND_TRACE_BEGIN = 2; - PERFETTO_ON_CONNECT = 3; - PERFETTO_ON_TRACING_DISABLED = 4; - PERFETTO_UPLOAD_DROPBOX_BEGIN = 5; - PERFETTO_UPLOAD_DROPBOX_SUCCESS = 6; - PERFETTO_UPLOAD_DROPBOX_FAILURE = 7; - PERFETTO_UPLOAD_INCIDENT_BEGIN = 8; - PERFETTO_UPLOAD_INCIDENT_SUCCESS = 9; - PERFETTO_UPLOAD_INCIDENT_FAILURE = 10; - PERFETTO_FINALIZE_TRACE_AND_EXIT = 11; - PERFETTO_TRIGGER_BEGIN = 12; - PERFETTO_TRIGGER_SUCCESS = 13; - PERFETTO_TRIGGER_FAILURE = 14; - PERFETTO_HIT_GUARDRAILS = 15; - PERFETTO_ON_TIMEOUT = 16; - PERFETTO_NOT_UPLOADING_EMPTY_TRACE = 17; - } - - // Which stage of the pipeline we are reporting from. - optional Event event = 1; - - // UUID matching the one set inside the SystemInfo trace packet. - optional int64 trace_uuid_lsb = 2; - optional int64 trace_uuid_msb = 3; -} - -/** - * Pulls client metrics on data transferred via Vehicle Maps Service. - * Metrics are keyed by uid + layer. - * - * Pulled from: - * packages/services/Car/service/src/com/android/car/stats/CarStatsService.java - */ -message VmsClientStats { - // UID of the VMS client app - optional int32 uid = 1 [(is_uid) = true]; - - // VMS layer definition - optional int32 layer_type = 2; - optional int32 layer_channel = 3; - optional int32 layer_version = 4; - - // Bytes and packets sent by the client for the layer - optional int64 tx_bytes = 5; - optional int64 tx_packets = 6; - - // Bytes and packets received by the client for the layer - optional int64 rx_bytes = 7; - optional int64 rx_packets = 8; - - // Bytes and packets dropped due to client error - optional int64 dropped_bytes = 9; - optional int64 dropped_packets = 10; -} - -/** - * State of a dangerous permission requested by a package - sampled - * Pulled from: StatsCompanionService.java with data obtained from PackageManager API -*/ -message DangerousPermissionStateSampled { - // Name of the permission - optional string permission_name = 1; - - // Uid of the package - optional int32 uid = 2 [(is_uid) = true]; - - // If the permission is granted to the uid - optional bool is_granted = 3; - - // Permission flags as per android.content.pm.PermissionFlags - optional int32 permission_flags = 4; -} - -/** - * HWUI stats for a given app. - */ -message GraphicsStats { - // The package name of the app - optional string package_name = 1; - - // The version code of the app - optional int64 version_code = 2; - - // The start & end timestamps in UTC as - // milliseconds since January 1, 1970 - // Compatible with java.util.Date#setTime() - optional int64 start_millis = 3; - - optional int64 end_millis = 4; - - // HWUI renders pipeline type: GL (1) or Vulkan (2). - enum PipelineType { - UNKNOWN = 0; - GL = 1; - VULKAN = 2; - } - - // HWUI renders pipeline type: GL or Vulkan. - optional PipelineType pipeline = 5; - - // Distinct frame count. - optional int32 total_frames = 6; - - // Number of "missed vsync" events. - optional int32 missed_vsync_count = 7; - - // Number of frames in triple-buffering scenario (high input latency) - optional int32 high_input_latency_count = 8; - - // Number of "slow UI thread" events. - optional int32 slow_ui_thread_count = 9; - - // Number of "slow bitmap upload" events. - optional int32 slow_bitmap_upload_count = 10; - - // Number of "slow draw" events. - optional int32 slow_draw_count = 11; - - // Number of frames that missed their deadline (aka, visibly janked) - optional int32 missed_deadline_count = 12; - - // The frame time histogram for the package - optional FrameTimingHistogram cpu_histogram = 13 - [(android.os.statsd.log_mode) = MODE_BYTES]; - - // The gpu frame time histogram for the package - optional FrameTimingHistogram gpu_histogram = 14 - [(android.os.statsd.log_mode) = MODE_BYTES]; - - // UI mainline module version. - optional int64 version_ui_module = 15; - - // If true, these are HWUI stats for up to a 24h period for a given app from today. - // If false, these are HWUI stats for a 24h period for a given app from the last complete - // day (yesterday). Stats from yesterday stay constant, while stats from today may change as - // more apps are running / rendering. - optional bool is_today = 16; -} - -/** - * Message related to dangerous (runtime) app ops access - */ -message RuntimeAppOpAccess { - // Uid of the package accessing app op - optional int32 uid = 1 [(is_uid) = true]; - - // Name of the package accessing app op - optional string package_name = 2; - - // deprecated - set to empty string - optional string op_deprecated = 3 [deprecated = true]; - - // attribution_tag; provided by developer when accessing related API, limited at 50 chars by - // API. Attributions must be provided through manifest using <attribution> tag available in R - // and above. - optional string attribution_tag = 4; - - // message related to app op access, limited to 600 chars by API - optional string message = 5; - - enum SamplingStrategy { - DEFAULT = 0; - UNIFORM = 1; - RARELY_USED = 2; - BOOT_TIME_SAMPLING = 3; - UNIFORM_OPS = 4; - } - - // sampling strategy used to collect this message - optional SamplingStrategy sampling_strategy = 6; - - // operation id - optional android.app.AppOpEnum op = 7 [default = APP_OP_NONE]; -} - -/* - * Logs userspace reboot outcome and duration. - * - * Logged from: - * frameworks/base/core/java/com/android/server/BootReceiver.java - */ -message UserspaceRebootReported { - // Possible outcomes of userspace reboot. - enum Outcome { - // Default value in case platform failed to determine the outcome. - OUTCOME_UNKNOWN = 0; - // Userspace reboot succeeded (i.e. boot completed without a fall back to hard reboot). - SUCCESS = 1; - // Userspace reboot shutdown sequence was aborted. - FAILED_SHUTDOWN_SEQUENCE_ABORTED = 2; - // Remounting userdata into checkpointing mode failed. - FAILED_USERDATA_REMOUNT = 3; - // Device didn't finish booting before timeout and userspace reboot watchdog issued a hard - // reboot. - FAILED_USERSPACE_REBOOT_WATCHDOG_TRIGGERED = 4; - } - // Outcome of userspace reboot. Always set. - optional Outcome outcome = 1; - // Duration of userspace reboot in case it has a successful outcome. - // Duration is measured as time between userspace reboot was initiated and until boot completed - // (e.g. sys.boot_completed=1). - optional int64 duration_millis = 2; - // State of primary user's (user0) credential encryption storage. - enum UserEncryptionState { - // Default value. - USER_ENCRYPTION_STATE_UNKNOWN = 0; - // Credential encrypted storage is unlocked. - UNLOCKED = 1; - // Credential encrypted storage is locked. - LOCKED = 2; - } - // State of primary user's encryption storage at the moment boot completed. Always set. - optional UserEncryptionState user_encryption_state = 3; -} - -/* - * Logs integrity check information during each install. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java - */ -message IntegrityCheckResultReported { - optional string package_name = 1; - optional string app_certificate_hash = 2; - optional int64 version_code = 3; - optional string installer_package_name = 4; - enum Response { - UNKNOWN = 0; - ALLOWED = 1; - REJECTED = 2; - FORCE_ALLOWED = 3; - } - optional Response response = 5; - // An estimate on the cause of the response. This will only be populated for - // REJECTED and FORCE_ALLOWED - optional bool caused_by_app_cert_rule = 6; - optional bool caused_by_installer_rule = 7; -} - -/** - * Logs the information about the rules and the provider whenever rules are - * pushed into AppIntegrityManager. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java - */ -message IntegrityRulesPushed { - optional bool success = 1; - // Package name of the app that pushed the rules. - optional string rule_provider = 2; - // Version string of arbitrary format provided by the rule provider to - // identify the rules. - optional string rule_version = 3; -} - -/** - * Logs when a cell broadcast message is received on the device. - * - * Logged from Cell Broadcast module and platform: - * packages/modules/CellBroadcastService/src/com/android/cellbroadcastservice/ - * packages/apps/CellBroadcastReceiver/ - * frameworks/opt/telephony/src/java/com/android/internal/telephony/CellBroadcastServiceManager.java - */ -message CellBroadcastMessageReported { - // The type of Cell Broadcast message - enum CbType { - UNKNOWN_TYPE = 0; - GSM = 1; - CDMA = 2; - CDMA_SPC = 3; - } - - // The parts of the cell broadcast message pipeline - enum ReportSource { - UNKNOWN_SOURCE = 0; - FRAMEWORK = 1; - CB_SERVICE = 2; - CB_RECEIVER_APP = 3; - } - - // GSM, CDMA, CDMA-SCP - optional CbType type = 1; - - // The source of the report - optional ReportSource source = 2; -} - -/** - * Logs when a cell broadcast message is filtered out, or otherwise intentionally not sent to CBR. - * - * Logged from CellBroadcastService module: - * packages/modules/CellBroadcastService/src/com/android/cellbroadcastservice/ - */ -message CellBroadcastMessageFiltered { - enum FilterReason { - NOT_FILTERED = 0; - DUPLICATE_MESSAGE = 1; - GEOFENCED_MESSAGE = 2; - AREA_INFO_MESSAGE = 3; - } - - // GSM, CDMA, CDMA-SCP - optional CellBroadcastMessageReported.CbType type = 1; - - // The source of the report - optional FilterReason filter = 2; -} - -/** - * Logs when an error occurs while handling a cell broadcast message; - * - * Logged from CellBroadcastService module: - * packages/modules/CellBroadcastService/src/com/android/cellbroadcastservice/ - */ -message CellBroadcastMessageError { - // The type of error raised when trying to handle a cell broadcast message - enum ErrorType { - UNKNOWN_TYPE = 0; - CDMA_DECODING_ERROR = 1; - CDMA_SCP_EMPTY = 2; - CDMA_SCP_HANDLING_ERROR = 3; - GSM_INVALID_HEADER_LENGTH = 4; - GSM_UNSUPPORTED_HEADER_MESSAGE_TYPE = 5; - GSM_UNSUPPORTED_HEADER_DATA_CODING_SCHEME = 6; - GSM_INVALID_PDU = 7; - GSM_INVALID_GEO_FENCING_DATA = 8; - GSM_UMTS_INVALID_WAC = 9; - FAILED_TO_INSERT_TO_DB = 10; - UNEXPECTED_GEOMETRY_FROM_FWK = 11; - UNEXPECTED_GSM_MESSAGE_TYPE_FROM_FWK = 12; - UNEXPECTED_CDMA_MESSAGE_TYPE_FROM_FWK = 13; - UNEXPECTED_CDMA_SCP_MESSAGE_TYPE_FROM_FWK = 14; - NO_CONNECTION_TO_CB_SERVICE = 15; - } - - // What kind of error occurred - optional ErrorType type = 1; - - // Exception message (or log message) associated with the error (max 1000 chars) - optional string exception_message = 2; -} - -/** - * Logs when a tune occurs through device's Frontend. - * This is atom ID 276. - * - * Logged from: - * frameworks/base/media/java/android/media/tv/tuner/Tuner.java - */ -message TvTunerStateChanged { - enum State { - UNKNOWN = 0; - TUNING = 1; // Signal is tuned - LOCKED = 2; // the signal is locked - NOT_LOCKED = 3; // the signal isn’t locked. - SIGNAL_LOST = 4; // the signal was locked, but is lost now. - SCANNING = 5; // the signal is scanned - SCAN_STOPPED = 6; // the scan is stopped. - } - // The uid of the application that sent this custom atom. - optional int32 uid = 1 [(is_uid) = true]; - // new state - optional State state = 2; -} - -/** - * Logs the status of a dvr playback or record. - * This is atom ID 279. - * - * Logged from: - * frameworks/base/media/java/android/media/tv/tuner/dvr - */ -message TvTunerDvrStatus { - enum Type { - UNKNOWN_TYPE = 0; - PLAYBACK = 1; // is a playback - RECORD = 2; // is a record - } - enum State { - UNKNOWN_STATE = 0; - STARTED = 1; // DVR is started - STOPPED = 2; // DVR is stopped - } - // The uid of the application that sent this custom atom. - optional int32 uid = 1 [(is_uid) = true]; - // DVR type - optional Type type = 2; - // DVR state - optional State state = 3; - // Identify the segment of a record or playback - optional int32 segment_id = 4; - // indicate how many overflow or underflow happened between started to stopped - optional int32 overflow_underflow_count = 5; -} - -/** - * Logs when a cas session opened through MediaCas. - * This is atom ID 280. - * - * Logged from: - * frameworks/base/media/java/android/media/MediaCas.java - */ -message TvCasSessionOpenStatus { - enum State { - UNKNOWN = 0; - SUCCEEDED = 1; // indicate that the session is opened successfully. - FAILED = 2; // indicate that the session isn’t opened successfully. - } - // The uid of the application that sent this custom atom. - optional int32 uid = 1 [(is_uid) = true]; - // Cas system Id - optional int32 cas_system_id = 2; - // State of the session - optional State state = 3; -} - -/** - * Logs for ContactsProvider general usage. - * This is atom ID 301. - * - * Logged from: - * packages/providers/ContactsProvider/src/com/android/providers/contacts/ContactsProvider2.java - */ -message ContactsProviderStatusReported { - enum ApiType { - UNKNOWN_API = 0; - QUERY = 1; - // INSERT includes insert and bulkInsert, and inserts triggered by applyBatch. - INSERT = 2; - // UPDATE and DELETE includes update/delete and the ones triggered by applyBatch. - UPDATE = 3; - DELETE = 4; - } - - enum ResultType { - UNKNOWN_RESULT = 0; - SUCCESS = 1; - FAIL = 2; - ILLEGAL_ARGUMENT = 3; - UNSUPPORTED_OPERATION = 4; - } - - enum CallerType { - UNSPECIFIED_CALLER_TYPE = 0; - CALLER_IS_SYNC_ADAPTER = 1; - CALLER_IS_NOT_SYNC_ADAPTER = 2; - } - - optional ApiType api_type = 1; - // Defined in - // packages/providers/ContactsProvider/src/com/android/providers/contacts/ContactsProvider2.java - optional int32 uri_type = 2; - optional CallerType caller_type = 3; - optional ResultType result_type = 4; - optional int32 result_count = 5; - optional int64 latency_micros = 6; -} - -/** - * Logs when an app is frozen or unfrozen. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/am/CachedAppOptimizer.java - */ -message AppFreezeChanged { - // The type of event. - enum Action { - UNKNOWN = 0; - FREEZE_APP = 1; - UNFREEZE_APP = 2; - } - optional Action action = 1; - - // Pid of the process being frozen. - optional int32 pid = 2; - - // Name of the process being frozen. - optional string process_name = 3; - - // Time since last unfrozen. - optional int64 time_unfrozen_millis = 4; -} - -/** - * Pulls information for a single voice call. - * - * Each pull creates multiple atoms, one for each call. The sequence is randomized when pulled. - * - * Pulled from: - * frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/MetricsCollector.java - */ -message VoiceCallSession { - // Bearer (IMS or CS) when the call started. - optional android.telephony.CallBearerEnum bearer_at_start = 1; - - // Bearer (IMS or CS) when the call ended. - // The bearer may change during the call, e.g. due to SRVCC. - optional android.telephony.CallBearerEnum bearer_at_end = 2; - - // Direction of the call (incoming or outgoing). - optional android.telephony.CallDirectionEnum direction = 3; - - // Time spent setting up the call. - optional android.telephony.CallSetupDurationEnum setup_duration = 4; - - // Whether the call ended before the setup was completed. - optional bool setup_failed = 5; - - // IMS reason code or CS disconnect cause. - // For IMS, see: frameworks/base/telephony/java/android/telephony/ims/ImsReasonInfo.java - // For CS, see: frameworks/base/telephony/java/android/telephony/DisconnectCause.java - optional int32 disconnect_reason_code = 6; - - // IMS extra code or CS precise disconnect cause. - // For IMS, this code is vendor-specific - // For CS, see: frameworks/base/telephony/java/android/telephony/PreciseDisconnectCause.java - optional int32 disconnect_extra_code = 7; - - // IMS extra message or CS vendor cause. - optional string disconnect_extra_message = 8; - - // Radio access technology (RAT) used when call started. - optional android.telephony.NetworkTypeEnum rat_at_start = 9; - - // Radio access technology (RAT) used when call terminated. - optional android.telephony.NetworkTypeEnum rat_at_end = 10; - - // Number of times RAT changed during the call. - optional int64 rat_switch_count = 11; - - // A bitmask of all codecs used during the call. - // See: frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java - optional int64 codec_bitmask = 12; - - // Number of other calls going on during call setup, for the same SIM slot. - optional int32 concurrent_call_count_at_start = 13; - - // Number of other calls going on during call termination, for the same SIM slot. - optional int32 concurrent_call_count_at_end = 14; - - // Index of the SIM used, 0 for single-SIM devices. - optional int32 sim_slot_index = 15; - - // Whether the device was in multi-SIM mode (with multiple active SIM profiles). - optional bool is_multi_sim = 16; - - // Whether the call was made with an eSIM profile. - optional bool is_esim = 17; - - // Carrier ID of the SIM card. - // See https://source.android.com/devices/tech/config/carrierid. - optional int32 carrier_id = 18; - - // Whether an SRVCC has been completed successfully for this call. - optional bool srvcc_completed = 19; - - // Number of SRVCC failures. - optional int64 srvcc_failure_count = 20; - - // Number of SRVCC cancellations. - optional int64 srvcc_cancellation_count = 21; - - // Whether the Real-Time Text (RTT) was ever used in the call (rather than whether RTT was - // enabled in the dialer's settings). - optional bool rtt_enabled = 22; - - // Whether this was an emergency call. - optional bool is_emergency = 23; - - // Whether the call was performed while roaming. - optional bool is_roaming = 24; - - // A random number used as the dimension field to pull multiple atoms. - optional int32 dimension = 25; - - // Signal strength at the end of the call. This value is applicable to both cellular and WiFi. - optional android.telephony.SignalStrengthEnum signal_strength_at_end = 26; - - // Band at the end of the call. Value 0 is used if the band is unknown. - // See GeranBands, UtranBands and EutranBands in IRadio interface, depending on the RAT at - // the end of the call. - optional int32 band_at_end = 27; - - // Time spent setting up the call in milliseconds. - // The time is measured from dial to ringing for outgoing calls, and from answer to connected - // for incoming calls. - optional int32 setup_duration_millis = 28; - - // Main codec quality. The codec quality was equal to or greater than this value for at least - // 70% of the call. - optional android.telephony.CodecQuality main_codec_quality = 29; - - // Whether video was enabled at any point during the call. - optional bool video_enabled = 30; - - // Radio access technology (RAT) used when call is connected. - optional android.telephony.NetworkTypeEnum rat_at_connected = 31; - - // Whether the call was a conference call (applicable only for calls over IMS). - optional bool is_multiparty = 32; -} - -/** - * Pulls voice call radio access technology (RAT) usage. - * - * Each pull creates multiple atoms, one for each carrier/RAT, the order of which is irrelevant to - * time. The atom will be skipped if not enough data is available. - * - * Pulled from: - * frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/MetricsCollector.java - */ -message VoiceCallRatUsage { - // Carrier ID (https://source.android.com/devices/tech/config/carrierid). - optional int32 carrier_id = 1; - - // Radio access technology. - optional android.telephony.NetworkTypeEnum rat = 2; - - // Total duration that voice calls spent on this carrier and RAT, rounded to 5 minute. - optional int64 total_duration_seconds = 3; - - // Total number of calls using this carrier and RAT. - // A call is counted once even if it used the RAT multiple times. - optional int64 call_count = 4; -} - -/** - * Pulls amount of time spend in each cellular service state. - * - * Each pull creates multiple atoms, one for each SIM slot/carrier/RAT(including ENDC), the order of - * which is irrelevant to time. If multi SIM settings changes during the period, durations will be - * counted separately before and after the change. Airplane mode does not count towards durations. - * - * Pulled from: - * frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/MetricsCollector.java - */ -message CellularServiceState { - // Radio access technology (RAT) for voice. - // NETWORK_TYPE_UNKNOWN when the device is out of service. - // NETWORK_TYPE_IWLAN when the device is using VoWiFi. - optional android.telephony.NetworkTypeEnum voice_rat = 1; - - // Radio access technology (RAT) for data. - // NETWORK_TYPE_UNKNOWN when the device is out of service. - // Only cellular RATs are valid and show where the device is camped. - optional android.telephony.NetworkTypeEnum data_rat = 2; - - // Whether the device was in roaming (domestic or international) for voice. - optional android.telephony.RoamingTypeEnum voice_roaming_type = 3; - - // Whether the device was in roaming (domestic or international) for data. - optional android.telephony.RoamingTypeEnum data_roaming_type = 4; - - // Whether the device is on LTE and has access to NR NSA, i.e. cell supports 5G (ENDC) and UE - // registration (attach/TAU) indicates ENDC is not restricted. - optional bool is_endc = 5; - - // Index of the SIM used, 0 for single-SIM devices. - optional int32 sim_slot_index = 6; - - // Whether the device was in multi-SIM mode (with multiple active SIM profiles). - optional bool is_multi_sim = 7; - - // Carrier ID of the SIM card. - // See https://source.android.com/devices/tech/config/carrierid. - optional int32 carrier_id = 8; - - // Total time spent in this service state, rounded to 5 minutes. - optional int32 total_time_seconds = 9; -} - -/** - * Pulls the number of times cellular data service state switches. - * - * Each pull creates multiple atoms, one for each RAT combination, the order of which is irrelevant - * to time. Switches for different SIM slots, carrier IDs, or multi-SIM settings are counted - * separately. - * - * Pulled from: - * frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/MetricsCollector.java - */ -message CellularDataServiceSwitch { - // Cellular RAT of the DATA domain from where the switch occurred. - optional android.telephony.NetworkTypeEnum rat_from = 1; - - // Cellular RAT of the DATA domain to where the switch occurred. - optional android.telephony.NetworkTypeEnum rat_to = 2; - - // Index of the SIM used, 0 for single-SIM devices. - optional int32 sim_slot_index = 3; - - // Whether the device was in multi-SIM mode (with multiple active SIM profiles). - optional bool is_multi_sim = 4; - - // Carrier ID of the SIM card. - // See https://source.android.com/devices/tech/config/carrierid. - optional int32 carrier_id = 5; - - // Number of switches from rat_from to rat_to. - optional int32 switch_count = 6; -} - -/** - * Pulls the number of active SIM slots and SIMs/eSIM profiles. - * - * Pulled from: - * frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/MetricsCollector.java - */ -message SimSlotState { - // Number of active SIM slots (both physical and eSIM profiles) in the device. - optional int32 active_slot_count = 1; - - // Number of SIM cards (both physical and active eSIM profiles). - // This number is always equal to or less than the number of active SIM slots. - optional int32 sim_count = 2; - - // Number of active eSIM profiles. - // This number is always equal to or less than the number of SIMs. - optional int32 esim_count = 3; -} - -/** - * Pulls supported cellular radio access technologies. - * - * This atom reports the capabilities of the device, rather than the network it has access to. - * - * Pulled from: - * frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/MetricsCollector.java - */ -message SupportedRadioAccessFamily { - // A bitmask of supported radio technologies. - // See android.telephony.TelephonyManager.NetworkTypeBitMask. - optional int64 network_type_bitmask = 1; -} - -/** - * Pulls information for a single incoming SMS. - * - * Each pull creates multiple atoms, one for each SMS. The sequence is randomized when pulled. - * - * Pulled from: - * frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/MetricsCollector.java - */ -message IncomingSms { - // Format of the SMS (3GPP or 3GPP2). - optional android.telephony.SmsFormatEnum sms_format = 1; - - // Technology of the SMS (CS or IMS). - optional android.telephony.SmsTechEnum sms_tech = 2; - - // Radio access technology (RAT) used for the SMS. It can be IWLAN in case of IMS. - optional android.telephony.NetworkTypeEnum rat = 3; - - // Type the SMS. - optional android.telephony.SmsTypeEnum sms_type = 4; - - // Number of total parts. - optional int32 total_parts = 5; - - // Number of received parts (if smaller than total parts, the SMS was dropped). - optional int32 received_parts = 6; - - // Indicates if the incoming SMS was blocked. - optional bool blocked = 7; - - // Indicate a specific error handling the SMS - optional android.telephony.SmsIncomingErrorEnum error = 8; - - // Whether the SMS was received while roaming. - optional bool is_roaming = 9; - - // Index of the SIM used, 0 for single-SIM devices. - optional int32 sim_slot_index = 10; - - // Whether the device was in multi-SIM mode (with multiple active SIM profiles). - optional bool is_multi_sim = 11; - - // Whether the message was received with an eSIM profile. - optional bool is_esim = 12; - - // Carrier ID of the SIM card used for the SMS. - // See https://source.android.com/devices/tech/config/carrierid. - optional int32 carrier_id = 13; - - // Random message ID. - optional int64 message_id = 14; -} - -/** - * Pulls information for a single outgoing SMS. - * - * Each pull creates multiple atoms, one for each SMS. The sequence is randomized when pulled. - * - * Pulled from: - * frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/MetricsCollector.java - */ -message OutgoingSms { - // Format of the SMS (3GPP or 3GPP2). - optional android.telephony.SmsFormatEnum sms_format = 1; - - // Technology of the SMS (CS or IMS). - optional android.telephony.SmsTechEnum sms_tech = 2; - - // Radio access technology (RAT) used for the SMS. It can be IWLAN in case of IMS. - optional android.telephony.NetworkTypeEnum rat = 3; - - // Result of the SMS sending. - optional android.telephony.SmsSendResultEnum send_result = 4; - - // Error code - // For IMS technology, see @SmsManager.Result in - // http://cs/android/frameworks/base/telephony/java/android/telephony/SmsManager.java - // For CS technology: - // - GSM format: see GsmSmsErrorCode (3GPP 27.005 clause 3.2.5) - // - CDMA format: see CdmaSmsErrorCode (3GPP2 N.S0005 (IS-41-C) Table 171) - optional int32 error_code = 5; - - // Whether the SMS was sent while roaming. - optional bool is_roaming = 6; - - // Whether the default SMS application generated the SMS (regardless of which application). - optional bool is_from_default_app = 7; - - // Index of the SIM used, 0 for single-SIM devices. - optional int32 sim_slot_index = 8; - - // Whether the device was in multi-SIM mode (with multiple active SIM profiles). - optional bool is_multi_sim = 9; - - // Whether the message was sent with an eSIM profile. - optional bool is_esim = 10; - - // Carrier ID of the SIM card used for the SMS. - // See https://source.android.com/devices/tech/config/carrierid. - optional int32 carrier_id = 11; - - // Random message ID. - optional int64 message_id = 12; - - // Retry count: 0 for the first attempt and then increasing for each attempt. - optional int32 retry_id = 13; -} - -/** - * Logs information about usage of airplane mode. - * - * Logged from: - * frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/AirplaneModeStats.java - */ -message AirplaneMode { - // Status of airplane mode - optional bool is_enabled = 1; - - // When is_enabled is false, indicates if this was a very short airplane mode toggle - // (i.e. airplane mode was disabled after less than 10 seconds from enablement). - optional bool short_toggle = 2; - - // Carrier ID of the SIM card. - // See https://source.android.com/devices/tech/config/carrierid. - optional int32 carrier_id = 3; -} - -/** - * Logs information about modem restarts. - * - * Logged from: - * frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/ModemRestartStats.java - */ -message ModemRestart { - // Software version of the modem, as provided by android.os.Build.getRadioVersion(). - optional string baseband_version = 1; - - // Reason of the modem restart, as provided in the modemReset indication of IRadio HAL. - optional string reason = 2; - - // Carrier ID of the first SIM card. - // See https://source.android.com/devices/tech/config/carrierid. - optional int32 carrier_id = 3; -} - -/** - * Logs the SIM card details when the carrier ID match is not complete. - * - * The atom is pushed when a SIM card is initialized and the MCC/MNC is not present in the - * carrier ID table, or the SIM card contains a GID1 value that is not present in the carrier ID - * table. This atom is pushed only once for each type of SIM card. - * - * Logged from: - * frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/CarrierIdMatchStats.java - */ -message CarrierIdMismatchReported { - // Matched carrier ID. The value -1 is used if no match is found. - optional int32 carrier_id = 1; - - // MCC/MNC of the SIM card. - optional string mcc_mnc = 2; - - // Group identifier (level 1) of the SIM card. - optional string gid1 = 3; - - // SPN value of the SIM card. - optional string spn = 4; - - // First record of the PNN in the SIM card. This field is populated only if the SPN is missing - // or empty. - optional string pnn = 5; -} - -/** - * Logs the version of the carrier ID matching table at first power up and when it is updated. - * - * Logged from: - * frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/CarrierIdMatchStats.java - */ -message CarrierIdTableUpdated { - // Version of the CarrierId matching table. - optional int32 table_version = 1; -} - -/** - * Pulls the version of the carrier ID matching table. - * - * Logged from: - * frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/MetricsCollector.java - */ -message CarrierIdTableVersion { - // Version of the CarrierId matching table. - optional int32 table_version = 1; -} - -/** - * Pulls information for a single data call session - * - * Each pull creates multiple atoms, one for each data call session. - * The sequence is randomized when pulled. - * - * Pulled from: - * frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/MetricsCollector.java - */ -message DataCallSession { - // A random number to be used as dimension to capture multiple atoms - optional int32 dimension = 1; - - // Whether the device was in multi-SIM mode (with multiple active SIM profiles). - optional bool is_multi_sim = 2; - - // Whether the call was made with an eSIM profile. - optional bool is_esim = 3; - - // Data profile of this call (for what purpose this call was made) - optional android.telephony.DataProfileEnum profile = 4; - - // APN type bitmask of the APN used: - // @ApnType in frameworks/base/telephony/java/android/telephony/Annotation.java. - optional int32 apn_type_bitmask = 5; - - // Carrier ID of the SIM - // See https://source.android.com/devices/tech/config/carrierid. - optional int32 carrier_id = 6; - - // Whether the subscription is roaming - optional bool is_roaming = 7; - - // Data RAT when the call ended, can be IWLAN for IMS/MMS, otherwise should be WWAN PS RAT. - // In the case that the connection hasn't ended yet, this field holds the current RAT. - // In the case the call ended due to Out Of Service (OOS), - // this field should be the last known RAT. - optional android.telephony.NetworkTypeEnum rat_at_end = 8; - - // Was the data call ended due to OOS - optional bool oos_at_end = 9; - - // Number of RAT switches during the data call - optional int64 rat_switch_count = 10; - - // Whether the call is on an opportunistic subscription - optional bool is_opportunistic = 11; - - // Packet data protocol used - optional android.telephony.ApnProtocolEnum ip_type = 12; - - // Whether the data call terminated before being established - optional bool setup_failed = 13; - - // Reason why the data call terminated, as in RIL_DataCallFailCause from ril.h - optional int32 failure_cause = 14; - - // Suggested retry back-off timer value from RIL - optional int32 suggested_retry_millis = 15; - - // Why the data call was deactivated - // Set by telephony for MO deactivations (unrelated to failure_cause) - optional android.telephony.DataDeactivateReasonEnum deactivate_reason = 16; - - // Duration of the data call, rounded into the closest 5 minutes. - optional int64 duration_minutes = 17; - - // Whether the data call is still connected when the atom is collected. - optional bool ongoing = 18; -} - -/** - * Logs data stall recovery event - * - * Logged from: - * frameworks/opt/telephony/src/java/com/android/internal/telephony/dataconnection/DcTracker.java - */ -message DataStallRecoveryReported { - // Carrier ID of the SIM - // See https://source.android.com/devices/tech/config/carrierid. - optional int32 carrier_id = 1; - - // Data RAT when the stall happened - optional android.telephony.NetworkTypeEnum rat = 2; - - // Signal strength when stall happened - optional android.telephony.SignalStrengthEnum signal_strength = 3; - - // Action taken to recover - optional android.telephony.DataStallRecoveryActionEnum action = 4; - - // Whether the subscription is opportunistic - optional bool is_opportunistic = 5; - - // Whether the device is in multi-SIM mode - optional bool is_multi_sim = 6; -} - -/** - * Logs gnss stats from location service provider - * - * Pulled from: - * frameworks/base/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java - */ -message GnssStats { - // Number of location reports since boot - optional int64 location_reports = 1; - - // Total pulled reports of Location failures since boot - optional int64 location_failure_reports = 2; - - // Number of time to first fix reports since boot - optional int64 time_to_first_fix_reports = 3; - - // Total pulled reported time to first fix (in milli-seconds) since boot - optional int64 time_to_first_fix_millis = 4; - - // Number of position accuracy reports since boot - optional int64 position_accuracy_reports = 5; - - // Total pulled reported position accuracy (in meters) since boot - optional int64 position_accuracy_meters = 6; - - // Number of top 4 average CN0 reports since boot - optional int64 top_four_average_cn0_reports = 7; - - // Total pulled reported of top 4 average CN0 (dB-mHz) since boot - optional int64 top_four_average_cn0_db_mhz = 8; - - // Number of l5 top 4 average CN0 reports since boot - optional int64 l5_top_four_average_cn0_reports = 9; - - // Total pulled reported of l5 top 4 average CN0 (dB-mHz) since boot - optional int64 l5_top_four_average_cn0_db_mhz = 10; - - // Total number of sv status messages reports since boot - optional int64 sv_status_reports = 11; - - // Total number of sv status messages reports, where sv is used in fix since boot - optional int64 sv_status_reports_used_in_fix = 12; - - // Total number of L5 sv status messages reports since boot - optional int64 l5_sv_status_reports = 13; - - // Total number of L5 sv status messages reports, where sv is used in fix since boot - optional int64 l5_sv_status_reports_used_in_fix = 14; -} - -/** - * Logs when an app is moved to a different standby bucket. - * - * Logged from: - * frameworks/base/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java - */ -message AppStandbyBucketChanged { - optional string package_name = 1; - - // Should be 0, 10, 11, 12, etc. where 0 is the owner. See UserHandle for more documentation. - optional int32 user_id = 2; - - // These enum values match the constants defined in UsageStatsManager.java. - enum Bucket { - BUCKET_UNKNOWN = 0; - BUCKET_EXEMPTED = 5; - BUCKET_ACTIVE = 10; - BUCKET_WORKING_SET = 20; - BUCKET_FREQUENT = 30; - BUCKET_RARE = 40; - BUCKET_RESTRICTED = 45; - BUCKET_NEVER = 50; - } - optional Bucket bucket = 3; - - enum MainReason { - MAIN_UNKNOWN = 0; - MAIN_DEFAULT = 0x0100; - MAIN_TIMEOUT = 0x0200; - MAIN_USAGE = 0x0300; - MAIN_FORCED_BY_USER = 0x0400; - MAIN_PREDICTED = 0x0500; - MAIN_FORCED_BY_SYSTEM = 0x0600; - } - optional MainReason main_reason = 4; - - // A more detailed reason for the standby bucket change. The sub reason name is dependent on - // the main reason. Values are one of the REASON_SUB_XXX constants defined in - // UsageStatsManager.java. - optional int32 sub_reason = 5; -} - -/** -* Reports a started sharesheet transaction. -* -* Logged from: -* frameworks/base/core/java/com/android/internal/app/ChooserActivity.java -*/ -message SharesheetStarted { - // The event_id (as for UiEventReported). - optional int32 event_id = 1; - // The calling app's package name. - optional string package_name = 2; - // An identifier to tie together multiple logs relating to the same share event - optional int32 instance_id = 3; - // The mime type of the share - optional string mime_type = 4; - // The number of direct targets the calling app is providing that will be shown. - optional int32 num_app_provided_direct_targets = 5; - // The number of app targets the calling app is providing that will be shown. - optional int32 num_app_provided_app_targets = 6; - // True if the share originates from the workprofile - optional bool is_workprofile = 7; - - enum SharesheetPreviewType { // Constants from ChooserActivity.java - CONTENT_PREVIEW_TYPE_UNKNOWN = 0; // Default for proto 2 / 3 compatibility. - CONTENT_PREVIEW_IMAGE = 1; // The preview shown in the sharesheet is an image. - CONTENT_PREVIEW_FILE = 2; // The preview shown in the sharesheet is a file. - CONTENT_PREVIEW_TEXT = 3; // The preview shown in the sharesheet is text. - } - // How the sharesheet preview is presented. - optional SharesheetPreviewType preview_type = 8; - - enum ResolverActivityIntent { // Intents handled by ResolverActivity.java - INTENT_DEFAULT = 0; - INTENT_ACTION_VIEW = 1; - INTENT_ACTION_EDIT = 2; - INTENT_ACTION_SEND = 3; - INTENT_ACTION_SENDTO = 4; - INTENT_ACTION_SEND_MULTIPLE = 5; - INTENT_ACTION_IMAGE_CAPTURE = 6; - INTENT_ACTION_MAIN = 7; - } - // The intent being processed (only SEND and SEND_MULTIPLE are system sharesheet) - optional ResolverActivityIntent intent_type = 9; -} - -/** - * Reports a ranking selection event. - * - * Logged from: - * frameworks/base/core/java/com/android/internal/app/ChooserActivity.java (sharesheet) - */ -message RankingSelected { - // The event_id (as for UiEventReported). - optional int32 event_id = 1; - // The relevant app's package name (can be source or picked package). - optional string package_name = 2; - // An identifier to tie together multiple logs relating to the same share event. - optional int32 instance_id = 3; - // Which of the ranked targets got picked, default starting position 0. - optional int32 position_picked = 4; -} - -/** - * Logs when TvSettings UI is interacted at. - * - * Logged from: packages/apps/TvSettings - */ -message TvSettingsUIInteracted { - - /** The UI action category */ - optional android.app.tvsettings.Action action = 1; - - /** The ID of the entry that the users actioned on */ - optional android.app.tvsettings.ItemId item_id = 2; -} - -/** - * Logs information about a package installation using package installer V2 APIs. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java - */ -message PackageInstallerV2Reported { - // Whether this installation uses Incremental File System - optional bool is_incremental = 1; - // Name of the package that is intended to be installed - optional string package_name = 2; - // The duration between when the install was requested to when the install has completed - optional int64 duration_millis = 3; - // Installation result in final integer, which are SystemApi's. - // Return_code 1 indicates success. - // For full list, see frameworks/base/core/java/android/content/pm/PackageManager.java - optional int32 return_code = 4; - // Total size of the APKs installed for this package - optional int64 apks_size_bytes = 5; -} - -/** - * Logs settings provider values. - * - * Use DeviceConfig.getProperties to get a list Setting key, query the data from content provider, - * then write the value to proto. - * - */ -message SettingSnapshot { - - // Setting key - optional string name = 1; - - enum SettingsValueType { - NOTASSIGNED = 0; - ASSIGNED_BOOL_TYPE = 1; - ASSIGNED_INT_TYPE = 2; - ASSIGNED_FLOAT_TYPE = 3; - ASSIGNED_STRING_TYPE = 4; - }; - // Setting value type - optional SettingsValueType type = 2; - - optional bool bool_value = 3; - - optional int32 int_value = 4; - - optional float float_value = 5; - - optional string str_value = 6; - - // Android user index. 0 for primary user, 10, 11 for secondary or profile user - optional int32 user_id = 7; -} - -/** - * An event logged to indicate that a user journey is about to be performed. This atom includes - * relevant information about the users involved in the journey. A UserLifecycleEventOccurred event - * will immediately follow this atom which will describe the event(s) and its state. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/am/UserController.java - * frameworks/base/services/core/java/com/android/server/pm/UserManagerService.java - */ -message UserLifecycleJourneyReported { - // An identifier to track a chain of user lifecycle events occurring (referenced in the - // UserLifecycleEventOccurred atom) - optional int64 session_id = 1; - - // Indicates what type of user journey this session is related to - enum Journey { - UNKNOWN = 0; // Undefined user lifecycle journey - USER_SWITCH_UI = 1; // A user switch journey where a UI is shown - USER_SWITCH_FG = 2; // A user switch journey without a UI shown - USER_START = 3; // A user start journey - USER_CREATE = 4; // A user creation journey - } - optional Journey journey = 2; - // Which user the journey is originating from - could be -1 for certain phases (eg USER_CREATE) - // This integer is a UserIdInt (eg 0 for the system user, 10 for secondary/guest) - optional int32 origin_user = 3; - // Which user the journey is targeting - // This integer is a UserIdInt (eg 0 for the system user, 10 for secondary/guest) - optional int32 target_user = 4; - - // What is the user type of the target user - // These should be in sync with USER_TYPE_* flags defined in UserManager.java - enum UserType { - TYPE_UNKNOWN = 0; - FULL_SYSTEM = 1; - FULL_SECONDARY = 2; - FULL_GUEST = 3; - FULL_DEMO = 4; - FULL_RESTRICTED = 5; - PROFILE_MANAGED = 6; - SYSTEM_HEADLESS = 7; - } - optional UserType user_type = 5; - // What are the flags attached to the target user - optional int32 user_flags = 6; -} - -/** - * An event logged when a specific user lifecycle event is performed. These events should be - * correlated with a UserLifecycleJourneyReported atom via the session_id. - * Note: journeys can span over multiple events, hence some events may share a single session id. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/am/UserController.java - * frameworks/base/services/core/java/com/android/server/pm/UserManagerService.java - */ -message UserLifecycleEventOccurred { - // An id which links back to user details (reported in the UserLifecycleJourneyReported atom) - optional int64 session_id = 1; - // The target user for this event (same as target_user in the UserLifecycleJourneyReported atom) - // This integer is a UserIdInt (eg 0 for the system user, 10 for secondary/guest) - optional int32 user_id = 2; - - enum Event { - UNKNOWN = 0; // Indicates that the associated user journey timed-out or resulted in an error - SWITCH_USER = 1; // Indicates that this is a user switch event - START_USER = 2; // Indicates that this is a user start event - CREATE_USER = 3; // Indicates that this is a user create event - USER_RUNNING_LOCKED = 4; // Indicates that user is running in locked state - UNLOCKING_USER = 5; // Indicates that this is a user unlocking event - UNLOCKED_USER = 6; // Indicates that this is a user unlocked event - } - optional Event event = 3; - - enum State { - NONE = 0; // Indicates the associated event has no start/end defined - BEGIN = 1; - FINISH = 2; - } - optional State state = 4; // Represents the state of an event (beginning/ending) -} - -/** - * Logs when accessibility shortcut clicked. - * - * Logged from: - * frameworks/base/services/accessibility/java/com/android/server/accessibility - */ -message AccessibilityShortcutReported { - // The accessibility feature(including installed a11y service, framework a11y feature, - // and installed a11y activity) package name that is assigned to the accessibility shortcut. - optional string package_name = 1; - - // The definition of the accessibility shortcut. - // From frameworks/base/core/proto/android/stats/accessibility/accessibility_enums.proto. - optional android.stats.accessibility.ShortcutType shortcut_type = 2; - - // The definition of the service status. - // From frameworks/base/core/proto/android/stats/accessibility/accessibility_enums.proto. - optional android.stats.accessibility.ServiceStatus service_status = 3; -} - -/** - * Logs when accessibility service status changed. - * - * Logged from: - * packages/apps/Settings/src/com/android/settings/accessibility - */ -message AccessibilityServiceReported { - // The accessibility service package name. - optional string package_name = 1; - - // The definition of the service status. - // From frameworks/base/core/proto/android/stats/accessibility/accessibility_enums.proto. - optional android.stats.accessibility.ServiceStatus service_status = 2; -} - -/** - * Logs when display wake up. - * - * Logged from: - * services/core/java/com/android/server/power/Notifier.java - */ - -message DisplayWakeReported { - // Wake_up_reason code - // If LOWORD(wake_up_reason) = 0 - // reference to HIWORD(wake_up_reason) PowerManager.WAKE_REASON_XXX - // else reference wake_up_reason to - // services/core/java/com/android/server/power/Notifier.java#onWakeUp - optional int32 wake_up_reason = 1; -} - -/** - * Logs app usage events. - */ -message AppUsageEventOccurred { - optional int32 uid = 1 [(is_uid) = true]; - optional string package_name = 2; - optional string class_name = 3; - - enum EventType { - NONE = 0; - MOVE_TO_FOREGROUND = 1; - MOVE_TO_BACKGROUND = 2; - } - optional EventType event_type = 4; -} - -/* - * Quality metrics logged when EVS cameras are active. - * - * Logged from: - * packages/services/Car/evs/manager/1.1/Enumerator.cpp - */ -message EvsUsageStatsReported { - - // Camera identifier to distinguish the source camera device. This is not - // globally unique and therefore cannot be used to identify the user and/or - // the device. - optional int32 device_id = 1; - - // Peak number of clients during the service - optional int32 peak_num_clients = 2; - - // Number of erroneous events during the service - optional int32 num_errors = 3; - - // Round trip latency of the very first frame - optional int64 first_latency_millis = 4; - - // Average frame round trip latency - optional float avg_latency_millis = 5; - - // Peak frame round trip latency - optional int64 peak_latency_millis = 6; - - // Total number of frames received - optional int64 total_frames = 7; - - // Number of frames ignored - optional int64 ignored_frames = 8; - - // Number of dropped frames to synchronize camera devices - optional int64 dropped_frames_to_sync = 9; - - // The duration of the service - optional int64 duration_millis = 10; -} - -/** - * Logs audio power usage stats. - * - * Pushed from: - * frameworks/av/services/mediametrics/AudioPowerUsage.cpp - */ -message AudioPowerUsageDataReported { - /** - * Device used for input/output - * - * All audio devices please refer to below file: - * system/media/audio/include/system/audio-base.h - * - * Define our own enum values because we don't report all audio devices. - * Currently, we only report built-in audio devices such as handset, speaker, - * built-in mics, common audio devices such as wired headset, usb headset - * and bluetooth devices. - */ - enum AudioDevice { - OUTPUT_EARPIECE = 0x1; // handset - OUTPUT_SPEAKER = 0x2; // dual speaker - OUTPUT_WIRED_HEADSET = 0x4; // 3.5mm headset - OUTPUT_USB_HEADSET = 0x8; // usb headset - OUTPUT_BLUETOOTH_SCO = 0x10; // bluetooth sco - OUTPUT_BLUETOOTH_A2DP = 0x20; // a2dp - OUTPUT_SPEAKER_SAFE = 0x40; // bottom speaker - - INPUT_DEVICE_BIT = 0x40000000; // non-negative positive int32. - INPUT_BUILTIN_MIC = 0x40000001; // buildin mic - INPUT_BUILTIN_BACK_MIC = 0x40000002; // buildin back mic - INPUT_WIRED_HEADSET_MIC = 0x40000004; // 3.5mm headset mic - INPUT_USB_HEADSET_MIC = 0x40000008; // usb headset mic - INPUT_BLUETOOTH_SCO = 0x40000010; // bluetooth sco mic - } - optional AudioDevice audio_device = 1; - - // Duration of the audio in seconds - optional int32 duration_secs = 2; - - // Average volume (0 ... 1.0) - optional float average_volume = 3; - - enum AudioType { - UNKNOWN_TYPE = 0; - VOICE_CALL_TYPE = 1; // voice call - VOIP_CALL_TYPE = 2; // voip call, including uplink and downlink - MEDIA_TYPE = 3; // music and system sound - RINGTONE_NOTIFICATION_TYPE = 4; // ringtone and notification - ALARM_TYPE = 5; // alarm type - // record type - CAMCORDER_TYPE = 6; // camcorder - RECORD_TYPE = 7; // other recording - } - optional AudioType type = 4; -} - -/** - * Pulls bytes transferred over WiFi and mobile networks sliced by uid, is_metered, and tag. - * - * Pulled from: - * StatsPullAtomService, which uses NetworkStatsService to query NetworkStats. - */ -message BytesTransferByTagAndMetered { - optional int32 uid = 1 [(is_uid) = true]; - - optional bool is_metered = 2; - - optional int32 tag = 3; - - optional int64 rx_bytes = 4; - - optional int64 rx_packets = 5; - - optional int64 tx_bytes = 6; - - optional int64 tx_packets = 7; -} - -/* - * Logs when the Media Output Switcher finishes a media switch operation. - * - * Logged from: - * packages/apps/Settings/src/com/android/settings/media/MediaOutputSliceWorker.java - */ -message MediaOutputOpSwitchReported { - // Source medium type before switching. - optional android.app.settings.mediaoutput.MediumType source = 1; - - // Target medium type after switching. - optional android.app.settings.mediaoutput.MediumType target = 2; - - // The result of switching. - optional android.app.settings.mediaoutput.SwitchResult result = 3; - - // The detail code of a switching result. - optional android.app.settings.mediaoutput.SubResult subresult = 4; - - /* - * The package name of a pre-installed app, whose media session is being switched. - */ - optional string media_session_package_name = 5; - - // The amount of available wired devices when a switching is being performed. - optional int32 available_wired_device_count = 6; - - // The amount of available Bluetooth devices a switching is being performed. - optional int32 available_bt_device_count = 7; - - // The amount of available remote devices when a switching is being performed. - optional int32 available_remote_device_count = 8; - - // The amount of applied devices within a remote dynamic group after a switching is done. - optional int32 applied_device_count_within_remote_group = 9; -} - -/** - * Logs when the Assistant is invoked. - * - * Logged from: - * frameworks/base/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java - */ -message AssistantInvocationReported { - - // The event_id (as for UiEventReported). - optional int32 event_id = 1; - - // The registered Assistant's uid and package (as for UiEventReported). - optional int32 uid = 2 [(is_uid) = true]; - optional string package_name = 3; - - // An identifier used to disambiguate which logs refer to a particular invocation of the - // Assistant (as for UiEventReported). - optional int32 instance_id = 4; - - // The state of the device at the time of invocation. - enum DeviceState { - UNKNOWN_DEVICE_STATE = 0; - AOD1 = 1; - AOD2 = 2; - BOUNCER = 3; - UNLOCKED_LOCKSCREEN = 4; - LAUNCHER_HOME = 5; - LAUNCHER_OVERVIEW = 6; - LAUNCHER_ALL_APPS = 7; - APP_DEFAULT = 8; - APP_IMMERSIVE = 9; - APP_FULLSCREEN = 10; - } - optional DeviceState device_state = 5; - - // Whether the Assistant handles were showing at the time of invocation. - optional bool assistant_handles_showing = 6; -} - -/** - * Logs when an AudioRecord finishes running on an audio device - * - * Logged from: - * frameworks/av/services/mediametrics/AudioAnalytics.cpp - */ -message MediametricsAudioRecordDeviceUsageReported { - // The devices connected to this AudioRecord. - // A string OR of various input device categories, e.g. "DEVICE1|DEVICE2". - // See lookup<INPUT_DEVICE>() in frameworks/av/services/mediametrics/AudioTypes.cpp - // See audio_device_t in system/media/audio/include/system/audio-base.h - optional string devices = 1; - - // The name of the remote device attached to the device, typically available for USB or BT. - // This may be empty for a fixed device, or separated by "|" if more than one. - optional string device_names = 2; - - // The amount of time spent in the device as measured by the active track in AudioFlinger. - optional int64 device_time_nanos = 3; - - // The audio data format used for encoding. - // An enumeration from system/media/audio/include/system/audio-base.h audio_format_t - optional string encoding = 4; - - // The client-server buffer framecount. - // The framecount is generally between 960 - 48000 for PCM encoding. - // The framecount represents raw buffer size in bytes for non-PCM encoding. - optional int32 frame_count = 5; - - // The number of audio intervals (contiguous, continuous playbacks). - optional int32 interval_count = 6; - - // The sample rate of the AudioRecord. - // A number generally between 8000-96000 (frames per second). - optional int32 sample_rate = 7; - - // The audio input flags used to construct the AudioRecord. - // A string OR from system/media/audio/include/system/audio-base.h audio_input_flags_t - optional string flags = 8; - - // The santized package name of the audio client associated with the AudioRecord. - // See getSanitizedPackageNameAndVersionCode() in - // frameworks/av/services/mediametrics/MediaMetricsService.cpp - optional string package_name = 9; - - // The selected device id (nonzero if a non-default device is selected) - optional int32 selected_device_id = 10; - - // The caller of the AudioRecord. - // See lookup<CALLER_NAME>() in frameworks/av/services/mediametrics/AudioTypes.cpp - optional string caller = 11; - - // The audio source for AudioRecord. - // An enumeration from system/media/audio/include/system/audio-base.h audio_source_t - optional string source = 12; -} - -/** - * Logs when an AudioThread finishes running on an audio device - * - * Logged from: - * frameworks/av/services/mediametrics/AudioAnalytics.cpp - */ -message MediametricsAudioThreadDeviceUsageReported { - // The devices connected to this audio thread. - // A string OR of various input device categories, e.g. "DEVICE1|DEVICE2". - // (for record threads): - // See lookup<INPUT_DEVICE> in frameworks/av/services/mediametrics/AudioTypes.cpp - // (for playback threads): - // See lookup<OUTPUT_DEVICE>() in frameworks/av/services/mediametrics/AudioTypes.cpp - // See audio_device_t in system/media/audio/include/system/audio-base.h - optional string devices = 1; - - // The name of the remote device attached to the device, typically available for USB or BT. - // This may be empty for a fixed device, or separated by "|" if more than one. - optional string device_names = 2; - - // The amount of time spent in the device as measured by the active track in AudioFlinger. - optional int64 device_time_nanos = 3; - - // The audio data format used for encoding. - // An enumeration from system/media/audio/include/system/audio-base.h audio_format_t - optional string encoding = 4; - - // The framecount of the buffer delivered to (or from) the HAL. - // The framecount is generally ~960 for PCM encoding. - // The framecount represents raw buffer size in bytes for non-PCM encoding. - optional int32 frame_count = 5; - - // The number of audio intervals (contiguous, continuous playbacks). - optional int32 interval_count = 6; - - // The sample rate of the audio thread. - // A number generally between 8000-96000 (frames per second). - optional int32 sample_rate = 7; - - // The audio flags used to construct the thread - // (for record threads): - // A string OR from system/media/audio/include/system/audio-base.h audio_input_flags_t - // (for playback threads): - // A string OR from system/media/audio/include/system/audio-base.h audio_output_flags_t - optional string flags = 8; - - // The number of underruns encountered for a playback thread or the - // number of overruns encountered for a capture thread. - optional int32 xruns = 9; - - // The type of thread - // A thread type enumeration from - // frameworks/av/mediametrics/services/Translate.h - optional string type = 10; -} - -/** - * Logs when an AudioTrack finishes running on an audio device - * - * Logged from: - * frameworks/av/services/mediametrics/AudioAnalytics.cpp - */ -message MediametricsAudioTrackDeviceUsageReported { - // The output devices connected to this AudioTrack. - // A string OR of various output device categories, e.g. "DEVICE1|DEVICE2". - // See lookup<OUTPUT_DEVICE>() in frameworks/av/services/mediametrics/AudioTypes.cpp - // See audio_device_t in system/media/audio/include/system/audio-base.h - optional string devices = 1; - - // The name of the remote device attached to the device, typically available for USB or BT. - // This may be empty for a fixed device, or separated by "|" if more than one. - optional string device_names = 2; - - // The amount of time spent in the device as measured by the active track in AudioFlinger. - optional int64 device_time_nanos = 3; - - // The audio data format used for encoding. - // An enumeration from system/media/audio/include/system/audio-base.h audio_format_t - optional string encoding = 4; - - // The client-server buffer framecount. - // The framecount is generally between 960 - 48000 for PCM encoding. - // The framecount represents raw buffer size in bytes for non-PCM encoding. - // A static track (see traits) may have a very large framecount. - optional int32 frame_count = 5; - - // The number of audio intervals (contiguous, continuous playbacks). - optional int32 interval_count = 6; - - // The sample rate of the AudioTrack. - // A number generally between 8000-96000 (frames per second). - optional int32 sample_rate = 7; - - // The audio flags used to construct the AudioTrack. - // A string OR from system/media/audio/include/system/audio-base.h audio_output_flags_t - optional string flags = 8; - - // The number of underruns encountered. - optional int32 xruns = 9; - - // The santized package name of the audio client associated with the AudioTrack. - // See getSanitizedPackageNameAndVersionCode() in - // frameworks/av/services/mediametrics/MediaMetricsService.cpp - optional string package_name = 10; - - // The latency of the last sample in the buffer in milliseconds. - optional float device_latency_millis = 11; - - // The startup time in milliseconds from start() to sample played. - optional float device_startup_millis = 12; - - // The average volume of the track on the device [ 0.f - 1.f ] - optional float device_volume = 13; - - // The selected device id (nonzero if a non-default device is selected) - optional int32 selected_device_id = 14; - - // The stream_type category for the AudioTrack. - // An enumeration from system/media/audio/include/system/audio-base.h audio_stream_type_t - optional string stream_type = 15; - - // The usage for the AudioTrack. - // An enumeration from system/media/audio/include/system/audio-base.h audio_usage_t - optional string usage = 16; - - // The content type of the AudioTrack. - // An enumeration from system/media/audio/include/system/audio-base.h audio_content_type_t - optional string content_type = 17; - - // The caller of the AudioTrack. - // See lookup<CALLER_NAME>() in frameworks/av/services/mediametrics/AudioTypes.cpp - optional string caller = 18; - - // The traits of the AudioTrack. - // A string OR of different traits, may be empty string. - // Only "static" is supported for R. - // See lookup<TRACK_TRAITS>() in frameworks/av/services/mediametrics/AudioTypes.cpp - optional string traits = 19; -} - -/** - * Logs the status of an audio device connection attempt. - * - * Logged from: - * frameworks/av/services/mediametrics/AudioAnalytics.cpp - */ -message MediametricsAudioDeviceConnectionReported { - // The input devices represented by this report. - // A string OR of various input device categories, e.g. "DEVICE1|DEVICE2". - // See lookup<INPUT_DEVICE>() in frameworks/av/services/mediametrics/AudioTypes.cpp - // See audio_device_t in system/media/audio/include/system/audio-base.h - optional string input_devices = 1; - - // The output devices represented by this report. - // A string OR of various output device categories. - // See lookup<OUTPUT_DEVICE>() in frameworks/av/services/mediametrics/AudioTypes.cpp - // See audio_device_t in system/media/audio/include/system/audio-base.h - optional string output_devices = 2; - - // The name of the remote device attached to the device, typically available for USB or BT. - // This may be empty for a fixed device, or separated by "|" if more than one. - optional string device_names = 3; - - // The result of the audio device connection. - // 0 indicates success: connection verified. - // 1 indicates unknown: connection not verified or not known if diverted properly. - // Other values indicate specific status. - // See DeviceConnectionResult in frameworks/av/services/mediametrics/AudioTypes.h - optional int32 result = 4; - - // Average milliseconds of time to connect - optional float time_to_connect_millis = 5; - - // Number of connections if aggregated statistics, otherwise 1. - optional int32 connection_count = 6; -} - -/** - * Logs: i) creation of different types of cryptographic keys in the keystore, - * ii) operations performed using the keys, - * iii) attestation of the keys - * Logged from: system/security/keystore/key_event_log_handler.cpp - */ -message KeystoreKeyEventReported { - - enum Algorithm { - /** Asymmetric algorithms. */ - RSA = 1; - // 2 removed, do not reuse. - EC = 3; - /** Block cipher algorithms */ - AES = 32; - TRIPLE_DES = 33; - /** MAC algorithms */ - HMAC = 128; - }; - /** Algorithm associated with the key */ - optional Algorithm algorithm = 1; - - /** Size of the key */ - optional int32 key_size = 2; - - enum KeyOrigin { - /** Generated in keymaster. Should not exist outside the TEE. */ - GENERATED = 0; - /** Derived inside keymaster. Likely exists off-device. */ - DERIVED = 1; - /** Imported into keymaster. Existed as cleartext in Android. */ - IMPORTED = 2; - /** Keymaster did not record origin. */ - UNKNOWN = 3; - /** Securely imported into Keymaster. */ - SECURELY_IMPORTED = 4; - }; - /* Logs whether the key was generated, imported, securely imported, or derived.*/ - optional KeyOrigin key_origin = 3; - - enum HardwareAuthenticatorType { - NONE = 0; - PASSWORD = 1; - FINGERPRINT = 2; - // Additional entries must be powers of 2. - }; - /** - * What auth types does this key require? If none, - * then no auth required. - */ - optional HardwareAuthenticatorType user_auth_type = 4; - - /** - * If user authentication is required, is the requirement time based? If it - * is not time based then this field will not be used and the key is per - * operation. Per operation keys must be user authenticated on each usage. - */ - optional int32 user_auth_key_timeout_secs = 5; - - /** - * padding mode, digest, block_mode and purpose should ideally be repeated - * fields. However, since statsd does not support repeated fields in - * pushed atoms, they are represented using bitmaps. - */ - - /** Track which padding mode is being used.*/ - optional int32 padding_mode_bitmap = 6; - - /** Track which digest is being used. */ - optional int32 digest_bitmap = 7; - - /** Track what block mode is being used (for encryption). */ - optional int32 block_mode_bitmap = 8; - - /** Track what purpose is this key serving. */ - optional int32 purpose_bitmap = 9; - - enum EcCurve { - P_224 = 0; - P_256 = 1; - P_384 = 2; - P_521 = 3; - }; - /** Which ec curve was selected if elliptic curve cryptography is in use **/ - optional EcCurve ec_curve = 10; - - enum KeyBlobUsageRequirements { - STANDALONE = 0; - REQUIRES_FILE_SYSTEM = 1; - }; - /** Standalone or is a file system required */ - optional KeyBlobUsageRequirements key_blob_usage_reqs = 11; - - enum Type { - key_operation = 0; - key_creation = 1; - key_attestation = 2; - } - /** Key creation event, operation event or attestation event? */ - optional Type type = 12; - - /** Was the key creation, operation, or attestation successful? */ - optional bool was_successful = 13; - - /** Response code or error code */ - optional int32 error_code = 14; -} - -// Blob Committer stats -// Keep in sync between: -// frameworks/base/core/proto/android/server/blobstoremanagerservice.proto -// frameworks/base/cmds/statsd/src/atoms.proto -message BlobCommitterProto { - // Committer app's uid - optional int32 uid = 1 [(is_uid) = true]; - - // Unix epoch timestamp of the commit in milliseconds - optional int64 commit_timestamp_millis = 2; - - // Flags of what access types the committer has set for the Blob - optional int32 access_mode = 3; - - // Number of packages that have been whitelisted for ACCESS_TYPE_WHITELIST - optional int32 num_whitelisted_package = 4; -} - -// Blob Leasee stats -// Keep in sync between: -// frameworks/base/core/proto/android/server/blobstoremanagerservice.proto -// frameworks/base/cmds/statsd/src/atoms.proto -message BlobLeaseeProto { - // Leasee app's uid - optional int32 uid = 1 [(is_uid) = true]; - - // Unix epoch timestamp for lease expiration in milliseconds - optional int64 lease_expiry_timestamp_millis = 2; -} - -// List of Blob Committers -// Keep in sync between: -// frameworks/base/core/proto/android/server/blobstoremanagerservice.proto -// frameworks/base/cmds/statsd/src/atoms.proto -message BlobCommitterListProto { - repeated BlobCommitterProto committer = 1; -} - -// List of Blob Leasees -// Keep in sync between: -// frameworks/base/core/proto/android/server/blobstoremanagerservice.proto -// frameworks/base/cmds/statsd/src/atoms.proto -message BlobLeaseeListProto { - repeated BlobLeaseeProto leasee = 1; -} - -/** - * Logs the current state of a Blob committed with BlobStoreManager - * - * Pulled from: - * frameworks/base/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java - */ -message BlobInfo { - // Id of the Blob - optional int64 blob_id = 1; - - // Size of the Blob data - optional int64 size = 2; - - // Unix epoch timestamp of the Blob's expiration in milliseconds - optional int64 expiry_timestamp_millis = 3; - - // List of committers of this Blob - optional BlobCommitterListProto committers = 4 [(log_mode) = MODE_BYTES]; - - // List of leasees of this Blob - optional BlobLeaseeListProto leasees = 5 [(log_mode) = MODE_BYTES]; -} - -/** - * Logs when the HDMI CEC active source changes. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/hdmi/HdmiCecAtomWriter.java - */ -message HdmiCecActiveSourceChanged { - // The logical address of the active source. - optional android.stats.hdmi.LogicalAddress active_source_logical_address = 1; - - // The physical address of the active source. Consists of four hexadecimal nibbles. - // Examples: 0x1234, 0x0000 (root device). 0xFFFF represents an unknown or invalid address. - // See section 8.7 in the HDMI 1.4b spec for details. - optional int32 active_source_physical_address = 2; - - // The relationship between this device and the active source. - optional android.stats.hdmi.PathRelationship local_relationship = 3; -} - -/** - * Logs when an HDMI CEC message is sent or received. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/hdmi/HdmiCecAtomWriter.java - */ -message HdmiCecMessageReported { - // The calling uid of the application that caused this atom to be written. - optional int32 uid = 1 [(is_uid) = true]; - - // Whether a HDMI CEC message is sent from this device, to this device, or neither. - optional android.stats.hdmi.MessageDirection direction = 2; - - // The HDMI CEC logical address of the initiator. - optional android.stats.hdmi.LogicalAddress initiator_logical_address = 3; - - // The HDMI CEC logical address of the destination. - optional android.stats.hdmi.LogicalAddress destination_logical_address = 4; - - // The opcode of the message. Ranges from 0x00 to 0xFF. - // For all values, see section "CEC 15 Message Descriptions" in the HDMI CEC 1.4b spec. - optional int32 opcode = 5; - - // The result of attempting to send the message on its final retransmission attempt. - // Only applicable to outgoing messages; set to SEND_MESSAGE_RESULT_UNKNOWN otherwise. - optional android.stats.hdmi.SendMessageResult send_message_result = 6; - - // Fields specific to <User Control Pressed> messages - - // The user control command that was received. - optional android.stats.hdmi.UserControlPressedCommand user_control_pressed_command = 7; - - // Fields specific to <Feature Abort> messages - - // The opcode of the message that was feature aborted. - // Set to 0x100 when unknown or not applicable. - optional int32 feature_abort_opcode = 8; - - // The reason for the feature abort. - optional android.stats.hdmi.FeatureAbortReason feature_abort_reason = 9; -} - -/** - * Pushes TLS handshake counters from Conscrypt. - * Pulled from: - * external/conscrypt/common/src/main/java/org/conscrypt/ConscryptEngineSocket.java - * external/conscrypt/common/src/main/java/org/conscrypt/ConscryptFileDescriptorSocket.java - */ -message TlsHandshakeReported { - optional bool success = 1; - - optional android.stats.tls.Protocol protocol = 2; - - optional android.stats.tls.CipherSuite cipher_suite = 3; - - optional int32 handshake_duration_millis = 4; -} - -/** - * Logs when a TextClassifier API is invoked. - * - * See frameworks/base/core/java/android/view/textclassifier/TextClassifier.java - * Logged from: external/libtextclassifier/java/ - */ -message TextClassifierApiUsageReported { - enum ApiType { - UNKNOWN_API = 0; - SUGGEST_SELECTION = 1; - CLASSIFY_TEXT = 2; - GENERATE_LINKS = 3; - DETECT_LANGUAGES = 4; - SUGGEST_CONVERSATION_ACTIONS = 5; - } - optional ApiType api_type = 1; - - enum ResultType { - UNKNOWN_RESULT = 0; - SUCCESS = 1; - FAIL = 2; - } - optional ResultType result_type = 2; - optional int64 latency_millis = 3; -} - -/** - * Logs the current state of an application before it is killed. - * - * Pushed from: - * packages/services/Car/watchdog/server/src/ApplicationTerminator.cpp - */ -message KilledAppStatsReported { - // Linux process uid for the package. - optional int32 uid = 1 [(is_uid) = true]; - - // Name of the package that was killed. - optional string package_name = 2; - - // State of the application when it was killed. - enum AppState { - UNKNOWN_APP_STATE = 0; - BACKGROUND = 1; - FOREGROUND = 2; - } - optional AppState app_state = 3; - - // System state indicating whether the system was in normal mode or garage mode. - enum SystemState { - UNKNOWN_SYSTEM_STATE = 0; - USER_INTERACTION_MODE = 1; - NO_USER_INTERACTION_MODE = 2; - } - optional SystemState system_state = 4; - - // Reason for killing the application. - // Keep in sync between: - // packages/services/Car/watchdog/server/src/ApplicationTerminator.h - // frameworks/base/cmds/statsd/src/atoms.proto - enum KillReason { - UNKNOWN_KILL_REASON = 0; - KILLED_ON_ANR = 1; - KILLED_ON_IO_OVERUSE = 2; - KILLED_ON_MEMORY_OVERUSE = 3; - } - optional KillReason kill_reason = 5; - - // Stats of the processes owned by the application when the application was killed. - // The process stack traces are not collected when the application was killed due to IO_OVERUSE. - optional ProcessStats process_stat = 6 [(log_mode) = MODE_BYTES]; - - // The application's I/O overuse stats logged only when the kill reason is KILLED_ON_IO_OVERUSE. - optional IoOveruseStats io_overuse_stats = 7 [(log_mode) = MODE_BYTES]; -} - -/** - * Logs I/O overuse stats for a package. - * - * Keep in sync between: - * packages/services/Car/watchdog/server/src/proto/statsd.proto - * frameworks/base/cmds/statsd/src/atoms.proto - * - * Logged from: - * packages/services/Car/watchdog/server/src/ApplicationTerminator.cpp - */ -message IoOveruseStats { - enum Period { - DAILY = 0; - WEEKLY = 1; - } - - // Threshold and usage stats period. - optional Period period = 1; - - // Threshold in-terms of write bytes defined for the package. - optional PerStateBytes threshold = 2; - - // Number of write bytes in each state for the specified period. - optional PerStateBytes written_bytes = 3; -}; - -/** - * Logs bytes attributed to each application and system states. - * - * Keep in sync between: - * packages/services/Car/watchdog/server/src/proto/statsd.proto - * frameworks/base/cmds/statsd/src/atoms.proto - * - * Logged from: - * packages/services/Car/watchdog/server/src/ApplicationTerminator.cpp - */ -message PerStateBytes { - // Number of bytes attributed to the application foreground. - optional int64 foreground_bytes = 1; - - // Number of bytes attributed to the application background. - optional int64 background_bytes = 2; - - // Number of bytes attributed to the garage mode. - optional int64 garage_mode_bytes = 3; -} - -/** - * Logs each ProcessStat in ProcessStats. - * Keep in sync between: - * packages/services/Car/watchdog/server/src/proto/statsd.proto - * frameworks/base/cmds/statsd/src/atoms.proto - * Logged from: - * packages/services/Car/watchdog/server/src/ApplicationTerminator.cpp - */ -message ProcessStats { - // Records the stats of the processes owned by an application. - repeated ProcessStat process_stat = 1; -} - -/** - * Logs a process's stats. - * Keep in sync between: - * packages/services/Car/watchdog/server/src/proto/statsd.proto - * frameworks/base/cmds/statsd/src/atoms.proto - * Logged from: - * packages/services/Car/watchdog/server/src/ApplicationTerminator.cpp - */ -message ProcessStat { - // Command name of the process. - optional string process_name = 1; - - // Process uptime. - optional uint64 uptime_milliseconds = 2; - - // Number of major page faults caused by the process and its children. - optional uint64 major_page_faults = 3; - - // Peak virtual memory size in kb. - optional uint64 vm_peak_kb = 4; - - // Virtual memory size in kb. - optional uint64 vm_size_kb = 5; - - // Peak resident set size (high water mark) in kb. - optional uint64 vm_hwm_kb = 6; - - // Resident set size in kb. - optional uint64 vm_rss_kb = 7; -} - -/* - * pushes Media playback information. - * Logged from - * (WIP) frameworks/av/services/mediaanalytics/playback_statsd.cpp - */ -message MediametricsPlaybackReported { - optional int32 uid = 1 [(is_uid) = true]; - - // Randomly generated playback ID. A Base64 encoded hex string representing a 128-bit integer - optional string playback_id = 2; - // The total length of the media in milliseconds. 0 for live contents. - optional int64 media_duration_millis = 3; - // Network, device, or mixed. - optional android.stats.mediametrics.StreamSourceType stream_source = 4; - // Stream type. DASH, HLS, etc - optional android.stats.mediametrics.StreamType stream_type = 5; - // Live, VOD, others - optional android.stats.mediametrics.PlaybackType playback_type = 6; - // DRM type - optional android.stats.mediametrics.DrmType drm_type = 7; - // Main, AD, others - optional android.stats.mediametrics.ContentType content_type = 8; - // Player name. E.g. ExoPlayer - optional string player_name = 9; - // Player version. E.g. 1.10.3e - optional string player_version = 10; - // Player related experiment IDs - optional Experiments experiment_ids = 11 [(log_mode) = MODE_BYTES]; - // Number of frames played. Dropped frames included. -1 means unknown. - optional int32 video_frames_played = 12; - // Number of frames dropped. -1 means unknown. - optional int32 video_frames_dropped = 13; - // Number of audio underruns. -1 means unknown. - optional int32 audio_underrun_count = 14; - // Total number of bytes read from the network - optional int64 network_bytes_read = 15; - // Total number of bytes read from on-device sources - optional int64 local_bytes_read = 16; - // Total transfer spent reading from the network in ms. - // For parallel requests, the overlapping time intervals are counted only once. - optional int64 network_transfer_duration_millis = 17; -} - -message MediaNetworkInfoChanged { - // Randomly generated playback ID. A Base64 encoded hex string representing a 128-bit integer - optional string playback_id = 1; - // New network type - optional android.stats.mediametrics.NetworkType type = 2; - // Network Start time, relative to playback creation time in millisecond. - // It's in absolute time (e.g. always ticks even if the playback is paused). - optional int64 time_since_playback_created_millis = 3; -} - -message MediaPlaybackStateChanged { - // Randomly generated playback ID. A Base64 encoded hex string representing a 128-bit integer - optional string playback_id = 1; - // New playback state - optional android.stats.mediametrics.PlaybackState playback_state = 2; - // State change time, relative to playback creation time in millisecond. - // It's in absolute time (e.g. always ticks even if the playback is paused). - optional int64 time_since_playback_created_millis = 3; -} - -message MediaPlaybackErrorReported { - // Randomly generated playback ID. A Base64 encoded hex string representing a 128-bit integer - optional string playback_id = 1; - // A shortened call stack of the error - optional string exception_stack = 2; - // Error code - optional android.stats.mediametrics.PlaybackErrorCode error_code = 3; - // Sub-code of error type specified by the error code. - optional int32 sub_error_code = 4; - // Error time, relative to playback creation time in millisecond. - // It's in absolute time (e.g. always ticks even if the playback is paused). - optional int64 time_since_playback_created_millis = 5; -} - -message MediaPlaybackTrackChanged { - // Randomly generated playback ID. A Base64 encoded hex string representing a 128-bit integer - optional string playback_id = 1; - // The track is on or off after the change - optional android.stats.mediametrics.TrackState state = 2; - // The reason of the track change - optional android.stats.mediametrics.TrackChangeReason reason = 3; - // The MIME type of the container. E.g. video/mp4 - optional string container_mime_type = 4; - // The sample MIME type of the track. E.g. video/avc - optional string sample_mime_type = 5; - - // Codec name - optional string codec_name = 6; - // Bits per second. 0 means unknown. - optional int32 bitrate = 7; - - // Track change time, relative to playback creation time in millisecond. - // It's in absolute time (e.g. always ticks even if the playback is paused). - optional int64 time_since_playback_created_millis = 8; - - // Track type. Audio, Video, Text - optional android.stats.mediametrics.TrackType type = 9; - // 2-letter ISO 639-1 language code. - optional string language = 10; - // IETF BCP 47 optional language region subtag based on a two-letter country code - optional string language_region = 11; - // Number of channels - optional int32 channel_count = 12; - // Samples per second - optional int32 sample_rate = 13; - // The width of the video in pixels. - optional int32 width = 14; - // The height of the video in pixels. - optional int32 height = 15; -} - -message Experiments { - // Experiment IDs sent by the player. - repeated int64 experiments = 1; -} - -/** - * Logs when a Wifi network scan happens. - * - * Logged from: - * frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiMetrics.java - */ -message WifiScanReported { - enum Type { - TYPE_UNKNOWN = 0; - - // Single scan. - TYPE_SINGLE = 1; - - // Background scan (deprecated, should not happen). - TYPE_BACKGROUND = 2; - } - - enum Result { - RESULT_UNKNOWN = 0; - - // Failed to start scan. - RESULT_FAILED_TO_START = 1; - - // The HAL reported a scan failure after the scan was started. - RESULT_FAILED_TO_SCAN = 2; - - // Scan succeeded. - RESULT_SUCCESS = 3; - } - - enum Source { - SOURCE_UNKNOWN = 0; - - // No work source set - not possible to determine the origin. - SOURCE_NO_WORK_SOURCE = 1; - - // The Wifi stack. - SOURCE_WIFI_STACK = 2; - - // GMS on behalf of some other app. - SOURCE_GMS = 3; - - // Settings app. - SOURCE_SETTINGS_APP = 4; - - // Other app directly. - SOURCE_OTHER_APP = 5; - } - - enum Importance { - IMPORTANCE_UNKNOWN = 0; - - // Foreground app. Corresponds to the value of - // ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND or less. - IMPORTANCE_FOREGROUND = 1; - - // Foreground service. Corresponds to the value of - // ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE - IMPORTANCE_FOREGROUND_SERVICE = 2; - - // Everything else. - IMPORTANCE_BACKGROUND = 3; - } - - // Scan type - optional Type type = 1; - - // Outcome: success/failure - optional Result result = 2; - - // What initiated a scan. - optional Source source = 3; - - // Process importance of the initiator. - // This is only available for non-system calls. - optional Importance importance = 4; - - // Time taken for the scan. - optional int32 scan_duration_millis = 5; - - // Count of found networks. - optional int32 count_networks_found = 6; -} - -/** - * Logs when a Wifi PNO (Preferred Network Offload) scan happens. - * - * Logged from: - * frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiMetrics.java - */ -message WifiPnoScanReported { - enum State { - UNKNOWN = 0; - - // Scan started. - STARTED = 1; - - // Scan failed to start (e.g. bad request, unsupported by hardware, etc). - FAILED_TO_START = 2; - - // Scan completed and a network was found. - // Note - due to implementation constraints, nothing is reported when a scan completes but - // doesn't find any networks. - FINISHED_NETWORKS_FOUND = 3; - - // Scan failed. - FAILED = 4; - } - - optional State state = 1; -} diff --git a/cmds/statsd/src/shell/shell_data.proto b/cmds/statsd/src/shell/shell_data.proto index 236bdbdd31f6..ec41cbc5caff 100644 --- a/cmds/statsd/src/shell/shell_data.proto +++ b/cmds/statsd/src/shell/shell_data.proto @@ -21,7 +21,7 @@ package android.os.statsd; option java_package = "com.android.os.statsd"; option java_outer_classname = "ShellDataProto"; -import "frameworks/base/cmds/statsd/src/atoms.proto"; +import "frameworks/proto_logging/stats/atoms.proto"; // The output of shell subscription, including both pulled and pushed subscriptions. message ShellData { diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto index ddd2725c9cb9..bb0796326f01 100644 --- a/cmds/statsd/src/stats_log.proto +++ b/cmds/statsd/src/stats_log.proto @@ -21,7 +21,7 @@ package android.os.statsd; option java_package = "com.android.os"; option java_outer_classname = "StatsLog"; -import "frameworks/base/cmds/statsd/src/atoms.proto"; +import "frameworks/proto_logging/stats/atoms.proto"; message DimensionsValue { optional int32 field = 1; diff --git a/cmds/statsd/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp index 5c170c07eb7d..bde59f497b19 100644 --- a/cmds/statsd/tests/LogEvent_test.cpp +++ b/cmds/statsd/tests/LogEvent_test.cpp @@ -16,8 +16,8 @@ #include <gtest/gtest.h> -#include "frameworks/base/cmds/statsd/src/atoms.pb.h" -#include "frameworks/base/core/proto/android/stats/launcher/launcher.pb.h" +#include "frameworks/proto_logging/stats/atoms.pb.h" +#include "frameworks/proto_logging/stats/enums/stats/launcher/launcher.pb.h" #include "log/log_event_list.h" #include "stats_event.h" diff --git a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp index 4fa4135e983f..1ba8593231b0 100644 --- a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp +++ b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp @@ -20,9 +20,9 @@ #include <vector> -#include "frameworks/base/cmds/statsd/src/atoms.pb.h" #include "frameworks/base/cmds/statsd/src/shell/shell_config.pb.h" #include "frameworks/base/cmds/statsd/src/shell/shell_data.pb.h" +#include "frameworks/proto_logging/stats/atoms.pb.h" #include "stats_event.h" #include "tests/metrics/metrics_test_helper.h" #include "tests/statsd_test_util.h" diff --git a/config/preloaded-classes b/config/preloaded-classes index 822b40be811a..8d2f1434b12f 100644 --- a/config/preloaded-classes +++ b/config/preloaded-classes @@ -5575,7 +5575,6 @@ android.os.AsyncTask android.os.BadParcelableException android.os.BaseBundle$NoImagePreloadHolder android.os.BaseBundle -android.os.BasicShellCommandHandler android.os.BatteryManager android.os.BatteryManagerInternal android.os.BatteryProperty$1 @@ -6750,6 +6749,7 @@ android.sysprop.-$$Lambda$TelephonyProperties$illdaSIVv8AlxP9uc8NqC3Ta1tA android.sysprop.-$$Lambda$TelephonyProperties$klELuV5zVSqFveC5l6c3FSJmLAU android.sysprop.-$$Lambda$TelephonyProperties$pFU8zg4eHAdooeRLJg1WBG52cKk android.sysprop.-$$Lambda$TelephonyProperties$sXc3eBCFirzHWb9pvClH7EsiM_Q +android.stats.camera.nano.CameraStreamProto android.sysprop.AdbProperties android.sysprop.ApexProperties android.sysprop.ContactsProperties @@ -11756,6 +11756,7 @@ com.android.net.module.annotation.VisibleForTesting$Visibility com.android.net.module.annotation.VisibleForTesting com.android.net.module.annotation.WorkerThread com.android.net.module.util.InetAddressUtils +com.android.modules.utils.BasicShellCommandHandler com.android.okhttp.Address com.android.okhttp.AndroidShimResponseCache com.android.okhttp.Authenticator diff --git a/core/api/current.txt b/core/api/current.txt index 697710f4c5c0..e302b25873b4 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -10158,6 +10158,7 @@ package android.content { method public abstract void grantUriPermission(String, android.net.Uri, int); method public abstract boolean isDeviceProtectedStorage(); method public boolean isRestricted(); + method public static boolean isUiContext(@NonNull android.content.Context); method public abstract boolean moveDatabaseFrom(android.content.Context, String); method public abstract boolean moveSharedPreferencesFrom(android.content.Context, String); method @NonNull public final android.content.res.TypedArray obtainStyledAttributes(@NonNull @StyleableRes int[]); @@ -12104,6 +12105,8 @@ package android.content.pm { method public abstract android.content.pm.PermissionInfo getPermissionInfo(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException; method @Deprecated public abstract int getPreferredActivities(@NonNull java.util.List<android.content.IntentFilter>, @NonNull java.util.List<android.content.ComponentName>, @Nullable String); method @Deprecated @NonNull public abstract java.util.List<android.content.pm.PackageInfo> getPreferredPackages(int); + method @NonNull public android.content.pm.PackageManager.Property getProperty(@NonNull String, @NonNull String) throws android.content.pm.PackageManager.NameNotFoundException; + method @NonNull public android.content.pm.PackageManager.Property getProperty(@NonNull String, @NonNull android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException; method @NonNull public abstract android.content.pm.ProviderInfo getProviderInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException; method @NonNull public abstract android.content.pm.ActivityInfo getReceiverInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException; method @NonNull public abstract android.content.res.Resources getResourcesForActivity(@NonNull android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException; @@ -12136,6 +12139,8 @@ package android.content.pm { method public boolean isPackageSuspended(); method @CheckResult public abstract boolean isPermissionRevokedByPolicy(@NonNull String, @NonNull String); method public abstract boolean isSafeMode(); + method @NonNull public java.util.List<android.content.pm.PackageManager.Property> queryActivityProperty(@NonNull String); + method @NonNull public java.util.List<android.content.pm.PackageManager.Property> queryApplicationProperty(@NonNull String); method @NonNull public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(@NonNull android.content.Intent, int); method @NonNull public abstract java.util.List<android.content.pm.ProviderInfo> queryContentProviders(@Nullable String, int, int); method @NonNull public abstract java.util.List<android.content.pm.InstrumentationInfo> queryInstrumentation(@NonNull String, int); @@ -12144,6 +12149,9 @@ package android.content.pm { method @NonNull public abstract java.util.List<android.content.pm.ResolveInfo> queryIntentContentProviders(@NonNull android.content.Intent, int); method @NonNull public abstract java.util.List<android.content.pm.ResolveInfo> queryIntentServices(@NonNull android.content.Intent, int); method @NonNull public abstract java.util.List<android.content.pm.PermissionInfo> queryPermissionsByGroup(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException; + method @NonNull public java.util.List<android.content.pm.PackageManager.Property> queryProviderProperty(@NonNull String); + method @NonNull public java.util.List<android.content.pm.PackageManager.Property> queryReceiverProperty(@NonNull String); + method @NonNull public java.util.List<android.content.pm.PackageManager.Property> queryServiceProperty(@NonNull String); method @Deprecated public abstract void removePackageFromPreferred(@NonNull String); method public abstract void removePermission(@NonNull String); method @RequiresPermission(value="android.permission.WHITELIST_RESTRICTED_PERMISSIONS", conditional=true) public boolean removeWhitelistedRestrictedPermission(@NonNull String, @NonNull String, int); @@ -12332,7 +12340,7 @@ package android.content.pm { field public static final int SIGNATURE_SECOND_NOT_SIGNED = -2; // 0xfffffffe field public static final int SIGNATURE_UNKNOWN_PACKAGE = -4; // 0xfffffffc field public static final int SYNCHRONOUS = 2; // 0x2 - field @Nullable public static final java.util.List<java.security.cert.Certificate> TRUST_ALL; + field @NonNull public static final java.util.List<java.security.cert.Certificate> TRUST_ALL; field @NonNull public static final java.util.List<java.security.cert.Certificate> TRUST_NONE; field public static final int VERIFICATION_ALLOW = 1; // 0x1 field public static final int VERIFICATION_REJECT = -1; // 0xffffffff @@ -12344,6 +12352,25 @@ package android.content.pm { ctor public PackageManager.NameNotFoundException(String); } + public static final class PackageManager.Property implements android.os.Parcelable { + method public int describeContents(); + method public boolean getBoolean(); + method @Nullable public String getClassName(); + method public float getFloat(); + method public int getInteger(); + method @NonNull public String getName(); + method @NonNull public String getPackageName(); + method public int getResourceId(); + method @Nullable public String getString(); + method public boolean isBoolean(); + method public boolean isFloat(); + method public boolean isInteger(); + method public boolean isResourceId(); + method public boolean isString(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.PackageManager.Property> CREATOR; + } + @Deprecated public class PackageStats implements android.os.Parcelable { ctor @Deprecated public PackageStats(String); ctor @Deprecated public PackageStats(android.os.Parcel); @@ -30556,6 +30583,7 @@ package android.net { method public static long getMobileRxPackets(); method public static long getMobileTxBytes(); method public static long getMobileTxPackets(); + method public static long getRxBytes(@NonNull String); method public static long getRxPackets(@NonNull String); method public static int getThreadStatsTag(); method public static int getThreadStatsUid(); @@ -30563,6 +30591,7 @@ package android.net { method public static long getTotalRxPackets(); method public static long getTotalTxBytes(); method public static long getTotalTxPackets(); + method public static long getTxBytes(@NonNull String); method public static long getTxPackets(@NonNull String); method public static long getUidRxBytes(int); method public static long getUidRxPackets(int); @@ -35793,6 +35822,7 @@ package android.os { method @NonNull public android.os.StrictMode.VmPolicy.Builder detectCredentialProtectedWhileLocked(); method @NonNull public android.os.StrictMode.VmPolicy.Builder detectFileUriExposure(); method @NonNull public android.os.StrictMode.VmPolicy.Builder detectImplicitDirectBoot(); + method @NonNull public android.os.StrictMode.VmPolicy.Builder detectIncorrectContextUse(); method @NonNull public android.os.StrictMode.VmPolicy.Builder detectLeakedClosableObjects(); method @NonNull public android.os.StrictMode.VmPolicy.Builder detectLeakedRegistrationObjects(); method @NonNull public android.os.StrictMode.VmPolicy.Builder detectLeakedSqlLiteObjects(); @@ -40948,6 +40978,19 @@ package android.se.omapi { package android.security { + public final class AppUriAuthenticationPolicy implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public java.util.Map<java.lang.String,java.util.Map<android.net.Uri,java.lang.String>> getAppAndUriMappings(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.security.AppUriAuthenticationPolicy> CREATOR; + } + + public static final class AppUriAuthenticationPolicy.Builder { + ctor public AppUriAuthenticationPolicy.Builder(); + method @NonNull public android.security.AppUriAuthenticationPolicy.Builder addAppAndUriMapping(@NonNull String, @NonNull android.net.Uri, @NonNull String); + method @NonNull public android.security.AppUriAuthenticationPolicy build(); + } + public final class AttestedKeyPair { ctor public AttestedKeyPair(@Nullable java.security.KeyPair, @NonNull java.util.List<java.security.cert.Certificate>); method @NonNull public java.util.List<java.security.cert.Certificate> getAttestationRecord(); @@ -40995,6 +41038,7 @@ package android.security { method public static void choosePrivateKeyAlias(@NonNull android.app.Activity, @NonNull android.security.KeyChainAliasCallback, @Nullable String[], @Nullable java.security.Principal[], @Nullable String, int, @Nullable String); method public static void choosePrivateKeyAlias(@NonNull android.app.Activity, @NonNull android.security.KeyChainAliasCallback, @Nullable String[], @Nullable java.security.Principal[], @Nullable android.net.Uri, @Nullable String); method @NonNull public static android.content.Intent createInstallIntent(); + method @NonNull public static android.content.Intent createManageCredentialsIntent(@NonNull android.security.AppUriAuthenticationPolicy); method @Nullable @WorkerThread public static java.security.cert.X509Certificate[] getCertificateChain(@NonNull android.content.Context, @NonNull String) throws java.lang.InterruptedException, android.security.KeyChainException; method @Nullable @WorkerThread public static java.security.PrivateKey getPrivateKey(@NonNull android.content.Context, @NonNull String) throws java.lang.InterruptedException, android.security.KeyChainException; method @Deprecated public static boolean isBoundKeyAlgorithm(@NonNull String); @@ -44525,6 +44569,7 @@ package android.telecom { method public android.telecom.PhoneAccount.Builder toBuilder(); method public void writeToParcel(android.os.Parcel, int); field public static final int CAPABILITY_ADHOC_CONFERENCE_CALLING = 16384; // 0x4000 + field public static final int CAPABILITY_CALL_COMPOSER = 32768; // 0x8000 field public static final int CAPABILITY_CALL_PROVIDER = 2; // 0x2 field public static final int CAPABILITY_CALL_SUBJECT = 64; // 0x40 field public static final int CAPABILITY_CONNECTION_MANAGER = 1; // 0x1 @@ -46190,12 +46235,6 @@ package android.telephony { field public static final int SCAN_TYPE_PERIODIC = 1; // 0x1 } - public final class PhoneCapability implements android.os.Parcelable { - method public int describeContents(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.telephony.PhoneCapability> CREATOR; - } - public class PhoneNumberFormattingTextWatcher implements android.text.TextWatcher { ctor public PhoneNumberFormattingTextWatcher(); ctor public PhoneNumberFormattingTextWatcher(String); @@ -46264,10 +46303,10 @@ package android.telephony { public class PhoneStateListener { ctor public PhoneStateListener(); - ctor @Deprecated public PhoneStateListener(@NonNull java.util.concurrent.Executor); + ctor public PhoneStateListener(@NonNull java.util.concurrent.Executor); method public void onActiveDataSubscriptionIdChanged(int); method public void onBarringInfoChanged(@NonNull android.telephony.BarringInfo); - method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onCallDisconnectCauseChanged(int, int); + method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onCallDisconnectCauseChanged(int, int); method public void onCallForwardingIndicatorChanged(boolean); method public void onCallStateChanged(int, String); method public void onCellInfoChanged(java.util.List<android.telephony.CellInfo>); @@ -46275,144 +46314,36 @@ package android.telephony { method public void onDataActivity(int); method public void onDataConnectionStateChanged(int); method public void onDataConnectionStateChanged(int, int); - method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onDisplayInfoChanged(@NonNull android.telephony.TelephonyDisplayInfo); + method @RequiresPermission("android.permission.READ_PHONE_STATE") public void onDisplayInfoChanged(@NonNull android.telephony.TelephonyDisplayInfo); method public void onEmergencyNumberListChanged(@NonNull java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>>); - method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo); + method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo); method public void onMessageWaitingIndicatorChanged(boolean); - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState); + method @RequiresPermission("android.permission.MODIFY_PHONE_STATE") public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState); method public void onRegistrationFailed(@NonNull android.telephony.CellIdentity, @NonNull String, int, int, int); method public void onServiceStateChanged(android.telephony.ServiceState); method @Deprecated public void onSignalStrengthChanged(int); method public void onSignalStrengthsChanged(android.telephony.SignalStrength); method public void onUserMobileDataStateChanged(boolean); - field @Deprecated public static final int LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE = 4194304; // 0x400000 - field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_BARRING_INFO = -2147483648; // 0x80000000 - field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_CALL_DISCONNECT_CAUSES = 33554432; // 0x2000000 - field @Deprecated public static final int LISTEN_CALL_FORWARDING_INDICATOR = 8; // 0x8 - field @Deprecated public static final int LISTEN_CALL_STATE = 32; // 0x20 - field @Deprecated public static final int LISTEN_CELL_INFO = 1024; // 0x400 - field @Deprecated public static final int LISTEN_CELL_LOCATION = 16; // 0x10 - field @Deprecated public static final int LISTEN_DATA_ACTIVITY = 128; // 0x80 - field @Deprecated public static final int LISTEN_DATA_CONNECTION_STATE = 64; // 0x40 - field @Deprecated public static final int LISTEN_DISPLAY_INFO_CHANGED = 1048576; // 0x100000 - field @Deprecated public static final int LISTEN_EMERGENCY_NUMBER_LIST = 16777216; // 0x1000000 - field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_IMS_CALL_DISCONNECT_CAUSES = 134217728; // 0x8000000 - field @Deprecated public static final int LISTEN_MESSAGE_WAITING_INDICATOR = 4; // 0x4 + field public static final int LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE = 4194304; // 0x400000 + field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_BARRING_INFO = -2147483648; // 0x80000000 + field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_CALL_DISCONNECT_CAUSES = 33554432; // 0x2000000 + field public static final int LISTEN_CALL_FORWARDING_INDICATOR = 8; // 0x8 + field public static final int LISTEN_CALL_STATE = 32; // 0x20 + field public static final int LISTEN_CELL_INFO = 1024; // 0x400 + field public static final int LISTEN_CELL_LOCATION = 16; // 0x10 + field public static final int LISTEN_DATA_ACTIVITY = 128; // 0x80 + field public static final int LISTEN_DATA_CONNECTION_STATE = 64; // 0x40 + field public static final int LISTEN_DISPLAY_INFO_CHANGED = 1048576; // 0x100000 + field public static final int LISTEN_EMERGENCY_NUMBER_LIST = 16777216; // 0x1000000 + field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_IMS_CALL_DISCONNECT_CAUSES = 134217728; // 0x8000000 + field public static final int LISTEN_MESSAGE_WAITING_INDICATOR = 4; // 0x4 field public static final int LISTEN_NONE = 0; // 0x0 - field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 4096; // 0x1000 - field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_REGISTRATION_FAILURE = 1073741824; // 0x40000000 - field @Deprecated public static final int LISTEN_SERVICE_STATE = 1; // 0x1 + field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 4096; // 0x1000 + field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_REGISTRATION_FAILURE = 1073741824; // 0x40000000 + field public static final int LISTEN_SERVICE_STATE = 1; // 0x1 field @Deprecated public static final int LISTEN_SIGNAL_STRENGTH = 2; // 0x2 - field @Deprecated public static final int LISTEN_SIGNAL_STRENGTHS = 256; // 0x100 - field @Deprecated public static final int LISTEN_USER_MOBILE_DATA_STATE = 524288; // 0x80000 - } - - public static interface PhoneStateListener.ActiveDataSubscriptionIdChangedListener { - method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onActiveDataSubscriptionIdChanged(int); - } - - public static interface PhoneStateListener.AlwaysReportedSignalStrengthsChangedListener { - method @RequiresPermission("android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH") public void onSignalStrengthsChanged(@NonNull android.telephony.SignalStrength); - } - - public static interface PhoneStateListener.BarringInfoChangedListener { - method @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void onBarringInfoChanged(@NonNull android.telephony.BarringInfo); - } - - public static interface PhoneStateListener.CallDisconnectCauseChangedListener { - method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onCallDisconnectCauseChanged(int, int); - } - - public static interface PhoneStateListener.CallForwardingIndicatorChangedListener { - method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onCallForwardingIndicatorChanged(boolean); - } - - public static interface PhoneStateListener.CallStateChangedListener { - method @RequiresPermission(android.Manifest.permission.READ_CALL_LOG) public void onCallStateChanged(int, @Nullable String); - } - - public static interface PhoneStateListener.CarrierNetworkChangeListener { - method public void onCarrierNetworkChange(boolean); - } - - public static interface PhoneStateListener.CellInfoChangedListener { - method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void onCellInfoChanged(@NonNull java.util.List<android.telephony.CellInfo>); - } - - public static interface PhoneStateListener.CellLocationChangedListener { - method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void onCellLocationChanged(@NonNull android.telephony.CellLocation); - } - - public static interface PhoneStateListener.DataActivationStateChangedListener { - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onDataActivationStateChanged(int); - } - - public static interface PhoneStateListener.DataActivityListener { - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onDataActivity(int); - } - - public static interface PhoneStateListener.DataConnectionStateChangedListener { - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onDataConnectionStateChanged(int, int); - } - - public static interface PhoneStateListener.DisplayInfoChangedListener { - method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onDisplayInfoChanged(@NonNull android.telephony.TelephonyDisplayInfo); - } - - public static interface PhoneStateListener.EmergencyNumberListChangedListener { - method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onEmergencyNumberListChanged(@NonNull java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>>); - } - - public static interface PhoneStateListener.ImsCallDisconnectCauseChangedListener { - method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo); - } - - public static interface PhoneStateListener.MessageWaitingIndicatorChangedListener { - method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onMessageWaitingIndicatorChanged(boolean); - } - - public static interface PhoneStateListener.PhoneCapabilityChangedListener { - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onPhoneCapabilityChanged(@NonNull android.telephony.PhoneCapability); - } - - public static interface PhoneStateListener.PhysicalChannelConfigChangedListener { - method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPhysicalChannelConfigChanged(@NonNull java.util.List<android.telephony.PhysicalChannelConfig>); - } - - public static interface PhoneStateListener.PreciseDataConnectionStateChangedListener { - method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState); - } - - public static interface PhoneStateListener.RegistrationFailedListener { - method @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void onRegistrationFailed(@NonNull android.telephony.CellIdentity, @NonNull String, int, int, int); - } - - public static interface PhoneStateListener.ServiceStateChangedListener { - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onServiceStateChanged(@NonNull android.telephony.ServiceState); - } - - public static interface PhoneStateListener.SignalStrengthsChangedListener { - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onSignalStrengthsChanged(@NonNull android.telephony.SignalStrength); - } - - public static interface PhoneStateListener.UserMobileDataStateChangedListener { - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onUserMobileDataStateChanged(boolean); - } - - public final class PhysicalChannelConfig implements android.os.Parcelable { - method public int describeContents(); - method public int getCellBandwidthDownlink(); - method public int getChannelNumber(); - method public int getConnectionStatus(); - method public int getNetworkType(); - method @IntRange(from=0, to=1007) public int getPhysicalCellId(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field public static final int CHANNEL_NUMBER_UNKNOWN = -1; // 0xffffffff - field public static final int CONNECTION_PRIMARY_SERVING = 1; // 0x1 - field public static final int CONNECTION_SECONDARY_SERVING = 2; // 0x2 - field public static final int CONNECTION_UNKNOWN = -1; // 0xffffffff - field @NonNull public static final android.os.Parcelable.Creator<android.telephony.PhysicalChannelConfig> CREATOR; - field public static final int PHYSICAL_CELL_ID_UNKNOWN = -1; // 0xffffffff + field public static final int LISTEN_SIGNAL_STRENGTHS = 256; // 0x100 + field public static final int LISTEN_USER_MOBILE_DATA_STATE = 524288; // 0x80000 } public final class PreciseDataConnectionState implements android.os.Parcelable { @@ -46595,6 +46526,8 @@ package android.telephony { field public static final int RESULT_RECEIVE_WHILE_ENCRYPTED = 504; // 0x1f8 field public static final int RESULT_REMOTE_EXCEPTION = 31; // 0x1f field public static final int RESULT_REQUEST_NOT_SUPPORTED = 24; // 0x18 + field public static final int RESULT_RIL_ACCESS_BARRED = 122; // 0x7a + field public static final int RESULT_RIL_BLOCKED_DUE_TO_CALL = 123; // 0x7b field public static final int RESULT_RIL_CANCELLED = 119; // 0x77 field public static final int RESULT_RIL_ENCODING_ERR = 109; // 0x6d field public static final int RESULT_RIL_INTERNAL_ERR = 113; // 0x71 @@ -46613,6 +46546,7 @@ package android.telephony { field public static final int RESULT_RIL_RADIO_NOT_AVAILABLE = 100; // 0x64 field public static final int RESULT_RIL_REQUEST_NOT_SUPPORTED = 114; // 0x72 field public static final int RESULT_RIL_REQUEST_RATE_LIMITED = 106; // 0x6a + field public static final int RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED = 121; // 0x79 field public static final int RESULT_RIL_SIM_ABSENT = 120; // 0x78 field public static final int RESULT_RIL_SMS_SEND_FAIL_RETRY = 101; // 0x65 field public static final int RESULT_RIL_SYSTEM_ERR = 108; // 0x6c @@ -46921,8 +46855,7 @@ package android.telephony { method public boolean isVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle); method public boolean isWorldPhone(); method @Deprecated public void listen(android.telephony.PhoneStateListener, int); - method @Deprecated public void listen(long, @NonNull android.telephony.PhoneStateListener); - method public void registerPhoneStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.PhoneStateListener); + method public void listen(long, @NonNull android.telephony.PhoneStateListener); method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void requestCellInfoUpdate(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback); method @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, java.util.concurrent.Executor, android.telephony.TelephonyScanManager.NetworkScanCallback); method public void sendDialerSpecialCode(String); @@ -46944,9 +46877,13 @@ package android.telephony { method @Deprecated public void setVoicemailRingtoneUri(android.telecom.PhoneAccountHandle, android.net.Uri); method @Deprecated public void setVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle, boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void switchMultiSimConfig(int); - method public void unregisterPhoneStateListener(@NonNull android.telephony.PhoneStateListener); method public void updateAvailableNetworks(@NonNull java.util.List<android.telephony.AvailableNetworkInfo>, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.lang.Integer>); field public static final String ACTION_CARRIER_MESSAGING_CLIENT_SERVICE = "android.telephony.action.CARRIER_MESSAGING_CLIENT_SERVICE"; + field public static final String ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE = "android.telephony.action.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE"; + field public static final String ACTION_CARRIER_SIGNAL_PCO_VALUE = "android.telephony.action.CARRIER_SIGNAL_PCO_VALUE"; + field public static final String ACTION_CARRIER_SIGNAL_REDIRECTED = "android.telephony.action.CARRIER_SIGNAL_REDIRECTED"; + field public static final String ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED = "android.telephony.action.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED"; + field public static final String ACTION_CARRIER_SIGNAL_RESET = "android.telephony.action.CARRIER_SIGNAL_RESET"; field public static final String ACTION_CONFIGURE_VOICEMAIL = "android.telephony.action.CONFIGURE_VOICEMAIL"; field public static final String ACTION_MULTI_SIM_CONFIG_CHANGED = "android.telephony.action.MULTI_SIM_CONFIG_CHANGED"; field public static final String ACTION_NETWORK_COUNTRY_CHANGED = "android.telephony.action.NETWORK_COUNTRY_CHANGED"; @@ -46989,16 +46926,23 @@ package android.telephony { field public static final int ERI_OFF = 1; // 0x1 field public static final int ERI_ON = 0; // 0x0 field public static final String EXTRA_ACTIVE_SIM_SUPPORTED_COUNT = "android.telephony.extra.ACTIVE_SIM_SUPPORTED_COUNT"; + field public static final String EXTRA_APN_PROTOCOL = "android.telephony.extra.APN_PROTOCOL"; + field public static final String EXTRA_APN_TYPE = "android.telephony.extra.APN_TYPE"; field public static final String EXTRA_CALL_VOICEMAIL_INTENT = "android.telephony.extra.CALL_VOICEMAIL_INTENT"; field public static final String EXTRA_CARRIER_ID = "android.telephony.extra.CARRIER_ID"; field public static final String EXTRA_CARRIER_NAME = "android.telephony.extra.CARRIER_NAME"; + field public static final String EXTRA_DATA_FAIL_CAUSE = "android.telephony.extra.DATA_FAIL_CAUSE"; + field public static final String EXTRA_DEFAULT_NETWORK_AVAILABLE = "android.telephony.extra.DEFAULT_NETWORK_AVAILABLE"; field public static final String EXTRA_HIDE_PUBLIC_SETTINGS = "android.telephony.extra.HIDE_PUBLIC_SETTINGS"; field @Deprecated public static final String EXTRA_INCOMING_NUMBER = "incoming_number"; field public static final String EXTRA_IS_REFRESH = "android.telephony.extra.IS_REFRESH"; field public static final String EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT = "android.telephony.extra.LAUNCH_VOICEMAIL_SETTINGS_INTENT"; field public static final String EXTRA_NETWORK_COUNTRY = "android.telephony.extra.NETWORK_COUNTRY"; field public static final String EXTRA_NOTIFICATION_COUNT = "android.telephony.extra.NOTIFICATION_COUNT"; + field public static final String EXTRA_PCO_ID = "android.telephony.extra.PCO_ID"; + field public static final String EXTRA_PCO_VALUE = "android.telephony.extra.PCO_VALUE"; field public static final String EXTRA_PHONE_ACCOUNT_HANDLE = "android.telephony.extra.PHONE_ACCOUNT_HANDLE"; + field public static final String EXTRA_REDIRECTION_URL = "android.telephony.extra.REDIRECTION_URL"; field public static final String EXTRA_SPECIFIC_CARRIER_ID = "android.telephony.extra.SPECIFIC_CARRIER_ID"; field public static final String EXTRA_SPECIFIC_CARRIER_NAME = "android.telephony.extra.SPECIFIC_CARRIER_NAME"; field public static final String EXTRA_STATE = "state"; diff --git a/core/api/system-current.txt b/core/api/system-current.txt index f9519f845d5c..99179ec89cbc 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -228,6 +228,7 @@ package android { field public static final String SHOW_KEYGUARD_MESSAGE = "android.permission.SHOW_KEYGUARD_MESSAGE"; field public static final String SHUTDOWN = "android.permission.SHUTDOWN"; field public static final String START_ACTIVITIES_FROM_BACKGROUND = "android.permission.START_ACTIVITIES_FROM_BACKGROUND"; + field public static final String START_FOREGROUND_SERVICES_FROM_BACKGROUND = "android.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND"; field public static final String STATUS_BAR_SERVICE = "android.permission.STATUS_BAR_SERVICE"; field public static final String STOP_APP_SWITCHES = "android.permission.STOP_APP_SWITCHES"; field public static final String SUBSTITUTE_NOTIFICATION_APP_NAME = "android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"; @@ -603,7 +604,7 @@ package android.app { method public static android.app.BroadcastOptions makeBasic(); method @RequiresPermission(android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND) public void setBackgroundActivityStartsAllowed(boolean); method public void setDontSendToRestrictedApps(boolean); - method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void setTemporaryAppWhitelistDuration(long); + method @RequiresPermission(anyOf={android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND, android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND}) public void setTemporaryAppWhitelistDuration(long); method public android.os.Bundle toBundle(); } @@ -1764,6 +1765,7 @@ package android.content { field public static final String BACKUP_SERVICE = "backup"; field public static final String BATTERY_STATS_SERVICE = "batterystats"; field public static final int BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS = 1048576; // 0x100000 + field public static final int BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND = 262144; // 0x40000 field public static final String BUGREPORT_SERVICE = "bugreport"; field public static final String CONTENT_SUGGESTIONS_SERVICE = "content_suggestions"; field public static final String CONTEXTHUB_SERVICE = "contexthub"; @@ -2280,6 +2282,7 @@ package android.content.pm { field public static final int PROTECTION_FLAG_DOCUMENTER = 262144; // 0x40000 field public static final int PROTECTION_FLAG_INCIDENT_REPORT_APPROVER = 1048576; // 0x100000 field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000 + field public static final int PROTECTION_FLAG_RECENTS = 33554432; // 0x2000000 field public static final int PROTECTION_FLAG_RETAIL_DEMO = 16777216; // 0x1000000 field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000 field public static final int PROTECTION_FLAG_WELLBEING = 131072; // 0x20000 @@ -4389,7 +4392,7 @@ package android.media { } public class MediaPlayer implements android.media.AudioRouting android.media.VolumeAutomation { - method @RequiresPermission("android.permission.BIND_IMS_SERVICE") public void setOnRtpRxNoticeListener(@NonNull android.content.Context, @NonNull android.media.MediaPlayer.OnRtpRxNoticeListener, @Nullable android.os.Handler); + method @RequiresPermission(android.Manifest.permission.BIND_IMS_SERVICE) public void setOnRtpRxNoticeListener(@NonNull android.content.Context, @NonNull android.media.MediaPlayer.OnRtpRxNoticeListener, @Nullable android.os.Handler); } public static interface MediaPlayer.OnRtpRxNoticeListener { @@ -5253,7 +5256,7 @@ package android.media.tv.tuner.filter { public class Filter implements java.lang.AutoCloseable { method public void close(); method public int configure(@NonNull android.media.tv.tuner.filter.FilterConfiguration); - method public int configureScramblingStatusEvent(int); + method public int configureMonitorEvent(int); method public int flush(); method public int getId(); method public long getId64Bit(); @@ -5261,6 +5264,8 @@ package android.media.tv.tuner.filter { method public int setDataSource(@Nullable android.media.tv.tuner.filter.Filter); method public int start(); method public int stop(); + field public static final int MONITOR_EVENT_IP_CID_CHANGE = 2; // 0x2 + field public static final int MONITOR_EVENT_SCRAMBLING_STATUS = 1; // 0x1 field public static final int SCRAMBLING_STATUS_NOT_SCRAMBLED = 2; // 0x2 field public static final int SCRAMBLING_STATUS_SCRAMBLED = 4; // 0x4 field public static final int SCRAMBLING_STATUS_UNKNOWN = 1; // 0x1 @@ -5307,6 +5312,10 @@ package android.media.tv.tuner.filter { ctor public FilterEvent(); } + public final class IpCidChangeEvent extends android.media.tv.tuner.filter.FilterEvent { + method public int getIpCid(); + } + public final class IpFilterConfiguration extends android.media.tv.tuner.filter.FilterConfiguration { method @NonNull public static android.media.tv.tuner.filter.IpFilterConfiguration.Builder builder(); method @NonNull @Size(min=4, max=16) public byte[] getDstIpAddress(); @@ -6684,6 +6693,7 @@ package android.net { method public final void sendNetworkCapabilities(@NonNull android.net.NetworkCapabilities); method public final void sendNetworkScore(@IntRange(from=0, to=99) int); method public final void sendSocketKeepaliveEvent(int, int); + method public final void setUnderlyingNetworks(@Nullable java.util.List<android.net.Network>); method public void unregister(); field public static final int VALIDATION_STATUS_NOT_VALID = 2; // 0x2 field public static final int VALIDATION_STATUS_VALID = 1; // 0x1 @@ -8957,11 +8967,8 @@ package android.service.attestation { public abstract class ImpressionAttestationService extends android.app.Service { ctor public ImpressionAttestationService(); method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent); - method @Nullable public abstract android.service.attestation.ImpressionToken onGenerateImpressionToken(@NonNull android.hardware.HardwareBuffer, @NonNull android.graphics.Rect, @NonNull String); - method public abstract int onVerifyImpressionToken(@NonNull android.service.attestation.ImpressionToken); - field public static final int VERIFICATION_STATUS_APP_DECLARED = 2; // 0x2 - field public static final int VERIFICATION_STATUS_OS_VERIFIED = 1; // 0x1 - field public static final int VERIFICATION_STATUS_UNKNOWN = 0; // 0x0 + method @Nullable public abstract android.service.attestation.ImpressionToken onGenerateImpressionToken(@NonNull String, @NonNull android.hardware.HardwareBuffer, @NonNull android.graphics.Rect, @NonNull String); + method public abstract boolean onVerifyImpressionToken(@NonNull String, @NonNull android.service.attestation.ImpressionToken); } public final class ImpressionToken implements android.os.Parcelable { @@ -10207,6 +10214,25 @@ package android.telephony { field @NonNull public static final android.os.Parcelable.Creator<android.telephony.DataSpecificRegistrationInfo> CREATOR; } + public final class DataThrottlingRequest implements android.os.Parcelable { + method public int describeContents(); + method public long getCompletionDurationMillis(); + method public int getDataThrottlingAction(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.DataThrottlingRequest> CREATOR; + field public static final int DATA_THROTTLING_ACTION_HOLD = 3; // 0x3 + field public static final int DATA_THROTTLING_ACTION_NO_DATA_THROTTLING = 0; // 0x0 + field public static final int DATA_THROTTLING_ACTION_THROTTLE_PRIMARY_CARRIER = 2; // 0x2 + field public static final int DATA_THROTTLING_ACTION_THROTTLE_SECONDARY_CARRIER = 1; // 0x1 + } + + public static final class DataThrottlingRequest.Builder { + ctor public DataThrottlingRequest.Builder(); + method @NonNull public android.telephony.DataThrottlingRequest build(); + method @NonNull public android.telephony.DataThrottlingRequest.Builder setCompletionDurationMillis(long); + method @NonNull public android.telephony.DataThrottlingRequest.Builder setDataThrottlingAction(int); + } + public final class ImsiEncryptionInfo implements android.os.Parcelable { method public int describeContents(); method @Nullable public String getKeyIdentifier(); @@ -10344,83 +10370,35 @@ package android.telephony { method public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber, int); method @Deprecated public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber); method public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber, int); - method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState); + method public void onPhysicalChannelConfigurationChanged(@NonNull java.util.List<android.telephony.PhysicalChannelConfig>); + method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState); method public void onRadioPowerStateChanged(int); method public void onSrvccStateChanged(int); method public void onVoiceActivationStateChanged(int); - field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED = 23; // 0x17 - field @RequiresPermission("android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH") public static final int EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED = 10; // 0xa - field @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static final int EVENT_BARRING_INFO_CHANGED = 32; // 0x20 - field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_CALL_ATTRIBUTES_CHANGED = 27; // 0x1b - field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_CALL_DISCONNECT_CAUSE_CHANGED = 26; // 0x1a - field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_CALL_FORWARDING_INDICATOR_CHANGED = 4; // 0x4 - field @RequiresPermission(android.Manifest.permission.READ_CALL_LOG) public static final int EVENT_CALL_STATE_CHANGED = 6; // 0x6 - field public static final int EVENT_CARRIER_NETWORK_CHANGED = 17; // 0x11 - field @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static final int EVENT_CELL_INFO_CHANGED = 11; // 0xb - field @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static final int EVENT_CELL_LOCATION_CHANGED = 5; // 0x5 - field public static final int EVENT_DATA_ACTIVATION_STATE_CHANGED = 19; // 0x13 - field public static final int EVENT_DATA_ACTIVITY_CHANGED = 8; // 0x8 - field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED = 14; // 0xe - field public static final int EVENT_DATA_CONNECTION_STATE_CHANGED = 7; // 0x7 - field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_DATA_ENABLED_CHANGED = 34; // 0x22 - field public static final int EVENT_DISPLAY_INFO_CHANGED = 21; // 0x15 - field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_EMERGENCY_NUMBER_LIST_CHANGED = 25; // 0x19 - field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED = 28; // 0x1c - field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_MESSAGE_WAITING_INDICATOR_CHANGED = 3; // 0x3 - field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_OEM_HOOK_RAW = 15; // 0xf - field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int EVENT_OUTGOING_EMERGENCY_CALL = 29; // 0x1d - field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int EVENT_OUTGOING_EMERGENCY_SMS = 30; // 0x1e - field public static final int EVENT_PHONE_CAPABILITY_CHANGED = 22; // 0x16 - field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED = 33; // 0x21 - field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_PRECISE_CALL_STATE_CHANGED = 12; // 0xc - field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED = 13; // 0xd - field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_RADIO_POWER_STATE_CHANGED = 24; // 0x18 - field @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static final int EVENT_REGISTRATION_FAILURE = 31; // 0x1f - field public static final int EVENT_SERVICE_STATE_CHANGED = 1; // 0x1 - field public static final int EVENT_SIGNAL_STRENGTHS_CHANGED = 9; // 0x9 - field public static final int EVENT_SIGNAL_STRENGTH_CHANGED = 2; // 0x2 - field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_SRVCC_STATE_CHANGED = 16; // 0x10 - field public static final int EVENT_USER_MOBILE_DATA_STATE_CHANGED = 20; // 0x14 - field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_VOICE_ACTIVATION_STATE_CHANGED = 18; // 0x12 - field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 67108864; // 0x4000000 - field @Deprecated @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_CALL = 268435456; // 0x10000000 - field @Deprecated @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 536870912; // 0x20000000 - field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_PRECISE_CALL_STATE = 2048; // 0x800 - field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_RADIO_POWER_STATE_CHANGED = 8388608; // 0x800000 - field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_SRVCC_STATE_CHANGED = 16384; // 0x4000 - field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_VOICE_ACTIVATION_STATE = 131072; // 0x20000 - } - - public static interface PhoneStateListener.CallAttributesChangedListener { - method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onCallAttributesChanged(@NonNull android.telephony.CallAttributes); - } - - public static interface PhoneStateListener.DataEnabledChangedListener { - method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onDataEnabledChanged(boolean, int); - } - - public static interface PhoneStateListener.OutgoingEmergencyCallListener { - method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber, int); - } - - public static interface PhoneStateListener.OutgoingEmergencySmsListener { - method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber, int); - } - - public static interface PhoneStateListener.PreciseCallStateChangedListener { - method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState); + field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 67108864; // 0x4000000 + field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_CALL = 268435456; // 0x10000000 + field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 536870912; // 0x20000000 + field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final long LISTEN_PHYSICAL_CHANNEL_CONFIGURATION = 4294967296L; // 0x100000000L + field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_PRECISE_CALL_STATE = 2048; // 0x800 + field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_RADIO_POWER_STATE_CHANGED = 8388608; // 0x800000 + field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_SRVCC_STATE_CHANGED = 16384; // 0x4000 + field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_VOICE_ACTIVATION_STATE = 131072; // 0x20000 } - public static interface PhoneStateListener.RadioPowerStateChangedListener { - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onRadioPowerStateChanged(int); - } - - public static interface PhoneStateListener.SrvccStateChangedListener { - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onSrvccStateChanged(int); - } - - public static interface PhoneStateListener.VoiceActivationStateChangedListener { - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onVoiceActivationStateChanged(int); + public final class PhysicalChannelConfig implements android.os.Parcelable { + method public int describeContents(); + method public int getCellBandwidthDownlink(); + method public int getChannelNumber(); + method public int getConnectionStatus(); + method public int getNetworkType(); + method @IntRange(from=0, to=1007) public int getPhysicalCellId(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field public static final int CHANNEL_NUMBER_UNKNOWN = -1; // 0xffffffff + field public static final int CONNECTION_PRIMARY_SERVING = 1; // 0x1 + field public static final int CONNECTION_SECONDARY_SERVING = 2; // 0x2 + field public static final int CONNECTION_UNKNOWN = -1; // 0xffffffff + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.PhysicalChannelConfig> CREATOR; + field public static final int PHYSICAL_CELL_ID_UNKNOWN = -1; // 0xffffffff } public final class PinResult implements android.os.Parcelable { @@ -10830,6 +10808,7 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getSimCardState(int); method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.Locale getSimLocale(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getSupportedRadioAccessFamily(); + method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<android.telephony.RadioAccessSpecifier> getSystemSelectionChannels(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public java.util.List<android.telephony.TelephonyHistogram> getTelephonyHistograms(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.UiccSlotInfo[] getUiccSlotsInfo(); method @Nullable public android.os.Bundle getVisualVoicemailSettings(); @@ -10873,6 +10852,7 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void resetOtaEmergencyNumberDbFilePath(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean resetRadioConfig(); method @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL) public void resetSettings(); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int sendThermalMitigationRequest(@NonNull android.telephony.ThermalMitigationRequest); method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setAllowedNetworkTypes(long); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCallForwarding(@NonNull android.telephony.CallForwardingInfo, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.lang.Integer>); @@ -10990,6 +10970,11 @@ package android.telephony { field public static final int SRVCC_STATE_HANDOVER_FAILED = 2; // 0x2 field public static final int SRVCC_STATE_HANDOVER_NONE = -1; // 0xffffffff field public static final int SRVCC_STATE_HANDOVER_STARTED = 0; // 0x0 + field public static final int THERMAL_MITIGATION_RESULT_INVALID_STATE = 3; // 0x3 + field public static final int THERMAL_MITIGATION_RESULT_MODEM_ERROR = 1; // 0x1 + field public static final int THERMAL_MITIGATION_RESULT_MODEM_NOT_AVAILABLE = 2; // 0x2 + field public static final int THERMAL_MITIGATION_RESULT_SUCCESS = 0; // 0x0 + field public static final int THERMAL_MITIGATION_RESULT_UNKNOWN_ERROR = 4; // 0x4 } public static interface TelephonyManager.CallForwardingInfoCallback { @@ -11009,6 +10994,24 @@ package android.telephony { field public static final int ERROR_UNKNOWN = 0; // 0x0 } + public final class ThermalMitigationRequest implements android.os.Parcelable { + method public int describeContents(); + method @Nullable public android.telephony.DataThrottlingRequest getDataThrottlingRequest(); + method public int getThermalMitigationAction(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ThermalMitigationRequest> CREATOR; + field public static final int THERMAL_MITIGATION_ACTION_DATA_THROTTLING = 0; // 0x0 + field public static final int THERMAL_MITIGATION_ACTION_RADIO_OFF = 2; // 0x2 + field public static final int THERMAL_MITIGATION_ACTION_VOICE_ONLY = 1; // 0x1 + } + + public static final class ThermalMitigationRequest.Builder { + ctor public ThermalMitigationRequest.Builder(); + method @NonNull public android.telephony.ThermalMitigationRequest build(); + method @NonNull public android.telephony.ThermalMitigationRequest.Builder setDataThrottlingRequest(@NonNull android.telephony.DataThrottlingRequest); + method @NonNull public android.telephony.ThermalMitigationRequest.Builder setThermalMitigationAction(int); + } + public final class UiccAccessRule implements android.os.Parcelable { ctor public UiccAccessRule(byte[], @Nullable String, long); method public int describeContents(); @@ -11086,6 +11089,35 @@ package android.telephony.data { field public static final String TYPE_XCAP_STRING = "xcap"; } + public final class ApnThrottleStatus implements android.os.Parcelable { + method public int describeContents(); + method public int getApnType(); + method public int getRetryType(); + method public int getSlotIndex(); + method public long getThrottleExpiryTimeMillis(); + method public int getThrottleType(); + method public int getTransportType(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.ApnThrottleStatus> CREATOR; + field public static final int RETRY_TYPE_HANDOVER = 3; // 0x3 + field public static final int RETRY_TYPE_NEW_CONNECTION = 2; // 0x2 + field public static final int RETRY_TYPE_NONE = 1; // 0x1 + field public static final int THROTTLE_TYPE_ELAPSED_TIME = 2; // 0x2 + field public static final int THROTTLE_TYPE_NONE = 1; // 0x1 + } + + public static final class ApnThrottleStatus.Builder { + ctor public ApnThrottleStatus.Builder(); + method @NonNull public android.telephony.data.ApnThrottleStatus build(); + method @NonNull public android.telephony.data.ApnThrottleStatus.Builder setApnType(int); + method @NonNull public android.telephony.data.ApnThrottleStatus.Builder setNoThrottle(); + method @NonNull public android.telephony.data.ApnThrottleStatus.Builder setRetryType(int); + method @NonNull public android.telephony.data.ApnThrottleStatus.Builder setSlotIndex(int); + method @NonNull public android.telephony.data.ApnThrottleStatus.Builder setThrottleExpiryTimeMillis(long); + method @NonNull public android.telephony.data.ApnThrottleStatus.Builder setTransportType(int); + field public static final long NO_THROTTLE_EXPIRY_TIME = -1L; // 0xffffffffffffffffL + } + public final class DataCallResponse implements android.os.Parcelable { method public int describeContents(); method @NonNull public java.util.List<android.net.LinkAddress> getAddresses(); @@ -11203,6 +11235,7 @@ package android.telephony.data { method public abstract void close(); method public void deactivateDataCall(int, int, @Nullable android.telephony.data.DataServiceCallback); method public final int getSlotIndex(); + method public final void notifyApnUnthrottled(@NonNull String); method public final void notifyDataCallListChanged(java.util.List<android.telephony.data.DataCallResponse>); method public void requestDataCallList(@NonNull android.telephony.data.DataServiceCallback); method public void setDataProfile(@NonNull java.util.List<android.telephony.data.DataProfile>, boolean, @NonNull android.telephony.data.DataServiceCallback); @@ -11213,6 +11246,7 @@ package android.telephony.data { } public class DataServiceCallback { + method public void onApnUnthrottled(@NonNull String); method public void onDataCallListChanged(@NonNull java.util.List<android.telephony.data.DataCallResponse>); method public void onDeactivateDataCallComplete(int); method public void onHandoverCancelled(int); @@ -11238,6 +11272,7 @@ package android.telephony.data { ctor public QualifiedNetworksService.NetworkAvailabilityProvider(int); method public abstract void close(); method public final int getSlotIndex(); + method public void reportApnThrottleStatusChanged(@NonNull java.util.List<android.telephony.data.ApnThrottleStatus>); method public final void updateQualifiedNetworkTypes(int, @NonNull java.util.List<java.lang.Integer>); } diff --git a/core/api/test-current.txt b/core/api/test-current.txt index f695adf3e01c..7f83fa14ae41 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -7,6 +7,7 @@ package android { field public static final String APPROVE_INCIDENT_REPORTS = "android.permission.APPROVE_INCIDENT_REPORTS"; field public static final String BIND_CELL_BROADCAST_SERVICE = "android.permission.BIND_CELL_BROADCAST_SERVICE"; field public static final String BRIGHTNESS_SLIDER_USAGE = "android.permission.BRIGHTNESS_SLIDER_USAGE"; + field public static final String BROADCAST_CLOSE_SYSTEM_DIALOGS = "android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS"; field public static final String CHANGE_APP_IDLE_STATE = "android.permission.CHANGE_APP_IDLE_STATE"; field public static final String CLEAR_APP_USER_DATA = "android.permission.CLEAR_APP_USER_DATA"; field public static final String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS"; @@ -302,8 +303,10 @@ package android.app { method public void destroy(); method @NonNull public android.os.ParcelFileDescriptor[] executeShellCommandRwe(@NonNull String); method @Deprecated public boolean grantRuntimePermission(String, String, android.os.UserHandle); + method public boolean injectInputEvent(@NonNull android.view.InputEvent, boolean, boolean); method @Deprecated public boolean revokeRuntimePermission(String, String, android.os.UserHandle); method public void syncInputTransactions(); + method public void syncInputTransactions(boolean); } public class UiModeManager { @@ -830,6 +833,14 @@ package android.hardware.soundtrigger { } +package android.inputmethodservice { + + public class InputMethodService extends android.inputmethodservice.AbstractInputMethodService { + field public static final long FINISH_INPUT_NO_FALLBACK_CONNECTION = 156215187L; // 0x94fa793L + } + +} + package android.location { public final class GnssClock implements android.os.Parcelable { @@ -1227,7 +1238,6 @@ package android.os { } public static final class StrictMode.VmPolicy.Builder { - method @NonNull public android.os.StrictMode.VmPolicy.Builder detectIncorrectContextUse(); method @NonNull public android.os.StrictMode.VmPolicy.Builder permitIncorrectContextUse(); } @@ -2081,12 +2091,15 @@ package android.view { } public interface WindowManager extends android.view.ViewManager { + method public default int getDisplayImePolicy(int); method public default void holdLock(android.os.IBinder, int); - method public default void setShouldShowIme(int, boolean); + method public default void setDisplayImePolicy(int, int); method public default void setShouldShowSystemDecors(int, boolean); method public default void setShouldShowWithInsecureKeyguard(int, boolean); - method public default boolean shouldShowIme(int); method public default boolean shouldShowSystemDecors(int); + field public static final int DISPLAY_IME_POLICY_FALLBACK_DISPLAY = 1; // 0x1 + field public static final int DISPLAY_IME_POLICY_HIDE = 2; // 0x2 + field public static final int DISPLAY_IME_POLICY_LOCAL = 0; // 0x0 } public static class WindowManager.LayoutParams extends android.view.ViewGroup.LayoutParams implements android.os.Parcelable { diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java index b1b9f4161ee5..b15fa27485be 100644 --- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java +++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java @@ -650,7 +650,6 @@ public class AccessibilityServiceInfo implements Parcelable { 0); flags = asAttributes.getInt( com.android.internal.R.styleable.AccessibilityService_accessibilityFlags, 0); - flags |= FLAG_REQUEST_2_FINGER_PASSTHROUGH; mSettingsActivityName = asAttributes.getString( com.android.internal.R.styleable.AccessibilityService_settingsActivity); if (asAttributes.getBoolean(com.android.internal.R.styleable diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index d9f34d856f7c..cdfe41e85917 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -34,6 +34,7 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; import android.database.DatabaseUtils; @@ -7617,7 +7618,7 @@ public class AppOpsManager { // Only collect app-ops when the proxy is trusted && (mContext.checkPermission(Manifest.permission.UPDATE_APP_OPS_STATS, -1, myUid) == PackageManager.PERMISSION_GRANTED - || isTrustedVoiceServiceProxy(mContext.getOpPackageName(), op))) { + || isTrustedVoiceServiceProxy(mContext, mContext.getOpPackageName(), op))) { collectNotedOpSync(op, proxiedAttributionTag); } } @@ -7628,30 +7629,43 @@ public class AppOpsManager { } } - private boolean isTrustedVoiceServiceProxy(String packageName, int code) { + /** + * Checks if the voice recognition service is a trust proxy. + * + * @return {@code true} if the package is a trust voice recognition service proxy + * @hide + */ + public static boolean isTrustedVoiceServiceProxy(Context context, String packageName, + int code) { // This is a workaround for R QPR, new API change is not allowed. We only allow the current // voice recognizer is also the voice interactor to noteproxy op. if (code != OP_RECORD_AUDIO) { return false; } final String voiceRecognitionComponent = Settings.Secure.getString( - mContext.getContentResolver(), Settings.Secure.VOICE_RECOGNITION_SERVICE); - final String voiceInteractionComponent = Settings.Secure.getString( - mContext.getContentResolver(), Settings.Secure.VOICE_INTERACTION_SERVICE); + context.getContentResolver(), Settings.Secure.VOICE_RECOGNITION_SERVICE); final String voiceRecognitionServicePackageName = getComponentPackageNameFromString(voiceRecognitionComponent); - final String voiceInteractionServicePackageName = - getComponentPackageNameFromString(voiceInteractionComponent); - return Objects.equals(packageName, voiceRecognitionServicePackageName) && Objects.equals( - voiceRecognitionServicePackageName, voiceInteractionServicePackageName); + return (Objects.equals(packageName, voiceRecognitionServicePackageName)) + && isPackagePreInstalled(context, packageName); } - private String getComponentPackageNameFromString(String from) { + private static String getComponentPackageNameFromString(String from) { ComponentName componentName = from != null ? ComponentName.unflattenFromString(from) : null; return componentName != null ? componentName.getPackageName() : ""; } + private static boolean isPackagePreInstalled(Context context, String packageName) { + try { + final PackageManager pm = context.getPackageManager(); + final ApplicationInfo info = pm.getApplicationInfo(packageName, 0); + return ((info.flags & ApplicationInfo.FLAG_SYSTEM) != 0); + } catch (PackageManager.NameNotFoundException e) { + return false; + } + } + /** * Do a quick check for whether an application might be able to perform an operation. * This is <em>not</em> a security check; you must use {@link #noteOp(String, int, String, diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 7cef93fe7547..34437afb614a 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -58,6 +58,8 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageInstaller; import android.content.pm.PackageItemInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.PackageManager.Property; import android.content.pm.ParceledListSlice; import android.content.pm.PermissionGroupInfo; import android.content.pm.PermissionInfo; @@ -3551,4 +3553,112 @@ public class ApplicationPackageManager extends PackageManager { throw e.rethrowAsRuntimeException(); } } + + @Override + public Property getProperty(String propertyName, String packageName) + throws NameNotFoundException { + Objects.requireNonNull(packageName); + Objects.requireNonNull(propertyName); + try { + final Property property = mPM.getProperty(propertyName, packageName, null); + if (property == null) { + throw new NameNotFoundException(); + } + return property; + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + @Override + public Property getProperty(String propertyName, ComponentName component) + throws NameNotFoundException { + Objects.requireNonNull(component); + Objects.requireNonNull(propertyName); + try { + final Property property = mPM.getProperty( + propertyName, component.getPackageName(), component.getClassName()); + if (property == null) { + throw new NameNotFoundException(); + } + return property; + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + @Override + public List<Property> queryApplicationProperty(String propertyName) { + Objects.requireNonNull(propertyName); + try { + final ParceledListSlice<Property> parceledList = + mPM.queryProperty(propertyName, TYPE_APPLICATION); + if (parceledList == null) { + return Collections.emptyList(); + } + return parceledList.getList(); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + @Override + public List<Property> queryActivityProperty(String propertyName) { + Objects.requireNonNull(propertyName); + try { + final ParceledListSlice<Property> parceledList = + mPM.queryProperty(propertyName, TYPE_ACTIVITY); + if (parceledList == null) { + return Collections.emptyList(); + } + return parceledList.getList(); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + @Override + public List<Property> queryProviderProperty(String propertyName) { + Objects.requireNonNull(propertyName); + try { + final ParceledListSlice<Property> parceledList = + mPM.queryProperty(propertyName, TYPE_PROVIDER); + if (parceledList == null) { + return Collections.emptyList(); + } + return parceledList.getList(); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + @Override + public List<Property> queryReceiverProperty(String propertyName) { + Objects.requireNonNull(propertyName); + try { + final ParceledListSlice<Property> parceledList = + mPM.queryProperty(propertyName, TYPE_RECEIVER); + if (parceledList == null) { + return Collections.emptyList(); + } + return parceledList.getList(); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + @Override + public List<Property> queryServiceProperty(String propertyName) { + Objects.requireNonNull(propertyName); + try { + final ParceledListSlice<Property> parceledList = + mPM.queryProperty(propertyName, TYPE_SERVICE); + if (parceledList == null) { + return Collections.emptyList(); + } + return parceledList.getList(); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } } diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java index 03dca30f8560..298c45582174 100644 --- a/core/java/android/app/BroadcastOptions.java +++ b/core/java/android/app/BroadcastOptions.java @@ -90,7 +90,9 @@ public class BroadcastOptions { * power allowlist when this broadcast is being delivered to it. * @param duration The duration in milliseconds; 0 means to not place on allowlist. */ - @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) + @RequiresPermission(anyOf = {android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, + android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND, + android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND}) public void setTemporaryAppWhitelistDuration(long duration) { mTemporaryAppWhitelistDuration = duration; } diff --git a/core/java/android/app/IUiAutomationConnection.aidl b/core/java/android/app/IUiAutomationConnection.aidl index 9eeb9f6a95bf..ec7d7832dc82 100644 --- a/core/java/android/app/IUiAutomationConnection.aidl +++ b/core/java/android/app/IUiAutomationConnection.aidl @@ -36,8 +36,8 @@ import android.os.ParcelFileDescriptor; interface IUiAutomationConnection { void connect(IAccessibilityServiceClient client, int flags); void disconnect(); - boolean injectInputEvent(in InputEvent event, boolean sync); - void syncInputTransactions(); + boolean injectInputEvent(in InputEvent event, boolean sync, boolean waitForAnimations); + void syncInputTransactions(boolean waitForAnimations); boolean setRotation(int rotation); Bitmap takeScreenshot(in Rect crop); boolean clearWindowContentFrameStats(int windowId); diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index 3e249bb24dd6..e9d63d2bc788 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -1119,7 +1119,8 @@ public class Instrumentation { } try { WindowManagerGlobal.getWindowManagerService().injectInputAfterTransactionsApplied(event, - InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH); + InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH, + true /* waitForAnimations */); } catch (RemoteException e) { } } diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 75f7ceca6450..a886beddf64c 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -67,6 +67,7 @@ import android.os.Parcelable; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; +import android.provider.Settings; import android.text.BidiFormatter; import android.text.SpannableStringBuilder; import android.text.Spanned; @@ -4887,12 +4888,6 @@ public class Notification implements Parcelable mN.mUsesStandardHeader = false; } - private RemoteViews applyStandardTemplate(int resId, int viewType, - TemplateBindResult result) { - return applyStandardTemplate(resId, - mParams.reset().viewType(viewType).fillTextsFrom(this), result); - } - private RemoteViews applyStandardTemplate(int resId, StandardTemplateParams p, TemplateBindResult result) { p.headerless(resId == getBaseLayoutResource() @@ -4906,7 +4901,7 @@ public class Notification implements Parcelable bindNotificationHeader(contentView, p); bindLargeIconAndApplyMargin(contentView, p, result); boolean showProgress = handleProgressBar(contentView, ex, p); - if (p.title != null && p.title.length() > 0) { + if (p.hasTitle()) { contentView.setViewVisibility(R.id.title, View.VISIBLE); contentView.setTextViewText(R.id.title, processTextSpans(p.title)); setTextViewColorPrimary(contentView, R.id.title, p); @@ -4914,7 +4909,7 @@ public class Notification implements Parcelable ? ViewGroup.LayoutParams.WRAP_CONTENT : ViewGroup.LayoutParams.MATCH_PARENT); } - if (p.text != null) { + if (p.text != null && p.text.length() != 0) { int textId = showProgress ? com.android.internal.R.id.text_line_1 : com.android.internal.R.id.text; contentView.setTextViewText(textId, processTextSpans(p.text)); @@ -5301,6 +5296,12 @@ public class Notification implements Parcelable contentView.setViewVisibility(R.id.app_name_text, View.GONE); return false; } + if (p.mHeaderless && p.hasTitle()) { + contentView.setViewVisibility(R.id.app_name_text, View.GONE); + // the headerless template will have the TITLE in this position; return true to + // keep the divider visible between that title and the next text element. + return true; + } contentView.setViewVisibility(R.id.app_name_text, View.VISIBLE); contentView.setTextViewText(R.id.app_name_text, loadHeaderAppName()); if (isColorized(p)) { @@ -5348,14 +5349,11 @@ public class Notification implements Parcelable big.setViewVisibility(R.id.notification_material_reply_text_3, View.GONE); big.setTextViewText(R.id.notification_material_reply_text_3, null); + final boolean snoozeEnabled = mContext.getContentResolver() != null + && (Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.SHOW_NOTIFICATION_SNOOZE, 0) == 1); big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target, - R.dimen.notification_content_margin); - } - - private RemoteViews applyStandardTemplateWithActions(int layoutId, int viewType, - TemplateBindResult result) { - return applyStandardTemplateWithActions(layoutId, - mParams.reset().viewType(viewType).fillTextsFrom(this), result); + snoozeEnabled ? 0 : R.dimen.notification_content_margin); } private static List<Notification.Action> filterOutContextualActions( @@ -5495,8 +5493,10 @@ public class Notification implements Parcelable return styleView; } } - return applyStandardTemplate(getBaseLayoutResource(), - StandardTemplateParams.VIEW_TYPE_NORMAL, null /* result */); + StandardTemplateParams p = mParams.reset() + .viewType(StandardTemplateParams.VIEW_TYPE_NORMAL) + .fillTextsFrom(this); + return applyStandardTemplate(getBaseLayoutResource(), p, null /* result */); } private boolean useExistingRemoteView() { @@ -5516,14 +5516,27 @@ public class Notification implements Parcelable result = mStyle.makeBigContentView(); hideLine1Text(result); } - if (result == null) { - result = applyStandardTemplateWithActions(getBigBaseLayoutResource(), - StandardTemplateParams.VIEW_TYPE_BIG, null /* result */); + if (result == null && bigContentViewRequired()) { + StandardTemplateParams p = mParams.reset() + .viewType(StandardTemplateParams.VIEW_TYPE_BIG) + .fillTextsFrom(this); + result = applyStandardTemplateWithActions(getBigBaseLayoutResource(), p, + null /* result */); } makeHeaderExpanded(result); return result; } + private boolean bigContentViewRequired() { + // If the big content view has no content, we can exempt the app from having to show it. + // TODO(b/173550917): add an UNDO style then force this requirement on apps targeting S + boolean exempt = mN.contentView != null && mN.bigContentView == null + && mStyle == null && mActions.size() == 0 + && mN.extras.getCharSequence(EXTRA_TITLE) == null + && mN.extras.getCharSequence(EXTRA_TEXT) == null; + return !exempt; + } + /** * Construct a RemoteViews for the final notification header only. This will not be * colorized. @@ -8689,18 +8702,24 @@ public class Notification implements Parcelable return makeStandardTemplateWithCustomContent(headsUpContentView); } TemplateBindResult result = new TemplateBindResult(); + StandardTemplateParams p = mBuilder.mParams.reset() + .viewType(StandardTemplateParams.VIEW_TYPE_HEADS_UP) + .hasCustomContent(headsUpContentView != null) + .fillTextsFrom(mBuilder); RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions( - mBuilder.getHeadsUpBaseLayoutResource(), - StandardTemplateParams.VIEW_TYPE_HEADS_UP, result); + mBuilder.getHeadsUpBaseLayoutResource(), p, result); buildIntoRemoteViewContent(remoteViews, headsUpContentView, result, true); return remoteViews; } private RemoteViews makeStandardTemplateWithCustomContent(RemoteViews customContent) { TemplateBindResult result = new TemplateBindResult(); + StandardTemplateParams p = mBuilder.mParams.reset() + .viewType(StandardTemplateParams.VIEW_TYPE_NORMAL) + .hasCustomContent(customContent != null) + .fillTextsFrom(mBuilder); RemoteViews remoteViews = mBuilder.applyStandardTemplate( - mBuilder.getBaseLayoutResource(), - StandardTemplateParams.VIEW_TYPE_NORMAL, result); + mBuilder.getBaseLayoutResource(), p, result); buildIntoRemoteViewContent(remoteViews, customContent, result, true); return remoteViews; } @@ -8710,9 +8729,12 @@ public class Notification implements Parcelable ? mBuilder.mN.contentView : mBuilder.mN.bigContentView; TemplateBindResult result = new TemplateBindResult(); + StandardTemplateParams p = mBuilder.mParams.reset() + .viewType(StandardTemplateParams.VIEW_TYPE_BIG) + .hasCustomContent(bigContentView != null) + .fillTextsFrom(mBuilder); RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions( - mBuilder.getBigBaseLayoutResource(), - StandardTemplateParams.VIEW_TYPE_BIG, result); + mBuilder.getBigBaseLayoutResource(), p, result); buildIntoRemoteViewContent(remoteViews, bigContentView, result, false); return remoteViews; } @@ -11025,6 +11047,7 @@ public class Notification implements Parcelable int mViewType = VIEW_TYPE_UNSPECIFIED; boolean mHeaderless; + boolean mHasCustomContent; boolean hasProgress = true; CharSequence title; CharSequence text; @@ -11038,6 +11061,7 @@ public class Notification implements Parcelable final StandardTemplateParams reset() { mViewType = VIEW_TYPE_UNSPECIFIED; mHeaderless = false; + mHasCustomContent = false; hasProgress = true; title = null; text = null; @@ -11049,6 +11073,10 @@ public class Notification implements Parcelable return this; } + final boolean hasTitle() { + return title != null && title.length() != 0 && !mHasCustomContent; + } + final StandardTemplateParams viewType(int viewType) { mViewType = viewType; return this; @@ -11064,6 +11092,11 @@ public class Notification implements Parcelable return this; } + final StandardTemplateParams hasCustomContent(boolean hasCustomContent) { + this.mHasCustomContent = hasCustomContent; + return this; + } + final StandardTemplateParams title(CharSequence title) { this.title = title; return this; diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java index f76a75749480..9dbf1ff6f7c9 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -179,6 +179,12 @@ public final class PendingIntent implements Parcelable { * extras change, and don't care that any entities that received your * previous PendingIntent will be able to launch it with your new * extras even if they are not explicitly given to it. + * + * <p>{@link #FLAG_UPDATE_CURRENT} still works even if {@link + * #FLAG_IMMUTABLE} is set - the creator of the PendingIntent can always + * update the PendingIntent itself. The IMMUTABLE flag only limits the + * ability to alter the semantics of the intent that is sent by {@link + * #send} by the invoker of {@link #send}. */ public static final int FLAG_UPDATE_CURRENT = 1<<27; @@ -187,6 +193,11 @@ public final class PendingIntent implements Parcelable { * This means that the additional intent argument passed to the send * methods to fill in unpopulated properties of this intent will be * ignored. + * + * <p>{@link #FLAG_IMMUTABLE} only limits the ability to alter the + * semantics of the intent that is sent by {@link #send} by the invoker of + * {@link #send}. The creator of the PendingIntent can always update the + * PendingIntent itself via {@link #FLAG_UPDATE_CURRENT}. */ public static final int FLAG_IMMUTABLE = 1<<26; diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java index b96b54ad8d21..3798de921dc7 100644 --- a/core/java/android/app/Service.java +++ b/core/java/android/app/Service.java @@ -697,6 +697,10 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac * service element of manifest file. The value of attribute * {@link android.R.attr#foregroundServiceType} can be multiple flags ORed together.</p> * + * @throws IllegalStateException If the app targeting API is + * {@link android.os.Build.VERSION_CODES#S} or later, and the service is restricted from + * becoming foreground service due to background restriction. + * * @param id The identifier for this notification as per * {@link NotificationManager#notify(int, Notification) * NotificationManager.notify(int, Notification)}; must not be 0. diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING index b0307d1eb939..d2be8a4a6597 100644 --- a/core/java/android/app/TEST_MAPPING +++ b/core/java/android/app/TEST_MAPPING @@ -33,10 +33,10 @@ }, { "file_patterns": ["(/|^)AppOpsManager.java"], - "name": "CtsStatsdHostTestCases", + "name": "CtsStatsdAtomHostTestCases", "options": [ { - "include-filter": "android.cts.statsd.atom.UidAtomTests#testAppOps" + "include-filter": "android.cts.statsdatom.appops.AppOpsTests#testAppOps" } ] }, diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java index 7fdf06f7233c..ca67dba45dd0 100644 --- a/core/java/android/app/TaskInfo.java +++ b/core/java/android/app/TaskInfo.java @@ -36,6 +36,7 @@ import android.util.Log; import android.window.WindowContainerToken; import java.util.ArrayList; +import java.util.Objects; /** * Stores information about a particular Task. @@ -220,6 +221,12 @@ public class TaskInfo { */ public Rect parentBounds; + /** + * Whether this task is focused. + * @hide + */ + public boolean isFocused; + TaskInfo() { // Do nothing } @@ -288,6 +295,37 @@ public class TaskInfo { } /** + * Returns {@code true} if parameters that are important for task organizers have changed + * and {@link com.android.server.wm.TaskOrginizerController} needs to notify listeners + * about that. + * @hide + */ + public boolean equalsForTaskOrganizer(@Nullable TaskInfo that) { + if (that == null) { + return false; + } + return topActivityType == that.topActivityType + && isResizeable == that.isResizeable + && Objects.equals(positionInParent, that.positionInParent) + && equalsLetterboxParams(that) + && pictureInPictureParams == that.pictureInPictureParams + && getWindowingMode() == that.getWindowingMode() + && Objects.equals(taskDescription, that.taskDescription) + && isFocused == that.isFocused; + } + + private boolean equalsLetterboxParams(TaskInfo that) { + return Objects.equals(letterboxActivityBounds, that.letterboxActivityBounds) + && Objects.equals( + getConfiguration().windowConfiguration.getBounds(), + that.getConfiguration().windowConfiguration.getBounds()) + && Objects.equals( + getConfiguration().windowConfiguration.getMaxBounds(), + that.getConfiguration().windowConfiguration.getMaxBounds()) + && Objects.equals(parentBounds, that.parentBounds); + } + + /** * Reads the TaskInfo from a parcel. */ void readFromParcel(Parcel source) { @@ -319,6 +357,7 @@ public class TaskInfo { positionInParent = source.readTypedObject(Point.CREATOR); parentTaskId = source.readInt(); parentBounds = source.readTypedObject(Rect.CREATOR); + isFocused = source.readBoolean(); } /** @@ -354,6 +393,7 @@ public class TaskInfo { dest.writeTypedObject(positionInParent, flags); dest.writeInt(parentTaskId); dest.writeTypedObject(parentBounds, flags); + dest.writeBoolean(isFocused); } @Override @@ -376,8 +416,9 @@ public class TaskInfo { + " launchCookies" + launchCookies + " letterboxActivityBounds=" + letterboxActivityBounds + " positionInParent=" + positionInParent - + " parentTaskId: " + parentTaskId - + " parentBounds: " + parentBounds + + " parentTaskId=" + parentTaskId + + " parentBounds=" + parentBounds + + " isFocused=" + isFocused + "}"; } } diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java index 1b0fd9edf4f8..787393ed0f6c 100644 --- a/core/java/android/app/UiAutomation.java +++ b/core/java/android/app/UiAutomation.java @@ -695,6 +695,9 @@ public final class UiAutomation { /** * A method for injecting an arbitrary input event. + * + * This method waits for all window container animations and surface operations to complete. + * * <p> * <strong>Note:</strong> It is caller's responsibility to recycle the event. * </p> @@ -704,12 +707,34 @@ public final class UiAutomation { * @return Whether event injection succeeded. */ public boolean injectInputEvent(InputEvent event, boolean sync) { + return injectInputEvent(event, sync, true /* waitForAnimations */); + } + + /** + * A method for injecting an arbitrary input event, optionally waiting for window animations to + * complete. + * <p> + * <strong>Note:</strong> It is caller's responsibility to recycle the event. + * </p> + * + * @param event The event to inject. + * @param sync Whether to inject the event synchronously. + * @param waitForAnimations Whether to wait for all window container animations and surface + * operations to complete. + * @return Whether event injection succeeded. + * + * @hide + */ + @TestApi + public boolean injectInputEvent(@NonNull InputEvent event, boolean sync, + boolean waitForAnimations) { try { if (DEBUG) { - Log.i(LOG_TAG, "Injecting: " + event + " sync: " + sync); + Log.i(LOG_TAG, "Injecting: " + event + " sync: " + sync + " waitForAnimations: " + + waitForAnimations); } // Calling out without a lock held. - return mUiAutomationConnection.injectInputEvent(event, sync); + return mUiAutomationConnection.injectInputEvent(event, sync, waitForAnimations); } catch (RemoteException re) { Log.e(LOG_TAG, "Error while injecting input event!", re); } @@ -726,7 +751,26 @@ public final class UiAutomation { public void syncInputTransactions() { try { // Calling out without a lock held. - mUiAutomationConnection.syncInputTransactions(); + mUiAutomationConnection.syncInputTransactions(true /* waitForAnimations */); + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error while syncing input transactions!", re); + } + } + + /** + * A request for WindowManagerService to wait until all input information has been sent from + * WindowManager to native InputManager and optionally wait for animations to complete. + * + * @param waitForAnimations Whether to wait for all window container animations and surface + * operations to complete. + * + * @hide + */ + @TestApi + public void syncInputTransactions(boolean waitForAnimations) { + try { + // Calling out without a lock held. + mUiAutomationConnection.syncInputTransactions(waitForAnimations); } catch (RemoteException re) { Log.e(LOG_TAG, "Error while syncing input transactions!", re); } diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java index 290e12191de8..7036b6e7dbc9 100644 --- a/core/java/android/app/UiAutomationConnection.java +++ b/core/java/android/app/UiAutomationConnection.java @@ -124,7 +124,7 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub { } @Override - public boolean injectInputEvent(InputEvent event, boolean sync) { + public boolean injectInputEvent(InputEvent event, boolean sync, boolean waitForAnimations) { synchronized (mLock) { throwIfCalledByNotTrustedUidLocked(); throwIfShutdownLocked(); @@ -134,7 +134,8 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub { : InputManager.INJECT_INPUT_EVENT_MODE_ASYNC; final long identity = Binder.clearCallingIdentity(); try { - return mWindowManager.injectInputAfterTransactionsApplied(event, mode); + return mWindowManager.injectInputAfterTransactionsApplied(event, mode, + waitForAnimations); } catch (RemoteException e) { } finally { Binder.restoreCallingIdentity(identity); @@ -143,7 +144,7 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub { } @Override - public void syncInputTransactions() { + public void syncInputTransactions(boolean waitForAnimations) { synchronized (mLock) { throwIfCalledByNotTrustedUidLocked(); throwIfShutdownLocked(); @@ -151,12 +152,11 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub { } try { - mWindowManager.syncInputTransactions(); + mWindowManager.syncInputTransactions(waitForAnimations); } catch (RemoteException e) { } } - @Override public boolean setRotation(int rotation) { synchronized (mLock) { diff --git a/core/java/android/app/WaitResult.java b/core/java/android/app/WaitResult.java index d65be9bded4f..77891e0fd007 100644 --- a/core/java/android/app/WaitResult.java +++ b/core/java/android/app/WaitResult.java @@ -40,14 +40,21 @@ public class WaitResult implements Parcelable { */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = {"LAUNCH_STATE_"}, value = { + LAUNCH_STATE_UNKNOWN, LAUNCH_STATE_COLD, LAUNCH_STATE_WARM, - LAUNCH_STATE_HOT + LAUNCH_STATE_HOT, + LAUNCH_STATE_RELAUNCH }) public @interface LaunchState { } /** + * Not considered as a launch event, e.g. the activity is already on top. + */ + public static final int LAUNCH_STATE_UNKNOWN = 0; + + /** * Cold launch sequence: a new process has started. */ public static final int LAUNCH_STATE_COLD = 1; @@ -62,6 +69,13 @@ public class WaitResult implements Parcelable { */ public static final int LAUNCH_STATE_HOT = 3; + /** + * Relaunch launch sequence: process reused, but activity has to be destroyed and created. + * E.g. the current device configuration is different from the background activity that will be + * brought to foreground, and the activity doesn't declare to handle the change. + */ + public static final int LAUNCH_STATE_RELAUNCH = 4; + public static final int INVALID_DELAY = -1; public int result; public boolean timeout; @@ -124,6 +138,8 @@ public class WaitResult implements Parcelable { return "WARM"; case LAUNCH_STATE_HOT: return "HOT"; + case LAUNCH_STATE_RELAUNCH: + return "RELAUNCH"; default: return "UNKNOWN (" + type + ")"; } diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 16ae081049ef..42427fa825eb 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -6436,6 +6436,8 @@ public class DevicePolicyManager { } /** + * Called by a privileged caller holding {@code BIND_DEVICE_ADMIN} permission to retrieve + * the remove warning for the given device admin. * @hide */ public void getRemoveWarning(@Nullable ComponentName admin, RemoteCallback result) { @@ -8505,6 +8507,8 @@ public class DevicePolicyManager { * it previously set with {@link #addUserRestriction(ComponentName, String)}. * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. + * @return a {@link Bundle} whose keys are the user restrictions, and the values a + * {@code boolean} indicating whether the restriction is set. * @throws SecurityException if {@code admin} is not a device or profile owner. */ public @NonNull Bundle getUserRestrictions(@NonNull ComponentName admin) { diff --git a/core/java/android/app/people/PeopleSpaceTile.java b/core/java/android/app/people/PeopleSpaceTile.java new file mode 100644 index 000000000000..f5674e5cd0ce --- /dev/null +++ b/core/java/android/app/people/PeopleSpaceTile.java @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.people; + +import android.annotation.NonNull; +import android.content.Intent; +import android.content.pm.ShortcutInfo; +import android.graphics.drawable.Icon; +import android.net.Uri; +import android.os.Parcel; +import android.os.Parcelable; +import android.service.notification.StatusBarNotification; + +/** + * The People Space tile contains all relevant information to render a tile in People Space: namely + * the data of any visible conversation notification associated, associated statuses, and the last + * interaction time. + * + * @hide + */ +public class PeopleSpaceTile implements Parcelable { + + private String mId; + private CharSequence mUserName; + private Icon mUserIcon; + private int mUid; + private Uri mContactUri; + private String mPackageName; + private long mLastInteractionTimestamp; + private boolean mIsImportantConversation; + private boolean mIsHiddenConversation; + private StatusBarNotification mNotification; + private Intent mIntent; + // TODO: add a List of the Status objects once created + + private PeopleSpaceTile(Builder b) { + mId = b.mId; + mUserName = b.mUserName; + mUserIcon = b.mUserIcon; + mContactUri = b.mContactUri; + mUid = b.mUid; + mPackageName = b.mPackageName; + mLastInteractionTimestamp = b.mLastInteractionTimestamp; + mIsImportantConversation = b.mIsImportantConversation; + mIsHiddenConversation = b.mIsHiddenConversation; + mNotification = b.mNotification; + mIntent = b.mIntent; + } + + public String getId() { + return mId; + } + + public CharSequence getUserName() { + return mUserName; + } + + public Icon getUserIcon() { + return mUserIcon; + } + + /** Returns the Uri associated with the user in Android Contacts database. */ + public Uri getContactUri() { + return mContactUri; + } + + public int getUid() { + return mUid; + } + + public String getPackageName() { + return mPackageName; + } + + /** Returns the timestamp of the last interaction. */ + public long getLastInteractionTimestamp() { + return mLastInteractionTimestamp; + } + + /** + * Whether the conversation is important. + */ + public boolean isImportantConversation() { + return mIsImportantConversation; + } + + /** + * Whether the conversation should be hidden. + */ + public boolean isHiddenConversation() { + return mIsHiddenConversation; + } + + /** + * If a notification is currently active that maps to the relevant shortcut ID, provides the + * {@link StatusBarNotification} associated. + */ + public StatusBarNotification getNotification() { + return mNotification; + } + + /** + * Provides an intent to launch. If present, we should manually launch the intent on tile + * click, rather than calling {@link android.content.pm.LauncherApps} to launch the shortcut ID. + * + * <p>This field should only be used if manually constructing a tile without an associated + * shortcut to launch (i.e. birthday tiles). + */ + public Intent getIntent() { + return mIntent; + } + + /** Builder to create a {@link PeopleSpaceTile}. */ + public static class Builder { + private String mId; + private CharSequence mUserName; + private Icon mUserIcon; + private Uri mContactUri; + private int mUid; + private String mPackageName; + private long mLastInteractionTimestamp; + private boolean mIsImportantConversation; + private boolean mIsHiddenConversation; + private StatusBarNotification mNotification; + private Intent mIntent; + + /** Builder for use only if a shortcut is not available for the tile. */ + public Builder(String id, String userName, Icon userIcon, Intent intent) { + mId = id; + mUserName = userName; + mUserIcon = userIcon; + mIntent = intent; + mPackageName = intent == null ? null : intent.getPackage(); + } + + public Builder(ShortcutInfo info) { + mId = info.getId(); + mUserName = info.getLabel(); + mUserIcon = info.getIcon(); + mUid = info.getUserId(); + mPackageName = info.getPackage(); + } + + /** Sets the ID for the tile. */ + public Builder setId(String id) { + mId = id; + return this; + } + + /** Sets the user name. */ + public Builder setUserName(CharSequence userName) { + mUserName = userName; + return this; + } + + /** Sets the icon shown for the user. */ + public Builder setUserIcon(Icon userIcon) { + mUserIcon = userIcon; + return this; + } + + /** Sets the Uri associated with the user in Android Contacts database. */ + public Builder setContactUri(Uri uri) { + mContactUri = uri; + return this; + } + + /** Sets the associated uid. */ + public Builder setUid(int uid) { + mUid = uid; + return this; + } + + /** Sets the package shown that provided the information. */ + public Builder setPackageName(String packageName) { + mPackageName = packageName; + return this; + } + + /** Sets the last interaction timestamp. */ + public Builder setLastInteractionTimestamp(long lastInteractionTimestamp) { + mLastInteractionTimestamp = lastInteractionTimestamp; + return this; + } + + /** Sets whether the conversation is important. */ + public Builder setIsImportantConversation(boolean isImportantConversation) { + mIsImportantConversation = isImportantConversation; + return this; + } + + /** Sets whether the conversation is hidden. */ + public Builder setIsHiddenConversation(boolean isHiddenConversation) { + mIsHiddenConversation = isHiddenConversation; + return this; + } + + /** Sets the associated notification. */ + public Builder setNotification(StatusBarNotification notification) { + mNotification = notification; + return this; + } + + /** Sets an intent to launch on click. */ + public Builder setIntent(Intent intent) { + mIntent = intent; + return this; + } + + /** Builds a {@link PeopleSpaceTile}. */ + @NonNull + public PeopleSpaceTile build() { + return new PeopleSpaceTile(this); + } + } + + private PeopleSpaceTile(Parcel in) { + mId = in.readString(); + mUserName = in.readCharSequence(); + mUserIcon = in.readParcelable(Icon.class.getClassLoader()); + mUid = in.readInt(); + mPackageName = in.readString(); + mLastInteractionTimestamp = in.readLong(); + mIsImportantConversation = in.readBoolean(); + mIsHiddenConversation = in.readBoolean(); + mNotification = in.readParcelable(StatusBarNotification.class.getClassLoader()); + mIntent = in.readParcelable(Intent.class.getClassLoader()); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mId); + dest.writeCharSequence(mUserName); + dest.writeParcelable(mUserIcon, flags); + dest.writeInt(mUid); + dest.writeString(mPackageName); + dest.writeLong(mLastInteractionTimestamp); + dest.writeParcelable(mNotification, flags); + dest.writeBoolean(mIsImportantConversation); + dest.writeBoolean(mIsHiddenConversation); + dest.writeParcelable(mIntent, flags); + } + + public static final @android.annotation.NonNull + Creator<PeopleSpaceTile> CREATOR = new Creator<PeopleSpaceTile>() { + public PeopleSpaceTile createFromParcel(Parcel source) { + return new PeopleSpaceTile(source); + } + + public PeopleSpaceTile[] newArray(int size) { + return new PeopleSpaceTile[size]; + } + }; +} diff --git a/core/java/android/bluetooth/BluetoothCodecStatus.java b/core/java/android/bluetooth/BluetoothCodecStatus.java index 3a65aaa0d16c..7764ebeb2e33 100644 --- a/core/java/android/bluetooth/BluetoothCodecStatus.java +++ b/core/java/android/bluetooth/BluetoothCodecStatus.java @@ -18,7 +18,6 @@ package android.bluetooth; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; -import android.os.Build; import android.os.Parcel; import android.os.Parcelable; @@ -40,7 +39,7 @@ public final class BluetoothCodecStatus implements Parcelable { * This extra represents the current codec status of the A2DP * profile. */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public static final String EXTRA_CODEC_STATUS = "android.bluetooth.extra.CODEC_STATUS"; @@ -199,7 +198,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return the current codec configuration */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public @Nullable BluetoothCodecConfig getCodecConfig() { return mCodecConfig; } @@ -209,7 +208,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return an array with the codecs local capabilities */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public @Nullable BluetoothCodecConfig[] getCodecsLocalCapabilities() { return mCodecsLocalCapabilities; } @@ -219,7 +218,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return an array with the codecs selectable capabilities */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public @Nullable BluetoothCodecConfig[] getCodecsSelectableCapabilities() { return mCodecsSelectableCapabilities; } diff --git a/core/java/android/companion/OWNERS b/core/java/android/companion/OWNERS new file mode 100644 index 000000000000..da723b3b67da --- /dev/null +++ b/core/java/android/companion/OWNERS @@ -0,0 +1 @@ +eugenesusla@google.com
\ No newline at end of file diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 37e0a44bdd95..8f92bf1e3253 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -63,6 +63,7 @@ import android.os.HandlerExecutor; import android.os.IBinder; import android.os.Looper; import android.os.StatFs; +import android.os.StrictMode; import android.os.UserHandle; import android.os.UserManager; import android.os.storage.StorageManager; @@ -366,6 +367,16 @@ public abstract class Context { /*********** Hidden flags below this line ***********/ /** + * Flag for {@link #bindService}: allow background foreground service starts from the bound + * service's process. + * This flag is only respected if the caller is holding + * {@link android.Manifest.permission#START_FOREGROUND_SERVICES_FROM_BACKGROUND}. + * @hide + */ + @SystemApi + public static final int BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND = 0x00040000; + + /** * Flag for {@link #bindService}: This flag is intended to be used only by the system to adjust * the scheduling policy for IMEs (and any other out-of-process user-visible components that * work closely with the top app) so that UI hosted in such services can have the same @@ -3106,6 +3117,10 @@ public abstract class Context { * @throws SecurityException If the caller does not have permission to access the service * or the service can not be found. * + * @throws IllegalStateException If the caller app's targeting API is + * {@link android.os.Build.VERSION_CODES#S} or later, and the foreground service is restricted + * from start due to background restriction. + * * @see #stopService * @see android.app.Service#startForeground(int, android.app.Notification) */ @@ -6285,4 +6300,25 @@ public abstract class Context { public boolean isUiContext() { throw new RuntimeException("Not implemented. Must override in a subclass."); } + + /** + * Returns {@code true} if the context is a UI context which can access UI components such as + * {@link WindowManager}, {@link android.view.LayoutInflater LayoutInflater} or + * {@link android.app.WallpaperManager WallpaperManager}. Accessing UI components from non-UI + * contexts throws {@link android.os.strictmode.Violation} if + * {@link StrictMode.VmPolicy.Builder#detectIncorrectContextUse()} is enabled. + * <p> + * Examples of UI contexts are + * an {@link android.app.Activity Activity}, a context created from + * {@link #createWindowContext(int, Bundle)} or + * {@link android.inputmethodservice.InputMethodService InputMethodService} + * </p> + * + * @see #getDisplay() + * @see #getSystemService(String) + * @see StrictMode.VmPolicy.Builder#detectIncorrectContextUse() + */ + public static boolean isUiContext(@NonNull Context context) { + return context.isUiContext(); + } } diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index d66a42a6232a..f634b8a54a0f 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -38,6 +38,7 @@ import android.content.pm.InstrumentationInfo; import android.content.pm.KeySet; import android.content.pm.ModuleInfo; import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; import android.content.pm.ProviderInfo; import android.content.pm.PermissionGroupInfo; @@ -797,4 +798,7 @@ interface IPackageManager { IBinder getHoldLockToken(); void holdLock(in IBinder token, in int durationMs); + + PackageManager.Property getProperty(String propertyName, String packageName, String className); + ParceledListSlice queryProperty(String propertyName, int componentType); } diff --git a/core/java/android/content/pm/PackageManager.aidl b/core/java/android/content/pm/PackageManager.aidl new file mode 100644 index 000000000000..31365a19f286 --- /dev/null +++ b/core/java/android/content/pm/PackageManager.aidl @@ -0,0 +1,20 @@ +/* +** +** Copyright 2020, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android.content.pm; + +parcelable PackageManager.Property; diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index cc484deeee49..044b3b2e8284 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -64,6 +64,8 @@ import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; import android.os.PersistableBundle; import android.os.RemoteException; import android.os.UserHandle; @@ -75,6 +77,7 @@ import android.permission.PermissionManager; import android.util.AndroidException; import android.util.Log; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import dalvik.system.VMRuntime; @@ -116,6 +119,243 @@ public abstract class PackageManager { } /** + * A property value set within the manifest. + * <p> + * The value of a property will only have a single type, as defined by + * the property itself. + */ + public static final class Property implements Parcelable { + private static final int TYPE_BOOLEAN = 1; + private static final int TYPE_FLOAT = 2; + private static final int TYPE_INTEGER = 3; + private static final int TYPE_RESOURCE = 4; + private static final int TYPE_STRING = 5; + private final String mName; + private final int mType; + private final String mClassName; + private final String mPackageName; + private boolean mBooleanValue; + private float mFloatValue; + private int mIntegerValue; + private String mStringValue; + + /** @hide */ + @VisibleForTesting + public Property(@NonNull String name, int type, + @NonNull String packageName, @Nullable String className) { + assert name != null; + assert type >= TYPE_BOOLEAN && type <= TYPE_STRING; + assert packageName != null; + this.mName = name; + this.mType = type; + this.mPackageName = packageName; + this.mClassName = className; + } + /** @hide */ + public Property(@NonNull String name, boolean value, + String packageName, String className) { + this(name, TYPE_BOOLEAN, packageName, className); + mBooleanValue = value; + } + /** @hide */ + public Property(@NonNull String name, float value, + String packageName, String className) { + this(name, TYPE_FLOAT, packageName, className); + mFloatValue = value; + } + /** @hide */ + public Property(@NonNull String name, int value, boolean isResource, + String packageName, String className) { + this(name, isResource ? TYPE_RESOURCE : TYPE_INTEGER, packageName, className); + mIntegerValue = value; + } + /** @hide */ + public Property(@NonNull String name, String value, + String packageName, String className) { + this(name, TYPE_STRING, packageName, className); + mStringValue = value; + } + + /** @hide */ + @VisibleForTesting + public int getType() { + return mType; + } + + /** + * Returns the name of the property. + */ + @NonNull public String getName() { + return mName; + } + + /** + * Returns the name of the package where this this property was defined. + */ + @NonNull public String getPackageName() { + return mPackageName; + } + + /** + * Returns the classname of the component where this property was defined. + * <p>If the property was defined within and <application> tag, retutrns + * {@code null} + */ + @Nullable public String getClassName() { + return mClassName; + } + + /** + * Returns the boolean value set for the property. + * <p>If the property is not of a boolean type, returns {@code false}. + */ + public boolean getBoolean() { + return mBooleanValue; + } + + /** + * Returns {@code true} if the property is a boolean type. Otherwise {@code false}. + */ + public boolean isBoolean() { + return mType == TYPE_BOOLEAN; + } + + /** + * Returns the float value set for the property. + * <p>If the property is not of a float type, returns {@code 0.0}. + */ + public float getFloat() { + return mFloatValue; + } + + /** + * Returns {@code true} if the property is a float type. Otherwise {@code false}. + */ + public boolean isFloat() { + return mType == TYPE_FLOAT; + } + + /** + * Returns the integer value set for the property. + * <p>If the property is not of an integer type, returns {@code 0}. + */ + public int getInteger() { + return mType == TYPE_INTEGER ? mIntegerValue : 0; + } + + /** + * Returns {@code true} if the property is an integer type. Otherwise {@code false}. + */ + public boolean isInteger() { + return mType == TYPE_INTEGER; + } + + /** + * Returns the a resource id set for the property. + * <p>If the property is not of a resource id type, returns {@code 0}. + */ + public int getResourceId() { + return mType == TYPE_RESOURCE ? mIntegerValue : 0; + } + + /** + * Returns {@code true} if the property is a resource id type. Otherwise {@code false}. + */ + public boolean isResourceId() { + return mType == TYPE_RESOURCE; + } + + /** + * Returns the a String value set for the property. + * <p>If the property is not a String type, returns {@code null}. + */ + @Nullable public String getString() { + return mStringValue; + } + + /** + * Returns {@code true} if the property is a String type. Otherwise {@code false}. + */ + public boolean isString() { + return mType == TYPE_STRING; + } + + /** + * Adds a mapping from the given key to this property's value in the provided + * {@link android.os.Bundle}. If the provided {@link android.os.Bundle} is + * {@code null}, creates a new {@link android.os.Bundle}. + * @hide + */ + public Bundle toBundle(Bundle outBundle) { + final Bundle b = outBundle == null ? new Bundle() : outBundle; + if (mType == TYPE_BOOLEAN) { + b.putBoolean(mName, mBooleanValue); + } else if (mType == TYPE_FLOAT) { + b.putFloat(mName, mFloatValue); + } else if (mType == TYPE_INTEGER) { + b.putInt(mName, mIntegerValue); + } else if (mType == TYPE_RESOURCE) { + b.putInt(mName, mIntegerValue); + } else if (mType == TYPE_STRING) { + b.putString(mName, mStringValue); + } + return b; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString(mName); + dest.writeInt(mType); + dest.writeString(mPackageName); + dest.writeString(mClassName); + if (mType == TYPE_BOOLEAN) { + dest.writeBoolean(mBooleanValue); + } else if (mType == TYPE_FLOAT) { + dest.writeFloat(mFloatValue); + } else if (mType == TYPE_INTEGER) { + dest.writeInt(mIntegerValue); + } else if (mType == TYPE_RESOURCE) { + dest.writeInt(mIntegerValue); + } else if (mType == TYPE_STRING) { + dest.writeString(mStringValue); + } + } + + @NonNull + public static final Creator<Property> CREATOR = new Creator<Property>() { + @Override + public Property createFromParcel(@NonNull Parcel source) { + final String name = source.readString(); + final int type = source.readInt(); + final String packageName = source.readString(); + final String className = source.readString(); + if (type == TYPE_BOOLEAN) { + return new Property(name, source.readBoolean(), packageName, className); + } else if (type == TYPE_FLOAT) { + return new Property(name, source.readFloat(), packageName, className); + } else if (type == TYPE_INTEGER) { + return new Property(name, source.readInt(), false, packageName, className); + } else if (type == TYPE_RESOURCE) { + return new Property(name, source.readInt(), true, packageName, className); + } else if (type == TYPE_STRING) { + return new Property(name, source.readString(), packageName, className); + } + return null; + } + + @Override + public Property[] newArray(int size) { + return new Property[size]; + } + }; + } + + /** * Listener for changes in permissions granted to a UID. * * @hide @@ -130,6 +370,41 @@ public abstract class PackageManager { public void onPermissionsChanged(int uid); } + /** @hide */ + public static final int TYPE_UNKNOWN = 0; + /** @hide */ + public static final int TYPE_ACTIVITY = 1; + /** @hide */ + public static final int TYPE_RECEIVER = 2; + /** @hide */ + public static final int TYPE_SERVICE = 3; + /** @hide */ + public static final int TYPE_PROVIDER = 4; + /** @hide */ + public static final int TYPE_APPLICATION = 5; + /** @hide */ + @IntDef(prefix = { "TYPE_" }, value = { + TYPE_UNKNOWN, + TYPE_ACTIVITY, + TYPE_RECEIVER, + TYPE_SERVICE, + TYPE_PROVIDER, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ComponentType {} + + /** @hide */ + @IntDef(prefix = { "TYPE_" }, value = { + TYPE_UNKNOWN, + TYPE_ACTIVITY, + TYPE_RECEIVER, + TYPE_SERVICE, + TYPE_PROVIDER, + TYPE_APPLICATION, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface PropertyLocation {} + /** * As a guiding principle: * <p> @@ -8124,7 +8399,7 @@ public abstract class PackageManager { * Trust any Installer to provide checksums for the package. * @see #requestChecksums */ - public static final @Nullable List<Certificate> TRUST_ALL = Collections.singletonList(null); + public static final @NonNull List<Certificate> TRUST_ALL = Collections.singletonList(null); /** * Don't trust any Installer to provide checksums for the package. @@ -8363,6 +8638,87 @@ public abstract class PackageManager { } /** + * Returns the property defined in the given package's <appliction> tag. + * + * @throws NameNotFoundException if either the given package is not installed or if the + * given property is not defined within the <application> tag. + */ + @NonNull + public Property getProperty(@NonNull String propertyName, @NonNull String packageName) + throws NameNotFoundException { + throw new UnsupportedOperationException( + "getProperty not implemented in subclass"); + } + + /** + * Returns the property defined in the given component declaration. + * + * @throws NameNotFoundException if either the given component does not exist or if the + * given property is not defined within the component declaration. + */ + @NonNull + public Property getProperty(@NonNull String propertyName, @NonNull ComponentName component) + throws NameNotFoundException { + throw new UnsupportedOperationException( + "getProperty not implemented in subclass"); + } + + /** + * Returns the property definition for all <application> tags. + * <p>If the property is not defined with any <application> tag, + * returns and empty list. + */ + @NonNull + public List<Property> queryApplicationProperty(@NonNull String propertyName) { + throw new UnsupportedOperationException( + "qeuryApplicationProperty not implemented in subclass"); + } + + /** + * Returns the property definition for all <activity> and <activity-alias> tags. + * <p>If the property is not defined with any <activity> and <activity-alias> tag, + * returns and empty list. + */ + @NonNull + public List<Property> queryActivityProperty(@NonNull String propertyName) { + throw new UnsupportedOperationException( + "qeuryActivityProperty not implemented in subclass"); + } + + /** + * Returns the property definition for all <provider> tags. + * <p>If the property is not defined with any <provider> tag, + * returns and empty list. + */ + @NonNull + public List<Property> queryProviderProperty(@NonNull String propertyName) { + throw new UnsupportedOperationException( + "qeuryProviderProperty not implemented in subclass"); + } + + /** + * Returns the property definition for all <receiver> tags. + * <p>If the property is not defined with any <receiver> tag, + * returns and empty list. + */ + @NonNull + public List<Property> queryReceiverProperty(@NonNull String propertyName) { + throw new UnsupportedOperationException( + "qeuryReceiverProperty not implemented in subclass"); + } + + /** + * Returns the property definition for all <service> tags. + * <p>If the property is not defined with any <service> tag, + * returns and empty list. + */ + @NonNull + public List<Property> queryServiceProperty(@NonNull String propertyName) { + throw new UnsupportedOperationException( + "qeuryServiceProperty not implemented in subclass"); + } + + /** * Grants implicit visibility of the package that provides an authority to a querying UID. * * @throws SecurityException when called by a package other than the contacts provider diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java index c6450ffdf91c..cd9ba6a5b451 100644 --- a/core/java/android/content/pm/PermissionInfo.java +++ b/core/java/android/content/pm/PermissionInfo.java @@ -251,6 +251,16 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { @SystemApi public static final int PROTECTION_FLAG_RETAIL_DEMO = 0x1000000; + /** + * Additional flag for {@link #protectionLevel}, corresponding + * to the <code>recents</code> value of + * {@link android.R.attr#protectionLevel}. + * + * @hide + */ + @SystemApi + public static final int PROTECTION_FLAG_RECENTS = 0x2000000; + /** @hide */ @IntDef(flag = true, prefix = { "PROTECTION_FLAG_" }, value = { PROTECTION_FLAG_PRIVILEGED, @@ -274,6 +284,7 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { PROTECTION_FLAG_APP_PREDICTOR, PROTECTION_FLAG_COMPANION, PROTECTION_FLAG_RETAIL_DEMO, + PROTECTION_FLAG_RECENTS, }) @Retention(RetentionPolicy.SOURCE) public @interface ProtectionFlags {} @@ -532,6 +543,9 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { if ((level & PermissionInfo.PROTECTION_FLAG_RETAIL_DEMO) != 0) { protLevel.append("|retailDemo"); } + if ((level & PermissionInfo.PROTECTION_FLAG_RECENTS) != 0) { + protLevel.append("|recents"); + } return protLevel.toString(); } diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java index 862563706da7..a60e6428418d 100644 --- a/core/java/android/content/pm/SharedLibraryInfo.java +++ b/core/java/android/content/pm/SharedLibraryInfo.java @@ -114,15 +114,6 @@ public final class SharedLibraryInfo implements Parcelable { mIsNative = isNative; } - /** @hide */ - public SharedLibraryInfo(String path, String packageName, List<String> codePaths, - String name, long version, int type, - VersionedPackage declaringPackage, List<VersionedPackage> dependentPackages, - List<SharedLibraryInfo> dependencies) { - this(path, packageName, codePaths, name, version, type, declaringPackage, dependentPackages, - dependencies, false /* isNative */); - } - private SharedLibraryInfo(Parcel parcel) { mPath = parcel.readString8(); mPackageName = parcel.readString8(); diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java index 9cda4d33503b..814759956772 100644 --- a/core/java/android/content/pm/parsing/ParsingPackage.java +++ b/core/java/android/content/pm/parsing/ParsingPackage.java @@ -23,6 +23,7 @@ import android.content.Intent; import android.content.pm.ConfigurationInfo; import android.content.pm.FeatureGroupInfo; import android.content.pm.FeatureInfo; +import android.content.pm.PackageManager.Property; import android.content.pm.PackageParser; import android.content.pm.parsing.component.ParsedActivity; import android.content.pm.parsing.component.ParsedAttribution; @@ -75,6 +76,9 @@ public interface ParsingPackage extends ParsingPackageRead { ParsingPackage addPreferredActivityFilter(String className, ParsedIntentInfo intentInfo); + /** Add a property to the application scope */ + ParsingPackage addProperty(Property property); + ParsingPackage addProtectedBroadcast(String protectedBroadcast); ParsingPackage addProvider(ParsedProvider parsedProvider); diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java index 2a15e0260c1a..b826b7a80e4e 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java +++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java @@ -31,6 +31,7 @@ import android.content.pm.ConfigurationInfo; import android.content.pm.FeatureGroupInfo; import android.content.pm.FeatureInfo; import android.content.pm.PackageInfo; +import android.content.pm.PackageManager.Property; import android.content.pm.PackageParser; import android.content.pm.parsing.component.ParsedActivity; import android.content.pm.parsing.component.ParsedAttribution; @@ -273,6 +274,9 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @Nullable private Bundle metaData; + @NonNull + private Map<String, Property> mProperties = emptyMap(); + @Nullable @DataClass.ParcelWith(ForInternedString.class) protected String volumeUuid; @@ -623,6 +627,15 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { } @Override + public ParsingPackageImpl addProperty(@Nullable Property property) { + if (property == null) { + return this; + } + this.mProperties = CollectionUtils.add(this.mProperties, property.getName(), property); + return this; + } + + @Override public ParsingPackageImpl addProtectedBroadcast(String protectedBroadcast) { if (!this.protectedBroadcasts.contains(protectedBroadcast)) { this.protectedBroadcasts = CollectionUtils.add(this.protectedBroadcasts, @@ -1172,6 +1185,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { dest.writeInt(this.gwpAsanMode); dest.writeSparseIntArray(this.minExtensionVersions); dest.writeLong(this.mBooleans); + dest.writeMap(this.mProperties); } public ParsingPackageImpl(Parcel in) { @@ -1290,7 +1304,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { this.gwpAsanMode = in.readInt(); this.minExtensionVersions = in.readSparseIntArray(); this.mBooleans = in.readLong(); - + this.mProperties = in.createTypedArrayMap(Property.CREATOR); assignDerivedFields(); } @@ -1528,6 +1542,12 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @NonNull @Override + public Map<String, Property> getProperties() { + return mProperties; + } + + @NonNull + @Override public Set<String> getUpgradeKeySets() { return upgradeKeySets; } diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java index acd6305a1442..13ae7a28360e 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageRead.java +++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java @@ -25,6 +25,7 @@ import android.content.pm.ConfigurationInfo; import android.content.pm.FeatureGroupInfo; import android.content.pm.FeatureInfo; import android.content.pm.PackageInfo; +import android.content.pm.PackageManager.Property; import android.content.pm.PackageParser; import android.content.pm.ServiceInfo; import android.content.pm.parsing.component.ParsedActivity; @@ -43,8 +44,6 @@ import android.util.Pair; import android.util.SparseArray; import android.util.SparseIntArray; -import com.android.internal.R; - import java.security.PublicKey; import java.util.List; import java.util.Map; @@ -204,6 +203,12 @@ public interface ParsingPackageRead extends Parcelable { List<String> getRequestedPermissions(); /** + * Returns the properties set on the application + */ + @NonNull + Map<String, Property> getProperties(); + + /** * Whether or not the app requested explicitly resizeable Activities. * A null value means nothing was explicitly requested. */ diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java index 6196854526e0..eae7d452ff74 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java +++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java @@ -42,6 +42,7 @@ import android.content.pm.FeatureGroupInfo; import android.content.pm.FeatureInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.Property; import android.content.pm.PackageParser; import android.content.pm.PackageParser.PackageParserException; import android.content.pm.PackageParser.SigningDetails; @@ -51,6 +52,7 @@ import android.content.pm.parsing.component.ParsedActivity; import android.content.pm.parsing.component.ParsedActivityUtils; import android.content.pm.parsing.component.ParsedAttribution; import android.content.pm.parsing.component.ParsedAttributionUtils; +import android.content.pm.parsing.component.ParsedComponent; import android.content.pm.parsing.component.ParsedInstrumentation; import android.content.pm.parsing.component.ParsedInstrumentationUtils; import android.content.pm.parsing.component.ParsedIntentInfo; @@ -699,12 +701,19 @@ public class ParsingPackageUtils { // note: application meta-data is stored off to the side, so it can // remain null in the primary copy (we like to avoid extra copies because // it can be large) - ParseResult<Bundle> metaDataResult = parseMetaData(pkg, res, parser, - pkg.getMetaData(), input); - if (metaDataResult.isSuccess()) { - pkg.setMetaData(metaDataResult.getResult()); + ParseResult<Property> metaDataResult = parseMetaData(pkg, null, res, + parser, "<meta-data>", input); + if (metaDataResult.isSuccess() && metaDataResult.getResult() != null) { + pkg.setMetaData(metaDataResult.getResult().toBundle(pkg.getMetaData())); } return metaDataResult; + case "property": + ParseResult<Property> propertyResult = parseMetaData(pkg, null, res, + parser, "<property>", input); + if (propertyResult.isSuccess()) { + pkg.addProperty(propertyResult.getResult()); + } + return propertyResult; case "uses-static-library": return parseUsesStaticLibrary(input, pkg, res, parser); case "uses-library": @@ -2085,13 +2094,19 @@ public class ParsingPackageUtils { // note: application meta-data is stored off to the side, so it can // remain null in the primary copy (we like to avoid extra copies because // it can be large) - ParseResult<Bundle> metaDataResult = parseMetaData(pkg, res, parser, - pkg.getMetaData(), input); - if (metaDataResult.isSuccess()) { - pkg.setMetaData(metaDataResult.getResult()); + final ParseResult<Property> metaDataResult = parseMetaData(pkg, null, res, + parser, "<meta-data>", input); + if (metaDataResult.isSuccess() && metaDataResult.getResult() != null) { + pkg.setMetaData(metaDataResult.getResult().toBundle(pkg.getMetaData())); } - return metaDataResult; + case "property": + final ParseResult<Property> propertyResult = parseMetaData(pkg, null, res, + parser, "<property>", input); + if (propertyResult.isSuccess()) { + pkg.addProperty(propertyResult.getResult()); + } + return propertyResult; case "static-library": return parseStaticLibrary(pkg, res, parser, input); case "library": @@ -2736,57 +2751,60 @@ public class ParsingPackageUtils { : input.error("must have at least one '.' separator"); } - public static ParseResult<Bundle> parseMetaData(ParsingPackage pkg, Resources res, - XmlResourceParser parser, Bundle data, ParseInput input) { + /** + * Parse a meta data defined on the enclosing tag. + * <p>Meta data can be defined by either <meta-data> or <property> elements. + */ + public static ParseResult<Property> parseMetaData(ParsingPackage pkg, ParsedComponent component, + Resources res, XmlResourceParser parser, String tagName, ParseInput input) { TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestMetaData); try { - if (data == null) { - data = new Bundle(); - } - - String name = TextUtils.safeIntern( + final Property property; + final String name = TextUtils.safeIntern( nonConfigString(0, R.styleable.AndroidManifestMetaData_name, sa)); if (name == null) { - return input.error("<meta-data> requires an android:name attribute"); + return input.error(tagName + " requires an android:name attribute"); } + final String packageName = pkg.getPackageName(); + final String className = component != null ? component.getName() : null; TypedValue v = sa.peekValue(R.styleable.AndroidManifestMetaData_resource); if (v != null && v.resourceId != 0) { - //Slog.i(TAG, "Meta data ref " + name + ": " + v); - data.putInt(name, v.resourceId); + property = new Property(name, v.resourceId, true, packageName, className); } else { v = sa.peekValue(R.styleable.AndroidManifestMetaData_value); - //Slog.i(TAG, "Meta data " + name + ": " + v); if (v != null) { if (v.type == TypedValue.TYPE_STRING) { - CharSequence cs = v.coerceToString(); - data.putString(name, cs != null ? cs.toString() : null); + final CharSequence cs = v.coerceToString(); + final String stringValue = cs != null ? cs.toString() : null; + property = new Property(name, stringValue, packageName, className); } else if (v.type == TypedValue.TYPE_INT_BOOLEAN) { - data.putBoolean(name, v.data != 0); + property = new Property(name, v.data != 0, packageName, className); } else if (v.type >= TypedValue.TYPE_FIRST_INT && v.type <= TypedValue.TYPE_LAST_INT) { - data.putInt(name, v.data); + property = new Property(name, v.data, false, packageName, className); } else if (v.type == TypedValue.TYPE_FLOAT) { - data.putFloat(name, v.getFloat()); + property = new Property(name, v.getFloat(), packageName, className); } else { if (!PackageParser.RIGID_PARSER) { Slog.w(TAG, - "<meta-data> only supports string, integer, float, color, " + tagName + " only supports string, integer, float, color, " + "boolean, and resource reference types: " + parser.getName() + " at " + pkg.getBaseApkPath() + " " + parser.getPositionDescription()); + property = null; } else { - return input.error("<meta-data> only supports string, integer, float, " + return input.error(tagName + " only supports string, integer, float, " + "color, boolean, and resource reference types"); } } } else { - return input.error("<meta-data> requires an android:value " + return input.error(tagName + " requires an android:value " + "or android:resource attribute"); } } - return input.success(data); + return input.success(property); } finally { sa.recycle(); } diff --git a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java index 511ee5d9580d..0d7198d4aa4a 100644 --- a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java +++ b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java @@ -356,6 +356,8 @@ public class ParsedActivityUtils { result = intentResult; } else if (parser.getName().equals("meta-data")) { result = ParsedComponentUtils.addMetaData(activity, pkg, resources, parser, input); + } else if (parser.getName().equals("property")) { + result = ParsedComponentUtils.addProperty(activity, pkg, resources, parser, input); } else if (!isReceiver && !isAlias && parser.getName().equals("preferred")) { ParseResult<ParsedIntentInfo> intentResult = parseIntentFilter(pkg, activity, true /*allowImplicitEphemeralVisibility*/, visibleToEphemeral, diff --git a/core/java/android/content/pm/parsing/component/ParsedComponent.java b/core/java/android/content/pm/parsing/component/ParsedComponent.java index 6323d6921394..4aed77ae641b 100644 --- a/core/java/android/content/pm/parsing/component/ParsedComponent.java +++ b/core/java/android/content/pm/parsing/component/ParsedComponent.java @@ -18,10 +18,13 @@ package android.content.pm.parsing.component; import static android.content.pm.parsing.ParsingPackageImpl.sForInternedString; +import static java.util.Collections.emptyMap; + import android.annotation.CallSuper; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ComponentName; +import android.content.pm.PackageManager.Property; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -35,6 +38,7 @@ import com.android.internal.util.Parcelling.BuiltIn.ForInternedString; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; /** @hide */ public abstract class ParsedComponent implements Parcelable { @@ -70,6 +74,8 @@ public abstract class ParsedComponent implements Parcelable { @Nullable protected Bundle metaData; + private Map<String, Property> mProperties = emptyMap(); + ParsedComponent() { } @@ -96,6 +102,11 @@ public abstract class ParsedComponent implements Parcelable { this.intents = CollectionUtils.add(this.intents, intent); } + /** Add a property to the component */ + public void addProperty(@NonNull Property property) { + this.mProperties = CollectionUtils.add(this.mProperties, property.getName(), property); + } + @NonNull public List<ParsedIntentInfo> getIntents() { return intents != null ? intents : Collections.emptyList(); @@ -142,6 +153,7 @@ public abstract class ParsedComponent implements Parcelable { sForInternedString.parcel(this.packageName, dest, flags); sForIntentInfos.parcel(this.getIntents(), dest, flags); dest.writeBundle(this.metaData); + dest.writeMap(this.mProperties); } protected ParsedComponent(Parcel in) { @@ -160,6 +172,7 @@ public abstract class ParsedComponent implements Parcelable { this.packageName = sForInternedString.unparcel(in); this.intents = sForIntentInfos.unparcel(in); this.metaData = in.readBundle(boot); + this.mProperties = in.createTypedArrayMap(Property.CREATOR); } @NonNull @@ -205,4 +218,9 @@ public abstract class ParsedComponent implements Parcelable { public Bundle getMetaData() { return metaData; } + + @NonNull + public Map<String, Property> getProperties() { + return mProperties; + } } diff --git a/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java b/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java index dd2fb5bd2d78..46b941955fc7 100644 --- a/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java +++ b/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java @@ -19,6 +19,7 @@ package android.content.pm.parsing.component; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.Property; import android.content.pm.parsing.ParsingPackage; import android.content.pm.parsing.ParsingPackageUtils; import android.content.pm.parsing.ParsingUtils; @@ -97,13 +98,29 @@ class ParsedComponentUtils { static ParseResult<Bundle> addMetaData(ParsedComponent component, ParsingPackage pkg, Resources resources, XmlResourceParser parser, ParseInput input) { - ParseResult<Bundle> result = ParsingPackageUtils.parseMetaData(pkg, resources, - parser, component.metaData, input); + ParseResult<Property> result = ParsingPackageUtils.parseMetaData(pkg, component, + resources, parser, "<meta-data>", input); if (result.isError()) { return input.error(result); } - Bundle bundle = result.getResult(); - component.metaData = bundle; - return input.success(bundle); + final Property property = result.getResult(); + if (property != null) { + component.metaData = property.toBundle(component.metaData); + } + return input.success(component.metaData); + } + + static ParseResult<Property> addProperty(ParsedComponent component, ParsingPackage pkg, + Resources resources, XmlResourceParser parser, ParseInput input) { + ParseResult<Property> result = ParsingPackageUtils.parseMetaData(pkg, component, + resources, parser, "<property>", input); + if (result.isError()) { + return input.error(result); + } + final Property property = result.getResult(); + if (property != null) { + component.addProperty(property); + } + return input.success(property); } } diff --git a/core/java/android/content/pm/parsing/component/ParsedProviderUtils.java b/core/java/android/content/pm/parsing/component/ParsedProviderUtils.java index 37cbeca1d23a..90691f1686aa 100644 --- a/core/java/android/content/pm/parsing/component/ParsedProviderUtils.java +++ b/core/java/android/content/pm/parsing/component/ParsedProviderUtils.java @@ -180,6 +180,9 @@ public class ParsedProviderUtils { case "meta-data": result = ParsedComponentUtils.addMetaData(provider, pkg, res, parser, input); break; + case "property": + result = ParsedComponentUtils.addProperty(provider, pkg, res, parser, input); + break; case "grant-uri-permission": { result = parseGrantUriPermission(provider, pkg, res, parser, input); break; diff --git a/core/java/android/content/pm/parsing/component/ParsedServiceUtils.java b/core/java/android/content/pm/parsing/component/ParsedServiceUtils.java index afe3c5494fcb..739bee2fb7d9 100644 --- a/core/java/android/content/pm/parsing/component/ParsedServiceUtils.java +++ b/core/java/android/content/pm/parsing/component/ParsedServiceUtils.java @@ -145,6 +145,10 @@ public class ParsedServiceUtils { case "meta-data": parseResult = ParsedComponentUtils.addMetaData(service, pkg, res, parser, input); break; + case "property": + parseResult = + ParsedComponentUtils.addProperty(service, pkg, res, parser, input); + break; default: parseResult = ParsingUtils.unknownTag(tag, pkg, parser, input); break; diff --git a/core/java/android/hardware/CameraSessionStats.java b/core/java/android/hardware/CameraSessionStats.java new file mode 100644 index 000000000000..f34e2bf5ddc2 --- /dev/null +++ b/core/java/android/hardware/CameraSessionStats.java @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.hardware; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.ArrayList; +import java.util.List; +/** + * The camera action state used for passing camera usage information from + * camera service to camera service proxy . + * + * Include camera id, facing, state, client apk name, apiLevel, isNdk, + * and session/stream statistics. + * + * @hide + */ +public class CameraSessionStats implements Parcelable { + public static final int CAMERA_STATE_OPEN = 0; + public static final int CAMERA_STATE_ACTIVE = 1; + public static final int CAMERA_STATE_IDLE = 2; + public static final int CAMERA_STATE_CLOSED = 3; + + /** + * Values for notifyCameraState facing + */ + public static final int CAMERA_FACING_BACK = 0; + public static final int CAMERA_FACING_FRONT = 1; + public static final int CAMERA_FACING_EXTERNAL = 2; + + /** + * Values for notifyCameraState api level + */ + public static final int CAMERA_API_LEVEL_1 = 1; + public static final int CAMERA_API_LEVEL_2 = 2; + + private String mCameraId; + private int mFacing; + private int mNewCameraState; + private String mClientName; + private int mApiLevel; + private boolean mIsNdk; + private int mLatencyMs; + private int mSessionType; + private int mInternalReconfigure; + private long mRequestCount; + private long mResultErrorCount; + private boolean mDeviceError; + private ArrayList<CameraStreamStats> mStreamStats; + + public CameraSessionStats() { + mFacing = -1; + mNewCameraState = -1; + mApiLevel = -1; + mIsNdk = false; + mLatencyMs = -1; + mSessionType = -1; + mInternalReconfigure = -1; + mRequestCount = 0; + mResultErrorCount = 0; + mDeviceError = false; + mStreamStats = new ArrayList<CameraStreamStats>(); + } + + public CameraSessionStats(String cameraId, int facing, int newCameraState, + String clientName, int apiLevel, boolean isNdk, int creationDuration, + int sessionType, int internalReconfigure) { + mCameraId = cameraId; + mFacing = facing; + mNewCameraState = newCameraState; + mClientName = clientName; + mApiLevel = apiLevel; + mIsNdk = isNdk; + mLatencyMs = creationDuration; + mSessionType = sessionType; + mInternalReconfigure = internalReconfigure; + mStreamStats = new ArrayList<CameraStreamStats>(); + } + + public static final @android.annotation.NonNull Parcelable.Creator<CameraSessionStats> CREATOR = + new Parcelable.Creator<CameraSessionStats>() { + @Override + public CameraSessionStats createFromParcel(Parcel in) { + return new CameraSessionStats(in); + } + + @Override + public CameraSessionStats[] newArray(int size) { + return new CameraSessionStats[size]; + } + }; + + private CameraSessionStats(Parcel in) { + readFromParcel(in); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mCameraId); + dest.writeInt(mFacing); + dest.writeInt(mNewCameraState); + dest.writeString(mClientName); + dest.writeInt(mApiLevel); + dest.writeBoolean(mIsNdk); + dest.writeInt(mLatencyMs); + dest.writeInt(mSessionType); + dest.writeInt(mInternalReconfigure); + dest.writeLong(mRequestCount); + dest.writeLong(mResultErrorCount); + dest.writeBoolean(mDeviceError); + dest.writeTypedList(mStreamStats); + } + + public void readFromParcel(Parcel in) { + mCameraId = in.readString(); + mFacing = in.readInt(); + mNewCameraState = in.readInt(); + mClientName = in.readString(); + mApiLevel = in.readInt(); + mIsNdk = in.readBoolean(); + mLatencyMs = in.readInt(); + mSessionType = in.readInt(); + mInternalReconfigure = in.readInt(); + mRequestCount = in.readLong(); + mResultErrorCount = in.readLong(); + mDeviceError = in.readBoolean(); + + ArrayList<CameraStreamStats> streamStats = new ArrayList<CameraStreamStats>(); + in.readTypedList(streamStats, CameraStreamStats.CREATOR); + mStreamStats = streamStats; + } + + public String getCameraId() { + return mCameraId; + } + + public int getFacing() { + return mFacing; + } + + public int getNewCameraState() { + return mNewCameraState; + } + + public String getClientName() { + return mClientName; + } + + public int getApiLevel() { + return mApiLevel; + } + + public boolean isNdk() { + return mIsNdk; + } + + public int getLatencyMs() { + return mLatencyMs; + } + + public int getSessionType() { + return mSessionType; + } + + public int getInternalReconfigureCount() { + return mInternalReconfigure; + } + + public long getRequestCount() { + return mRequestCount; + } + + public long getResultErrorCount() { + return mResultErrorCount; + } + + public boolean getDeviceErrorFlag() { + return mDeviceError; + } + + public List<CameraStreamStats> getStreamStats() { + return mStreamStats; + } +} diff --git a/core/java/android/hardware/CameraStreamStats.java b/core/java/android/hardware/CameraStreamStats.java new file mode 100644 index 000000000000..ae801b639d51 --- /dev/null +++ b/core/java/android/hardware/CameraStreamStats.java @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.hardware; + +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +import java.util.ArrayList; +/** + * The camera stream statistics used for passing camera stream information from + * camera service to camera service proxy. + * + * Include camera stream configuration, request/error counts, startup latency, + * and latency/jitter histograms. + * + * @hide + */ +public class CameraStreamStats implements Parcelable { + + private int mWidth; + private int mHeight; + private int mFormat; + private int mDataSpace; + private long mUsage; + private long mRequestCount; + private long mErrorCount; + private int mStartLatencyMs; + private int mMaxHalBuffers; + private int mMaxAppBuffers; + + private static final String TAG = "CameraStreamStats"; + + public CameraStreamStats() { + mWidth = 0; + mHeight = 0; + mFormat = 0; + mDataSpace = 0; + mUsage = 0; + mRequestCount = 0; + mErrorCount = 0; + mStartLatencyMs = 0; + mMaxHalBuffers = 0; + mMaxAppBuffers = 0; + } + + public CameraStreamStats(int width, int height, int format, + int dataSpace, long usage, long requestCount, long errorCount, + int startLatencyMs, int maxHalBuffers, int maxAppBuffers) { + mWidth = width; + mHeight = height; + mFormat = format; + mDataSpace = dataSpace; + mUsage = usage; + mRequestCount = requestCount; + mErrorCount = errorCount; + mStartLatencyMs = startLatencyMs; + mMaxHalBuffers = maxHalBuffers; + mMaxAppBuffers = maxAppBuffers; + } + + public static final @android.annotation.NonNull Parcelable.Creator<CameraStreamStats> CREATOR = + new Parcelable.Creator<CameraStreamStats>() { + @Override + public CameraStreamStats createFromParcel(Parcel in) { + try { + CameraStreamStats streamStats = new CameraStreamStats(in); + return streamStats; + } catch (Exception e) { + Log.e(TAG, "Exception creating CameraStreamStats from parcel", e); + return null; + } + } + + @Override + public CameraStreamStats[] newArray(int size) { + return new CameraStreamStats[size]; + } + }; + + private CameraStreamStats(Parcel in) { + readFromParcel(in); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mWidth); + dest.writeInt(mHeight); + dest.writeInt(mFormat); + dest.writeInt(mDataSpace); + dest.writeLong(mUsage); + dest.writeLong(mRequestCount); + dest.writeLong(mErrorCount); + dest.writeInt(mStartLatencyMs); + dest.writeInt(mMaxHalBuffers); + dest.writeInt(mMaxAppBuffers); + } + + public void readFromParcel(Parcel in) { + mWidth = in.readInt(); + mHeight = in.readInt(); + mFormat = in.readInt(); + mDataSpace = in.readInt(); + mUsage = in.readLong(); + mRequestCount = in.readLong(); + mErrorCount = in.readLong(); + mStartLatencyMs = in.readInt(); + mMaxHalBuffers = in.readInt(); + mMaxAppBuffers = in.readInt(); + } + + public int getWidth() { + return mWidth; + } + + public int getHeight() { + return mHeight; + } + + public int getFormat() { + return mFormat; + } + + public int getDataSpace() { + return mDataSpace; + } + + public long getUsage() { + return mUsage; + } + + public long getRequestCount() { + return mRequestCount; + } + + public long getErrorCount() { + return mErrorCount; + } + + public int getStartLatencyMs() { + return mStartLatencyMs; + } + + public int getMaxHalBuffers() { + return mMaxHalBuffers; + } + + public int getMaxAppBuffers() { + return mMaxAppBuffers; + } +} diff --git a/core/java/android/hardware/biometrics/SensorPropertiesInternal.java b/core/java/android/hardware/biometrics/SensorPropertiesInternal.java index 2189de0827b7..0b81c6c8cc25 100644 --- a/core/java/android/hardware/biometrics/SensorPropertiesInternal.java +++ b/core/java/android/hardware/biometrics/SensorPropertiesInternal.java @@ -16,13 +16,10 @@ package android.hardware.biometrics; -import android.annotation.IntDef; +import android.annotation.NonNull; import android.os.Parcel; import android.os.Parcelable; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - /** * The base class containing all modality-agnostic information. This is a superset of the * {@link android.hardware.biometrics.common.CommonProps}, and provides backwards-compatible @@ -35,6 +32,11 @@ public class SensorPropertiesInternal implements Parcelable { @SensorProperties.Strength public final int sensorStrength; public final int maxEnrollmentsPerUser; + public static SensorPropertiesInternal from(@NonNull SensorPropertiesInternal prop) { + return new SensorPropertiesInternal(prop.sensorId, prop.sensorStrength, + prop.maxEnrollmentsPerUser); + } + protected SensorPropertiesInternal(int sensorId, @SensorProperties.Strength int sensorStrength, int maxEnrollmentsPerUser) { this.sensorId = sensorId; @@ -72,4 +74,10 @@ public class SensorPropertiesInternal implements Parcelable { dest.writeInt(sensorStrength); dest.writeInt(maxEnrollmentsPerUser); } + + @Override + public String toString() { + return "ID: " + sensorId + ", Strength: " + sensorStrength + + ", MaxEnrollmentsPerUser: " + maxEnrollmentsPerUser; + } } diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java index b6f4bd3c4c28..9a9163c724ff 100644 --- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java @@ -27,6 +27,7 @@ import android.hardware.camera2.utils.TaskDrainer; import android.hardware.camera2.utils.TaskSingleDrainer; import android.os.Binder; import android.os.Handler; +import android.os.SystemClock; import android.util.Log; import android.view.Surface; @@ -1002,7 +1003,7 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession // begin transition to unconfigured mDeviceImpl.configureStreamsChecked(/*inputConfig*/null, /*outputs*/null, /*operatingMode*/ ICameraDeviceUser.NORMAL_MODE, - /*sessionParams*/ null); + /*sessionParams*/ null, SystemClock.uptimeMillis()); } catch (CameraAccessException e) { // OK: do not throw checked exceptions. Log.e(TAG, mIdString + "Exception while unconfiguring outputs: ", e); diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java index 819d966e3bfe..f564ad7436de 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java @@ -45,6 +45,7 @@ import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; import android.os.ServiceSpecificException; +import android.os.SystemClock; import android.util.Log; import android.util.Range; import android.util.Size; @@ -374,7 +375,8 @@ public class CameraDeviceImpl extends CameraDevice outputConfigs.add(new OutputConfiguration(s)); } configureStreamsChecked(/*inputConfig*/null, outputConfigs, - /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/ null); + /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/ null, + SystemClock.uptimeMillis()); } @@ -395,12 +397,15 @@ public class CameraDeviceImpl extends CameraDevice * @param operatingMode If the stream configuration is for a normal session, * a constrained high speed session, or something else. * @param sessionParams Session parameters. + * @param createSessionStartTimeMs The timestamp when session creation starts, measured by + * uptimeMillis(). * @return whether or not the configuration was successful * * @throws CameraAccessException if there were any unexpected problems during configuration */ public boolean configureStreamsChecked(InputConfiguration inputConfig, - List<OutputConfiguration> outputs, int operatingMode, CaptureRequest sessionParams) + List<OutputConfiguration> outputs, int operatingMode, CaptureRequest sessionParams, + long createSessionStartTime) throws CameraAccessException { // Treat a null input the same an empty list if (outputs == null) { @@ -479,9 +484,10 @@ public class CameraDeviceImpl extends CameraDevice int offlineStreamIds[]; if (sessionParams != null) { offlineStreamIds = mRemoteDevice.endConfigure(operatingMode, - sessionParams.getNativeCopy()); + sessionParams.getNativeCopy(), createSessionStartTime); } else { - offlineStreamIds = mRemoteDevice.endConfigure(operatingMode, null); + offlineStreamIds = mRemoteDevice.endConfigure(operatingMode, null, + createSessionStartTime); } mOfflineSupport.clear(); @@ -650,6 +656,7 @@ public class CameraDeviceImpl extends CameraDevice List<OutputConfiguration> outputConfigurations, CameraCaptureSession.StateCallback callback, Executor executor, int operatingMode, CaptureRequest sessionParams) throws CameraAccessException { + long createSessionStartTime = SystemClock.uptimeMillis(); synchronized(mInterfaceLock) { if (DEBUG) { Log.d(TAG, "createCaptureSessionInternal"); @@ -677,7 +684,7 @@ public class CameraDeviceImpl extends CameraDevice try { // configure streams and then block until IDLE configureSuccess = configureStreamsChecked(inputConfig, outputConfigurations, - operatingMode, sessionParams); + operatingMode, sessionParams, createSessionStartTime); if (configureSuccess == true && inputConfig != null) { input = mRemoteDevice.getInputSurface(); } diff --git a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java index fa7301bb72c3..ba4395f70214 100644 --- a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java +++ b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java @@ -110,11 +110,11 @@ public class ICameraDeviceUserWrapper { } } - public int[] endConfigure(int operatingMode, CameraMetadataNative sessionParams) - throws CameraAccessException { + public int[] endConfigure(int operatingMode, CameraMetadataNative sessionParams, + long startTimeMs) throws CameraAccessException { try { return mRemoteDevice.endConfigure(operatingMode, (sessionParams == null) ? - new CameraMetadataNative() : sessionParams); + new CameraMetadataNative() : sessionParams, startTimeMs); } catch (Throwable t) { CameraManager.throwAsPublicException(t); throw new UnsupportedOperationException("Unexpected exception", t); diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index b046d1df5b8c..7b4889f0a1b3 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -358,7 +358,7 @@ public final class DisplayManagerGlobal { // listener. DisplayInfo display = getDisplayInfoLocked(displayId); if (display != null) { - float refreshRate = display.getMode().getRefreshRate(); + float refreshRate = display.getRefreshRate(); // Signal native callbacks if we ever set a refresh rate. nSignalNativeCallbacks(refreshRate); } @@ -862,7 +862,7 @@ public final class DisplayManagerGlobal { if (display != null) { // We need to tell AChoreographer instances the current refresh rate so that apps // can get it for free once a callback first registers. - float refreshRate = display.getMode().getRefreshRate(); + float refreshRate = display.getRefreshRate(); nSignalNativeCallbacks(refreshRate); } } diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index defcab7c3035..5a03adee4eab 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -65,6 +65,12 @@ public abstract class DisplayManagerInternal { public abstract boolean isProximitySensorAvailable(); /** + * Returns the id of the {@link com.android.server.display.DisplayGroup} to which the provided + * display belongs. + */ + public abstract int getDisplayGroupId(int displayId); + + /** * Screenshot for internal system-only use such as rotation, etc. This method includes * secure layers and the result should never be exposed to non-system applications. * This method does not apply any rotation and provides the output in natural orientation. diff --git a/core/java/android/hardware/face/FaceSensorPropertiesInternal.java b/core/java/android/hardware/face/FaceSensorPropertiesInternal.java index e91554b532b0..b9c0d12de22b 100644 --- a/core/java/android/hardware/face/FaceSensorPropertiesInternal.java +++ b/core/java/android/hardware/face/FaceSensorPropertiesInternal.java @@ -19,7 +19,6 @@ package android.hardware.face; import android.hardware.biometrics.SensorProperties; import android.hardware.biometrics.SensorPropertiesInternal; import android.os.Parcel; -import android.os.Parcelable; /** * Container for face sensor properties. @@ -78,4 +77,9 @@ public class FaceSensorPropertiesInternal extends SensorPropertiesInternal { dest.writeBoolean(supportsFaceDetection); dest.writeBoolean(supportsSelfIllumination); } + + @Override + public String toString() { + return "ID: " + sensorId + ", Strength: " + sensorStrength; + } } diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java index f1534d95a16c..eef4089ac336 100644 --- a/core/java/android/hardware/hdmi/HdmiControlManager.java +++ b/core/java/android/hardware/hdmi/HdmiControlManager.java @@ -832,6 +832,22 @@ public final class HdmiControlManager { } /** + * For CEC source devices (OTT/STB/Audio system): toggle the power status of the HDMI-connected + * display and follow the display's new power status. + * For all other devices: no functionality. + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.HDMI_CEC) + public void toggleAndFollowTvPower() { + try { + mService.toggleAndFollowTvPower(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Controls whether volume control commands via HDMI CEC are enabled. * * <p>When disabled: diff --git a/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java b/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java index fab56b8cea49..202e0907f803 100644 --- a/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java +++ b/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java @@ -67,6 +67,11 @@ public final class HdmiControlServiceWrapper { } @Override + public void toggleAndFollowTvPower() { + HdmiControlServiceWrapper.this.toggleAndFollowTvPower(); + } + + @Override public void queryDisplayStatus(IHdmiControlCallback callback) { HdmiControlServiceWrapper.this.queryDisplayStatus(callback); } @@ -360,6 +365,9 @@ public final class HdmiControlServiceWrapper { public void oneTouchPlay(IHdmiControlCallback callback) {} /** @hide */ + public void toggleAndFollowTvPower() {} + + /** @hide */ public void queryDisplayStatus(IHdmiControlCallback callback) {} /** @hide */ diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl index af9d3accd00e..6d0c688f701e 100644 --- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl +++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl @@ -42,6 +42,7 @@ interface IHdmiControlService { int[] getSupportedTypes(); HdmiDeviceInfo getActiveSource(); void oneTouchPlay(IHdmiControlCallback callback); + void toggleAndFollowTvPower(); void queryDisplayStatus(IHdmiControlCallback callback); void addHdmiControlStatusChangeListener(IHdmiControlStatusChangeListener listener); void removeHdmiControlStatusChangeListener(IHdmiControlStatusChangeListener listener); diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java index 7fb73ceab4f4..81ea2f5a17dd 100644 --- a/core/java/android/hardware/input/InputManager.java +++ b/core/java/android/hardware/input/InputManager.java @@ -56,6 +56,7 @@ import android.view.PointerIcon; import android.view.VerifiedInputEvent; import android.view.WindowManager.LayoutParams; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.SomeArgs; import com.android.internal.util.ArrayUtils; @@ -275,6 +276,21 @@ public final class InputManager { * * @hide */ + @VisibleForTesting + public static InputManager resetInstance(IInputManager inputManagerService) { + synchronized (InputManager.class) { + sInstance = new InputManager(inputManagerService); + return sInstance; + } + } + + /** + * Gets an instance of the input manager. + * + * @return The input manager instance. + * + * @hide + */ @UnsupportedAppUsage public static InputManager getInstance() { synchronized (InputManager.class) { @@ -1428,7 +1444,7 @@ public final class InputManager { @Override public boolean hasAmplitudeControl() { - return false; + return true; } /** diff --git a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java index e9de27456f97..0766917642e8 100644 --- a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java +++ b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java @@ -54,6 +54,8 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub private static final int DO_VIEW_CLICKED = 115; private static final int DO_NOTIFY_IME_HIDDEN = 120; private static final int DO_REMOVE_IME_SURFACE = 130; + private static final int DO_FINISH_INPUT = 140; + @UnsupportedAppUsage HandlerCaller mCaller; @@ -141,6 +143,10 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub mInputMethodSession.removeImeSurface(); return; } + case DO_FINISH_INPUT: { + mInputMethodSession.finishInput(); + return; + } } Log.w(TAG, "Unhandled message code: " + msg.what); } @@ -222,6 +228,10 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_FINISH_SESSION)); } + @Override + public void finishInput() { + mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_FINISH_INPUT)); + } private final class ImeInputEventReceiver extends InputEventReceiver implements InputMethodSession.EventCallback { private final SparseArray<InputEvent> mPendingEvents = new SparseArray<InputEvent>(); diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index ae260e16806f..4a5d831cd705 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -61,9 +61,12 @@ import android.annotation.IntDef; import android.annotation.MainThread; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.TestApi; import android.annotation.UiContext; import android.app.ActivityManager; import android.app.Dialog; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledSince; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.pm.PackageManager; @@ -411,7 +414,29 @@ public class InputMethodService extends AbstractInputMethodService { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) int mTheme = 0; - + + /** + * Finish the {@link InputConnection} when the device becomes + * {@link android.os.PowerManager#isInteractive non-interactive}. + * + * <p> + * If enabled by the current {@link InputMethodService input method}, the current input + * connection will be {@link InputMethodService#onFinishInput finished} whenever the devices + * becomes non-interactive. + * + * <p> + * If not enabled, the current input connection will instead be silently deactivated when the + * devices becomes non-interactive, and an {@link InputMethodService#onFinishInput + * onFinishInput()} {@link InputMethodService#onStartInput onStartInput()} pair is dispatched + * when the device becomes interactive again. + * + * @hide + */ + @TestApi + @ChangeId + @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S) + public static final long FINISH_INPUT_NO_FALLBACK_CONNECTION = 156215187L; // This is a bug id. + LayoutInflater mInflater; TypedArray mThemeAttrs; @UnsupportedAppUsage @@ -2325,7 +2350,7 @@ public class InputMethodService extends AbstractInputMethodService { } void doStartInput(InputConnection ic, EditorInfo attribute, boolean restarting) { - if (!restarting) { + if (!restarting && mInputStarted) { doFinishInput(); } ImeTracing.getInstance().triggerServiceDump("InputMethodService#doStartInput", this); diff --git a/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java b/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java index dbb669be1402..2db9ed1103fa 100644 --- a/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java +++ b/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java @@ -23,6 +23,7 @@ import android.os.Bundle; import android.os.Debug; import android.os.Handler; import android.os.Looper; +import android.os.RemoteException; import android.os.ResultReceiver; import android.util.Log; import android.view.InputChannel; @@ -38,8 +39,8 @@ import android.view.inputmethod.EditorInfo; import android.view.inputmethod.ExtractedText; import com.android.internal.annotations.GuardedBy; -import com.android.internal.inputmethod.IMultiClientInputMethodSession; import com.android.internal.inputmethod.CancellationGroup; +import com.android.internal.inputmethod.IMultiClientInputMethodSession; import com.android.internal.os.SomeArgs; import com.android.internal.util.function.pooled.PooledLambda; import com.android.internal.view.IInputContext; @@ -303,6 +304,12 @@ final class MultiClientInputMethodClientCallbackAdaptor { // no-op for multi-session reportNotSupported(); } + + @Override + public void finishInput() throws RemoteException { + // no-op for multi-session + reportNotSupported(); + } } private static final class MultiClientInputMethodSessionImpl diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java index 923b9b76a1a5..81e6e788734b 100644 --- a/core/java/android/net/LinkProperties.java +++ b/core/java/android/net/LinkProperties.java @@ -81,8 +81,10 @@ public final class LinkProperties implements Parcelable { private final transient boolean mParcelSensitiveFields; private static final int MIN_MTU = 68; - /* package-visibility - Used in other files (such as Ikev2VpnProfile) as minimum iface MTU. */ - static final int MIN_MTU_V6 = 1280; + + /** @hide */ + public static final int MIN_MTU_V6 = 1280; + private static final int MAX_MTU = 10000; private static final int INET6_ADDR_LENGTH = 16; diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java index 44ebff99f3e9..0676ad4e2322 100644 --- a/core/java/android/net/NetworkAgent.java +++ b/core/java/android/net/NetworkAgent.java @@ -40,6 +40,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.time.Duration; import java.util.ArrayList; +import java.util.List; import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; @@ -174,6 +175,14 @@ public abstract class NetworkAgent { public static final int EVENT_NETWORK_SCORE_CHANGED = BASE + 4; /** + * Sent by the NetworkAgent to ConnectivityService to pass the current + * list of underlying networks. + * obj = array of Network objects + * @hide + */ + public static final int EVENT_UNDERLYING_NETWORKS_CHANGED = BASE + 5; + + /** * Sent by ConnectivityService to the NetworkAgent to inform the agent of the * networks status - whether we could use the network or could not, due to * either a bad network configuration (no internet link) or captive portal. @@ -217,7 +226,13 @@ public abstract class NetworkAgent { * The key for the redirect URL in the Bundle argument of {@code CMD_REPORT_NETWORK_STATUS}. * @hide */ - public static String REDIRECT_URL_KEY = "redirect URL"; + public static final String REDIRECT_URL_KEY = "redirect URL"; + + /** + * Bundle key for the underlying networks in {@code EVENT_UNDERLYING_NETWORKS_CHANGED}. + * @hide + */ + public static final String UNDERLYING_NETWORKS_KEY = "underlyingNetworks"; /** * Sent by the NetworkAgent to ConnectivityService to indicate this network was @@ -650,6 +665,33 @@ public abstract class NetworkAgent { } /** + * Must be called by the agent when the network's underlying networks change. + * + * <p>{@code networks} is one of the following: + * <ul> + * <li><strong>a non-empty array</strong>: an array of one or more {@link Network}s, in + * decreasing preference order. For example, if this VPN uses both wifi and mobile (cellular) + * networks to carry app traffic, but prefers or uses wifi more than mobile, wifi should appear + * first in the array.</li> + * <li><strong>an empty array</strong>: a zero-element array, meaning that the VPN has no + * underlying network connection, and thus, app traffic will not be sent or received.</li> + * <li><strong>null</strong>: (default) signifies that the VPN uses whatever is the system's + * default network. I.e., it doesn't use the {@code bindSocket} or {@code bindDatagramSocket} + * APIs mentioned above to send traffic over specific channels.</li> + * </ul> + * + * @param underlyingNetworks the new list of underlying networks. + * @see {@link VpnService.Builder#setUnderlyingNetworks(Network[])} + */ + public final void setUnderlyingNetworks(@Nullable List<Network> underlyingNetworks) { + final ArrayList<Network> underlyingArray = (underlyingNetworks != null) + ? new ArrayList<>(underlyingNetworks) : null; + final Bundle bundle = new Bundle(); + bundle.putParcelableArrayList(UNDERLYING_NETWORKS_KEY, underlyingArray); + queueOrSendMessage(EVENT_UNDERLYING_NETWORKS_CHANGED, bundle); + } + + /** * Inform ConnectivityService that this agent has now connected. * Call {@link #unregister} to disconnect. */ diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java index f806b565b127..8dad11ffa731 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/core/java/android/net/NetworkCapabilities.java @@ -339,10 +339,14 @@ public final class NetworkCapabilities implements Parcelable { public static final int NET_CAPABILITY_PARTIAL_CONNECTIVITY = 24; /** + * Indicates that this network is temporarily unmetered. + * <p> * This capability will be set for networks that are generally metered, but are currently * unmetered, e.g., because the user is in a particular area. This capability can be changed at * any time. When it is removed, applications are responsible for stopping any data transfer * that should not occur on a metered network. + * Note that most apps should use {@link #NET_CAPABILITY_NOT_METERED} instead. For more + * information, see https://developer.android.com/about/versions/11/features/5g#meteredness. */ public static final int NET_CAPABILITY_TEMPORARILY_NOT_METERED = 25; @@ -370,8 +374,8 @@ public final class NetworkCapabilities implements Parcelable { | (1 << NET_CAPABILITY_FOREGROUND) | (1 << NET_CAPABILITY_NOT_CONGESTED) | (1 << NET_CAPABILITY_NOT_SUSPENDED) - | (1 << NET_CAPABILITY_PARTIAL_CONNECTIVITY - | (1 << NET_CAPABILITY_TEMPORARILY_NOT_METERED)); + | (1 << NET_CAPABILITY_PARTIAL_CONNECTIVITY) + | (1 << NET_CAPABILITY_TEMPORARILY_NOT_METERED); /** * Network capabilities that are not allowed in NetworkRequests. This exists because the @@ -708,6 +712,7 @@ public final class NetworkCapabilities implements Parcelable { if (ArrayUtils.contains(originalAdministratorUids, creatorUid)) { setAdministratorUids(new int[] {creatorUid}); } + // There is no need to clear the UIDs, they have already been cleared by clearAll() above. } /** @@ -801,7 +806,9 @@ public final class NetworkCapabilities implements Parcelable { */ private static final int TEST_NETWORKS_ALLOWED_TRANSPORTS = 1 << TRANSPORT_TEST // Test ethernet networks can be created with EthernetManager#setIncludeTestInterfaces - | 1 << TRANSPORT_ETHERNET; + | 1 << TRANSPORT_ETHERNET + // Test VPN networks can be created but their UID ranges must be empty. + | 1 << TRANSPORT_VPN; /** * Adds the given transport type to this {@code NetworkCapability} instance. @@ -1802,21 +1809,27 @@ public final class NetworkCapabilities implements Parcelable { sb.append(" OwnerUid: ").append(mOwnerUid); } - if (mAdministratorUids.length == 0) { - sb.append(" AdministratorUids: ").append(Arrays.toString(mAdministratorUids)); + if (!ArrayUtils.isEmpty(mAdministratorUids)) { + sb.append(" AdminUids: ").append(Arrays.toString(mAdministratorUids)); + } + + if (mRequestorUid != Process.INVALID_UID) { + sb.append(" RequestorUid: ").append(mRequestorUid); + } + + if (mRequestorPackageName != null) { + sb.append(" RequestorPkg: ").append(mRequestorPackageName); } if (null != mSSID) { sb.append(" SSID: ").append(mSSID); } + if (mPrivateDnsBroken) { - sb.append(" Private DNS is broken"); + sb.append(" PrivateDnsBroken"); } - sb.append(" RequestorUid: ").append(mRequestorUid); - sb.append(" RequestorPackageName: ").append(mRequestorPackageName); - sb.append("]"); return sb.toString(); } diff --git a/core/java/android/net/NetworkProvider.java b/core/java/android/net/NetworkProvider.java index d31218d9b67b..a17a49897d39 100644 --- a/core/java/android/net/NetworkProvider.java +++ b/core/java/android/net/NetworkProvider.java @@ -51,13 +51,6 @@ public class NetworkProvider { public static final int ID_NONE = -1; /** - * A hardcoded ID for NetworkAgents representing VPNs. These agents are not created by any - * provider, so they use this constant for clarity instead of NONE. - * @hide only used by ConnectivityService. - */ - public static final int ID_VPN = -2; - - /** * The first providerId value that will be allocated. * @hide only used by ConnectivityService. */ diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java index 4e019cf0732e..fa650617f380 100644 --- a/core/java/android/net/TrafficStats.java +++ b/core/java/android/net/TrafficStats.java @@ -597,10 +597,17 @@ public class TrafficStats { } /** - * Return the number of packets transmitted on the specified interface since - * device boot. Statistics are measured at the network layer, so both TCP and + * Return the number of packets transmitted on the specified interface since the interface + * was created. Statistics are measured at the network layer, so both TCP and * UDP usage are included. * + * Note that the returned values are partial statistics that do not count data from several + * sources and do not apply several adjustments that are necessary for correctness, such + * as adjusting for VPN apps, IPv6-in-IPv4 translation, etc. These values can be used to + * determine whether traffic is being transferred on the specific interface but are not a + * substitute for the more accurate statistics provided by the {@link NetworkStatsManager} + * APIs. + * * @param iface The name of the interface. * @return The number of transmitted packets. */ @@ -613,10 +620,17 @@ public class TrafficStats { } /** - * Return the number of packets received on the specified interface since - * device boot. Statistics are measured at the network layer, so both TCP + * Return the number of packets received on the specified interface since the interface was + * created. Statistics are measured at the network layer, so both TCP * and UDP usage are included. * + * Note that the returned values are partial statistics that do not count data from several + * sources and do not apply several adjustments that are necessary for correctness, such + * as adjusting for VPN apps, IPv6-in-IPv4 translation, etc. These values can be used to + * determine whether traffic is being transferred on the specific interface but are not a + * substitute for the more accurate statistics provided by the {@link NetworkStatsManager} + * APIs. + * * @param iface The name of the interface. * @return The number of received packets. */ @@ -628,9 +642,22 @@ public class TrafficStats { } } - /** {@hide} */ - @UnsupportedAppUsage - public static long getTxBytes(String iface) { + /** + * Return the number of bytes transmitted on the specified interface since the interface + * was created. Statistics are measured at the network layer, so both TCP and + * UDP usage are included. + * + * Note that the returned values are partial statistics that do not count data from several + * sources and do not apply several adjustments that are necessary for correctness, such + * as adjusting for VPN apps, IPv6-in-IPv4 translation, etc. These values can be used to + * determine whether traffic is being transferred on the specific interface but are not a + * substitute for the more accurate statistics provided by the {@link NetworkStatsManager} + * APIs. + * + * @param iface The name of the interface. + * @return The number of transmitted bytes. + */ + public static long getTxBytes(@NonNull String iface) { try { return getStatsService().getIfaceStats(iface, TYPE_TX_BYTES); } catch (RemoteException e) { @@ -638,9 +665,22 @@ public class TrafficStats { } } - /** {@hide} */ - @UnsupportedAppUsage - public static long getRxBytes(String iface) { + /** + * Return the number of bytes received on the specified interface since the interface + * was created. Statistics are measured at the network layer, so both TCP + * and UDP usage are included. + * + * Note that the returned values are partial statistics that do not count data from several + * sources and do not apply several adjustments that are necessary for correctness, such + * as adjusting for VPN apps, IPv6-in-IPv4 translation, etc. These values can be used to + * determine whether traffic is being transferred on the specific interface but are not a + * substitute for the more accurate statistics provided by the {@link NetworkStatsManager} + * APIs. + * + * @param iface The name of the interface. + * @return The number of received bytes. + */ + public static long getRxBytes(@NonNull String iface) { try { return getStatsService().getIfaceStats(iface, TYPE_RX_BYTES); } catch (RemoteException e) { diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java new file mode 100644 index 000000000000..8160edc87440 --- /dev/null +++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.net.vcn; + +import android.annotation.NonNull; + +/** + * This class represents a configuration for a connection to a Virtual Carrier Network gateway. + * + * <p>Each VcnGatewayConnectionConfig represents a single logical connection to a carrier gateway, + * and may provide one or more telephony services (as represented by network capabilities). Each + * gateway is expected to provide mobility for a given session as the device roams across {@link + * Network}s. + * + * <p>A VCN connection based on this configuration will be brought up dynamically based on device + * settings, and filed NetworkRequests. Underlying networks will be selected based on the services + * required by this configuration (as represented by network capabilities), and must be part of the + * subscription group under which this configuration is registered (see {@link + * VcnManager#setVcnConfig}). + * + * <p>Services that can be provided by a VCN network, or required for underlying networks are + * limited to services provided by cellular networks: + * + * <ul> + * <li>{@link android.net.NetworkCapabilities.NET_CAPABILITY_MMS} + * <li>{@link android.net.NetworkCapabilities.NET_CAPABILITY_SUPL} + * <li>{@link android.net.NetworkCapabilities.NET_CAPABILITY_DUN} + * <li>{@link android.net.NetworkCapabilities.NET_CAPABILITY_FOTA} + * <li>{@link android.net.NetworkCapabilities.NET_CAPABILITY_IMS} + * <li>{@link android.net.NetworkCapabilities.NET_CAPABILITY_CBS} + * <li>{@link android.net.NetworkCapabilities.NET_CAPABILITY_IA} + * <li>{@link android.net.NetworkCapabilities.NET_CAPABILITY_RCS} + * <li>{@link android.net.NetworkCapabilities.NET_CAPABILITY_XCAP} + * <li>{@link android.net.NetworkCapabilities.NET_CAPABILITY_EIMS} + * <li>{@link android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET} + * <li>{@link android.net.NetworkCapabilities.NET_CAPABILITY_MCX} + * </ul> + * + * @hide + */ +public final class VcnGatewayConnectionConfig { + private VcnGatewayConnectionConfig() { + validate(); + } + + // TODO: Implement getters, validators, etc + + /** + * Validates this configuration + * + * @hide + */ + private void validate() { + // TODO: implement validation logic + } + + // Parcelable methods + + /** This class is used to incrementally build {@link VcnGatewayConnectionConfig} objects */ + public static class Builder { + // TODO: Implement this builder + + /** + * Builds and validates the VcnGatewayConnectionConfig + * + * @return an immutable VcnGatewayConnectionConfig instance + */ + @NonNull + public VcnGatewayConnectionConfig build() { + return new VcnGatewayConnectionConfig(); + } + } +} diff --git a/core/java/android/os/BasicShellCommandHandler.java b/core/java/android/os/BasicShellCommandHandler.java deleted file mode 100644 index 366da3db0010..000000000000 --- a/core/java/android/os/BasicShellCommandHandler.java +++ /dev/null @@ -1,342 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.os; - -import android.util.Log; - -import java.io.BufferedInputStream; -import java.io.FileDescriptor; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.PrintWriter; - -/** - * Helper for implementing {@link Binder#onShellCommand Binder.onShellCommand}. This is meant to - * be copied into mainline modules, so this class must not use any hidden APIs. - * - * @hide - */ -public abstract class BasicShellCommandHandler { - static final String TAG = "ShellCommand"; - static final boolean DEBUG = false; - - private Binder mTarget; - private FileDescriptor mIn; - private FileDescriptor mOut; - private FileDescriptor mErr; - private String[] mArgs; - - private String mCmd; - private int mArgPos; - private String mCurArgData; - - private FileInputStream mFileIn; - private FileOutputStream mFileOut; - private FileOutputStream mFileErr; - - private PrintWriter mOutPrintWriter; - private PrintWriter mErrPrintWriter; - private InputStream mInputStream; - - public void init(Binder target, FileDescriptor in, FileDescriptor out, FileDescriptor err, - String[] args, int firstArgPos) { - mTarget = target; - mIn = in; - mOut = out; - mErr = err; - mArgs = args; - mCmd = null; - mArgPos = firstArgPos; - mCurArgData = null; - mFileIn = null; - mFileOut = null; - mFileErr = null; - mOutPrintWriter = null; - mErrPrintWriter = null; - mInputStream = null; - } - - public int exec(Binder target, FileDescriptor in, FileDescriptor out, FileDescriptor err, - String[] args) { - String cmd; - int start; - if (args != null && args.length > 0) { - cmd = args[0]; - start = 1; - } else { - cmd = null; - start = 0; - } - init(target, in, out, err, args, start); - mCmd = cmd; - - if (DEBUG) { - RuntimeException here = new RuntimeException("here"); - here.fillInStackTrace(); - Log.d(TAG, "Starting command " + mCmd + " on " + mTarget, here); - Log.d(TAG, "Calling uid=" + Binder.getCallingUid() - + " pid=" + Binder.getCallingPid()); - } - int res = -1; - try { - res = onCommand(mCmd); - if (DEBUG) Log.d(TAG, "Executed command " + mCmd + " on " + mTarget); - } catch (Throwable e) { - // Unlike usual calls, in this case if an exception gets thrown - // back to us we want to print it back in to the dump data, since - // that is where the caller expects all interesting information to - // go. - PrintWriter eout = getErrPrintWriter(); - eout.println(); - eout.println("Exception occurred while executing '" + mCmd + "':"); - e.printStackTrace(eout); - } finally { - if (DEBUG) Log.d(TAG, "Flushing output streams on " + mTarget); - if (mOutPrintWriter != null) { - mOutPrintWriter.flush(); - } - if (mErrPrintWriter != null) { - mErrPrintWriter.flush(); - } - if (DEBUG) Log.d(TAG, "Sending command result on " + mTarget); - } - if (DEBUG) Log.d(TAG, "Finished command " + mCmd + " on " + mTarget); - return res; - } - - /** - * Return the raw FileDescriptor for the output stream. - */ - public FileDescriptor getOutFileDescriptor() { - return mOut; - } - - /** - * Return direct raw access (not buffered) to the command's output data stream. - */ - public OutputStream getRawOutputStream() { - if (mFileOut == null) { - mFileOut = new FileOutputStream(mOut); - } - return mFileOut; - } - - /** - * Return a PrintWriter for formatting output to {@link #getRawOutputStream()}. - */ - public PrintWriter getOutPrintWriter() { - if (mOutPrintWriter == null) { - mOutPrintWriter = new PrintWriter(getRawOutputStream()); - } - return mOutPrintWriter; - } - - /** - * Return the raw FileDescriptor for the error stream. - */ - public FileDescriptor getErrFileDescriptor() { - return mErr; - } - - /** - * Return direct raw access (not buffered) to the command's error output data stream. - */ - public OutputStream getRawErrorStream() { - if (mFileErr == null) { - mFileErr = new FileOutputStream(mErr); - } - return mFileErr; - } - - /** - * Return a PrintWriter for formatting output to {@link #getRawErrorStream()}. - */ - public PrintWriter getErrPrintWriter() { - if (mErr == null) { - return getOutPrintWriter(); - } - if (mErrPrintWriter == null) { - mErrPrintWriter = new PrintWriter(getRawErrorStream()); - } - return mErrPrintWriter; - } - - /** - * Return the raw FileDescriptor for the input stream. - */ - public FileDescriptor getInFileDescriptor() { - return mIn; - } - - /** - * Return direct raw access (not buffered) to the command's input data stream. - */ - public InputStream getRawInputStream() { - if (mFileIn == null) { - mFileIn = new FileInputStream(mIn); - } - return mFileIn; - } - - /** - * Return buffered access to the command's {@link #getRawInputStream()}. - */ - public InputStream getBufferedInputStream() { - if (mInputStream == null) { - mInputStream = new BufferedInputStream(getRawInputStream()); - } - return mInputStream; - } - - /** - * Return the next option on the command line -- that is an argument that - * starts with '-'. If the next argument is not an option, null is returned. - */ - public String getNextOption() { - if (mCurArgData != null) { - String prev = mArgs[mArgPos - 1]; - throw new IllegalArgumentException("No argument expected after \"" + prev + "\""); - } - if (mArgPos >= mArgs.length) { - return null; - } - String arg = mArgs[mArgPos]; - if (!arg.startsWith("-")) { - return null; - } - mArgPos++; - if (arg.equals("--")) { - return null; - } - if (arg.length() > 1 && arg.charAt(1) != '-') { - if (arg.length() > 2) { - mCurArgData = arg.substring(2); - return arg.substring(0, 2); - } else { - mCurArgData = null; - return arg; - } - } - mCurArgData = null; - return arg; - } - - /** - * Return the next argument on the command line, whatever it is; if there are - * no arguments left, return null. - */ - public String getNextArg() { - if (mCurArgData != null) { - String arg = mCurArgData; - mCurArgData = null; - return arg; - } else if (mArgPos < mArgs.length) { - return mArgs[mArgPos++]; - } else { - return null; - } - } - - public String peekNextArg() { - if (mCurArgData != null) { - return mCurArgData; - } else if (mArgPos < mArgs.length) { - return mArgs[mArgPos]; - } else { - return null; - } - } - - /** - * @return all the remaining arguments in the command without moving the current position. - */ - public String[] peekRemainingArgs() { - int remaining = getRemainingArgsCount(); - String[] args = new String[remaining]; - for (int pos = mArgPos; pos < mArgs.length; pos++) { - args[pos - mArgPos] = mArgs[pos]; - } - return args; - } - - /** - * Returns number of arguments that haven't been processed yet. - */ - public int getRemainingArgsCount() { - if (mArgPos >= mArgs.length) { - return 0; - } - return mArgs.length - mArgPos; - } - - /** - * Return the next argument on the command line, whatever it is; if there are - * no arguments left, throws an IllegalArgumentException to report this to the user. - */ - public String getNextArgRequired() { - String arg = getNextArg(); - if (arg == null) { - String prev = mArgs[mArgPos - 1]; - throw new IllegalArgumentException("Argument expected after \"" + prev + "\""); - } - return arg; - } - - public int handleDefaultCommands(String cmd) { - if (cmd == null || "help".equals(cmd) || "-h".equals(cmd)) { - onHelp(); - } else { - getOutPrintWriter().println("Unknown command: " + cmd); - } - return -1; - } - - public Binder getTarget() { - return mTarget; - } - - public String[] getAllArgs() { - return mArgs; - } - - /** - * Implement parsing and execution of a command. If it isn't a command you understand, - * call {@link #handleDefaultCommands(String)} and return its result as a last resort. - * Use {@link #getNextOption()}, {@link #getNextArg()}, and {@link #getNextArgRequired()} - * to process additional command line arguments. Command output can be written to - * {@link #getOutPrintWriter()} and errors to {@link #getErrPrintWriter()}. - * - * <p class="caution">Note that no permission checking has been done before entering this - * function, so you need to be sure to do your own security verification for any commands you - * are executing. The easiest way to do this is to have the ShellCommand contain - * only a reference to your service's aidl interface, and do all of your command - * implementations on top of that -- that way you can rely entirely on your executing security - * code behind that interface.</p> - * - * @param cmd The first command line argument representing the name of the command to execute. - * @return Return the command result; generally 0 or positive indicates success and - * negative values indicate error. - */ - public abstract int onCommand(String cmd); - - /** - * Implement this to print help text about your command to {@link #getOutPrintWriter()}. - */ - public abstract void onHelp(); -} diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 00023a5caf75..9c2ae4e7c48a 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -2481,6 +2481,29 @@ public abstract class BatteryStats implements Parcelable { "group", "compl", "dorm", "uninit" }; + /** + * Returned value if energy data is unavailable + * + * {@hide} + */ + public static final long ENERGY_DATA_UNAVAILABLE = -1; + + /** + * Returns the energy in microjoules that the screen consumed while on. + * Will return {@link #ENERGY_DATA_UNAVAILABLE} if data is unavailable + * + * {@hide} + */ + public abstract long getScreenOnEnergy(); + + /** + * Returns the energy in microjoules that the screen consumed while in doze + * Will return {@link #ENERGY_DATA_UNAVAILABLE} if data is unavailable + * + * {@hide} + */ + public abstract long getScreenDozeEnergy(); + public static final BitDescription[] HISTORY_STATE_DESCRIPTIONS = new BitDescription[] { new BitDescription(HistoryItem.STATE_CPU_RUNNING_FLAG, "running", "r"), new BitDescription(HistoryItem.STATE_WAKE_LOCK_FLAG, "wake_lock", "w"), diff --git a/core/java/android/os/ParcelableHolder.java b/core/java/android/os/ParcelableHolder.java index 95c07b6b2451..fb9fa109bfa3 100644 --- a/core/java/android/os/ParcelableHolder.java +++ b/core/java/android/os/ParcelableHolder.java @@ -170,16 +170,21 @@ public final class ParcelableHolder implements Parcelable { mParcelable = null; + int dataSize = parcel.readInt(); + if (dataSize < 0) { + throw new IllegalArgumentException("dataSize from parcel is negative"); + } else if (dataSize == 0) { + if (mParcel != null) { + mParcel.recycle(); + mParcel = null; + } + return; + } if (mParcel == null) { mParcel = Parcel.obtain(); } mParcel.setDataPosition(0); mParcel.setDataSize(0); - - int dataSize = parcel.readInt(); - if (dataSize < 0) { - throw new IllegalArgumentException("dataSize from parcel is negative"); - } int dataStartPos = parcel.dataPosition(); mParcel.appendFrom(parcel, dataStartPos, dataSize); @@ -196,6 +201,11 @@ public final class ParcelableHolder implements Parcelable { return; } + if (mParcelable == null) { + parcel.writeInt(0); + return; + } + int sizePos = parcel.dataPosition(); parcel.writeInt(0); int dataStartPos = parcel.dataPosition(); diff --git a/core/java/android/os/ShellCommand.java b/core/java/android/os/ShellCommand.java index 3358ce13ed52..a2173a6d2360 100644 --- a/core/java/android/os/ShellCommand.java +++ b/core/java/android/os/ShellCommand.java @@ -19,15 +19,9 @@ package android.os; import android.compat.annotation.UnsupportedAppUsage; import android.util.Slog; -import com.android.internal.util.FastPrintWriter; +import com.android.modules.utils.BasicShellCommandHandler; -import java.io.BufferedInputStream; import java.io.FileDescriptor; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.PrintWriter; /** * Helper for implementing {@link Binder#onShellCommand Binder.onShellCommand}. diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java index c89adadfbf2d..086180e7ead4 100644 --- a/core/java/android/os/StrictMode.java +++ b/core/java/android/os/StrictMode.java @@ -85,8 +85,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Deque; import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; import java.util.concurrent.Executor; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.atomic.AtomicInteger; @@ -1045,22 +1043,22 @@ public final class StrictMode { /** * Detect attempts to invoke a method on a {@link Context} that is not suited for such * operation. - * <p>An example of this is trying to obtain an instance of visual service (e.g. + * <p>An example of this is trying to obtain an instance of UI service (e.g. * {@link android.view.WindowManager}) from a non-visual {@link Context}. This is not * allowed, since a non-visual {@link Context} is not adjusted to any visual area, and * therefore can report incorrect metrics or resources. * @see Context#getDisplay() * @see Context#getSystemService(String) - * @hide */ - @TestApi public @NonNull Builder detectIncorrectContextUse() { return enable(DETECT_VM_INCORRECT_CONTEXT_USE); } /** * Disable detection of incorrect context use. - * TODO(b/149790106): Fix usages and remove. + * + * @see #detectIncorrectContextUse() + * * @hide */ @TestApi diff --git a/core/java/android/os/strictmode/UntaggedSocketViolation.java b/core/java/android/os/strictmode/UntaggedSocketViolation.java index 3b1ef253b2d2..c34d6e821f7a 100644 --- a/core/java/android/os/strictmode/UntaggedSocketViolation.java +++ b/core/java/android/os/strictmode/UntaggedSocketViolation.java @@ -18,7 +18,7 @@ package android.os.strictmode; public final class UntaggedSocketViolation extends Violation { /** @hide */ public UntaggedSocketViolation() { - super("Untagged socket detected; use TrafficStats.setThreadSocketTag() to " + super("Untagged socket detected; use TrafficStats.setTrafficStatsTag() to " + "track all network usage"); } } diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index 0c4f8de449e6..714bcea9c01f 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -464,6 +464,13 @@ public final class DeviceConfig { */ public static final String NAMESPACE_LATENCY_TRACKER = "latency_tracker"; + /** + * InteractionJankMonitor properties definitions. + * + * @hide + */ + public static final String NAMESPACE_INTERACTION_JANK_MONITOR = "interaction_jank_monitor"; + private static final Object sLock = new Object(); @GuardedBy("sLock") private static ArrayMap<OnPropertiesChangedListener, Pair<String, Executor>> sListeners = diff --git a/core/java/android/provider/OWNERS b/core/java/android/provider/OWNERS index 8b7d6ad851f9..97e015646df0 100644 --- a/core/java/android/provider/OWNERS +++ b/core/java/android/provider/OWNERS @@ -1,4 +1,5 @@ per-file DeviceConfig.java = svetoslavganov@google.com per-file DeviceConfig.java = hackbod@google.com +per-file DeviceConfig.java = schfan@google.com diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java index e32ffa6e9d05..18921639f55d 100644 --- a/core/java/android/security/keymaster/KeymasterDefs.java +++ b/core/java/android/security/keymaster/KeymasterDefs.java @@ -94,6 +94,8 @@ public final class KeymasterDefs { public static final int KM_TAG_ATTESTATION_ID_MEID = KM_BYTES | 715; public static final int KM_TAG_ATTESTATION_ID_MANUFACTURER = KM_BYTES | 716; public static final int KM_TAG_ATTESTATION_ID_MODEL = KM_BYTES | 717; + public static final int KM_TAG_VENDOR_PATCHLEVEL = KM_UINT | 718; + public static final int KM_TAG_BOOT_PATCHLEVEL = KM_UINT | 719; public static final int KM_TAG_DEVICE_UNIQUE_ATTESTATION = KM_BOOL | 720; public static final int KM_TAG_ASSOCIATED_DATA = KM_BYTES | 1000; diff --git a/core/java/android/service/attestation/IImpressionAttestationService.aidl b/core/java/android/service/attestation/IImpressionAttestationService.aidl index 8e858b8aea77..fcbc51febada 100644 --- a/core/java/android/service/attestation/IImpressionAttestationService.aidl +++ b/core/java/android/service/attestation/IImpressionAttestationService.aidl @@ -18,8 +18,8 @@ package android.service.attestation; import android.graphics.Rect; import android.hardware.HardwareBuffer; -import android.service.attestation.ImpressionToken; import android.os.RemoteCallback; +import android.service.attestation.ImpressionToken; /** * Service used to handle impression attestation requests. @@ -31,22 +31,26 @@ oneway interface IImpressionAttestationService { * Generates the impression token that can be used to validate that the system generated the * token. * - * @param screenshot The token for the window where the view is shown. + * @param salt The salt to use when generating the hmac. This should be unique to the caller so + * the token cannot be verified by any other process. + * @param screenshot The screenshot to generate the hash and add to the token. * @param bounds The size and position of the content being attested in the window. * @param hashAlgorithm The String for the hashing algorithm to use based on values in * {@link #SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS}. * @param Callback The callback invoked to send back the impression token. */ - void generateImpressionToken(in HardwareBuffer screenshot, in Rect bounds, + void generateImpressionToken(in String salt, in HardwareBuffer screenshot, in Rect bounds, in String hashAlgorithm, in RemoteCallback callback); /** * Call to verify that the impressionToken passed in was generated by the system. The result - * will be sent in the callback as an integer with the key {@link #EXTRA_VERIFICATION_STATUS} - * and will be one of the values in {@link VerificationStatus}. + * will be sent in the callback as a boolean with the key {@link #EXTRA_VERIFICATION_STATUS}. * + * @param salt The salt value to use when verifying the hmac. This should be the same value that + * was passed to {@link generateImpressionToken()} to generate the token. * @param impressionToken The token to verify that it was generated by the system. * @param callback The callback invoked to send back the verification status. */ - void verifyImpressionToken(in ImpressionToken impressionToken, in RemoteCallback callback); + void verifyImpressionToken(in String salt, in ImpressionToken impressionToken, + in RemoteCallback callback); } diff --git a/core/java/android/service/attestation/ImpressionAttestationService.java b/core/java/android/service/attestation/ImpressionAttestationService.java index 923ab7a65d1b..05ad5f02efe5 100644 --- a/core/java/android/service/attestation/ImpressionAttestationService.java +++ b/core/java/android/service/attestation/ImpressionAttestationService.java @@ -18,7 +18,6 @@ package android.service.attestation; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; -import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; @@ -50,22 +49,10 @@ public abstract class ImpressionAttestationService extends Service { public static final String EXTRA_VERIFICATION_STATUS = "android.service.attestation.extra.VERIFICATION_STATUS"; - /** @hide */ - @IntDef(prefix = {"VERIFICATION_STATUS_"}, value = { - VERIFICATION_STATUS_UNKNOWN, - VERIFICATION_STATUS_OS_VERIFIED, - VERIFICATION_STATUS_APP_DECLARED - }) - public @interface VerificationStatus { - } - - public static final int VERIFICATION_STATUS_UNKNOWN = 0; - public static final int VERIFICATION_STATUS_OS_VERIFIED = 1; - public static final int VERIFICATION_STATUS_APP_DECLARED = 2; - /** * Manifest metadata key for the resource string array containing the names of all impression * attestation algorithms provided by the service. + * * @hide */ public static final String SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS = @@ -74,6 +61,7 @@ public abstract class ImpressionAttestationService extends Service { /** * The {@link Intent} action that must be declared as handled by a service in its manifest * for the system to recognize it as an impression attestation providing service. + * * @hide */ public static final String SERVICE_INTERFACE = @@ -102,6 +90,8 @@ public abstract class ImpressionAttestationService extends Service { * Generates the impression token that can be used to validate that the system * generated the token. * + * @param salt The salt to use when generating the hmac. This should be unique to the + * caller so the token cannot be verified by any other process. * @param screenshot The screenshot buffer for the content to attest. * @param bounds The size and position of the content being attested in the window. * @param hashAlgorithm The String for the hashing algorithm to use based values in @@ -110,51 +100,57 @@ public abstract class ImpressionAttestationService extends Service { * Returns null when the arguments sent are invalid. */ @Nullable - public abstract ImpressionToken onGenerateImpressionToken(@NonNull HardwareBuffer screenshot, - @NonNull Rect bounds, @NonNull String hashAlgorithm); + public abstract ImpressionToken onGenerateImpressionToken(@NonNull String salt, + @NonNull HardwareBuffer screenshot, @NonNull Rect bounds, + @NonNull String hashAlgorithm); /** * Call to verify that the impressionToken passed in was generated by the system. * + * @param salt The salt value to use when verifying the hmac. This should be the + * same value that was passed to + * {@link #onGenerateImpressionToken(String, + * HardwareBuffer, Rect, String)} to + * generate the token. * @param impressionToken The token to verify that it was generated by the system. - * @return A {@link VerificationStatus} about whether the token was generated by the system. + * @return true if the token can be verified that it was generated by the system. */ - public abstract @VerificationStatus int onVerifyImpressionToken( + public abstract boolean onVerifyImpressionToken(@NonNull String salt, @NonNull ImpressionToken impressionToken); - private void generateImpressionToken(HardwareBuffer screenshot, Rect bounds, + private void generateImpressionToken(String salt, HardwareBuffer screenshot, Rect bounds, String hashAlgorithm, RemoteCallback callback) { - ImpressionToken impressionToken = onGenerateImpressionToken(screenshot, bounds, + ImpressionToken impressionToken = onGenerateImpressionToken(salt, screenshot, bounds, hashAlgorithm); final Bundle data = new Bundle(); data.putParcelable(EXTRA_IMPRESSION_TOKEN, impressionToken); callback.sendResult(data); } - private void verifyImpressionToken(ImpressionToken impressionToken, + private void verifyImpressionToken(String salt, ImpressionToken impressionToken, RemoteCallback callback) { - @VerificationStatus int verificationStatus = onVerifyImpressionToken(impressionToken); + boolean verificationStatus = onVerifyImpressionToken(salt, impressionToken); final Bundle data = new Bundle(); - data.putInt(EXTRA_VERIFICATION_STATUS, verificationStatus); + data.putBoolean(EXTRA_VERIFICATION_STATUS, verificationStatus); callback.sendResult(data); } private final class ImpressionAttestationServiceWrapper extends IImpressionAttestationService.Stub { @Override - public void generateImpressionToken(HardwareBuffer screenshot, Rect bounds, + public void generateImpressionToken(String salt, HardwareBuffer screenshot, Rect bounds, String hashAlgorithm, RemoteCallback callback) { mHandler.sendMessage( obtainMessage(ImpressionAttestationService::generateImpressionToken, - ImpressionAttestationService.this, screenshot, bounds, hashAlgorithm, - callback)); + ImpressionAttestationService.this, salt, screenshot, bounds, + hashAlgorithm, callback)); } @Override - public void verifyImpressionToken(ImpressionToken impressionToken, + public void verifyImpressionToken(String salt, ImpressionToken impressionToken, RemoteCallback callback) { mHandler.sendMessage(obtainMessage(ImpressionAttestationService::verifyImpressionToken, - ImpressionAttestationService.this, impressionToken, callback)); + ImpressionAttestationService.this, salt, impressionToken, callback)); } } } diff --git a/core/java/android/service/quicksettings/TileService.java b/core/java/android/service/quicksettings/TileService.java index b4b5819c7d5f..53290e2ede5a 100644 --- a/core/java/android/service/quicksettings/TileService.java +++ b/core/java/android/service/quicksettings/TileService.java @@ -176,6 +176,9 @@ public class TileService extends Service { @Override public void onDestroy() { + // As this call will come asynchronously in the main thread, prevent calls from the binder + // being processed after this. + mHandler.removeCallbacksAndMessages(null); if (mListening) { onStopListening(); mListening = false; diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java index bfa3123c9d84..479a0c16c747 100644 --- a/core/java/android/telephony/PhoneStateListener.java +++ b/core/java/android/telephony/PhoneStateListener.java @@ -17,10 +17,7 @@ package android.telephony; import android.Manifest; -import android.annotation.CallbackExecutor; -import android.annotation.IntDef; import android.annotation.NonNull; -import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.compat.annotation.ChangeId; @@ -31,27 +28,17 @@ import android.os.Handler; import android.os.HandlerExecutor; import android.os.Looper; import android.telephony.Annotation.CallState; -import android.telephony.Annotation.DataActivityType; -import android.telephony.Annotation.DisconnectCauses; -import android.telephony.Annotation.NetworkType; -import android.telephony.Annotation.PreciseDisconnectCauses; import android.telephony.Annotation.RadioPowerState; import android.telephony.Annotation.SimActivationState; import android.telephony.Annotation.SrvccState; import android.telephony.emergency.EmergencyNumber; import android.telephony.ims.ImsReasonInfo; -import android.telephony.NetworkRegistrationInfo.Domain; -import android.telephony.TelephonyManager.DataEnabledReason; -import android.telephony.TelephonyManager.DataState; -import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.IPhoneStateListener; import dalvik.system.VMRuntime; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; import java.util.List; import java.util.Map; @@ -126,9 +113,7 @@ public class PhoneStateListener { * * @see #onServiceStateChanged * @see ServiceState - * @deprecated Use {@link ServiceStateChangedListener} instead. */ - @Deprecated public static final int LISTEN_SERVICE_STATE = 0x00000001; /** @@ -136,7 +121,8 @@ public class PhoneStateListener { * {@more} * * @see #onSignalStrengthChanged - * @deprecated Use {@link SignalStrengthsChangedListener} instead. + * + * @deprecated by {@link #LISTEN_SIGNAL_STRENGTHS} */ @Deprecated public static final int LISTEN_SIGNAL_STRENGTH = 0x00000002; @@ -152,9 +138,7 @@ public class PhoneStateListener { * voicemail icon. * * @see #onMessageWaitingIndicatorChanged - * @deprecated Use {@link MessageWaitingIndicatorChangedListener} instead. */ - @Deprecated public static final int LISTEN_MESSAGE_WAITING_INDICATOR = 0x00000004; /** @@ -165,9 +149,7 @@ public class PhoneStateListener { * {@link TelephonyManager#hasCarrierPrivileges}). * * @see #onCallForwardingIndicatorChanged - * @deprecated Use {@link CallForwardingIndicatorChangedListener} instead. */ - @Deprecated public static final int LISTEN_CALL_FORWARDING_INDICATOR = 0x00000008; /** @@ -183,9 +165,7 @@ public class PhoneStateListener { * instead. * * @see #onCellLocationChanged - * @deprecated Use {@link CellLocationChangedListener} instead. */ - @Deprecated public static final int LISTEN_CELL_LOCATION = 0x00000010; /** @@ -193,18 +173,14 @@ public class PhoneStateListener { * {@more} * * @see #onCallStateChanged - * @deprecated Use {@link CallStateChangedListener} instead. */ - @Deprecated public static final int LISTEN_CALL_STATE = 0x00000020; /** * Listen for changes to the data connection state (cellular). * * @see #onDataConnectionStateChanged - * @deprecated Use {@link DataConnectionStateChangedListener} instead. */ - @Deprecated public static final int LISTEN_DATA_CONNECTION_STATE = 0x00000040; /** @@ -215,9 +191,7 @@ public class PhoneStateListener { * data-traffic icon. * * @see #onDataActivity - * @deprecated Use {@link DataActivityListener} instead. */ - @Deprecated public static final int LISTEN_DATA_ACTIVITY = 0x00000080; /** @@ -227,9 +201,7 @@ public class PhoneStateListener { * icon. * * @see #onSignalStrengthsChanged - * @deprecated Use {@link SignalStrengthsChangedListener} instead. */ - @Deprecated public static final int LISTEN_SIGNAL_STRENGTHS = 0x00000100; /** @@ -239,9 +211,7 @@ public class PhoneStateListener { * @see #onSignalStrengthsChanged * * @hide - * @deprecated Use {@link AlwaysReportedSignalStrengthsChangedListener} instead. */ - @Deprecated @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) public static final int LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH = 0x00000200; @@ -252,9 +222,7 @@ public class PhoneStateListener { * permission. * * @see #onCellInfoChanged - * @deprecated Use {@link CellInfoChangedListener} instead. */ - @Deprecated public static final int LISTEN_CELL_INFO = 0x00000400; /** @@ -266,10 +234,8 @@ public class PhoneStateListener { * (see {@link TelephonyManager#hasCarrierPrivileges}). * * @hide - * @deprecated Use {@link PreciseCallStateChangedListener} instead. */ - @Deprecated - @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) + @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE)) @SystemApi public static final int LISTEN_PRECISE_CALL_STATE = 0x00000800; @@ -281,10 +247,8 @@ public class PhoneStateListener { * (see {@link TelephonyManager#hasCarrierPrivileges}). * * @see #onPreciseDataConnectionStateChanged - * @deprecated Use {@link PreciseDataConnectionStateChangedListener} instead. */ - @Deprecated - @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) + @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE)) public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 0x00001000; /** @@ -294,7 +258,7 @@ public class PhoneStateListener { * READ_PRECISE_PHONE_STATE} * @see #onDataConnectionRealTimeInfoChanged(DataConnectionRealTimeInfo) * - * @deprecated Use {@link TelephonyManager#requestModemActivityInfo} instead. + * @deprecated Use {@link TelephonyManager#getModemActivityInfo()} * @hide */ @Deprecated @@ -307,9 +271,7 @@ public class PhoneStateListener { * * @see #onServiceStateChanged(ServiceState) * @hide - * @deprecated Use {@link SrvccStateChangedListener} instead. */ - @Deprecated @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_SRVCC_STATE_CHANGED = 0x00004000; @@ -327,11 +289,10 @@ public class PhoneStateListener { /** * Listen for carrier network changes indicated by a carrier app. * - * @see android.service.carrier.CarrierService#notifyCarrierNetworkChange(boolean) + * @see #onCarrierNetworkRequest + * @see TelephonyManager#notifyCarrierNetworkChange(boolean) * @hide - * @deprecated Use {@link CarrierNetworkChangeListener} instead. */ - @Deprecated public static final int LISTEN_CARRIER_NETWORK_CHANGE = 0x00010000; /** @@ -350,9 +311,7 @@ public class PhoneStateListener { * * @see #onVoiceActivationStateChanged * @hide - * @deprecated Use {@link VoiceActivationStateChangedListener} instead. */ - @Deprecated @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_VOICE_ACTIVATION_STATE = 0x00020000; @@ -364,24 +323,20 @@ public class PhoneStateListener { * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN - * + * {@more} * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates data service has been * fully activated * * @see #onDataActivationStateChanged * @hide - * @deprecated Use {@link DataActivationStateChangedListener} instead. */ - @Deprecated public static final int LISTEN_DATA_ACTIVATION_STATE = 0x00040000; /** * Listen for changes to the user mobile data state * * @see #onUserMobileDataStateChanged - * @deprecated Use {@link UserMobileDataStateChangedListener} instead. */ - @Deprecated public static final int LISTEN_USER_MOBILE_DATA_STATE = 0x00080000; /** @@ -392,9 +347,7 @@ public class PhoneStateListener { * {@link TelephonyManager#hasCarrierPrivileges}). * * @see #onDisplayInfoChanged - * @deprecated Use {@link DisplayInfoChangedListener} instead. */ - @Deprecated public static final int LISTEN_DISPLAY_INFO_CHANGED = 0x00100000; /** @@ -402,9 +355,7 @@ public class PhoneStateListener { * * @see #onPhoneCapabilityChanged * @hide - * @deprecated Use {@link PhoneCapabilityChangedListener} instead. */ - @Deprecated public static final int LISTEN_PHONE_CAPABILITY_CHANGE = 0x00200000; /** @@ -414,19 +365,17 @@ public class PhoneStateListener { * subscription user selected as default data subscription in DSDS mode. * * @see #onActiveDataSubscriptionIdChanged - * @deprecated Use {@link ActiveDataSubscriptionIdChangedListener} instead. */ - @Deprecated public static final int LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE = 0x00400000; /** * Listen for changes to the radio power state. * + * <p>Requires permission {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} + * * @see #onRadioPowerStateChanged * @hide - * @deprecated Use {@link RadioPowerStateChangedListener} instead. */ - @Deprecated @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_RADIO_POWER_STATE_CHANGED = 0x00800000; @@ -436,10 +385,7 @@ public class PhoneStateListener { * * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}). - * - * @deprecated Use {@link EmergencyNumberListChangedListener} instead. */ - @Deprecated public static final int LISTEN_EMERGENCY_NUMBER_LIST = 0x01000000; /** @@ -450,10 +396,8 @@ public class PhoneStateListener { * or the calling app has carrier privileges * (see {@link TelephonyManager#hasCarrierPrivileges}). * - * @deprecated Use {@link CallDisconnectCauseChangedListener} instead. */ - @Deprecated - @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) + @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE)) public static final int LISTEN_CALL_DISCONNECT_CAUSES = 0x02000000; /** @@ -465,11 +409,9 @@ public class PhoneStateListener { * * @see #onCallAttributesChanged * @hide - * @deprecated Use {@link CallAttributesChangedListener} instead. */ - @Deprecated @SystemApi - @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) + @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE)) public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 0x04000000; /** @@ -481,20 +423,18 @@ public class PhoneStateListener { * (see {@link TelephonyManager#hasCarrierPrivileges}). * * @see #onImsCallDisconnectCauseChanged(ImsReasonInfo) - * @deprecated Use {@link ImsCallDisconnectCauseChangedListener} instead. */ - @Deprecated - @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) + @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE)) public static final int LISTEN_IMS_CALL_DISCONNECT_CAUSES = 0x08000000; /** * Listen for the emergency number placed from an outgoing call. * + * <p>Requires permission {@link android.Manifest.permission#READ_ACTIVE_EMERGENCY_SESSION} + * * @see #onOutgoingEmergencyCall * @hide - * @deprecated Use {@link OutgoingEmergencyCallListener} instead. */ - @Deprecated @SystemApi @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_CALL = 0x10000000; @@ -502,11 +442,11 @@ public class PhoneStateListener { /** * Listen for the emergency number placed from an outgoing SMS. * + * <p>Requires permission {@link android.Manifest.permission#READ_ACTIVE_EMERGENCY_SESSION} + * * @see #onOutgoingEmergencySms * @hide - * @deprecated Use {@link OutgoingEmergencySmsListener} instead. */ - @Deprecated @SystemApi @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 0x20000000; @@ -525,9 +465,7 @@ public class PhoneStateListener { * of whether the calling app has carrier privileges. * * @see #onRegistrationFailed - * @deprecated Use {@link RegistrationFailedListener} instead. */ - @Deprecated @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_REGISTRATION_FAILURE = 0x40000000; @@ -541,525 +479,19 @@ public class PhoneStateListener { * of whether the calling app has carrier privileges. * * @see #onBarringInfoChanged - * @deprecated Use {@link BarringInfoChangedListener} instead. */ - @Deprecated @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_BARRING_INFO = 0x80000000; /** - * Event for changes to the network service state (cellular). - * - * @see ServiceStateChangedListener#onServiceStateChanged - * @see ServiceState - * - * @hide - */ - @SystemApi - public static final int EVENT_SERVICE_STATE_CHANGED = 1; - - /** - * Event for changes to the network signal strength (cellular). - * - * @see SignalStrengthsChangedListener#onSignalStrengthsChanged - * - * @hide - */ - @SystemApi - public static final int EVENT_SIGNAL_STRENGTH_CHANGED = 2; - - /** - * Event for changes to the message-waiting indicator. - * - * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE} or that - * the calling app has carrier privileges (see - * {@link TelephonyManager#hasCarrierPrivileges}). - * <p> - * Example: The status bar uses this to determine when to display the - * voicemail icon. - * - * @see MessageWaitingIndicatorChangedListener#onMessageWaitingIndicatorChanged - * - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) - public static final int EVENT_MESSAGE_WAITING_INDICATOR_CHANGED = 3; - - /** - * Event for changes to the call-forwarding indicator. - * - * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE} or that - * the calling app has carrier privileges (see - * {@link TelephonyManager#hasCarrierPrivileges}). - * - * @see CallForwardingIndicatorChangedListener#onCallForwardingIndicatorChanged - * - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) - public static final int EVENT_CALL_FORWARDING_INDICATOR_CHANGED = 4; - - /** - * Event for changes to the device's cell location. Note that - * this will result in frequent callbacks to the listener. - * - * If you need regular location updates but want more control over - * the update interval or location precision, you can set up a listener - * through the {@link android.location.LocationManager location manager} - * instead. - * - * @see CellLocationChangedListener#onCellLocationChanged - * - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) - public static final int EVENT_CELL_LOCATION_CHANGED = 5; - - /** - * Event for changes to the device call state. - * - * @see CallStateChangedListener#onCallStateChanged - * - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.READ_CALL_LOG) - public static final int EVENT_CALL_STATE_CHANGED = 6; - - /** - * Event for changes to the data connection state (cellular). - * - * @see DataConnectionStateChangedListener#onDataConnectionStateChanged - * - * @hide - */ - @SystemApi - public static final int EVENT_DATA_CONNECTION_STATE_CHANGED = 7; - - /** - * Event for changes to the direction of data traffic on the data - * connection (cellular). - * - * Example: The status bar uses this to display the appropriate - * data-traffic icon. - * - * @see DataActivityListener#onDataActivity - * - * @hide - */ - @SystemApi - public static final int EVENT_DATA_ACTIVITY_CHANGED = 8; - - /** - * Event for changes to the network signal strengths (cellular). - * <p> - * Example: The status bar uses this to control the signal-strength - * icon. - * - * @see SignalStrengthsChangedListener#onSignalStrengthsChanged - * - * @hide - */ - @SystemApi - public static final int EVENT_SIGNAL_STRENGTHS_CHANGED = 9; - - /** - * Event for changes of the network signal strengths (cellular) always reported from modem, - * even in some situations such as the screen of the device is off. - * - * @see AlwaysReportedSignalStrengthsChangedListener#onSignalStrengthsChanged - * - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) - public static final int EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED = 10; - - /** - * Event for changes to observed cell info. - * - * @see CellInfoChangedListener#onCellInfoChanged - * - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) - public static final int EVENT_CELL_INFO_CHANGED = 11; - - /** - * Event for {@link android.telephony.Annotation.PreciseCallStates} of ringing, - * background and foreground calls. - * - * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} - * or the calling app has carrier privileges - * (see {@link TelephonyManager#hasCarrierPrivileges}). - * - * @see PreciseCallStateChangedListener#onPreciseCallStateChanged - * - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) - public static final int EVENT_PRECISE_CALL_STATE_CHANGED = 12; - - /** - * Event for {@link PreciseDataConnectionState} on the data connection (cellular). - * - * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} - * or the calling app has carrier privileges - * (see {@link TelephonyManager#hasCarrierPrivileges}). - * - * @see PreciseDataConnectionStateChangedListener#onPreciseDataConnectionStateChanged - * - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) - public static final int EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED = 13; - - /** - * Event for real time info for all data connections (cellular)). - * - * @see #onDataConnectionRealTimeInfoChanged(DataConnectionRealTimeInfo) - * - * @deprecated Use {@link TelephonyManager#requestModemActivityInfo} - * @hide - */ - @Deprecated - @SystemApi - @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) - public static final int EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED = 14; - - /** - * Event for OEM hook raw event - * - * @see #onOemHookRawEvent - * - * @hide - */ - @SystemApi - @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - public static final int EVENT_OEM_HOOK_RAW = 15; - - /** - * Event for changes to the SRVCC state of the active call. - * - * @see SrvccStateChangedListener#onSrvccStateChanged - * - * @hide - */ - @SystemApi - @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - public static final int EVENT_SRVCC_STATE_CHANGED = 16; - - /** - * Event for carrier network changes indicated by a carrier app. - * - * @see android.service.carrier.CarrierService#notifyCarrierNetworkChange(boolean) - * @see CarrierNetworkChangeListener#onCarrierNetworkChange - * - * @hide - */ - @SystemApi - public static final int EVENT_CARRIER_NETWORK_CHANGED = 17; - - /** - * Event for changes to the sim voice activation state - * - * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING - * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED - * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED - * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED - * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN - * - * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates voice service has been - * fully activated - * - * @see VoiceActivationStateChangedListener#onVoiceActivationStateChanged - * - * @hide - */ - @SystemApi - @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - public static final int EVENT_VOICE_ACTIVATION_STATE_CHANGED = 18; - - /** - * Event for changes to the sim data activation state - * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING - * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED - * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED - * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED - * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN - * - * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates data service has been - * fully activated - * - * @see DataActivationStateChangedListener#onDataActivationStateChanged - * @hide - */ - @SystemApi - public static final int EVENT_DATA_ACTIVATION_STATE_CHANGED = 19; - - /** - * Event for changes to the user mobile data state - * - * @see UserMobileDataStateChangedListener#onUserMobileDataStateChanged - * - * @hide - */ - @SystemApi - public static final int EVENT_USER_MOBILE_DATA_STATE_CHANGED = 20; - - /** - * Event for display info changed event. - * - * @see DisplayInfoChangedListener#onDisplayInfoChanged - * - * @hide - */ - @SystemApi - public static final int EVENT_DISPLAY_INFO_CHANGED = 21; - - /** - * Event for changes to the phone capability. - * - * @see PhoneCapabilityChangedListener#onPhoneCapabilityChanged - * - * @hide - */ - @SystemApi - public static final int EVENT_PHONE_CAPABILITY_CHANGED = 22; - - /** - * Event for changes to active data subscription ID. Active data subscription is - * the current subscription used to setup Cellular Internet data. For example, - * it could be the current active opportunistic subscription in use, or the - * subscription user selected as default data subscription in DSDS mode. - * - * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling - * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}). - * - * @see ActiveDataSubscriptionIdChangedListener#onActiveDataSubscriptionIdChanged - * - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) - public static final int EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED = 23; - - /** - * Event for changes to the radio power state. - * - * @see RadioPowerStateChangedListener#onRadioPowerStateChanged - * - * @hide - */ - @SystemApi - @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - public static final int EVENT_RADIO_POWER_STATE_CHANGED = 24; - - /** - * Event for changes to emergency number list based on all active subscriptions. - * - * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling - * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}). - * - * @see EmergencyNumberListChangedListener#onEmergencyNumberListChanged - * - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) - public static final int EVENT_EMERGENCY_NUMBER_LIST_CHANGED = 25; - - /** - * Event for call disconnect causes which contains {@link DisconnectCause} and - * {@link PreciseDisconnectCause}. - * - * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} - * or the calling app has carrier privileges - * (see {@link TelephonyManager#hasCarrierPrivileges}). - * - * @see CallDisconnectCauseChangedListener#onCallDisconnectCauseChanged - * - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) - public static final int EVENT_CALL_DISCONNECT_CAUSE_CHANGED = 26; - - /** - * Event for changes to the call attributes of a currently active call. - * - * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} - * or the calling app has carrier privileges - * (see {@link TelephonyManager#hasCarrierPrivileges}). - * - * @see CallAttributesChangedListener#onCallAttributesChanged + * Listen for changes to the physical channel configuration. * + * @see #onPhysicalChannelConfigurationChanged * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) - public static final int EVENT_CALL_ATTRIBUTES_CHANGED = 27; - - /** - * Event for IMS call disconnect causes which contains - * {@link android.telephony.ims.ImsReasonInfo} - * - * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} - * or the calling app has carrier privileges - * (see {@link TelephonyManager#hasCarrierPrivileges}). - * - * @see ImsCallDisconnectCauseChangedListener#onImsCallDisconnectCauseChanged(ImsReasonInfo) - * - * @hide - */ - @SystemApi - @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) - public static final int EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED = 28; - - /** - * Event for the emergency number placed from an outgoing call. - * - * @see OutgoingEmergencyCallListener#onOutgoingEmergencyCall - * - * @hide - */ - @SystemApi - @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) - public static final int EVENT_OUTGOING_EMERGENCY_CALL = 29; - - /** - * Event for the emergency number placed from an outgoing SMS. - * - * @see OutgoingEmergencySmsListener#onOutgoingEmergencySms - * - * @hide - */ - @SystemApi - @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) - public static final int EVENT_OUTGOING_EMERGENCY_SMS = 30; - - /** - * Event for registration failures. - * - * Event for indications that a registration procedure has failed in either the CS or PS - * domain. This indication does not necessarily indicate a change of service state, which should - * be tracked via {@link #EVENT_SERVICE_STATE_CHANGED}. - * - * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} or - * the calling app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}). - * - * <p>Also requires the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission, regardless - * of whether the calling app has carrier privileges. - * - * @see RegistrationFailedListener#onRegistrationFailed - * - * @hide - */ - @SystemApi - @RequiresPermission(allOf = { - Manifest.permission.READ_PRECISE_PHONE_STATE, - Manifest.permission.ACCESS_FINE_LOCATION - }) - public static final int EVENT_REGISTRATION_FAILURE = 31; - - /** - * Event for Barring Information for the current registered / camped cell. - * - * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} or - * the calling app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}). - * - * <p>Also requires the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission, regardless - * of whether the calling app has carrier privileges. - * - * @see BarringInfoChangedListener#onBarringInfoChanged - * - * @hide - */ - @SystemApi - @RequiresPermission(allOf = { - Manifest.permission.READ_PRECISE_PHONE_STATE, - Manifest.permission.ACCESS_FINE_LOCATION - }) - public static final int EVENT_BARRING_INFO_CHANGED = 32; - - /** - * Event for changes to the physical channel configuration. - * - * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} - * or the calling app has carrier privileges - * (see {@link TelephonyManager#hasCarrierPrivileges}). - * - * @see PhysicalChannelConfigChangedListener#onPhysicalChannelConfigChanged - * - * @hide - */ - @SystemApi - @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) - public static final int EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED = 33; - - - /** - * Event for changes to the data enabled. - * - * Event for indications that the enabled status of current data has changed. - * - * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} - * or the calling app has carrier privileges - * (see {@link TelephonyManager#hasCarrierPrivileges}). - * - * @see DataEnabledChangedListener#onDataEnabledChanged - * - * @hide - */ - @SystemApi - @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) - public static final int EVENT_DATA_ENABLED_CHANGED = 34; - - /** @hide */ - @IntDef(prefix = { "EVENT_" }, value = { - EVENT_SERVICE_STATE_CHANGED, - EVENT_SIGNAL_STRENGTH_CHANGED, - EVENT_MESSAGE_WAITING_INDICATOR_CHANGED, - EVENT_CALL_FORWARDING_INDICATOR_CHANGED, - EVENT_CELL_LOCATION_CHANGED, - EVENT_CALL_STATE_CHANGED, - EVENT_DATA_CONNECTION_STATE_CHANGED, - EVENT_DATA_ACTIVITY_CHANGED, - EVENT_SIGNAL_STRENGTHS_CHANGED, - EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED, - EVENT_CELL_INFO_CHANGED, - EVENT_PRECISE_CALL_STATE_CHANGED, - EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED, - EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED, - EVENT_OEM_HOOK_RAW, - EVENT_SRVCC_STATE_CHANGED, - EVENT_CARRIER_NETWORK_CHANGED, - EVENT_VOICE_ACTIVATION_STATE_CHANGED, - EVENT_DATA_ACTIVATION_STATE_CHANGED, - EVENT_USER_MOBILE_DATA_STATE_CHANGED, - EVENT_DISPLAY_INFO_CHANGED, - EVENT_PHONE_CAPABILITY_CHANGED, - EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED, - EVENT_RADIO_POWER_STATE_CHANGED, - EVENT_EMERGENCY_NUMBER_LIST_CHANGED, - EVENT_CALL_DISCONNECT_CAUSE_CHANGED, - EVENT_CALL_ATTRIBUTES_CHANGED, - EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED, - EVENT_OUTGOING_EMERGENCY_CALL, - EVENT_OUTGOING_EMERGENCY_SMS, - EVENT_REGISTRATION_FAILURE, - EVENT_BARRING_INFO_CHANGED, - EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED, - EVENT_DATA_ENABLED_CHANGED - }) - @Retention(RetentionPolicy.SOURCE) - public @interface TelephonyEvent {} + public static final long LISTEN_PHYSICAL_CHANNEL_CONFIGURATION = 0x100000000L; /* * Subscription used to listen to the phone state changes @@ -1072,13 +504,9 @@ public class PhoneStateListener { /** * @hide */ - //TODO: The maxTargetSdk should be S if the build time tool updates it. @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - @UnsupportedAppUsage( - maxTargetSdk = Build.VERSION_CODES.R, - publicAlternatives = "Use {@code TelephonyManager#registerPhoneStateListener(" + - "Executor, PhoneStateListener)} instead") - public IPhoneStateListener callback; + @UnsupportedAppUsage + public final IPhoneStateListener callback; /** * Create a PhoneStateListener for the Phone with the default subscription. @@ -1135,737 +563,17 @@ public class PhoneStateListener { * The Executor must not be null. * * @param executor a non-null Executor that will execute callbacks for the PhoneStateListener. - * @deprecated Use - * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)} instead. */ - @Deprecated public PhoneStateListener(@NonNull Executor executor) { this(null, executor); } - /** - * @hide - */ - public void setExecutor(@NonNull @CallbackExecutor Executor executor) { - if (executor == null) { + private PhoneStateListener(Integer subId, Executor e) { + if (e == null) { throw new IllegalArgumentException("PhoneStateListener Executor must be non-null"); } - callback = new IPhoneStateListenerStub(this, executor); - } - - private PhoneStateListener(Integer subId, Executor e) { - setExecutor(e); mSubId = subId; - } - - /** - * Interface for service state listener. - */ - public interface ServiceStateChangedListener { - /** - * Callback invoked when device service state changes on the registered subscription. - * Note, the registration subscription ID comes from {@link TelephonyManager} object - * which registers PhoneStateListener by - * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. - * If this TelephonyManager object was created with - * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the - * subscription ID. Otherwise, this callback applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. - * - * The instance of {@link ServiceState} passed as an argument here will have various - * levels of location information stripped from it depending on the location permissions - * that your app holds. - * Only apps holding the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission will - * receive all the information in {@link ServiceState}. - * - * @see ServiceState#STATE_EMERGENCY_ONLY - * @see ServiceState#STATE_IN_SERVICE - * @see ServiceState#STATE_OUT_OF_SERVICE - * @see ServiceState#STATE_POWER_OFF - */ - @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) - public void onServiceStateChanged(@NonNull ServiceState serviceState); - } - - /** - * Interface for message waiting indicator listener. - */ - public interface MessageWaitingIndicatorChangedListener { - /** - * Callback invoked when the message-waiting indicator changes on the registered - * subscription. - * Note, the registration subscription ID comes from {@link TelephonyManager} object by - * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. - * If this TelephonyManager object was created with - * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the - * subscription ID. Otherwise, this callback applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. - */ - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) - public void onMessageWaitingIndicatorChanged(boolean mwi); - } - - /** - * Interface for call-forwarding indicator listener. - */ - public interface CallForwardingIndicatorChangedListener { - /** - * Callback invoked when the call-forwarding indicator changes on the registered - * subscription. - * Note, the registration subscription ID comes from {@link TelephonyManager} object - * which registers PhoneStateListener by - * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. - * If this TelephonyManager object was created with - * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the - * subscription ID. Otherwise, this callback applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. - */ - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) - public void onCallForwardingIndicatorChanged(boolean cfi); - } - - /** - * Interface for device cell location listener. - */ - public interface CellLocationChangedListener { - /** - * Callback invoked when device cell location changes on the registered subscription. - * Note, the registration subscription ID comes from {@link TelephonyManager} object - * which registers PhoneStateListener by - * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. - * If this TelephonyManager object was created with - * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the - * subscription ID. Otherwise, this callback applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. - */ - @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) - public void onCellLocationChanged(@NonNull CellLocation location); - } - - /** - * Interface for call state listener. - */ - public interface CallStateChangedListener { - /** - * Callback invoked when device call state changes. - * <p> - * Reports the state of Telephony (mobile) calls on the device for the registered s - * ubscription. - * <p> - * Note: the registration subscription ID comes from {@link TelephonyManager} object - * which registers PhoneStateListener by - * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. - * If this TelephonyManager object was created with - * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the - * subscription ID. Otherwise, this callback applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. - * <p> - * Note: The state returned here may differ from that returned by - * {@link TelephonyManager#getCallState()}. Receivers of this callback should be aware that - * calling {@link TelephonyManager#getCallState()} from within this callback may return a - * different state than the callback reports. - * - * @param state call state - * @param phoneNumber call phone number. If application does not have - * {@link android.Manifest.permission#READ_CALL_LOG} permission or carrier - * privileges (see {@link TelephonyManager#hasCarrierPrivileges}), an empty string will be - * passed as an argument. - */ - @RequiresPermission(android.Manifest.permission.READ_CALL_LOG) - public void onCallStateChanged(@CallState int state, @Nullable String phoneNumber); - } - - /** - * Interface for data connection state listener. - */ - public interface DataConnectionStateChangedListener { - /** - * Callback invoked when connection state changes on the registered subscription. - * Note, the registration subscription ID comes from {@link TelephonyManager} object - * which registers PhoneStateListener by - * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. - * If this TelephonyManager object was created with - * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the - * subscription ID. Otherwise, this callback applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. - * - * @see TelephonyManager#DATA_DISCONNECTED - * @see TelephonyManager#DATA_CONNECTING - * @see TelephonyManager#DATA_CONNECTED - * @see TelephonyManager#DATA_SUSPENDED - * - * @param state is the current state of data connection. - * @param networkType is the current network type of data connection. - */ - @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) - public void onDataConnectionStateChanged(@DataState int state, - @NetworkType int networkType); - } - - /** - * Interface for data activity state listener. - */ - public interface DataActivityListener { - /** - * Callback invoked when data activity state changes on the registered subscription. - * Note, the registration subscription ID comes from {@link TelephonyManager} object - * which registers PhoneStateListener by - * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. - * If this TelephonyManager object was created with - * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the - * subscription ID. Otherwise, this callback applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. - * - * @see TelephonyManager#DATA_ACTIVITY_NONE - * @see TelephonyManager#DATA_ACTIVITY_IN - * @see TelephonyManager#DATA_ACTIVITY_OUT - * @see TelephonyManager#DATA_ACTIVITY_INOUT - * @see TelephonyManager#DATA_ACTIVITY_DORMANT - */ - @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) - public void onDataActivity(@DataActivityType int direction); - } - - /** - * Interface for network signal strengths listener. - */ - public interface SignalStrengthsChangedListener { - /** - * Callback invoked when network signal strengths changes on the registered subscription. - * Note, the registration subscription ID comes from {@link TelephonyManager} object - * which registers PhoneStateListener by - * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. - * If this TelephonyManager object was created with - * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the - * subscription ID. Otherwise, this callback applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. - */ - @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) - public void onSignalStrengthsChanged(@NonNull SignalStrength signalStrength); - } - - /** - * Interface for network signal strengths listener which always reported from modem. - */ - public interface AlwaysReportedSignalStrengthsChangedListener { - /** - * Callback always invoked from modem when network signal strengths changes on the - * registered subscription. - * Note, the registration subscription ID comes from {@link TelephonyManager} object - * which registers PhoneStateListener by - * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. - * If this TelephonyManager object was created with - * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the - * subscription ID. Otherwise, this callback applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. - */ - @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) - public void onSignalStrengthsChanged(@NonNull SignalStrength signalStrength); - } - - /** - * Interface for cell info listener. - */ - public interface CellInfoChangedListener { - /** - * Callback invoked when a observed cell info has changed or new cells have been added - * or removed on the registered subscription. - * Note, the registration subscription ID s from {@link TelephonyManager} object - * which registersPhoneStateListener by - * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. - * If this TelephonyManager object was created with - * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the - * subscription ID. Otherwise, this callback applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. - * - * @param cellInfo is the list of currently visible cells. - */ - @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) - public void onCellInfoChanged(@NonNull List<CellInfo> cellInfo); - } - - /** - * Interface for precise device call state listener. - * - * @hide - */ - @SystemApi - public interface PreciseCallStateChangedListener { - /** - * Callback invoked when precise device call state changes on the registered subscription. - * Note, the registration subscription ID comes from {@link TelephonyManager} object - * which registers PhoneStateListener by - * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. - * If this TelephonyManager object was created with - * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the - * subscription ID. Otherwise, this callback applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. - * - * @param callState {@link PreciseCallState} - */ - @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) - public void onPreciseCallStateChanged(@NonNull PreciseCallState callState); - } - - /** - * Interface for call disconnect cause listener. - */ - public interface CallDisconnectCauseChangedListener { - /** - * Callback invoked when call disconnect cause changes on the registered subscription. - * Note, the registration subscription ID comes from {@link TelephonyManager} object - * which registers PhoneStateListener by - * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. - * If this TelephonyManager object was created with - * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the - * subscription ID. Otherwise, this callback applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. - * - * @param disconnectCause {@link DisconnectCause}. - * @param preciseDisconnectCause {@link PreciseDisconnectCause}. - */ - @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) - public void onCallDisconnectCauseChanged(@DisconnectCauses int disconnectCause, - @PreciseDisconnectCauses int preciseDisconnectCause); - } - - /** - * Interface for IMS call disconnect cause listener. - */ - public interface ImsCallDisconnectCauseChangedListener { - /** - * Callback invoked when IMS call disconnect cause changes on the registered subscription. - * Note, the registration subscription ID comes from {@link TelephonyManager} object - * which registers PhoneStateListener by - * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. - * If this TelephonyManager object was created with - * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the - * subscription ID. Otherwise, this callback applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. - * - * @param imsReasonInfo {@link ImsReasonInfo} contains details on why IMS call failed. - * - */ - @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) - public void onImsCallDisconnectCauseChanged(@NonNull ImsReasonInfo imsReasonInfo); - } - - /** - * Interface for precise data connection state listener. - */ - public interface PreciseDataConnectionStateChangedListener { - /** - * Callback providing update about the default/internet data connection on the registered - * subscription. - * - * Note, the registration subscription ID comes from {@link TelephonyManager} object - * which registers PhoneStateListener by - * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. - * If this TelephonyManager object was created with - * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the - * subscription ID. Otherwise, this callback applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. - * - * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} - * or the calling app has carrier privileges - * (see {@link TelephonyManager#hasCarrierPrivileges}). - * - * @param dataConnectionState {@link PreciseDataConnectionState} - */ - @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) - public void onPreciseDataConnectionStateChanged( - @NonNull PreciseDataConnectionState dataConnectionState); - } - - /** - * Interface for Single Radio Voice Call Continuity listener. - * - * @hide - */ - @SystemApi - public interface SrvccStateChangedListener { - /** - * Callback invoked when there has been a change in the Single Radio Voice Call Continuity - * (SRVCC) state for the currently active call on the registered subscription. - * - * Note, the registration subscription ID comes from {@link TelephonyManager} object - * which registers PhoneStateListener by - * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. - * If this TelephonyManager object was created with - * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the - * subscription ID. Otherwise, this callback applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. - */ - @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - public void onSrvccStateChanged(@SrvccState int srvccState); - } - - /** - * Interface for SIM voice activation state listener. - * - * @hide - */ - @SystemApi - public interface VoiceActivationStateChangedListener { - /** - * Callback invoked when the SIM voice activation state has changed on the registered - * subscription. - * Note, the registration subscription ID comes from {@link TelephonyManager} object - * which registers PhoneStateListener by - * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. - * If this TelephonyManager object was created with - * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the - * subscription ID. Otherwise, this callback applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. - * - * @param state is the current SIM voice activation state - */ - @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - public void onVoiceActivationStateChanged(@SimActivationState int state); - - } - - /** - * Interface for SIM data activation state listener. - */ - public interface DataActivationStateChangedListener { - /** - * Callback invoked when the SIM data activation state has changed on the registered - * subscription. - * Note, the registration subscription ID comes from {@link TelephonyManager} object - * which registers PhoneStateListener by - * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. - * If this TelephonyManager object was created with - * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the - * subscription ID. Otherwise, this callback applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. - * - * @param state is the current SIM data activation state - */ - @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) - public void onDataActivationStateChanged(@SimActivationState int state); - } - - /** - * Interface for user mobile data state listener. - */ - public interface UserMobileDataStateChangedListener { - /** - * Callback invoked when the user mobile data state has changed on the registered - * subscription. - * Note, the registration subscription ID comes from {@link TelephonyManager} object - * which registers PhoneStateListener by - * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. - * If this TelephonyManager object was created with - * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the - * subscription ID. Otherwise, this callback applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. - * - * @param enabled indicates whether the current user mobile data state is enabled or - * disabled. - */ - @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) - public void onUserMobileDataStateChanged(boolean enabled); - } - - /** - * Interface for display info listener. - */ - public interface DisplayInfoChangedListener { - /** - * Callback invoked when the display info has changed on the registered subscription. - * <p> The {@link TelephonyDisplayInfo} contains status information shown to the user - * based on carrier policy. - * - * @param telephonyDisplayInfo The display information. - */ - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) - public void onDisplayInfoChanged(@NonNull TelephonyDisplayInfo telephonyDisplayInfo); - } - - /** - * Interface for the current emergency number list listener. - */ - public interface EmergencyNumberListChangedListener { - /** - * Callback invoked when the current emergency number list has changed on the registered - * subscription. - * - * Note, the registered subscription is associated with {@link TelephonyManager} object - * on which - * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)} - * was called. - * If this TelephonyManager object was created with - * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the - * given subscription ID. Otherwise, this callback applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. - * - * @param emergencyNumberList Map associating all active subscriptions on the device with - * the list of emergency numbers originating from that - * subscription. - * If there are no active subscriptions, the map will contain a - * single entry with - * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} as - * the key and a list of emergency numbers as the value. If no - * emergency number information is available, the value will be - * empty. - */ - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) - public void onEmergencyNumberListChanged( - @NonNull Map<Integer, List<EmergencyNumber>> emergencyNumberList); - } - - /** - * Interface for outgoing emergency call listener. - * - * @hide - */ - @SystemApi - public interface OutgoingEmergencyCallListener { - /** - * Callback invoked when an outgoing call is placed to an emergency number. - * - * This method will be called when an emergency call is placed on any subscription - * (including the no-SIM case), regardless of which subscription this listener was - * registered on. - * - * The default implementation of this method calls - * {@link #onOutgoingEmergencyCall(EmergencyNumber)} for backwards compatibility purposes. - * Do not call {@code super(...)} from within your implementation unless you want - * {@link #onOutgoingEmergencyCall(EmergencyNumber)} to be called as well. - * - * @param placedEmergencyNumber The {@link EmergencyNumber} the emergency call was - * placed to. - * @param subscriptionId The subscription ID used to place the emergency call. If the - * emergency call was placed without a valid subscription - * (e.g. when there are no SIM cards in the device), this will be - * equal to {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}. - */ - @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) - public void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber, - int subscriptionId); - } - - /** - * Interface for outgoing emergency sms listener. - * - * @hide - */ - @SystemApi - public interface OutgoingEmergencySmsListener { - /** - * Smsback invoked when an outgoing sms is sent to an emergency number. - * - * This method will be called when an emergency sms is sent on any subscription, - * regardless of which subscription this listener was registered on. - * - * The default implementation of this method calls - * {@link #onOutgoingEmergencySms(EmergencyNumber)} for backwards compatibility purposes. Do - * not call {@code super(...)} from within your implementation unless you want - * {@link #onOutgoingEmergencySms(EmergencyNumber)} to be called as well. - * - * @param sentEmergencyNumber The {@link EmergencyNumber} the emergency sms was sent to. - * @param subscriptionId The subscription ID used to send the emergency sms. - */ - @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) - public void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber, - int subscriptionId); - } - - /** - * Interface for phone capability listener. - * - */ - public interface PhoneCapabilityChangedListener { - /** - * Callback invoked when phone capability changes. - * Note, this callback triggers regardless of registered subscription. - * - * @param capability the new phone capability - */ - @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) - public void onPhoneCapabilityChanged(@NonNull PhoneCapability capability); - } - - /** - * Interface for active data subscription ID listener. - */ - public interface ActiveDataSubscriptionIdChangedListener { - /** - * Callback invoked when active data subscription ID changes. - * Note, this callback triggers regardless of registered subscription. - * - * @param subId current subscription used to setup Cellular Internet data. - * For example, it could be the current active opportunistic subscription - * in use, or the subscription user selected as default data subscription in - * DSDS mode. - */ - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) - public void onActiveDataSubscriptionIdChanged(int subId); - } - - /** - * Interface for modem radio power state listener. - * - * @hide - */ - @SystemApi - public interface RadioPowerStateChangedListener { - /** - * Callback invoked when modem radio power state changes on the registered subscription. - * Note, the registration subscription ID comes from {@link TelephonyManager} object - * which registers PhoneStateListener by - * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. - * If this TelephonyManager object was created with - * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the - * subscription ID. Otherwise, this callback applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. - * - * @param state the modem radio power state - */ - @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - public void onRadioPowerStateChanged(@RadioPowerState int state); - } - - /** - * Interface for carrier network listener. - */ - public interface CarrierNetworkChangeListener { - /** - * Callback invoked when telephony has received notice from a carrier - * app that a network action that could result in connectivity loss - * has been requested by an app using - * {@link android.service.carrier.CarrierService#notifyCarrierNetworkChange(boolean)} - * - * This is optional and is only used to allow the system to provide alternative UI while - * telephony is performing an action that may result in intentional, temporary network - * lack of connectivity. - * - * Note, this callback is pinned to the registered subscription and will be invoked when - * the notifying carrier app has carrier privilege rule on the registered - * subscription. {@link android.telephony.TelephonyManager#hasCarrierPrivileges} - * - * @param active If the carrier network change is or shortly will be active, - * {@code true} indicate that showing alternative UI, {@code false} otherwise. - */ - public void onCarrierNetworkChange(boolean active); - } - - /** - * Interface for registration failures listener. - */ - public interface RegistrationFailedListener { - /** - * Report that Registration or a Location/Routing/Tracking Area update has failed. - * - * <p>Indicate whenever a registration procedure, including a location, routing, or tracking - * area update fails. This includes procedures that do not necessarily result in a change of - * the modem's registration status. If the modem's registration status changes, that is - * reflected in the onNetworkStateChanged() and subsequent - * get{Voice/Data}RegistrationState(). - * - * <p>Because registration failures are ephemeral, this callback is not sticky. - * Registrants will not receive the most recent past value when registering. - * - * @param cellIdentity the CellIdentity, which must include the globally unique identifier - * for the cell (for example, all components of the CGI or ECGI). - * @param chosenPlmn a 5 or 6 digit alphanumeric PLMN (MCC|MNC) among those broadcast by the - * cell that was chosen for the failed registration attempt. - * @param domain DOMAIN_CS, DOMAIN_PS or both in case of a combined procedure. - * @param causeCode the primary failure cause code of the procedure. - * For GSM/UMTS (MM), values are in TS 24.008 Sec 10.5.95 - * For GSM/UMTS (GMM), values are in TS 24.008 Sec 10.5.147 - * For LTE (EMM), cause codes are TS 24.301 Sec 9.9.3.9 - * For NR (5GMM), cause codes are TS 24.501 Sec 9.11.3.2 - * Integer.MAX_VALUE if this value is unused. - * @param additionalCauseCode the cause code of any secondary/combined procedure - * if appropriate. For UMTS, if a combined attach succeeds for - * PS only, then the GMM cause code shall be included as an - * additionalCauseCode. For LTE (ESM), cause codes are in - * TS 24.301 9.9.4.4. Integer.MAX_VALUE if this value is unused. - */ - @RequiresPermission(allOf = { - Manifest.permission.READ_PRECISE_PHONE_STATE, - Manifest.permission.ACCESS_FINE_LOCATION - }) - public void onRegistrationFailed(@NonNull CellIdentity cellIdentity, - @NonNull String chosenPlmn, @Domain int domain, - int causeCode, int additionalCauseCode); - } - - /** - * Interface for call attributes listener. - * - * @hide - */ - @SystemApi - public interface CallAttributesChangedListener { - /** - * Callback invoked when the call attributes changes on the registered subscription. - * Note, the registration subscription ID comes from {@link TelephonyManager} object - * which registers PhoneStateListener by - * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. - * If this TelephonyManager object was created with - * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the - * subscription ID. Otherwise, this callback applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. - * - * @param callAttributes the call attributes - */ - @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) - void onCallAttributesChanged(@NonNull CallAttributes callAttributes); - } - - /** - * Interface for barring information listener. - */ - public interface BarringInfoChangedListener { - /** - * Report updated barring information for the current camped/registered cell. - * - * <p>Barring info is provided for all services applicable to the current camped/registered - * cell, for the registered PLMN and current access class/access category. - * - * @param barringInfo for all services on the current cell. - * @see android.telephony.BarringInfo - */ - @RequiresPermission(allOf = { - Manifest.permission.READ_PRECISE_PHONE_STATE, - Manifest.permission.ACCESS_FINE_LOCATION - }) - public void onBarringInfoChanged(@NonNull BarringInfo barringInfo); - } - - /** - * Interface for current physical channel configuration listener. - */ - public interface PhysicalChannelConfigChangedListener { - /** - * Callback invoked when the current physical channel configuration has changed - * - * @param configs List of the current {@link PhysicalChannelConfig}s - */ - @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) - public void onPhysicalChannelConfigChanged(@NonNull List<PhysicalChannelConfig> configs); - } - - /** - * Interface for data enabled listener. - * - * @hide - */ - @SystemApi - public interface DataEnabledChangedListener { - /** - * Callback invoked when the data enabled changes. - * - * @param enabled {@code true} if data is enabled, otherwise disabled. - * @param reason Reason for data enabled/disabled. - * See {@link TelephonyManager.DataEnabledReason}. - */ - @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) - public void onDataEnabledChanged(boolean enabled, - @DataEnabledReason int reason); + callback = new IPhoneStateListenerStub(this, e); } /** @@ -1999,7 +707,6 @@ public class PhoneStateListener { * same as above, but with the network type. Both called. */ public void onDataConnectionStateChanged(int state, int networkType) { - // default implementation empty } /** @@ -2047,7 +754,6 @@ public class PhoneStateListener { * @param cellInfo is the list of currently visible cells. */ public void onCellInfoChanged(List<CellInfo> cellInfo) { - // default implementation empty } /** @@ -2061,7 +767,7 @@ public class PhoneStateListener { * @param callState {@link PreciseCallState} * @hide */ - @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) + @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE)) @SystemApi public void onPreciseCallStateChanged(@NonNull PreciseCallState callState) { // default implementation empty @@ -2080,9 +786,9 @@ public class PhoneStateListener { * @param preciseDisconnectCause {@link PreciseDisconnectCause}. * */ - @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) - public void onCallDisconnectCauseChanged(@DisconnectCauses int disconnectCause, - @PreciseDisconnectCauses int preciseDisconnectCause) { + @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE)) + public void onCallDisconnectCauseChanged(@Annotation.DisconnectCauses int disconnectCause, + int preciseDisconnectCause) { // default implementation empty } @@ -2098,7 +804,7 @@ public class PhoneStateListener { * @param imsReasonInfo {@link ImsReasonInfo} contains details on why IMS call failed. * */ - @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) + @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE)) public void onImsCallDisconnectCauseChanged(@NonNull ImsReasonInfo imsReasonInfo) { // default implementation empty } @@ -2120,7 +826,7 @@ public class PhoneStateListener { * * @param dataConnectionState {@link PreciseDataConnectionState} */ - @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresPermission((android.Manifest.permission.MODIFY_PHONE_STATE)) public void onPreciseDataConnectionStateChanged( @NonNull PreciseDataConnectionState dataConnectionState) { // default implementation empty @@ -2158,7 +864,6 @@ public class PhoneStateListener { */ @SystemApi public void onSrvccStateChanged(@SrvccState int srvccState) { - // default implementation empty } @@ -2177,7 +882,6 @@ public class PhoneStateListener { */ @SystemApi public void onVoiceActivationStateChanged(@SimActivationState int state) { - // default implementation empty } /** @@ -2194,7 +898,6 @@ public class PhoneStateListener { * @hide */ public void onDataActivationStateChanged(@SimActivationState int state) { - // default implementation empty } /** @@ -2222,7 +925,7 @@ public class PhoneStateListener { * * @param telephonyDisplayInfo The display information. */ - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @RequiresPermission((android.Manifest.permission.READ_PHONE_STATE)) public void onDisplayInfoChanged(@NonNull TelephonyDisplayInfo telephonyDisplayInfo) { // default implementation empty } @@ -2355,7 +1058,7 @@ public class PhoneStateListener { * @param capability the new phone capability * @hide */ - public void onPhoneCapabilityChanged(@NonNull PhoneCapability capability) { + public void onPhoneCapabilityChanged(PhoneCapability capability) { // default implementation empty } @@ -2399,8 +1102,7 @@ public class PhoneStateListener { * subId. Otherwise, this callback applies to * {@link SubscriptionManager#getDefaultSubscriptionId()}. * - * Requires permission {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} - * + * @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE} * @param state the modem radio power state * @hide */ @@ -2476,6 +1178,18 @@ public class PhoneStateListener { } /** + * Callback invoked when the current physical channel configuration has changed + * + * @param configs List of the current {@link PhysicalChannelConfig}s + * @hide + */ + @SystemApi + public void onPhysicalChannelConfigurationChanged( + @NonNull List<PhysicalChannelConfig> configs) { + // default implementation empty + } + + /** * The callback methods need to be called on the handler thread where * this object was created. If the binder did that for us it'd be nice. * @@ -2498,6 +1212,7 @@ public class PhoneStateListener { public void onServiceStateChanged(ServiceState serviceState) { PhoneStateListener psl = mPhoneStateListenerWeakRef.get(); if (psl == null) return; + Binder.withCleanCallingIdentity( () -> mExecutor.execute(() -> psl.onServiceStateChanged(serviceState))); } @@ -2505,6 +1220,7 @@ public class PhoneStateListener { public void onSignalStrengthChanged(int asu) { PhoneStateListener psl = mPhoneStateListenerWeakRef.get(); if (psl == null) return; + Binder.withCleanCallingIdentity( () -> mExecutor.execute(() -> psl.onSignalStrengthChanged(asu))); } @@ -2512,6 +1228,7 @@ public class PhoneStateListener { public void onMessageWaitingIndicatorChanged(boolean mwi) { PhoneStateListener psl = mPhoneStateListenerWeakRef.get(); if (psl == null) return; + Binder.withCleanCallingIdentity( () -> mExecutor.execute(() -> psl.onMessageWaitingIndicatorChanged(mwi))); } @@ -2519,6 +1236,7 @@ public class PhoneStateListener { public void onCallForwardingIndicatorChanged(boolean cfi) { PhoneStateListener psl = mPhoneStateListenerWeakRef.get(); if (psl == null) return; + Binder.withCleanCallingIdentity( () -> mExecutor.execute(() -> psl.onCallForwardingIndicatorChanged(cfi))); } @@ -2530,6 +1248,7 @@ public class PhoneStateListener { cellIdentity == null ? CellLocation.getEmpty() : cellIdentity.asCellLocation(); PhoneStateListener psl = mPhoneStateListenerWeakRef.get(); if (psl == null) return; + Binder.withCleanCallingIdentity( () -> mExecutor.execute(() -> psl.onCellLocationChanged(location))); } @@ -2537,6 +1256,7 @@ public class PhoneStateListener { public void onCallStateChanged(int state, String incomingNumber) { PhoneStateListener psl = mPhoneStateListenerWeakRef.get(); if (psl == null) return; + Binder.withCleanCallingIdentity( () -> mExecutor.execute(() -> psl.onCallStateChanged(state, incomingNumber))); } @@ -2544,6 +1264,7 @@ public class PhoneStateListener { public void onDataConnectionStateChanged(int state, int networkType) { PhoneStateListener psl = mPhoneStateListenerWeakRef.get(); if (psl == null) return; + if (state == TelephonyManager.DATA_DISCONNECTING && VMRuntime.getRuntime().getTargetSdkVersion() < Build.VERSION_CODES.R) { Binder.withCleanCallingIdentity(() -> mExecutor.execute( @@ -2564,6 +1285,7 @@ public class PhoneStateListener { public void onDataActivity(int direction) { PhoneStateListener psl = mPhoneStateListenerWeakRef.get(); if (psl == null) return; + Binder.withCleanCallingIdentity( () -> mExecutor.execute(() -> psl.onDataActivity(direction))); } @@ -2571,6 +1293,7 @@ public class PhoneStateListener { public void onSignalStrengthsChanged(SignalStrength signalStrength) { PhoneStateListener psl = mPhoneStateListenerWeakRef.get(); if (psl == null) return; + Binder.withCleanCallingIdentity( () -> mExecutor.execute(() -> psl.onSignalStrengthsChanged(signalStrength))); } @@ -2578,6 +1301,7 @@ public class PhoneStateListener { public void onCellInfoChanged(List<CellInfo> cellInfo) { PhoneStateListener psl = mPhoneStateListenerWeakRef.get(); if (psl == null) return; + Binder.withCleanCallingIdentity( () -> mExecutor.execute(() -> psl.onCellInfoChanged(cellInfo))); } @@ -2585,6 +1309,7 @@ public class PhoneStateListener { public void onPreciseCallStateChanged(PreciseCallState callState) { PhoneStateListener psl = mPhoneStateListenerWeakRef.get(); if (psl == null) return; + Binder.withCleanCallingIdentity( () -> mExecutor.execute(() -> psl.onPreciseCallStateChanged(callState))); } @@ -2592,6 +1317,7 @@ public class PhoneStateListener { public void onCallDisconnectCauseChanged(int disconnectCause, int preciseDisconnectCause) { PhoneStateListener psl = mPhoneStateListenerWeakRef.get(); if (psl == null) return; + Binder.withCleanCallingIdentity( () -> mExecutor.execute(() -> psl.onCallDisconnectCauseChanged( disconnectCause, preciseDisconnectCause))); @@ -2601,6 +1327,7 @@ public class PhoneStateListener { PreciseDataConnectionState dataConnectionState) { PhoneStateListener psl = mPhoneStateListenerWeakRef.get(); if (psl == null) return; + Binder.withCleanCallingIdentity( () -> mExecutor.execute( () -> psl.onPreciseDataConnectionStateChanged(dataConnectionState))); @@ -2618,6 +1345,7 @@ public class PhoneStateListener { public void onSrvccStateChanged(int state) { PhoneStateListener psl = mPhoneStateListenerWeakRef.get(); if (psl == null) return; + Binder.withCleanCallingIdentity( () -> mExecutor.execute(() -> psl.onSrvccStateChanged(state))); } @@ -2625,6 +1353,7 @@ public class PhoneStateListener { public void onVoiceActivationStateChanged(int activationState) { PhoneStateListener psl = mPhoneStateListenerWeakRef.get(); if (psl == null) return; + Binder.withCleanCallingIdentity( () -> mExecutor.execute( () -> psl.onVoiceActivationStateChanged(activationState))); @@ -2633,6 +1362,7 @@ public class PhoneStateListener { public void onDataActivationStateChanged(int activationState) { PhoneStateListener psl = mPhoneStateListenerWeakRef.get(); if (psl == null) return; + Binder.withCleanCallingIdentity( () -> mExecutor.execute( () -> psl.onDataActivationStateChanged(activationState))); @@ -2641,6 +1371,7 @@ public class PhoneStateListener { public void onUserMobileDataStateChanged(boolean enabled) { PhoneStateListener psl = mPhoneStateListenerWeakRef.get(); if (psl == null) return; + Binder.withCleanCallingIdentity( () -> mExecutor.execute( () -> psl.onUserMobileDataStateChanged(enabled))); @@ -2649,6 +1380,7 @@ public class PhoneStateListener { public void onDisplayInfoChanged(TelephonyDisplayInfo telephonyDisplayInfo) { PhoneStateListener psl = mPhoneStateListenerWeakRef.get(); if (psl == null) return; + Binder.withCleanCallingIdentity( () -> mExecutor.execute( () -> psl.onDisplayInfoChanged(telephonyDisplayInfo))); @@ -2657,6 +1389,7 @@ public class PhoneStateListener { public void onOemHookRawEvent(byte[] rawData) { PhoneStateListener psl = mPhoneStateListenerWeakRef.get(); if (psl == null) return; + Binder.withCleanCallingIdentity( () -> mExecutor.execute(() -> psl.onOemHookRawEvent(rawData))); } @@ -2664,6 +1397,7 @@ public class PhoneStateListener { public void onCarrierNetworkChange(boolean active) { PhoneStateListener psl = mPhoneStateListenerWeakRef.get(); if (psl == null) return; + Binder.withCleanCallingIdentity( () -> mExecutor.execute(() -> psl.onCarrierNetworkChange(active))); } @@ -2671,6 +1405,7 @@ public class PhoneStateListener { public void onEmergencyNumberListChanged(Map emergencyNumberList) { PhoneStateListener psl = mPhoneStateListenerWeakRef.get(); if (psl == null) return; + Binder.withCleanCallingIdentity( () -> mExecutor.execute( () -> psl.onEmergencyNumberListChanged(emergencyNumberList))); @@ -2680,6 +1415,7 @@ public class PhoneStateListener { int subscriptionId) { PhoneStateListener psl = mPhoneStateListenerWeakRef.get(); if (psl == null) return; + Binder.withCleanCallingIdentity( () -> mExecutor.execute( () -> psl.onOutgoingEmergencyCall(placedEmergencyNumber, @@ -2690,6 +1426,7 @@ public class PhoneStateListener { int subscriptionId) { PhoneStateListener psl = mPhoneStateListenerWeakRef.get(); if (psl == null) return; + Binder.withCleanCallingIdentity( () -> mExecutor.execute( () -> psl.onOutgoingEmergencySms(sentEmergencyNumber, subscriptionId))); @@ -2698,6 +1435,7 @@ public class PhoneStateListener { public void onPhoneCapabilityChanged(PhoneCapability capability) { PhoneStateListener psl = mPhoneStateListenerWeakRef.get(); if (psl == null) return; + Binder.withCleanCallingIdentity( () -> mExecutor.execute(() -> psl.onPhoneCapabilityChanged(capability))); } @@ -2705,6 +1443,7 @@ public class PhoneStateListener { public void onRadioPowerStateChanged(@RadioPowerState int state) { PhoneStateListener psl = mPhoneStateListenerWeakRef.get(); if (psl == null) return; + Binder.withCleanCallingIdentity( () -> mExecutor.execute(() -> psl.onRadioPowerStateChanged(state))); } @@ -2712,6 +1451,7 @@ public class PhoneStateListener { public void onCallAttributesChanged(CallAttributes callAttributes) { PhoneStateListener psl = mPhoneStateListenerWeakRef.get(); if (psl == null) return; + Binder.withCleanCallingIdentity( () -> mExecutor.execute(() -> psl.onCallAttributesChanged(callAttributes))); } @@ -2719,6 +1459,7 @@ public class PhoneStateListener { public void onActiveDataSubIdChanged(int subId) { PhoneStateListener psl = mPhoneStateListenerWeakRef.get(); if (psl == null) return; + Binder.withCleanCallingIdentity( () -> mExecutor.execute(() -> psl.onActiveDataSubscriptionIdChanged(subId))); } @@ -2726,51 +1467,44 @@ public class PhoneStateListener { public void onImsCallDisconnectCauseChanged(ImsReasonInfo disconnectCause) { PhoneStateListener psl = mPhoneStateListenerWeakRef.get(); if (psl == null) return; + Binder.withCleanCallingIdentity( () -> mExecutor.execute( () -> psl.onImsCallDisconnectCauseChanged(disconnectCause))); + } public void onRegistrationFailed(@NonNull CellIdentity cellIdentity, - @NonNull String chosenPlmn, int domain, int causeCode, int additionalCauseCode) { + @NonNull String chosenPlmn, int domain, + int causeCode, int additionalCauseCode) { PhoneStateListener psl = mPhoneStateListenerWeakRef.get(); if (psl == null) return; + Binder.withCleanCallingIdentity( () -> mExecutor.execute(() -> psl.onRegistrationFailed( - cellIdentity, chosenPlmn, domain, causeCode, additionalCauseCode))); + cellIdentity, chosenPlmn, domain, causeCode, additionalCauseCode))); // default implementation empty } public void onBarringInfoChanged(BarringInfo barringInfo) { PhoneStateListener psl = mPhoneStateListenerWeakRef.get(); if (psl == null) return; + Binder.withCleanCallingIdentity( () -> mExecutor.execute(() -> psl.onBarringInfoChanged(barringInfo))); } - public void onPhysicalChannelConfigChanged(List<PhysicalChannelConfig> configs) { + public void onPhysicalChannelConfigurationChanged(List<PhysicalChannelConfig> configs) { PhoneStateListener psl = mPhoneStateListenerWeakRef.get(); - PhysicalChannelConfigChangedListener listener = - (PhysicalChannelConfigChangedListener) mPhoneStateListenerWeakRef.get(); - if (listener == null) return; - Binder.withCleanCallingIdentity( - () -> mExecutor.execute(() -> listener.onPhysicalChannelConfigChanged( - configs))); - } - - public void onDataEnabledChanged(boolean enabled, @DataEnabledReason int reason) { - if ((mPhoneStateListenerWeakRef.get() instanceof DataEnabledChangedListener)) { - DataEnabledChangedListener listener = - (DataEnabledChangedListener) mPhoneStateListenerWeakRef.get(); - if (listener == null) return; + if (psl == null) return; - Binder.withCleanCallingIdentity( - () -> mExecutor.execute(() -> listener.onDataEnabledChanged( - enabled, reason))); - } + Binder.withCleanCallingIdentity( + () -> mExecutor.execute( + () -> psl.onPhysicalChannelConfigurationChanged(configs))); } } + private void log(String s) { Rlog.d(LOG_TAG, s); } diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java index c706e2179f19..24ed29a66654 100644 --- a/core/java/android/telephony/TelephonyRegistryManager.java +++ b/core/java/android/telephony/TelephonyRegistryManager.java @@ -15,7 +15,6 @@ */ package android.telephony; -import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -25,9 +24,6 @@ import android.compat.annotation.EnabledAfter; import android.content.Context; import android.os.Binder; import android.os.Build; -import android.os.Handler; -import android.os.HandlerExecutor; -import android.os.Looper; import android.os.RemoteException; import android.os.ServiceManager; import android.telephony.Annotation.CallState; @@ -41,7 +37,6 @@ import android.telephony.Annotation.SimActivationState; import android.telephony.Annotation.SrvccState; import android.telephony.emergency.EmergencyNumber; import android.telephony.ims.ImsReasonInfo; -import android.util.ArraySet; import android.util.Log; import com.android.internal.telephony.IOnSubscriptionsChangedListener; @@ -50,7 +45,6 @@ import com.android.internal.telephony.ITelephonyRegistry; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.concurrent.Executor; /** @@ -212,7 +206,7 @@ public class TelephonyRegistryManager { } /** - * To check the SDK version for {@link #listenWithEventList}. + * To check the SDK version for {@link #listenForSubscriber}. */ @ChangeId @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.P) @@ -224,23 +218,23 @@ public class TelephonyRegistryManager { * @param pkg Package name * @param featureId Feature ID * @param listener Listener providing callback - * @param events List events + * @param events Events * @param notifyNow Whether to notify instantly */ - public void listenWithEventList(int subId, @NonNull String pkg, @NonNull String featureId, - @NonNull PhoneStateListener listener, @NonNull int[] events, boolean notifyNow) { + public void listenForSubscriber(int subId, @NonNull String pkg, @NonNull String featureId, + @NonNull PhoneStateListener listener, long events, boolean notifyNow) { try { // subId from PhoneStateListener is deprecated Q on forward, use the subId from // TelephonyManager instance. Keep using subId from PhoneStateListener for pre-Q. if (Compatibility.isChangeEnabled(LISTEN_CODE_CHANGE)) { // Since mSubId in PhoneStateListener is deprecated from Q on forward, this is // the only place to set mSubId and its for "informational" only. - listener.mSubId = (events.length == 0) + listener.mSubId = (events == PhoneStateListener.LISTEN_NONE) ? SubscriptionManager.INVALID_SUBSCRIPTION_ID : subId; } else if (listener.mSubId != null) { subId = listener.mSubId; } - sRegistry.listenWithEventList( + sRegistry.listenForSubscriber( subId, pkg, featureId, listener.callback, events, notifyNow); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -777,349 +771,13 @@ public class TelephonyRegistryManager { * @param subId the subId * @param configs a list of {@link PhysicalChannelConfig}, the configs of physical channel. */ - public void notifyPhysicalChannelConfigForSubscriber( + public void notifyPhysicalChannelConfigurationForSubscriber( int subId, List<PhysicalChannelConfig> configs) { try { - sRegistry.notifyPhysicalChannelConfigForSubscriber(subId, configs); + sRegistry.notifyPhysicalChannelConfigurationForSubscriber(subId, configs); } catch (RemoteException ex) { // system server crash } } - public @NonNull Set<Integer> getEventsFromListener(@NonNull PhoneStateListener listener) { - - Set<Integer> eventList = new ArraySet<>(); - - if (listener instanceof PhoneStateListener.ServiceStateChangedListener) { - eventList.add(PhoneStateListener.EVENT_SERVICE_STATE_CHANGED); - } - - if (listener instanceof PhoneStateListener.MessageWaitingIndicatorChangedListener) { - eventList.add(PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED); - } - - if (listener instanceof PhoneStateListener.CallForwardingIndicatorChangedListener) { - eventList.add(PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED); - } - - if (listener instanceof PhoneStateListener.CellLocationChangedListener) { - eventList.add(PhoneStateListener.EVENT_CELL_LOCATION_CHANGED); - } - - if (listener instanceof PhoneStateListener.CallStateChangedListener) { - eventList.add(PhoneStateListener.EVENT_CALL_STATE_CHANGED); - } - - if (listener instanceof PhoneStateListener.DataConnectionStateChangedListener) { - eventList.add(PhoneStateListener.EVENT_DATA_CONNECTION_STATE_CHANGED); - } - - if (listener instanceof PhoneStateListener.DataActivityListener) { - eventList.add(PhoneStateListener.EVENT_DATA_ACTIVITY_CHANGED); - } - - if (listener instanceof PhoneStateListener.SignalStrengthsChangedListener) { - eventList.add(PhoneStateListener.EVENT_SIGNAL_STRENGTHS_CHANGED); - } - - if (listener instanceof PhoneStateListener.AlwaysReportedSignalStrengthsChangedListener) { - eventList.add(PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED); - } - - if (listener instanceof PhoneStateListener.CellInfoChangedListener) { - eventList.add(PhoneStateListener.EVENT_CELL_INFO_CHANGED); - } - - if (listener instanceof PhoneStateListener.PreciseCallStateChangedListener) { - eventList.add(PhoneStateListener.EVENT_PRECISE_CALL_STATE_CHANGED); - } - - if (listener instanceof PhoneStateListener.CallDisconnectCauseChangedListener) { - eventList.add(PhoneStateListener.EVENT_CALL_DISCONNECT_CAUSE_CHANGED); - } - - if (listener instanceof PhoneStateListener.ImsCallDisconnectCauseChangedListener) { - eventList.add(PhoneStateListener.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED); - } - - if (listener instanceof PhoneStateListener.PreciseDataConnectionStateChangedListener) { - eventList.add(PhoneStateListener.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED); - } - - if (listener instanceof PhoneStateListener.SrvccStateChangedListener) { - eventList.add(PhoneStateListener.EVENT_SRVCC_STATE_CHANGED); - } - - if (listener instanceof PhoneStateListener.VoiceActivationStateChangedListener) { - eventList.add(PhoneStateListener.EVENT_VOICE_ACTIVATION_STATE_CHANGED); - } - - if (listener instanceof PhoneStateListener.DataActivationStateChangedListener) { - eventList.add(PhoneStateListener.EVENT_DATA_ACTIVATION_STATE_CHANGED); - } - - if (listener instanceof PhoneStateListener.UserMobileDataStateChangedListener) { - eventList.add(PhoneStateListener.EVENT_USER_MOBILE_DATA_STATE_CHANGED); - } - - if (listener instanceof PhoneStateListener.DisplayInfoChangedListener) { - eventList.add(PhoneStateListener.EVENT_DISPLAY_INFO_CHANGED); - } - - if (listener instanceof PhoneStateListener.EmergencyNumberListChangedListener) { - eventList.add(PhoneStateListener.EVENT_EMERGENCY_NUMBER_LIST_CHANGED); - } - - if (listener instanceof PhoneStateListener.OutgoingEmergencyCallListener) { - eventList.add(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_CALL); - } - - if (listener instanceof PhoneStateListener.OutgoingEmergencySmsListener) { - eventList.add(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_SMS); - } - - if (listener instanceof PhoneStateListener.PhoneCapabilityChangedListener) { - eventList.add(PhoneStateListener.EVENT_PHONE_CAPABILITY_CHANGED); - } - - if (listener instanceof PhoneStateListener.ActiveDataSubscriptionIdChangedListener) { - eventList.add(PhoneStateListener.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED); - } - - if (listener instanceof PhoneStateListener.RadioPowerStateChangedListener) { - eventList.add(PhoneStateListener.EVENT_RADIO_POWER_STATE_CHANGED); - } - - if (listener instanceof PhoneStateListener.CarrierNetworkChangeListener) { - eventList.add(PhoneStateListener.EVENT_CARRIER_NETWORK_CHANGED); - } - - if (listener instanceof PhoneStateListener.RegistrationFailedListener) { - eventList.add(PhoneStateListener.EVENT_REGISTRATION_FAILURE); - } - - if (listener instanceof PhoneStateListener.CallAttributesChangedListener) { - eventList.add(PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED); - } - - if (listener instanceof PhoneStateListener.BarringInfoChangedListener) { - eventList.add(PhoneStateListener.EVENT_BARRING_INFO_CHANGED); - } - - if (listener instanceof PhoneStateListener.PhysicalChannelConfigChangedListener) { - eventList.add(PhoneStateListener.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED); - } - - if (listener instanceof PhoneStateListener.DataEnabledChangedListener) { - eventList.add(PhoneStateListener.EVENT_DATA_ENABLED_CHANGED); - } - - return eventList; - } - - private @NonNull Set<Integer> getEventsFromBitmask(int eventMask) { - - Set<Integer> eventList = new ArraySet<>(); - - if ((eventMask & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) { - eventList.add(PhoneStateListener.EVENT_SERVICE_STATE_CHANGED); - } - - if ((eventMask & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) { - eventList.add(PhoneStateListener.EVENT_SIGNAL_STRENGTH_CHANGED); - } - - if ((eventMask & PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) != 0) { - eventList.add(PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED); - } - - if ((eventMask & PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) != 0) { - eventList.add(PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED); - } - - if ((eventMask & PhoneStateListener.LISTEN_CELL_LOCATION) != 0) { - eventList.add(PhoneStateListener.EVENT_CELL_LOCATION_CHANGED); - } - - if ((eventMask & PhoneStateListener.LISTEN_CALL_STATE) != 0) { - eventList.add(PhoneStateListener.EVENT_CALL_STATE_CHANGED); - } - - if ((eventMask & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) { - eventList.add(PhoneStateListener.EVENT_DATA_CONNECTION_STATE_CHANGED); - } - - if ((eventMask & PhoneStateListener.LISTEN_DATA_ACTIVITY) != 0) { - eventList.add(PhoneStateListener.EVENT_DATA_ACTIVITY_CHANGED); - } - - if ((eventMask & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0) { - eventList.add(PhoneStateListener.EVENT_SIGNAL_STRENGTHS_CHANGED); - } - - if ((eventMask & PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) != 0) { - eventList.add(PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED); - } - - if ((eventMask & PhoneStateListener.LISTEN_CELL_INFO) != 0) { - eventList.add(PhoneStateListener.EVENT_CELL_INFO_CHANGED); - } - - if ((eventMask & PhoneStateListener.LISTEN_PRECISE_CALL_STATE) != 0) { - eventList.add(PhoneStateListener.EVENT_PRECISE_CALL_STATE_CHANGED); - } - - if ((eventMask & PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE) != 0) { - eventList.add(PhoneStateListener.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED); - } - - if ((eventMask & PhoneStateListener.LISTEN_DATA_CONNECTION_REAL_TIME_INFO) != 0) { - eventList.add(PhoneStateListener.EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED); - } - - if ((eventMask & PhoneStateListener.LISTEN_OEM_HOOK_RAW_EVENT) != 0) { - eventList.add(PhoneStateListener.EVENT_OEM_HOOK_RAW); - } - - if ((eventMask & PhoneStateListener.LISTEN_SRVCC_STATE_CHANGED) != 0) { - eventList.add(PhoneStateListener.EVENT_SRVCC_STATE_CHANGED); - } - - if ((eventMask & PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE) != 0) { - eventList.add(PhoneStateListener.EVENT_CARRIER_NETWORK_CHANGED); - } - - if ((eventMask & PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE) != 0) { - eventList.add(PhoneStateListener.EVENT_VOICE_ACTIVATION_STATE_CHANGED); - } - - if ((eventMask & PhoneStateListener.LISTEN_DATA_ACTIVATION_STATE) != 0) { - eventList.add(PhoneStateListener.EVENT_DATA_ACTIVATION_STATE_CHANGED); - } - - if ((eventMask & PhoneStateListener.LISTEN_USER_MOBILE_DATA_STATE) != 0) { - eventList.add(PhoneStateListener.EVENT_USER_MOBILE_DATA_STATE_CHANGED); - } - - if ((eventMask & PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED) != 0) { - eventList.add(PhoneStateListener.EVENT_DISPLAY_INFO_CHANGED); - } - - if ((eventMask & PhoneStateListener.LISTEN_PHONE_CAPABILITY_CHANGE) != 0) { - eventList.add(PhoneStateListener.EVENT_PHONE_CAPABILITY_CHANGED); - } - - if ((eventMask & PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE) != 0) { - eventList.add(PhoneStateListener.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED); - } - - if ((eventMask & PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED) != 0) { - eventList.add(PhoneStateListener.EVENT_RADIO_POWER_STATE_CHANGED); - } - - if ((eventMask & PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST) != 0) { - eventList.add(PhoneStateListener.EVENT_EMERGENCY_NUMBER_LIST_CHANGED); - } - - if ((eventMask & PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES) != 0) { - eventList.add(PhoneStateListener.EVENT_CALL_DISCONNECT_CAUSE_CHANGED); - } - - if ((eventMask & PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED) != 0) { - eventList.add(PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED); - } - - if ((eventMask & PhoneStateListener.LISTEN_IMS_CALL_DISCONNECT_CAUSES) != 0) { - eventList.add(PhoneStateListener.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED); - } - - if ((eventMask & PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_CALL) != 0) { - eventList.add(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_CALL); - } - - if ((eventMask & PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_SMS) != 0) { - eventList.add(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_SMS); - } - - if ((eventMask & PhoneStateListener.LISTEN_REGISTRATION_FAILURE) != 0) { - eventList.add(PhoneStateListener.EVENT_REGISTRATION_FAILURE); - } - - if ((eventMask & PhoneStateListener.LISTEN_BARRING_INFO) != 0) { - eventList.add(PhoneStateListener.EVENT_BARRING_INFO_CHANGED); - } - return eventList; - - } - - /** - * Registers a listener object to receive notification of changes - * in specified telephony states. - * <p> - * To register a listener, pass a {@link PhoneStateListener} which implements - * interfaces of events. For example, - * FakeServiceStateChangedListener extends {@link PhoneStateListener} implements - * {@link PhoneStateListener.ServiceStateChangedListener}. - * - * At registration, and when a specified telephony state changes, the telephony manager invokes - * the appropriate callback method on the listener object and passes the current (updated) - * values. - * <p> - * - * If this TelephonyManager object has been created with - * {@link TelephonyManager#createForSubscriptionId}, applies to the given subId. - * Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}. - * To listen events for multiple subIds, pass a separate listener object to - * each TelephonyManager object created with {@link TelephonyManager#createForSubscriptionId}. - * - * Note: if you call this method while in the middle of a binder transaction, you <b>must</b> - * call {@link android.os.Binder#clearCallingIdentity()} before calling this method. A - * {@link SecurityException} will be thrown otherwise. - * - * This API should be used sparingly -- large numbers of listeners will cause system - * instability. If a process has registered too many listeners without unregistering them, it - * may encounter an {@link IllegalStateException} when trying to register more listeners. - * - * @param listener The {@link PhoneStateListener} object to register. - */ - public void registerPhoneStateListener(@NonNull @CallbackExecutor Executor executor, int subId, - String pkgName, String attributionTag, @NonNull PhoneStateListener listener, - boolean notifyNow) { - registerPhoneStateListener(executor, subId, pkgName, attributionTag, listener, - getEventsFromListener(listener), notifyNow); - } - - public void registerPhoneStateListenerWithEvents(int subId, String pkgName, - String attributionTag, @NonNull PhoneStateListener listener, int events, - boolean notifyNow) { - if (Looper.myLooper() == null) { - Looper.prepare(); - } - - registerPhoneStateListener(new HandlerExecutor(new Handler(Looper.myLooper())), - subId, pkgName, attributionTag, listener, getEventsFromBitmask(events), notifyNow); - } - - private void registerPhoneStateListener(@NonNull @CallbackExecutor Executor executor, int subId, - String pkgName, String attributionTag, @NonNull PhoneStateListener listener, - @NonNull Set<Integer> events, boolean notifyNow) { - if (listener == null) { - throw new IllegalStateException("telephony service is null."); - } - - listener.setExecutor(executor); - listenWithEventList(subId, pkgName, attributionTag, listener, - events.stream().mapToInt(i -> i).toArray(), notifyNow); - } - - /** - * Unregister an existing {@link PhoneStateListener}. - * - * @param listener The {@link PhoneStateListener} object to unregister. - */ - public void unregisterPhoneStateListener(int subId, String pkgName, String attributionTag, - @NonNull PhoneStateListener listener, - boolean notifyNow) { - listenWithEventList(subId, pkgName, attributionTag, listener, new int[0], notifyNow); - } } diff --git a/core/proto/android/bluetooth/a2dp/enums.proto b/core/java/android/uwb/AngleOfArrivalSupport.aidl index 5a025bdd6c10..57666ff8bca9 100644 --- a/core/proto/android/bluetooth/a2dp/enums.proto +++ b/core/java/android/uwb/AngleOfArrivalSupport.aidl @@ -1,5 +1,5 @@ /* - * Copyright 2018 The Android Open Source Project + * Copyright 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,22 +14,31 @@ * limitations under the License. */ -syntax = "proto2"; -package android.bluetooth.a2dp; +package android.uwb; -option java_outer_classname = "BluetoothA2dpProtoEnums"; -option java_multiple_files = true; +/** + * @hide + */ +@Backing(type="int") +enum AngleOfArrivalSupport { + /** + * The device does not support angle of arrival + */ + NONE, -// A2dp playback state enum, defined from: -// frameworks/base/core/java/android/bluetooth/BluetoothA2dp.java -enum PlaybackStateEnum { - PLAYBACK_STATE_UNKNOWN = 0; - PLAYBACK_STATE_PLAYING = 10; - PLAYBACK_STATE_NOT_PLAYING = 11; -} + /** + * The device supports planar angle of arrival + */ + TWO_DIMENSIONAL, -enum AudioCodingModeEnum { - AUDIO_CODING_MODE_UNKNOWN = 0; - AUDIO_CODING_MODE_HARDWARE = 1; - AUDIO_CODING_MODE_SOFTWARE = 2; + /** + * The device does supports three dimensional angle of arrival with hemispherical azimuth angles + */ + THREE_DIMENSIONAL_HEMISPHERICAL, + + /** + * The device does supports three dimensional angle of arrival with full azimuth angles + */ + THREE_DIMENSIONAL_SPHERICAL, } + diff --git a/core/java/android/uwb/CloseReason.aidl b/core/java/android/uwb/CloseReason.aidl new file mode 100644 index 000000000000..bef129e2c1c7 --- /dev/null +++ b/core/java/android/uwb/CloseReason.aidl @@ -0,0 +1,58 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.uwb; + +/** + * @hide + */ +@Backing(type="int") +enum CloseReason { + /** + * Unknown reason + */ + UNKNOWN, + + /** + * A local API call triggered the close, such as a call to + * IUwbAdapter.stopRanging. + */ + LOCAL_API, + + /** + * The maximum number of sessions has been reached. This error may be generated + * for an active session if a higher priority session begins. + */ + MAX_SESSIONS_REACHED, + + /** + * The system state has changed resulting in the session ending (e.g. the user + * disables UWB, or the user's locale changes and an active channel is no longer + * permitted to be used). + */ + SYSTEM_POLICY, + + /** + * The remote device has requested to terminate the session + */ + REMOTE_REQUEST, + + /** + * The session was closed for a protocol specific reason + */ + PROTOCOL_SPECIFIC, +} + diff --git a/core/java/android/uwb/IUwbAdapter.aidl b/core/java/android/uwb/IUwbAdapter.aidl new file mode 100644 index 000000000000..d29ed34804f1 --- /dev/null +++ b/core/java/android/uwb/IUwbAdapter.aidl @@ -0,0 +1,170 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.uwb; + +import android.os.PersistableBundle; +import android.uwb.AngleOfArrivalSupport; +import android.uwb.IUwbAdapterStateCallbacks; +import android.uwb.IUwbRangingCallbacks; +import android.uwb.SessionHandle; + +/** + * @hide + */ +interface IUwbAdapter { + /* + * Register the callbacks used to notify the framework of events and data + * + * The provided callback's IUwbAdapterStateCallbacks#onAdapterStateChanged + * function must be called immediately following registration with the current + * state of the UWB adapter. + * + * @param callbacks callback to provide range and status updates to the framework + */ + void registerAdapterStateCallbacks(in IUwbAdapterStateCallbacks adapterStateCallbacks); + + /* + * Unregister the callbacks used to notify the framework of events and data + * + * Calling this function with an unregistered callback is a no-op + * + * @param callbacks callback to unregister + */ + void unregisterAdapterStateCallbacks(in IUwbAdapterStateCallbacks callbacks); + + /** + * Returns true if ranging is supported, false otherwise + */ + boolean isRangingSupported(); + + /** + * Get the angle of arrival supported by this device + * + * @return the angle of arrival type supported + */ + AngleOfArrivalSupport getAngleOfArrivalSupport(); + + /** + * Generates a list of the supported 802.15.4z channels + * + * The list must be prioritized in the order of preferred channel usage. + * + * The list must only contain channels that are permitted to be used in the + * device's current location. + * + * @return an array of support channels on the device for the current location. + */ + int[] getSupportedChannels(); + + /** + * Generates a list of the supported 802.15.4z preamble codes + * + * The list must be prioritized in the order of preferred preamble usage. + * + * The list must only contain preambles that are permitted to be used in the + * device's current location. + * + * @return an array of supported preambles on the device for the current + * location. + */ + int[] getSupportedPreambleCodes(); + + /** + * Get the accuracy of the ranging timestamps + * + * @return accuracy of the ranging timestamps in nanoseconds + */ + long getTimestampResolutionNanos(); + + /** + * Get the supported number of simultaneous ranging sessions + * + * @return the supported number of simultaneous ranging sessions + */ + int getMaxSimultaneousSessions(); + + /** + * Get the maximum number of remote devices per session + * + * @return the maximum number of remote devices supported in a single session + */ + int getMaxRemoteDevicesPerSession(); + + /** + * Provides the capabilities and features of the device + * + * @return specification specific capabilities and features of the device + */ + PersistableBundle getSpecificationInfo(); + + /** + * Request to start a new ranging session + * + * This function must return before calling IUwbAdapterCallbacks + * #onRangingStarted, #onRangingClosed, or #onRangingResult. + * + * A ranging session does not need to be started before returning. + * + * IUwbAdapterCallbacks#onRangingStarted must be called within + * RANGING_SESSION_START_THRESHOLD_MS milliseconds of #startRanging being called + * if the ranging session is scheduled to start successfully. + * + * IUwbAdapterCallbacks#onRangingStartFailed must be called within + * RANGING_SESSION_START_THRESHOLD_MS milliseconds of #startRanging being called + * if the ranging session fails to be scheduled to start successfully. + * + * @param rangingCallbacks the callbacks used to deliver ranging information + * @param parameters the configuration to use for ranging + * @return a SessionHandle used to identify this ranging request + */ + SessionHandle startRanging(in IUwbRangingCallbacks rangingCallbacks, + in PersistableBundle parameters); + + /** + * Stop and close ranging for the session associated with the given handle + * + * Calling with an invalid handle or a handle that has already been closed + * is a no-op. + * + * IUwbAdapterCallbacks#onRangingClosed must be called within + * RANGING_SESSION_CLOSE_THRESHOLD_MS of #stopRanging being called. + * + * @param sessionHandle the session handle to stop ranging for + */ + void closeRanging(in SessionHandle sessionHandle); + + /** + * The maximum allowed time to start a ranging session. + */ + const int RANGING_SESSION_START_THRESHOLD_MS = 3000; // Value TBD + + /** + * The maximum allowed time to notify the framework that a session has been + * closed. + */ + const int RANGING_SESSION_CLOSE_THRESHOLD_MS = 3000; // Value TBD + + /** + * Ranging scheduling time unit (RSTU) for High Rate Pulse (HRP) PHY + */ + const int HIGH_RATE_PULSE_CHIRPS_PER_RSTU = 416; + + /** + * Ranging scheduling time unit (RSTU) for Low Rate Pulse (LRP) PHY + */ + const int LOW_RATE_PULSE_CHIRPS_PER_RSTU = 1; +} diff --git a/core/proto/android/stats/enums.proto b/core/java/android/uwb/IUwbAdapterStateCallbacks.aidl index 8f8055ed2451..d928eabae465 100644 --- a/core/proto/android/stats/enums.proto +++ b/core/java/android/uwb/IUwbAdapterStateCallbacks.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,16 +14,19 @@ * limitations under the License. */ -syntax = "proto2"; +package android.uwb; -package android.stats; -option java_outer_classname = "StatsEnums"; +import android.uwb.StateChangeReason; -enum EventType { - // Unknown. - TYPE_UNKNOWN = 0; - CONTENT_SUGGESTIONS_CLASSIFY_CONTENT_CALL_SUCCEEDED = 1; - CONTENT_SUGGESTIONS_CLASSIFY_CONTENT_CALL_FAILED = 2; - CONTENT_SUGGESTIONS_SUGGEST_CONTENT_CALL_SUCCEEDED = 3; - CONTENT_SUGGESTIONS_SUGGEST_CONTENT_CALL_FAILED = 4; -} +/** + * @hide + */ +interface IUwbAdapterStateCallbacks { + /** + * Called whenever the adapter state changes + * + * @param isEnabled true if the adapter is enabled, false otherwise + * @param reason the reason that the state has changed + */ + void onAdapterStateChanged(boolean isEnabled, StateChangeReason reason); +}
\ No newline at end of file diff --git a/core/java/android/uwb/IUwbRangingCallbacks.aidl b/core/java/android/uwb/IUwbRangingCallbacks.aidl new file mode 100644 index 000000000000..1fc3bfd818c3 --- /dev/null +++ b/core/java/android/uwb/IUwbRangingCallbacks.aidl @@ -0,0 +1,73 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.uwb; + +import android.os.PersistableBundle; +import android.uwb.CloseReason; +import android.uwb.RangingReport; +import android.uwb.SessionHandle; +import android.uwb.StartFailureReason; + +/** + * @hide + */ +interface IUwbRangingCallbacks { + /** + * Called when ranging has started + * + * May output parameters generated by the lower layers that must be sent to the + * remote device(s). The PersistableBundle must be constructed using the UWB + * support library. + * + * @param sessionHandle the session the callback is being invoked for + * @param rangingOutputParameters parameters generated by the lower layer that + * should be sent to the remote device. + */ + void onRangingStarted(in SessionHandle sessionHandle, + in PersistableBundle parameters); + + /** + * Called when a ranging session fails to start + * + * @param sessionHandle the session the callback is being invoked for + * @param reason the reason the session failed to start + * @param parameters protocol specific parameters + */ + void onRangingStartFailed(in SessionHandle sessionHandle, StartFailureReason reason, + in PersistableBundle parameters); + /** + * Called when a ranging session is closed + * + * @param sessionHandle the session the callback is being invoked for + * @param reason the reason the session was closed + * @param parameters protocol specific parameters + */ + void onRangingClosed(in SessionHandle sessionHandle, CloseReason reason, + in PersistableBundle parameters); + + /** + * Provides a new RangingResult to the framework + * + * The reported timestamp for a ranging measurement must be calculated as the + * time which the ranging round that generated this measurement concluded. + * + * @param sessionHandle an identifier to associate the ranging results with a + * session that is active + * @param result the ranging report + */ + void onRangingResult(in SessionHandle sessionHandle, in RangingReport result); +} diff --git a/core/java/android/uwb/MeasurementStatus.aidl b/core/java/android/uwb/MeasurementStatus.aidl new file mode 100644 index 000000000000..5fa15549e84d --- /dev/null +++ b/core/java/android/uwb/MeasurementStatus.aidl @@ -0,0 +1,39 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.uwb; + +/** + * @hide + */ +@Backing(type="int") +enum MeasurementStatus { + /** + * Ranging was successful + */ + SUCCESS, + + /** + * The remote device is out of range + */ + FAILURE_OUT_OF_RANGE, + + /** + * An unknown failure has occurred. + */ + FAILURE_UNKNOWN, +} + diff --git a/core/proto/android/stats/storage/storage_enums.proto b/core/java/android/uwb/RangingReport.aidl index 6892e287472f..c32747ae58da 100644 --- a/core/proto/android/stats/storage/storage_enums.proto +++ b/core/java/android/uwb/RangingReport.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,13 +14,6 @@ * limitations under the License. */ -syntax = "proto2"; +package android.uwb; -package android.stats.storage; - -enum ExternalStorageType { - UNKNOWN = 0; - SD_CARD = 1; - USB = 2; - OTHER = 3; -} +parcelable RangingReport; diff --git a/core/proto/android/stats/devicepolicy/device_policy.proto b/core/java/android/uwb/SessionHandle.aidl index af30cf3f9941..58a7dbb2ef9f 100644 --- a/core/proto/android/stats/devicepolicy/device_policy.proto +++ b/core/java/android/uwb/SessionHandle.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,11 +14,6 @@ * limitations under the License. */ -syntax = "proto2"; +package android.uwb; -package android.stats.devicepolicy; -option java_multiple_files = true; - -message StringList { - repeated string string_value = 1; -} +parcelable SessionHandle; diff --git a/core/java/android/uwb/SessionHandle.java b/core/java/android/uwb/SessionHandle.java new file mode 100644 index 000000000000..928fcbdcf1c7 --- /dev/null +++ b/core/java/android/uwb/SessionHandle.java @@ -0,0 +1,79 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.uwb; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * @hide + */ +public final class SessionHandle implements Parcelable { + private final int mId; + + public SessionHandle(int id) { + mId = id; + } + + protected SessionHandle(Parcel in) { + mId = in.readInt(); + } + + public static final Creator<SessionHandle> CREATOR = new Creator<SessionHandle>() { + @Override + public SessionHandle createFromParcel(Parcel in) { + return new SessionHandle(in); + } + + @Override + public SessionHandle[] newArray(int size) { + return new SessionHandle[size]; + } + }; + + public int getId() { + return mId; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mId); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (obj instanceof SessionHandle) { + SessionHandle other = (SessionHandle) obj; + return mId == other.mId; + } + return false; + } + + @Override + public String toString() { + return "SessionHandle [id=" + mId + "]"; + } +} diff --git a/core/java/android/uwb/StartFailureReason.aidl b/core/java/android/uwb/StartFailureReason.aidl new file mode 100644 index 000000000000..4d9c962f529b --- /dev/null +++ b/core/java/android/uwb/StartFailureReason.aidl @@ -0,0 +1,52 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.uwb; + +/** + * @hide + */ +@Backing(type="int") +enum StartFailureReason { + /** + * Unknown start failure reason + */ + UNKNOWN, + + /** + * The provided parameters were invalid and ranging could not start + */ + BAD_PARAMETERS, + + /** + * The maximum number of sessions has been reached. This error may be generated + * for an active session if a higher priority session begins. + */ + MAX_SESSIONS_REACHED, + + /** + * The system state has changed resulting in the session ending (e.g. the user + * disables UWB, or the user's locale changes and an active channel is no longer + * permitted to be used). + */ + SYSTEM_POLICY, + + /** + * The session could not start because of a protocol specific reason. + */ + PROTOCOL_SPECIFIC, +} + diff --git a/core/java/android/uwb/StateChangeReason.aidl b/core/java/android/uwb/StateChangeReason.aidl new file mode 100644 index 000000000000..46a6e2edfa22 --- /dev/null +++ b/core/java/android/uwb/StateChangeReason.aidl @@ -0,0 +1,45 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.uwb; + +/** + * @hide + */ +@Backing(type="int") +enum StateChangeReason { + /** + * The state changed for an unknown reason + */ + UNKNOWN, + + /** + * The adapter state changed because a session started. + */ + SESSION_STARTED, + + + /** + * The adapter state changed because all sessions were closed. + */ + ALL_SESSIONS_CLOSED, + + /** + * The adapter state changed because of a device system change. + */ + SYSTEM_POLICY, +} + diff --git a/packages/SystemUI/src/com/android/systemui/classifier/PointerCountEvaluator.java b/core/java/android/uwb/UwbAddress.aidl index d7a3af0360af..a202b1a1f51d 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/PointerCountEvaluator.java +++ b/core/java/android/uwb/UwbAddress.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Android Open Source Project + * Copyright 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -11,13 +11,9 @@ * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and - * limitations under the License + * limitations under the License. */ -package com.android.systemui.classifier; +package android.uwb; -public class PointerCountEvaluator { - public static float evaluate(int value) { - return (value - 1) * (value - 1); - } -} +parcelable UwbAddress; diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java index 3da3184afae1..59299f6b15eb 100644 --- a/core/java/android/view/Choreographer.java +++ b/core/java/android/view/Choreographer.java @@ -276,7 +276,7 @@ public final class Choreographer { private static float getRefreshRate() { DisplayInfo di = DisplayManagerGlobal.getInstance().getDisplayInfo( Display.DEFAULT_DISPLAY); - return di.getMode().getRefreshRate(); + return di.getRefreshRate(); } /** @@ -944,7 +944,7 @@ public final class Choreographer { private VsyncEventData mLastVsyncEventData = new VsyncEventData(); public FrameDisplayEventReceiver(Looper looper, int vsyncSource) { - super(looper, vsyncSource, CONFIG_CHANGED_EVENT_SUPPRESS); + super(looper, vsyncSource, 0); } // TODO(b/116025192): physicalDisplayId is ignored because SF only emits VSYNC events for diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index 3021aa6a0783..56c7e27151e0 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -456,6 +456,9 @@ public final class Display { // TODO (b/114338689): Remove the flag and use WindowManager#REMOVE_CONTENT_MODE_DESTROY public static final int REMOVE_MODE_DESTROY_CONTENT = 1; + /** @hide */ + public static final int DISPLAY_MODE_ID_FOR_FRAME_RATE_OVERRIDE = 0xFF; + /** * Internal method to create a display. * The display created with this method will have a static {@link DisplayAdjustments} applied. @@ -886,7 +889,7 @@ public final class Display { public float getRefreshRate() { synchronized (this) { updateDisplayInfoLocked(); - return mDisplayInfo.getMode().getRefreshRate(); + return mDisplayInfo.getRefreshRate(); } } @@ -1391,6 +1394,23 @@ public final class Display { } /** + * Returns true if the display is in an off state such as {@link #STATE_OFF}. + * @hide + */ + public static boolean isOffState(int state) { + return state == STATE_OFF; + } + + /** + * Returns true if the display is in an on state such as {@link #STATE_ON} + * or {@link #STATE_VR} or {@link #STATE_ON_SUSPEND}. + * @hide + */ + public static boolean isOnState(int state) { + return state == STATE_ON || state == STATE_VR || state == STATE_ON_SUSPEND; + } + + /** * A mode supported by a given display. * * @see Display#getSupportedModes() @@ -1509,6 +1529,16 @@ public final class Display { Float.floatToIntBits(mRefreshRate) == Float.floatToIntBits(refreshRate); } + /** + * Returns {@code true} if this mode equals to the other mode in all parameters except + * the refresh rate. + * + * @hide + */ + public boolean equalsExceptRefreshRate(@Nullable Display.Mode other) { + return mWidth == other.mWidth && mHeight == other.mHeight; + } + @Override public boolean equals(@Nullable Object other) { if (this == other) { diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java index e8a4ed44b7c8..5d4a4e52975a 100644 --- a/core/java/android/view/DisplayEventReceiver.java +++ b/core/java/android/view/DisplayEventReceiver.java @@ -23,6 +23,8 @@ import android.os.Looper; import android.os.MessageQueue; import android.util.Log; +import com.android.internal.annotations.VisibleForTesting; + import dalvik.annotation.optimization.FastNative; import dalvik.system.CloseGuard; @@ -56,18 +58,18 @@ public abstract class DisplayEventReceiver { public static final int VSYNC_SOURCE_SURFACE_FLINGER = 1; /** - * Specifies to suppress config changed events from being generated from Surface Flinger. + * Specifies to generate config changed events from Surface Flinger. * <p> * Needs to be kept in sync with frameworks/native/include/gui/ISurfaceComposer.h */ - public static final int CONFIG_CHANGED_EVENT_SUPPRESS = 0; + public static final int EVENT_REGISTRATION_CONFIG_CHANGED_FLAG = 0x1; /** - * Specifies to generate config changed events from Surface Flinger. + * Specifies to generate frame rate override events from Surface Flinger. * <p> * Needs to be kept in sync with frameworks/native/include/gui/ISurfaceComposer.h */ - public static final int CONFIG_CHANGED_EVENT_DISPATCH = 1; + public static final int EVENT_REGISTRATION_FRAME_RATE_OVERRIDE_FLAG = 0x2; private static final String TAG = "DisplayEventReceiver"; @@ -81,7 +83,7 @@ public abstract class DisplayEventReceiver { private MessageQueue mMessageQueue; private static native long nativeInit(WeakReference<DisplayEventReceiver> receiver, - MessageQueue messageQueue, int vsyncSource, int configChanged); + MessageQueue messageQueue, int vsyncSource, int eventRegistration); private static native void nativeDispose(long receiverPtr); @FastNative private static native void nativeScheduleVsync(long receiverPtr); @@ -93,7 +95,7 @@ public abstract class DisplayEventReceiver { */ @UnsupportedAppUsage public DisplayEventReceiver(Looper looper) { - this(looper, VSYNC_SOURCE_APP, CONFIG_CHANGED_EVENT_SUPPRESS); + this(looper, VSYNC_SOURCE_APP, 0); } /** @@ -101,17 +103,17 @@ public abstract class DisplayEventReceiver { * * @param looper The looper to use when invoking callbacks. * @param vsyncSource The source of the vsync tick. Must be on of the VSYNC_SOURCE_* values. - * @param configChanged Whether to dispatch config changed events. Must be one of the - * CONFIG_CHANGED_EVENT_* values. + * @param eventRegistration Which events to dispatch. Must be a bitfield consist of the + * EVENT_REGISTRATION_*_FLAG values. */ - public DisplayEventReceiver(Looper looper, int vsyncSource, int configChanged) { + public DisplayEventReceiver(Looper looper, int vsyncSource, int eventRegistration) { if (looper == null) { throw new IllegalArgumentException("looper must not be null"); } mMessageQueue = looper.getQueue(); mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this), mMessageQueue, - vsyncSource, configChanged); + vsyncSource, eventRegistration); mCloseGuard.open("dispose"); } @@ -206,6 +208,41 @@ public abstract class DisplayEventReceiver { } /** + * Represents a mapping between a UID and an override frame rate + */ + public static class FrameRateOverride { + // The application uid + public final int uid; + + // The frame rate that this application runs at + public final float frameRateHz; + + + @VisibleForTesting + public FrameRateOverride(int uid, float frameRateHz) { + this.uid = uid; + this.frameRateHz = frameRateHz; + } + + @Override + public String toString() { + return "{uid=" + uid + " frameRateHz=" + frameRateHz + "}"; + } + } + + /** + * Called when frame rate override event is received. + * + * @param timestampNanos The timestamp of the event, in the {@link System#nanoTime()} + * timebase. + * @param physicalDisplayId Stable display ID that uniquely describes a (display, port) pair. + * @param overrides The mappings from uid to frame rates + */ + public void onFrameRateOverridesChanged(long timestampNanos, long physicalDisplayId, + FrameRateOverride[] overrides) { + } + + /** * Schedules a single vertical sync pulse to be delivered when the next * display frame begins. */ @@ -240,4 +277,11 @@ public abstract class DisplayEventReceiver { onConfigChanged(timestampNanos, physicalDisplayId, configId); } + // Called from native code. + @SuppressWarnings("unused") + private void dispatchFrameRateOverrides(long timestampNanos, long physicalDisplayId, + FrameRateOverride[] overrides) { + onFrameRateOverridesChanged(timestampNanos, physicalDisplayId, overrides); + } + } diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java index fe9a1a76bbaf..020160584f67 100644 --- a/core/java/android/view/DisplayInfo.java +++ b/core/java/android/view/DisplayInfo.java @@ -261,6 +261,11 @@ public final class DisplayInfo implements Parcelable { public String ownerPackageName; /** + * The refresh rate override for this app. 0 means no override. + */ + public float refreshRateOverride; + + /** * @hide * Get current remove mode of the display - what actions should be performed with the display's * content when it is removed. @@ -332,7 +337,8 @@ public final class DisplayInfo implements Parcelable { && state == other.state && ownerUid == other.ownerUid && Objects.equals(ownerPackageName, other.ownerPackageName) - && removeMode == other.removeMode; + && removeMode == other.removeMode + && refreshRateOverride == other.refreshRateOverride; } @Override @@ -376,6 +382,7 @@ public final class DisplayInfo implements Parcelable { ownerUid = other.ownerUid; ownerPackageName = other.ownerPackageName; removeMode = other.removeMode; + refreshRateOverride = other.refreshRateOverride; } public void readFromParcel(Parcel source) { @@ -421,6 +428,7 @@ public final class DisplayInfo implements Parcelable { ownerPackageName = source.readString8(); uniqueId = source.readString8(); removeMode = source.readInt(); + refreshRateOverride = source.readFloat(); } @Override @@ -465,6 +473,7 @@ public final class DisplayInfo implements Parcelable { dest.writeString8(ownerPackageName); dest.writeString8(uniqueId); dest.writeInt(removeMode); + dest.writeFloat(refreshRateOverride); } @Override @@ -472,6 +481,17 @@ public final class DisplayInfo implements Parcelable { return 0; } + /** + * Returns the refresh rate the application would experience. + */ + public float getRefreshRate() { + if (refreshRateOverride > 0) { + return refreshRateOverride; + } + + return getMode().getRefreshRate(); + } + public Display.Mode getMode() { return findMode(modeId); } @@ -675,6 +695,9 @@ public final class DisplayInfo implements Parcelable { } sb.append(", removeMode "); sb.append(removeMode); + sb.append(", refreshRateOverride "); + sb.append(refreshRateOverride); + sb.append("}"); return sb.toString(); } diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 924fc6d6dca0..1a6eea554cae 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -648,42 +648,44 @@ interface IWindowManager void setShouldShowSystemDecors(int displayId, boolean shouldShow); /** - * Indicates that the display should show IME. + * Indicates the policy for how the display should show IME. * * @param displayId The id of the display. - * @return {@code true} if the display should show IME. + * @return The policy for how the display should show IME. * @see KeyguardManager#isDeviceSecure() * @see KeyguardManager#isDeviceLocked() */ - boolean shouldShowIme(int displayId); + int getDisplayImePolicy(int displayId); /** - * Sets that the display should show IME. + * Sets the policy for how the display should show IME. * * @param displayId The id of the display. - * @param shouldShow Indicates that the display should show IME. + * @param imePolicy Indicates the policy for how the display should show IME. * @see KeyguardManager#isDeviceSecure() * @see KeyguardManager#isDeviceLocked() */ - void setShouldShowIme(int displayId, boolean shouldShow); + void setDisplayImePolicy(int displayId, int imePolicy); /** - * Waits for transactions to get applied before injecting input. - * This includes waiting for the input windows to get sent to InputManager. + * Waits for transactions to get applied before injecting input, optionally waiting for + * animations to complete. This includes waiting for the input windows to get sent to + * InputManager. * * This is needed for testing since the system add windows and injects input * quick enough that the windows don't have time to get sent to InputManager. */ - boolean injectInputAfterTransactionsApplied(in InputEvent ev, int mode); + boolean injectInputAfterTransactionsApplied(in InputEvent ev, int mode, + boolean waitForAnimations); /** - * Waits until all animations have completed and input information has been sent from - * WindowManager to native InputManager. + * Waits until input information has been sent from WindowManager to native InputManager, + * optionally waiting for animations to complete. * * This is needed for testing since we need to ensure input information has been propagated to * native InputManager before proceeding with tests. */ - void syncInputTransactions(); + void syncInputTransactions(boolean waitForAnimations); /** * Returns whether SurfaceFlinger layer tracing is enabled. diff --git a/core/java/android/view/ImeFocusController.java b/core/java/android/view/ImeFocusController.java index efc0bd2785f4..4a5c95f43d46 100644 --- a/core/java/android/view/ImeFocusController.java +++ b/core/java/android/view/ImeFocusController.java @@ -222,6 +222,25 @@ public final class ImeFocusController { } /** + * To handle the lifecycle of the input connection when the device interactivity state changed. + * (i.e. Calling IMS#onFinishInput when the device screen-off and Calling IMS#onStartInput + * when the device screen-on again). + */ + @UiThread + public void onInteractiveChanged(boolean interactive) { + final InputMethodManagerDelegate immDelegate = getImmDelegate(); + if (!immDelegate.isCurrentRootView(mViewRootImpl)) { + return; + } + if (interactive) { + final View focusedView = mViewRootImpl.mView.findFocus(); + onViewFocusChanged(focusedView, focusedView != null); + } else { + mDelegate.finishInputAndReportToIme(); + } + } + + /** * @param windowAttribute {@link WindowManager.LayoutParams} to be checked. * @return Whether the window is in local focus mode or not. */ @@ -256,6 +275,7 @@ public final class ImeFocusController { @WindowManager.LayoutParams.SoftInputModeFlags int softInputMode, int windowFlags, boolean forceNewFocus); void finishInput(); + void finishInputAndReportToIme(); void closeCurrentIme(); void finishComposingText(); void setCurrentRootView(ViewRootImpl rootView); diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java index 22ac4dcd2cfe..8da833a28efa 100644 --- a/core/java/android/view/InputDevice.java +++ b/core/java/android/view/InputDevice.java @@ -29,6 +29,8 @@ import android.os.Parcel; import android.os.Parcelable; import android.os.Vibrator; +import com.android.internal.annotations.VisibleForTesting; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; @@ -423,9 +425,13 @@ public final class InputDevice implements Parcelable { } }; - // Called by native code. + /** + * Called by native code + * @hide + */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - private InputDevice(int id, int generation, int controllerNumber, String name, int vendorId, + @VisibleForTesting + public InputDevice(int id, int generation, int controllerNumber, String name, int vendorId, int productId, String descriptor, boolean isExternal, int sources, int keyboardType, KeyCharacterMap keyCharacterMap, boolean hasVibrator, boolean hasMicrophone, boolean hasButtonUnderPad) { diff --git a/core/java/android/view/InputEventReceiver.java b/core/java/android/view/InputEventReceiver.java index 038dff654ba6..7d1adc36964b 100644 --- a/core/java/android/view/InputEventReceiver.java +++ b/core/java/android/view/InputEventReceiver.java @@ -144,6 +144,20 @@ public abstract class InputEventReceiver { } /** + * Called when a Pointer Capture event is received. + * + * @param pointerCaptureEnabled if true, the window associated with this input channel has just + * received Pointer Capture + * if false, the window associated with this input channel has just + * lost Pointer Capture + * @see View#requestPointerCapture() + * @see View#releasePointerCapture() + */ + // Called from native code. + public void onPointerCaptureEvent(boolean pointerCaptureEnabled) { + } + + /** * Called when a batched input event is pending. * * The batched input event will continue to accumulate additional movement diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java index f7fbb1ce0ab9..673ed0d8b95d 100644 --- a/core/java/android/view/NotificationHeaderView.java +++ b/core/java/android/view/NotificationHeaderView.java @@ -43,12 +43,13 @@ import java.util.ArrayList; */ @RemoteViews.RemoteView public class NotificationHeaderView extends FrameLayout { - private final int mContentEndMargin; private final int mHeadingEndMargin; + private final int mTouchableHeight; private OnClickListener mExpandClickListener; private HeaderTouchListener mTouchListener = new HeaderTouchListener(); private NotificationTopLineView mTopLineView; private NotificationExpandButton mExpandButton; + private View mAltExpandTarget; private CachingIconView mIcon; private Drawable mBackground; private boolean mEntireHeaderClickable; @@ -82,8 +83,8 @@ public class NotificationHeaderView extends FrameLayout { int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); Resources res = getResources(); - mContentEndMargin = res.getDimensionPixelSize(R.dimen.notification_content_margin_end); mHeadingEndMargin = res.getDimensionPixelSize(R.dimen.notification_heading_margin_end); + mTouchableHeight = res.getDimensionPixelSize(R.dimen.notification_header_touchable_height); mEntireHeaderClickable = res.getBoolean(R.bool.config_notificationHeaderClickableForExpand); } @@ -93,6 +94,7 @@ public class NotificationHeaderView extends FrameLayout { mIcon = findViewById(R.id.icon); mTopLineView = findViewById(R.id.notification_top_line); mExpandButton = findViewById(R.id.expand_button); + mAltExpandTarget = findViewById(R.id.alternate_expand_target); setClipToPadding(false); } @@ -146,6 +148,7 @@ public class NotificationHeaderView extends FrameLayout { public void setOnClickListener(@Nullable OnClickListener l) { mExpandClickListener = l; mExpandButton.setOnClickListener(mExpandClickListener); + mAltExpandTarget.setOnClickListener(mExpandClickListener); updateTouchListener(); } @@ -187,6 +190,7 @@ public class NotificationHeaderView extends FrameLayout { private final ArrayList<Rect> mTouchRects = new ArrayList<>(); private Rect mExpandButtonRect; + private Rect mAltExpandTargetRect; private int mTouchSlop; private boolean mTrackGesture; private float mDownX; @@ -199,6 +203,7 @@ public class NotificationHeaderView extends FrameLayout { mTouchRects.clear(); addRectAroundView(mIcon); mExpandButtonRect = addRectAroundView(mExpandButton); + mAltExpandTargetRect = addRectAroundView(mAltExpandTarget); addWidthRect(); mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); } @@ -206,7 +211,7 @@ public class NotificationHeaderView extends FrameLayout { private void addWidthRect() { Rect r = new Rect(); r.top = 0; - r.bottom = (int) (32 * getResources().getDisplayMetrics().density); + r.bottom = mTouchableHeight; r.left = 0; r.right = getWidth(); mTouchRects.add(r); @@ -277,7 +282,8 @@ public class NotificationHeaderView extends FrameLayout { return true; } if (mExpandOnlyOnButton) { - return mExpandButtonRect.contains((int) x, (int) y); + return mExpandButtonRect.contains((int) x, (int) y) + || mAltExpandTargetRect.contains((int) x, (int) y); } for (int i = 0; i < mTouchRects.size(); i++) { Rect r = mTouchRects.get(i); diff --git a/core/java/android/view/NotificationTopLineView.java b/core/java/android/view/NotificationTopLineView.java index a8eabe5a7967..05636de8e8e4 100644 --- a/core/java/android/view/NotificationTopLineView.java +++ b/core/java/android/view/NotificationTopLineView.java @@ -97,10 +97,8 @@ public class NotificationTopLineView extends ViewGroup { final int givenWidth = MeasureSpec.getSize(widthMeasureSpec); final int givenHeight = MeasureSpec.getSize(heightMeasureSpec); final boolean wrapHeight = MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST; - int wrapContentWidthSpec = MeasureSpec.makeMeasureSpec(givenWidth, - MeasureSpec.AT_MOST); - int wrapContentHeightSpec = MeasureSpec.makeMeasureSpec(givenHeight, - MeasureSpec.AT_MOST); + int wrapContentWidthSpec = MeasureSpec.makeMeasureSpec(givenWidth, MeasureSpec.AT_MOST); + int heightSpec = MeasureSpec.makeMeasureSpec(givenHeight, MeasureSpec.AT_MOST); int totalWidth = getPaddingStart(); int maxChildHeight = -1; mMaxAscent = -1; @@ -114,7 +112,7 @@ public class NotificationTopLineView extends ViewGroup { final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); int childWidthSpec = getChildMeasureSpec(wrapContentWidthSpec, lp.leftMargin + lp.rightMargin, lp.width); - int childHeightSpec = getChildMeasureSpec(wrapContentHeightSpec, + int childHeightSpec = getChildMeasureSpec(heightSpec, lp.topMargin + lp.bottomMargin, lp.height); child.measure(childWidthSpec, childHeightSpec); totalWidth += lp.leftMargin + lp.rightMargin + child.getMeasuredWidth(); @@ -131,37 +129,37 @@ public class NotificationTopLineView extends ViewGroup { int endMargin = Math.max(mHeaderTextMarginEnd, getPaddingEnd()); if (totalWidth > givenWidth - endMargin) { int overFlow = totalWidth - givenWidth + endMargin; - if (mAppName != null) { - // We are overflowing, lets shrink the app name first - overFlow = shrinkViewForOverflow(wrapContentHeightSpec, overFlow, mAppName, - mChildMinWidth); - } - if (mTitle != null) { - // still overflowing, we shrink the title text - overFlow = shrinkViewForOverflow(wrapContentHeightSpec, overFlow, mTitle, - mChildMinWidth); - } + // First shrink the app name, down to a minimum size + overFlow = shrinkViewForOverflow(heightSpec, overFlow, mAppName, mChildMinWidth); + + // Next, shrink the header text (this usually has subText) + // This shrinks the subtext first, but not all the way (yet!) + overFlow = shrinkViewForOverflow(heightSpec, overFlow, mHeaderText, mChildMinWidth); - // still overflowing, we shrink the header text - overFlow = shrinkViewForOverflow(wrapContentHeightSpec, overFlow, mHeaderText, 0); + // Next, shrink the secondary header text (this rarely has conversationTitle) + overFlow = shrinkViewForOverflow(heightSpec, overFlow, mSecondaryHeaderText, 0); - // still overflowing, finally we shrink the secondary header text - shrinkViewForOverflow(wrapContentHeightSpec, overFlow, mSecondaryHeaderText, - 0); + // Next, shrink the title text (this has contentTitle; only in headerless views) + overFlow = shrinkViewForOverflow(heightSpec, overFlow, mTitle, mChildMinWidth); + + // Finally, if there is still overflow, shrink the header down to 0 if still necessary. + shrinkViewForOverflow(heightSpec, overFlow, mHeaderText, 0); } setMeasuredDimension(givenWidth, wrapHeight ? maxChildHeight : givenHeight); } private int shrinkViewForOverflow(int heightSpec, int overFlow, View targetView, int minimumWidth) { - final int oldWidth = targetView.getMeasuredWidth(); - if (overFlow > 0 && targetView.getVisibility() != GONE && oldWidth > minimumWidth) { - // we're still too big - int newSize = Math.max(minimumWidth, oldWidth - overFlow); - int childWidthSpec = MeasureSpec.makeMeasureSpec(newSize, MeasureSpec.AT_MOST); - targetView.measure(childWidthSpec, heightSpec); - overFlow -= oldWidth - newSize; + if (targetView != null) { + final int oldWidth = targetView.getMeasuredWidth(); + if (overFlow > 0 && targetView.getVisibility() != GONE && oldWidth > minimumWidth) { + // we're still too big + int newSize = Math.max(minimumWidth, oldWidth - overFlow); + int childWidthSpec = MeasureSpec.makeMeasureSpec(newSize, MeasureSpec.AT_MOST); + targetView.measure(childWidthSpec, heightSpec); + overFlow -= oldWidth - newSize; + } } return overFlow; } diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 436acef17c4d..ef2153e074e7 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -461,6 +461,40 @@ public interface WindowManager extends ViewManager { @interface RemoveContentMode {} /** + * Display IME Policy: The IME should appear on the local display. + * @hide + */ + @TestApi + int DISPLAY_IME_POLICY_LOCAL = 0; + + /** + * Display IME Policy: The IME should appear on the fallback display. + * @hide + */ + @TestApi + int DISPLAY_IME_POLICY_FALLBACK_DISPLAY = 1; + + /** + * Display IME Policy: The IME should be hidden. + * + * Setting this policy will prevent the IME from making a connection. This + * will prevent any IME from receiving metadata about input. + * @hide + */ + @TestApi + int DISPLAY_IME_POLICY_HIDE = 2; + + /** + * @hide + */ + @IntDef({ + DISPLAY_IME_POLICY_LOCAL, + DISPLAY_IME_POLICY_FALLBACK_DISPLAY, + DISPLAY_IME_POLICY_HIDE, + }) + @interface DisplayImePolicy {} + + /** * Exception that is thrown when trying to add view whose * {@link LayoutParams} {@link LayoutParams#token} * is invalid. @@ -695,27 +729,26 @@ public interface WindowManager extends ViewManager { } /** - * Sets that the display should show IME. + * Sets the policy for how the display should show IME. * * @param displayId Display ID. - * @param shouldShow Indicates that the display should show IME. + * @param imePolicy Indicates the policy for how the display should show IME. * @hide */ @TestApi - default void setShouldShowIme(int displayId, boolean shouldShow) { + default void setDisplayImePolicy(int displayId, @DisplayImePolicy int imePolicy) { } /** - * Indicates that the display should show IME. + * Indicates the policy for how the display should show IME. * * @param displayId The id of the display. - * @return {@code true} if the display should show IME when an input field becomes - * focused on it. + * @return The policy for how the display should show IME. * @hide */ @TestApi - default boolean shouldShowIme(int displayId) { - return false; + default @DisplayImePolicy int getDisplayImePolicy(int displayId) { + return DISPLAY_IME_POLICY_FALLBACK_DISPLAY; } public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable { @@ -2124,15 +2157,6 @@ public interface WindowManager extends ViewManager { public static final int PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY = 0x00100000; /** - * Flag to indicate that this window should be considered a screen decoration similar to the - * nav bar and status bar. This will cause this window to affect the window insets reported - * to other windows when it is visible. - * @hide - */ - @RequiresPermission(permission.STATUS_BAR_SERVICE) - public static final int PRIVATE_FLAG_IS_SCREEN_DECOR = 0x00400000; - - /** * Flag to indicate that the status bar window is in a state such that it forces showing * the navigation bar unless the navigation bar window is explicitly set to * {@link View#GONE}. @@ -2237,7 +2261,6 @@ public interface WindowManager extends ViewManager { PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE, SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS, PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY, - PRIVATE_FLAG_IS_SCREEN_DECOR, PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION, PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC, PRIVATE_FLAG_USE_BLAST, @@ -2325,10 +2348,6 @@ public interface WindowManager extends ViewManager { equals = PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY, name = "IS_ROUNDED_CORNERS_OVERLAY"), @ViewDebug.FlagToString( - mask = PRIVATE_FLAG_IS_SCREEN_DECOR, - equals = PRIVATE_FLAG_IS_SCREEN_DECOR, - name = "IS_SCREEN_DECOR"), - @ViewDebug.FlagToString( mask = PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION, equals = PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION, name = "STATUS_FORCE_SHOW_NAVIGATION"), @@ -2826,7 +2845,6 @@ public interface WindowManager extends ViewManager { /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef( - flag = true, value = {LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT, LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES, LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER, diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java index d56929e06906..3384bbe4fd76 100644 --- a/core/java/android/view/WindowManagerImpl.java +++ b/core/java/android/view/WindowManagerImpl.java @@ -201,20 +201,20 @@ public final class WindowManagerImpl implements WindowManager { } @Override - public void setShouldShowIme(int displayId, boolean shouldShow) { + public void setDisplayImePolicy(int displayId, @DisplayImePolicy int imePolicy) { try { - WindowManagerGlobal.getWindowManagerService().setShouldShowIme(displayId, shouldShow); + WindowManagerGlobal.getWindowManagerService().setDisplayImePolicy(displayId, imePolicy); } catch (RemoteException e) { } } @Override - public boolean shouldShowIme(int displayId) { + public @DisplayImePolicy int getDisplayImePolicy(int displayId) { try { - return WindowManagerGlobal.getWindowManagerService().shouldShowIme(displayId); + return WindowManagerGlobal.getWindowManagerService().getDisplayImePolicy(displayId); } catch (RemoteException e) { } - return false; + return DISPLAY_IME_POLICY_FALLBACK_DISPLAY; } @Override diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java index 6a2a8d35a007..1ab9edf0caa2 100644 --- a/core/java/android/view/inputmethod/BaseInputConnection.java +++ b/core/java/android/view/inputmethod/BaseInputConnection.java @@ -43,6 +43,8 @@ import android.view.KeyEvent; import android.view.OnReceiveContentListener; import android.view.View; +import com.android.internal.util.Preconditions; + class ComposingText implements NoCopySpan { } @@ -504,7 +506,7 @@ public class BaseInputConnection implements InputConnection { */ @Nullable public CharSequence getTextBeforeCursor(@IntRange(from = 0) int length, int flags) { - if (length < 0) return null; + Preconditions.checkArgumentNonnegative(length); final Editable content = getEditable(); if (content == null) return null; @@ -563,7 +565,7 @@ public class BaseInputConnection implements InputConnection { */ @Nullable public CharSequence getTextAfterCursor(@IntRange(from = 0) int length, int flags) { - if (length < 0) return null; + Preconditions.checkArgumentNonnegative(length); final Editable content = getEditable(); if (content == null) return null; @@ -600,7 +602,8 @@ public class BaseInputConnection implements InputConnection { @Nullable public SurroundingText getSurroundingText( @IntRange(from = 0) int beforeLength, @IntRange(from = 0) int afterLength, int flags) { - if (beforeLength < 0 || afterLength < 0) return null; + Preconditions.checkArgumentNonnegative(beforeLength); + Preconditions.checkArgumentNonnegative(afterLength); final Editable content = getEditable(); if (content == null) return null; diff --git a/core/java/android/view/inputmethod/DumpableInputConnection.java b/core/java/android/view/inputmethod/DumpableInputConnection.java new file mode 100644 index 000000000000..9819a5734491 --- /dev/null +++ b/core/java/android/view/inputmethod/DumpableInputConnection.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view.inputmethod; + +import android.annotation.NonNull; +import android.util.proto.ProtoOutputStream; + +/** @hide */ +public interface DumpableInputConnection { + + /** + * Method used to dump state of InputConnection implementations of interest. + * + * @param proto Stream to write the state to + * @param fieldId FieldId of DumpableInputConnection as defined in the parent message + */ + void dumpDebug(@NonNull ProtoOutputStream proto, long fieldId); +} diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 3c89a4bfad59..c3d3985b5ac3 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -22,6 +22,7 @@ import static android.util.imetracing.ImeTracing.PROTO_ARG; import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.DISPLAY_ID; import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.EDITOR_INFO; import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.IME_INSETS_SOURCE_CONSUMER; +import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.INPUT_CONNECTION; import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.INPUT_METHOD_MANAGER; import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.VIEW_ROOT_IMPL; import static android.view.inputmethod.InputMethodManagerProto.ACTIVE; @@ -599,6 +600,27 @@ public final class InputMethodManager { } /** + * Used by {@link ImeFocusController} to finish input connection and callback + * {@link InputMethodService#onFinishInput()}. + * + * This method is especially for when ImeFocusController received device screen-off event to + * ensure the entire finish input connection and the connection lifecycle callback to + * IME can be done for security concern. + */ + @Override + public void finishInputAndReportToIme() { + synchronized (mH) { + finishInputLocked(); + if (mCurMethod != null) { + try { + mCurMethod.finishInput(); + } catch (RemoteException e) { + } + } + } + } + + /** * Used by {@link ImeFocusController} to hide current input method editor. */ @Override @@ -849,12 +871,23 @@ public final class InputMethodManager { case MSG_SET_ACTIVE: { final boolean active = msg.arg1 != 0; final boolean fullscreen = msg.arg2 != 0; + final boolean reportToImeController = msg.obj != null && (boolean) msg.obj; if (DEBUG) { Log.i(TAG, "handleMessage: MSG_SET_ACTIVE " + active + ", was " + mActive); } synchronized (mH) { mActive = active; mFullscreenMode = fullscreen; + + // Report active state to ImeFocusController to handle IME input + // connection lifecycle callback when it allowed. + final ImeFocusController controller = getFocusController(); + final View rootView = mCurRootView != null ? mCurRootView.getView() : null; + if (controller != null && rootView != null && reportToImeController) { + rootView.post(() -> controller.onInteractiveChanged(active)); + return; + } + if (!active) { // Some other client has starting using the IME, so note // that this happened and make sure our own editor's @@ -966,9 +999,9 @@ public final class InputMethodManager { private final InputMethodManager mParentInputMethodManager; private final WeakReference<View> mServedView; - ControlledInputConnectionWrapper(Looper mainLooper, InputConnection conn, + ControlledInputConnectionWrapper(Looper icLooper, InputConnection conn, InputMethodManager inputMethodManager, View servedView) { - super(mainLooper, conn); + super(icLooper, conn); mParentInputMethodManager = inputMethodManager; mServedView = new WeakReference<>(servedView); } @@ -1014,6 +1047,18 @@ public final class InputMethodManager { + " mServedView=" + mServedView.get() + "}"; } + + void dumpDebug(ProtoOutputStream proto, long fieldId) { + // Check that the call is initiated in the main thread of the current InputConnection + // {@link InputConnection#getHandler} since the messages to IInputConnectionWrapper are + // executed on this thread. Otherwise the messages are dispatched to the correct thread + // in IInputConnectionWrapper, but this is not wanted while dumpng, for performance + // reasons. + if (getInputConnection() instanceof DumpableInputConnection && Looper.myLooper() + == getLooper()) { + ((DumpableInputConnection) getInputConnection()).dumpDebug(proto, fieldId); + } + } } private static class ImeThreadFactory implements ThreadFactory { @@ -1061,8 +1106,9 @@ public final class InputMethodManager { } @Override - public void setActive(boolean active, boolean fullscreen) { - mH.obtainMessage(MSG_SET_ACTIVE, active ? 1 : 0, fullscreen ? 1 : 0).sendToTarget(); + public void setActive(boolean active, boolean fullscreen, boolean reportToImeController) { + mH.obtainMessage(MSG_SET_ACTIVE, active ? 1 : 0, fullscreen ? 1 : 0, + reportToImeController).sendToTarget(); } @Override @@ -2174,6 +2220,7 @@ public final class InputMethodManager { * @hide */ public void notifyImeHidden(IBinder windowToken) { + ImeTracing.getInstance().triggerClientDump("InputMethodManager#notifyImeHidden", this); synchronized (mH) { try { if (mCurMethod != null && mCurRootView != null @@ -3279,6 +3326,9 @@ public final class InputMethodManager { if (mImeInsetsConsumer != null) { mImeInsetsConsumer.dumpDebug(proto, IME_INSETS_SOURCE_CONSUMER); } + if (mServedInputConnectionWrapper != null) { + mServedInputConnectionWrapper.dumpDebug(proto, INPUT_CONNECTION); + } } } } diff --git a/core/java/android/webkit/PacProcessor.java b/core/java/android/webkit/PacProcessor.java index d7b4e8bba884..21fa6fc88c13 100644 --- a/core/java/android/webkit/PacProcessor.java +++ b/core/java/android/webkit/PacProcessor.java @@ -54,7 +54,7 @@ public interface PacProcessor { */ @NonNull static PacProcessor createInstance() { - throw new UnsupportedOperationException("Not implemented"); + return WebViewFactory.getProvider().createPacProcessor(); } /** diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java index e74ce53306c1..e08ccfddc4c5 100644 --- a/core/java/android/widget/SelectionActionModeHelper.java +++ b/core/java/android/widget/SelectionActionModeHelper.java @@ -1022,7 +1022,13 @@ public final class SelectionActionModeHelper { protected SelectionResult doInBackground(Void... params) { final Runnable onTimeOut = this::onTimeOut; mTextView.postDelayed(onTimeOut, mTimeOutDuration); - final SelectionResult result = mSelectionResultSupplier.get(); + SelectionResult result = null; + try { + result = mSelectionResultSupplier.get(); + } catch (IllegalStateException e) { + // TODO(b/174300371): Only swallows the exception if the TCSession is destroyed + Log.w(LOG_TAG, "TextClassificationAsyncTask failed.", e); + } mTextView.removeCallbacks(onTimeOut); return result; } diff --git a/core/java/android/window/DisplayAreaOrganizer.java b/core/java/android/window/DisplayAreaOrganizer.java index 6e20452ad061..1ac188cf2486 100644 --- a/core/java/android/window/DisplayAreaOrganizer.java +++ b/core/java/android/window/DisplayAreaOrganizer.java @@ -101,6 +101,19 @@ public class DisplayAreaOrganizer extends WindowOrganizer { public static final int FEATURE_VENDOR_FIRST = FEATURE_SYSTEM_LAST + 1; /** + * Last possible vendor specific display area id. + * @hide + */ + public static final int FEATURE_VENDOR_LAST = FEATURE_VENDOR_FIRST + 10_000; + + /** + * Task display areas that can be created at runtime start with this value. + * @see #createTaskDisplayArea(int, int, String) + * @hide + */ + public static final int FEATURE_RUNTIME_TASK_CONTAINER_FIRST = FEATURE_VENDOR_LAST + 1; + + /** * Registers a DisplayAreaOrganizer to manage display areas for a given feature. A feature can * not be registered by multiple organizers at the same time. * @@ -132,6 +145,50 @@ public class DisplayAreaOrganizer extends WindowOrganizer { } /** + * Creates a persistent task display area. It will be added to be the top most task display area + * in the root. + * + * The new created TDA is organized by the organizer, and will be deleted on calling + * {@link #deleteTaskDisplayArea(WindowContainerToken)} or {@link #unregisterOrganizer()}. + * + * @param displayId the display to create the new task display area in. + * @param rootFeatureId the root display area to create the new task display area in. Caller can + * use {@link #FEATURE_ROOT} as the root of the logical display. + * @param name the name for the new task display area. + * @return the new created task display area. + * @throws IllegalArgumentException if failed to create a new task display area. + * @hide + */ + @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) + @CallSuper + @NonNull + public DisplayAreaAppearedInfo createTaskDisplayArea(int displayId, int rootFeatureId, + @NonNull String name) { + try { + return getController().createTaskDisplayArea( + mInterface, displayId, rootFeatureId, name); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Deletes a persistent task display area. It can only be one that created by an organizer. + * + * @throws IllegalArgumentException if failed to delete the task display area. + * @hide + */ + @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) + @CallSuper + public void deleteTaskDisplayArea(@NonNull WindowContainerToken taskDisplayArea) { + try { + getController().deleteTaskDisplayArea(taskDisplayArea); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Called when a DisplayArea of the registered window type can be controlled by this organizer. * It will not be called for the DisplayAreas that exist when {@link #registerOrganizer(int)} is * called. diff --git a/core/java/android/window/IDisplayAreaOrganizerController.aidl b/core/java/android/window/IDisplayAreaOrganizerController.aidl index edabcf8ad0de..26fa434506cf 100644 --- a/core/java/android/window/IDisplayAreaOrganizerController.aidl +++ b/core/java/android/window/IDisplayAreaOrganizerController.aidl @@ -19,6 +19,7 @@ package android.window; import android.content.pm.ParceledListSlice; import android.window.DisplayAreaAppearedInfo; import android.window.IDisplayAreaOrganizer; +import android.window.WindowContainerToken; /** @hide */ interface IDisplayAreaOrganizerController { @@ -37,4 +38,28 @@ interface IDisplayAreaOrganizerController { * Unregisters a previously registered display area organizer. */ void unregisterOrganizer(in IDisplayAreaOrganizer organizer); + + /** + * Creates a persistent task display area. It will be added to be the top most task display area + * in the root. + * + * The new created TDA is organized by the organizer, and will be deleted on calling + * {@link #deleteTaskDisplayArea(WindowContainerToken)} or {@link #unregisterOrganizer()}. + * + * @param displayId the display to create the new task display area in. + * @param rootFeatureId the root display area to create the new task display area in. Caller can + * use {@link #FEATURE_ROOT} as the root of the logical display. + * @param name the name for the new task display area. + * @return the new created task display area. + * @throws IllegalArgumentException if failed to create a new task display area. + */ + DisplayAreaAppearedInfo createTaskDisplayArea(in IDisplayAreaOrganizer organizer, int displayId, + int rootFeatureId, in String name); + + /** + * Deletes a persistent task display area. It can only be one that created by an organizer. + * + * @throws IllegalArgumentException if failed to delete the task display area. + */ + void deleteTaskDisplayArea(in WindowContainerToken taskDisplayArea); } diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java index 0eef84765890..b4e7d6a9269f 100644 --- a/core/java/android/window/TransitionInfo.java +++ b/core/java/android/window/TransitionInfo.java @@ -19,6 +19,7 @@ package android.window; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.graphics.Point; import android.graphics.Rect; import android.os.Parcel; import android.os.Parcelable; @@ -66,6 +67,9 @@ public final class TransitionInfo implements Parcelable { private final @WindowManager.TransitionOldType int mType; private final ArrayList<Change> mChanges = new ArrayList<>(); + private SurfaceControl mRootLeash; + private final Point mRootOffset = new Point(); + /** @hide */ public TransitionInfo(@WindowManager.TransitionOldType int type) { mType = type; @@ -74,6 +78,9 @@ public final class TransitionInfo implements Parcelable { private TransitionInfo(Parcel in) { mType = in.readInt(); in.readList(mChanges, null /* classLoader */); + mRootLeash = new SurfaceControl(); + mRootLeash.readFromParcel(in); + mRootOffset.readFromParcel(in); } @Override @@ -81,6 +88,8 @@ public final class TransitionInfo implements Parcelable { public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeInt(mType); dest.writeList(mChanges); + mRootLeash.writeToParcel(dest, flags); + mRootOffset.writeToParcel(dest, flags); } @NonNull @@ -103,10 +112,35 @@ public final class TransitionInfo implements Parcelable { return 0; } + /** @see #getRootLeash() */ + public void setRootLeash(@NonNull SurfaceControl leash, int offsetLeft, int offsetTop) { + mRootLeash = leash; + mRootOffset.set(offsetLeft, offsetTop); + } + public int getType() { return mType; } + /** + * @return a surfacecontrol that can serve as a parent surfacecontrol for all the changing + * participants to animate within. This will generally be placed at the highest-z-order + * shared ancestor of all participants. + */ + @NonNull + public SurfaceControl getRootLeash() { + if (mRootLeash == null) { + throw new IllegalStateException("Trying to get a leash which wasn't set"); + } + return mRootLeash; + } + + /** @return the offset (relative to the screen) of the root leash. */ + @NonNull + public Point getRootOffset() { + return mRootOffset; + } + @NonNull public List<Change> getChanges() { return mChanges; @@ -136,7 +170,7 @@ public final class TransitionInfo implements Parcelable { @Override public String toString() { StringBuilder sb = new StringBuilder(); - sb.append("{t=" + mType + " c=["); + sb.append("{t=" + mType + " ro=" + mRootOffset + " c=["); for (int i = 0; i < mChanges.size(); ++i) { if (i > 0) { sb.append(','); @@ -167,8 +201,9 @@ public final class TransitionInfo implements Parcelable { private WindowContainerToken mParent; private final SurfaceControl mLeash; private int mMode = TRANSIT_NONE; - private final Rect mStartBounds = new Rect(); - private final Rect mEndBounds = new Rect(); + private final Rect mStartAbsBounds = new Rect(); + private final Rect mEndAbsBounds = new Rect(); + private final Point mEndRelOffset = new Point(); public Change(@Nullable WindowContainerToken container, @NonNull SurfaceControl leash) { mContainer = container; @@ -181,8 +216,9 @@ public final class TransitionInfo implements Parcelable { mLeash = new SurfaceControl(); mLeash.readFromParcel(in); mMode = in.readInt(); - mStartBounds.readFromParcel(in); - mEndBounds.readFromParcel(in); + mStartAbsBounds.readFromParcel(in); + mEndAbsBounds.readFromParcel(in); + mEndRelOffset.readFromParcel(in); } /** Sets the parent of this change's container. The parent must be a participant or null. */ @@ -195,14 +231,19 @@ public final class TransitionInfo implements Parcelable { mMode = mode; } - /** Sets the bounds this container occupied before the change */ - public void setStartBounds(@Nullable Rect rect) { - mStartBounds.set(rect); + /** Sets the bounds this container occupied before the change in screen space */ + public void setStartAbsBounds(@Nullable Rect rect) { + mStartAbsBounds.set(rect); } - /** Sets the bounds this container will occupy after the change */ - public void setEndBounds(@Nullable Rect rect) { - mEndBounds.set(rect); + /** Sets the bounds this container will occupy after the change in screen space */ + public void setEndAbsBounds(@Nullable Rect rect) { + mEndAbsBounds.set(rect); + } + + /** Sets the offset of this container from its parent surface */ + public void setEndRelOffset(int left, int top) { + mEndRelOffset.set(left, top); } /** @return the container that is changing. May be null if non-remotable (eg. activity) */ @@ -230,8 +271,8 @@ public final class TransitionInfo implements Parcelable { * is coming into existence. */ @NonNull - public Rect getStartBounds() { - return mStartBounds; + public Rect getStartAbsBounds() { + return mStartAbsBounds; } /** @@ -239,8 +280,16 @@ public final class TransitionInfo implements Parcelable { * is disappearing. */ @NonNull - public Rect getEndBounds() { - return mEndBounds; + public Rect getEndAbsBounds() { + return mEndAbsBounds; + } + + /** + * @return the offset of the container's surface from its parent surface after the change. + */ + @NonNull + public Point getEndRelOffset() { + return mEndRelOffset; } /** @return the leash or surface to animate for this container */ @@ -256,8 +305,9 @@ public final class TransitionInfo implements Parcelable { dest.writeTypedObject(mParent, flags); mLeash.writeToParcel(dest, flags); dest.writeInt(mMode); - mStartBounds.writeToParcel(dest, flags); - mEndBounds.writeToParcel(dest, flags); + mStartAbsBounds.writeToParcel(dest, flags); + mEndAbsBounds.writeToParcel(dest, flags); + mEndRelOffset.writeToParcel(dest, flags); } @NonNull @@ -283,8 +333,8 @@ public final class TransitionInfo implements Parcelable { @Override public String toString() { return "{" + mContainer + "(" + mParent + ") leash=" + mLeash - + " m=" + modeToString(mMode) + " sb=" + mStartBounds - + " eb=" + mEndBounds + "}"; + + " m=" + modeToString(mMode) + " sb=" + mStartAbsBounds + + " eb=" + mEndAbsBounds + " eo=" + mEndRelOffset + "}"; } } } diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl index e6a166140d89..6d98a5982c4f 100644 --- a/core/java/com/android/internal/app/IBatteryStats.aidl +++ b/core/java/com/android/internal/app/IBatteryStats.aidl @@ -52,7 +52,7 @@ interface IBatteryStats { @UnsupportedAppUsage byte[] getStatistics(); - ParcelFileDescriptor getStatisticsStream(); + ParcelFileDescriptor getStatisticsStream(boolean updateAll); // Return true if we see the battery as currently charging. @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) diff --git a/core/java/com/android/internal/compat/ChangeReporter.java b/core/java/com/android/internal/compat/ChangeReporter.java index 7a87be35ea98..b9d3df678a91 100644 --- a/core/java/com/android/internal/compat/ChangeReporter.java +++ b/core/java/com/android/internal/compat/ChangeReporter.java @@ -223,7 +223,7 @@ public final class ChangeReporter { FrameworkStatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__SOURCE__SYSTEM_SERVER; @Retention(RetentionPolicy.SOURCE) - @IntDef(flag = true, prefix = { "STATE_" }, value = { + @IntDef(prefix = { "STATE_" }, value = { STATE_UNKNOWN_STATE, STATE_ENABLED, STATE_DISABLED, @@ -233,7 +233,7 @@ public final class ChangeReporter { } @Retention(RetentionPolicy.SOURCE) - @IntDef(flag = true, prefix = { "SOURCE_" }, value = { + @IntDef(prefix = { "SOURCE_" }, value = { SOURCE_UNKNOWN_SOURCE, SOURCE_APP_PROCESS, SOURCE_SYSTEM_SERVER diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java index 3b5fecfc600a..0ede1b86b524 100644 --- a/core/java/com/android/internal/content/FileSystemProvider.java +++ b/core/java/com/android/internal/content/FileSystemProvider.java @@ -20,15 +20,14 @@ import android.annotation.CallSuper; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ContentResolver; +import android.content.Context; import android.content.Intent; import android.content.res.AssetFileDescriptor; import android.database.Cursor; -import android.database.DatabaseUtils; import android.database.MatrixCursor; import android.database.MatrixCursor.RowBuilder; import android.graphics.Point; import android.net.Uri; -import android.os.Binder; import android.os.Bundle; import android.os.CancellationSignal; import android.os.FileObserver; @@ -39,7 +38,6 @@ import android.provider.DocumentsContract; import android.provider.DocumentsContract.Document; import android.provider.DocumentsProvider; import android.provider.MediaStore; -import android.provider.MediaStore.Files.FileColumns; import android.provider.MetadataReader; import android.system.Int64Ref; import android.text.TextUtils; @@ -66,6 +64,7 @@ import java.nio.file.attribute.BasicFileAttributes; import java.util.Arrays; import java.util.LinkedList; import java.util.List; +import java.util.Locale; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Predicate; @@ -271,8 +270,7 @@ public abstract class FileSystemProvider extends DocumentsProvider { throw new IllegalStateException("Failed to touch " + file + ": " + e); } } - MediaStore.scanFile(getContext().getContentResolver(), file); - + updateMediaStore(getContext(), file); return childId; } @@ -295,7 +293,9 @@ public abstract class FileSystemProvider extends DocumentsProvider { onDocIdChanged(afterDocId); final File afterVisibleFile = getFileForDocId(afterDocId, true); - moveInMediaStore(beforeVisibleFile, afterVisibleFile); + + updateMediaStore(getContext(), beforeVisibleFile); + updateMediaStore(getContext(), afterVisibleFile); if (!TextUtils.equals(docId, afterDocId)) { return afterDocId; @@ -323,17 +323,23 @@ public abstract class FileSystemProvider extends DocumentsProvider { onDocIdChanged(sourceDocumentId); onDocIdDeleted(sourceDocumentId); onDocIdChanged(docId); - moveInMediaStore(visibleFileBefore, getFileForDocId(docId, true)); - + // update the database + updateMediaStore(getContext(), visibleFileBefore); + updateMediaStore(getContext(), getFileForDocId(docId, true)); return docId; } - private void moveInMediaStore(@Nullable File oldVisibleFile, @Nullable File newVisibleFile) { - if (oldVisibleFile != null) { - MediaStore.scanFile(getContext().getContentResolver(), oldVisibleFile); - } - if (newVisibleFile != null) { - MediaStore.scanFile(getContext().getContentResolver(), newVisibleFile); + private static void updateMediaStore(@NonNull Context context, File file) { + if (file != null) { + final ContentResolver resolver = context.getContentResolver(); + final String noMedia = ".nomedia"; + // For file, check whether the file name is .nomedia or not. + // If yes, scan the parent directory to update all files in the directory. + if (!file.isDirectory() && file.getName().toLowerCase(Locale.ROOT).endsWith(noMedia)) { + MediaStore.scanFile(resolver, file.getParentFile()); + } else { + MediaStore.scanFile(resolver, file); + } } } @@ -354,35 +360,7 @@ public abstract class FileSystemProvider extends DocumentsProvider { onDocIdChanged(docId); onDocIdDeleted(docId); - removeFromMediaStore(visibleFile); - } - - private void removeFromMediaStore(@Nullable File visibleFile) - throws FileNotFoundException { - // visibleFolder is null if we're removing a document from external thumb drive or SD card. - if (visibleFile != null) { - final long token = Binder.clearCallingIdentity(); - - try { - final ContentResolver resolver = getContext().getContentResolver(); - final Uri externalUri = MediaStore.Files.getContentUri("external"); - final Bundle queryArgs = new Bundle(); - queryArgs.putInt(MediaStore.QUERY_ARG_MATCH_PENDING, MediaStore.MATCH_INCLUDE); - - // Remove the media store entry corresponding to visibleFile and if it is a - // directory, also remove media store entries for any files inside this directory. - // Logic borrowed from com.android.providers.media.scan.ModernMediaScanner. - final String pathEscapedForLike = DatabaseUtils.escapeForLike( - visibleFile.getAbsolutePath()); - ContentResolver.includeSqlSelectionArgs(queryArgs, - FileColumns.DATA + " LIKE ? ESCAPE '\\' OR " - + FileColumns.DATA + " LIKE ? ESCAPE '\\'", - new String[] {pathEscapedForLike + "/%", pathEscapedForLike}); - resolver.delete(externalUri, queryArgs); - } finally { - Binder.restoreCallingIdentity(token); - } - } + updateMediaStore(getContext(), visibleFile); } @Override diff --git a/core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java b/core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java index 70505bc5895b..6c01780dac76 100644 --- a/core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java +++ b/core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java @@ -33,7 +33,10 @@ import java.util.List; * * @param <S> the concrete remote service class * @param <I> the interface of the binder service + * + * @deprecated Use {@link ServiceConnector} to manage remote service connections */ +@Deprecated public abstract class AbstractMultiplePendingRequestsRemoteService<S extends AbstractMultiplePendingRequestsRemoteService<S, I>, I extends IInterface> extends AbstractRemoteService<S, I> { diff --git a/core/java/com/android/internal/infra/AbstractRemoteService.java b/core/java/com/android/internal/infra/AbstractRemoteService.java index 722e5c102fcf..f63ac2e14e20 100644 --- a/core/java/com/android/internal/infra/AbstractRemoteService.java +++ b/core/java/com/android/internal/infra/AbstractRemoteService.java @@ -58,9 +58,12 @@ import java.util.ArrayList; * @param <S> the concrete remote service class * @param <I> the interface of the binder service * + * @deprecated Use {@link ServiceConnector} to manage remote service connections + * * @hide */ //TODO(b/117779333): improve javadoc above instead of using Autofill as an example +@Deprecated public abstract class AbstractRemoteService<S extends AbstractRemoteService<S, I>, I extends IInterface> implements DeathRecipient { private static final int MSG_BIND = 1; diff --git a/core/java/com/android/internal/infra/AbstractSinglePendingRequestRemoteService.java b/core/java/com/android/internal/infra/AbstractSinglePendingRequestRemoteService.java index 2ebf2fd820d8..0d9af8c8bcf5 100644 --- a/core/java/com/android/internal/infra/AbstractSinglePendingRequestRemoteService.java +++ b/core/java/com/android/internal/infra/AbstractSinglePendingRequestRemoteService.java @@ -33,8 +33,11 @@ import java.io.PrintWriter; * @param <S> the concrete remote service class * @param <I> the interface of the binder service * + * @deprecated Use {@link ServiceConnector} to manage remote service connections + * * @hide */ +@Deprecated public abstract class AbstractSinglePendingRequestRemoteService<S extends AbstractSinglePendingRequestRemoteService<S, I>, I extends IInterface> extends AbstractRemoteService<S, I> { diff --git a/core/java/com/android/internal/infra/OWNERS b/core/java/com/android/internal/infra/OWNERS new file mode 100644 index 000000000000..45503582b2c5 --- /dev/null +++ b/core/java/com/android/internal/infra/OWNERS @@ -0,0 +1,6 @@ +per-file AndroidFuture.java = eugenesusla@google.com +per-file RemoteStream.java = eugenesusla@google.com +per-file PerUser.java = eugenesusla@google.com +per-file ServiceConnector.java = eugenesusla@google.com +per-file AndroidFuture.aidl = eugenesusla@google.com +per-file IAndroidFuture.aidl = eugenesusla@google.com
\ No newline at end of file diff --git a/core/java/com/android/internal/inputmethod/Completable.java b/core/java/com/android/internal/inputmethod/Completable.java index 16473b98b813..d8d1a7df6aa8 100644 --- a/core/java/com/android/internal/inputmethod/Completable.java +++ b/core/java/com/android/internal/inputmethod/Completable.java @@ -19,6 +19,7 @@ package com.android.internal.inputmethod; import android.annotation.AnyThread; import android.annotation.NonNull; import android.annotation.Nullable; +import android.util.Log; import com.android.internal.annotations.GuardedBy; @@ -275,4 +276,53 @@ public final class Completable { */ public static final class InputBindResult extends Values<com.android.internal.view.InputBindResult> { } + + /** + * Await the result by the {@link Completable.Int}, and log it if there is no result after + * given timeout. + * + * @return the result once {@link ValueBase#onComplete()} + */ + @AnyThread + public static int getResultOrZero(@NonNull Completable.Int value, String tag, + @NonNull String methodName, @Nullable CancellationGroup cancellationGroup, + int maxWaitTime) { + final boolean timedOut = value.await(maxWaitTime, TimeUnit.MILLISECONDS, cancellationGroup); + if (value.hasValue()) { + return value.getValue(); + } + logInternal(tag, methodName, timedOut, maxWaitTime, 0); + return 0; + } + + /** + * Await the result by the {@link Completable.Values}, and log it if there is no result after + * given timeout. + * + * @return the result once {@link ValueBase#onComplete()} + */ + @AnyThread + @Nullable + public static <T> T getResultOrNull(@NonNull Completable.Values<T> value, String tag, + @NonNull String methodName, @Nullable CancellationGroup cancellationGroup, + int maxWaitTime) { + final boolean timedOut = value.await(maxWaitTime, TimeUnit.MILLISECONDS, cancellationGroup); + if (value.hasValue()) { + return value.getValue(); + } + logInternal(tag, methodName, timedOut, maxWaitTime, null); + return null; + } + + @AnyThread + private static void logInternal(String tag, @Nullable String methodName, boolean timedOut, + int maxWaitTime, @Nullable Object defaultValue) { + if (timedOut) { + Log.w(tag, methodName + " didn't respond in " + maxWaitTime + " msec." + + " Returning default: " + defaultValue); + } else { + Log.w(tag, methodName + " was canceled before complete. Returning default: " + + defaultValue); + } + } } diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java index c0d46f632500..571ac1bbeac6 100644 --- a/core/java/com/android/internal/jank/FrameTracker.java +++ b/core/java/com/android/internal/jank/FrameTracker.java @@ -37,34 +37,33 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai //TODO (163431584): need also consider other refresh rates. private static final long JANK_THRESHOLD_NANOS = 1000000000 / 60; private static final long UNKNOWN_TIMESTAMP = -1; + public static final int NANOS_IN_MILLISECOND = 1_000_000; private final HardwareRendererObserver mObserver; + private final int mTraceThresholdMissedFrames; + private final int mTraceThresholdFrameTimeMillis; private final ThreadedRendererWrapper mRendererWrapper; private final FrameMetricsWrapper mMetricsWrapper; private long mBeginTime = UNKNOWN_TIMESTAMP; private long mEndTime = UNKNOWN_TIMESTAMP; - private boolean mShouldTriggerTrace; private boolean mSessionEnd; + private boolean mCancelled = false; private int mTotalFramesCount = 0; private int mMissedFramesCount = 0; private long mMaxFrameTimeNanos = 0; private Session mSession; - /** - * Constructor of FrameTracker. - * @param session a trace session. - * @param handler a handler for handling callbacks. - * @param renderer a ThreadedRendererWrapper instance. - * @param metrics a FrameMetricsWrapper instance. - */ public FrameTracker(@NonNull Session session, @NonNull Handler handler, - @NonNull ThreadedRendererWrapper renderer, @NonNull FrameMetricsWrapper metrics) { + @NonNull ThreadedRendererWrapper renderer, @NonNull FrameMetricsWrapper metrics, + int traceThresholdMissedFrames, int traceThresholdFrameTimeMillis) { mSession = session; mRendererWrapper = renderer; mMetricsWrapper = metrics; mObserver = new HardwareRendererObserver(this, mMetricsWrapper.getTiming(), handler); + mTraceThresholdMissedFrames = traceThresholdMissedFrames; + mTraceThresholdFrameTimeMillis = traceThresholdFrameTimeMillis; } /** @@ -109,13 +108,15 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai } Trace.endAsyncSection(mSession.getName(), (int) mBeginTime); mRendererWrapper.removeObserver(mObserver); - mBeginTime = UNKNOWN_TIMESTAMP; - mEndTime = UNKNOWN_TIMESTAMP; - mShouldTriggerTrace = false; + mCancelled = true; } @Override public synchronized void onFrameMetricsAvailable(int dropCountSinceLastInvocation) { + if (mCancelled) { + return; + } + // Since this callback might come a little bit late after the end() call. // We should keep tracking the begin / end timestamp. // Then compare with vsync timestamp to check if the frame is in the duration of the CUJ. @@ -136,13 +137,14 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai Trace.traceCounter(Trace.TRACE_TAG_APP, mSession.getName() + "#totalFrames", mTotalFramesCount); Trace.traceCounter(Trace.TRACE_TAG_APP, mSession.getName() + "#maxFrameTimeMillis", - (int) (mMaxFrameTimeNanos / 1_000_000)); + (int) (mMaxFrameTimeNanos / NANOS_IN_MILLISECOND)); // Trigger perfetto if necessary. - if (mShouldTriggerTrace) { - if (DEBUG) { - Log.v(TAG, "Found janky frame, triggering perfetto."); - } + boolean overMissedFramesThreshold = mTraceThresholdMissedFrames != -1 + && mMissedFramesCount >= mTraceThresholdMissedFrames; + boolean overFrameTimeThreshold = mTraceThresholdFrameTimeMillis != -1 + && mMaxFrameTimeNanos >= mTraceThresholdFrameTimeMillis * NANOS_IN_MILLISECOND; + if (overMissedFramesThreshold || overFrameTimeThreshold) { triggerPerfetto(); } if (mSession.logToStatsd()) { @@ -168,7 +170,6 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai if (isJankyFrame) { mMissedFramesCount += 1; - mShouldTriggerTrace = true; } } diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java index fcaa963feda2..771a72c98a7a 100644 --- a/core/java/com/android/internal/jank/InteractionJankMonitor.java +++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java @@ -36,7 +36,10 @@ import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_IN import android.annotation.IntDef; import android.annotation.NonNull; +import android.os.Build; +import android.os.HandlerExecutor; import android.os.HandlerThread; +import android.provider.DeviceConfig; import android.util.Log; import android.util.SparseArray; import android.view.View; @@ -47,6 +50,7 @@ import com.android.internal.jank.FrameTracker.ThreadedRendererWrapper; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; /** @@ -55,9 +59,21 @@ import java.util.concurrent.TimeUnit; */ public class InteractionJankMonitor { private static final String TAG = InteractionJankMonitor.class.getSimpleName(); - private static final boolean DEBUG = false; private static final String DEFAULT_WORKER_NAME = TAG + "-Worker"; private static final long DEFAULT_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(5L); + private static final String SETTINGS_ENABLED_KEY = "enabled"; + private static final String SETTINGS_SAMPLING_INTERVAL_KEY = "sampling_interval"; + private static final String SETTINGS_THRESHOLD_MISSED_FRAMES_KEY = + "trace_threshold_missed_frames"; + private static final String SETTINGS_THRESHOLD_FRAME_TIME_MILLIS_KEY = + "trace_threshold_frame_time_millis"; + /** Default to being enabled on debug builds. */ + private static final boolean DEFAULT_ENABLED = Build.IS_DEBUGGABLE; + /** Default to collecting data for all CUJs. */ + private static final int DEFAULT_SAMPLING_INTERVAL = 1; + /** Default to triggering trace if 3 frames are missed OR a frame takes at least 64ms */ + private static final int DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES = 3; + private static final int DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS = 64; // Every value must have a corresponding entry in CUJ_STATSD_INTERACTION_TYPE. public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE = 0; @@ -106,12 +122,20 @@ public class InteractionJankMonitor { private static volatile InteractionJankMonitor sInstance; + private final DeviceConfig.OnPropertiesChangedListener mPropertiesChangedListener = + this::updateProperties; + private ThreadedRendererWrapper mRenderer; private FrameMetricsWrapper mMetrics; private SparseArray<FrameTracker> mRunningTrackers; private SparseArray<Runnable> mTimeoutActions; private HandlerThread mWorker; + private boolean mInitialized; + private boolean mEnabled = DEFAULT_ENABLED; + private int mSamplingInterval = DEFAULT_SAMPLING_INTERVAL; + private int mTraceThresholdMissedFrames = DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES; + private int mTraceThresholdFrameTimeMillis = DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS; /** @hide */ @IntDef({ @@ -134,10 +158,12 @@ public class InteractionJankMonitor { CUJ_NOTIFICATION_APP_START, }) @Retention(RetentionPolicy.SOURCE) - public @interface CujType {} + public @interface CujType { + } /** * Get the singleton of InteractionJankMonitor. + * * @return instance of InteractionJankMonitor */ public static InteractionJankMonitor getInstance() { @@ -154,6 +180,7 @@ public class InteractionJankMonitor { /** * This constructor should be only public to tests. + * * @param worker the worker thread for the callbacks */ @VisibleForTesting @@ -165,11 +192,11 @@ public class InteractionJankMonitor { /** * Init InteractionJankMonitor for later instrumentation. + * * @param view Any view in the view tree to get context and ThreadedRenderer. * @return boolean true if the instance has been initialized successfully. */ public boolean init(@NonNull View view) { - //TODO (163505250): This should be no-op if not in droid food rom. if (!mInitialized) { synchronized (this) { if (!mInitialized) { @@ -180,7 +207,20 @@ public class InteractionJankMonitor { mRenderer = new ThreadedRendererWrapper(view.getThreadedRenderer()); mMetrics = new FrameMetricsWrapper(); mWorker.start(); + mEnabled = DEFAULT_ENABLED; + mSamplingInterval = DEFAULT_SAMPLING_INTERVAL; mInitialized = true; + + // Post initialization to the background in case we're running on the main + // thread. + mWorker.getThreadHandler().post( + () -> mPropertiesChangedListener.onPropertiesChanged( + DeviceConfig.getProperties( + DeviceConfig.NAMESPACE_INTERACTION_JANK_MONITOR))); + DeviceConfig.addOnPropertiesChangedListener( + DeviceConfig.NAMESPACE_INTERACTION_JANK_MONITOR, + new HandlerExecutor(mWorker.getThreadHandler()), + mPropertiesChangedListener); } } } @@ -189,6 +229,7 @@ public class InteractionJankMonitor { /** * Create a {@link FrameTracker} instance. + * * @param session the session associates with this tracker * @return instance of the FrameTracker */ @@ -196,47 +237,50 @@ public class InteractionJankMonitor { public FrameTracker createFrameTracker(Session session) { synchronized (this) { if (!mInitialized) return null; - return new FrameTracker(session, mWorker.getThreadHandler(), mRenderer, mMetrics); + return new FrameTracker(session, mWorker.getThreadHandler(), mRenderer, mMetrics, + mTraceThresholdMissedFrames, mTraceThresholdFrameTimeMillis); } } /** * Begin a trace session, must invoke {@link #init(View)} before invoking this method. + * * @param cujType the specific {@link InteractionJankMonitor.CujType}. * @return boolean true if the tracker is started successfully, false otherwise. */ public boolean begin(@CujType int cujType) { - //TODO (163505250): This should be no-op if not in droid food rom. synchronized (this) { - return begin(cujType, 0L /* timeout */); + return begin(cujType, DEFAULT_TIMEOUT_MS); } } /** * Begin a trace session, must invoke {@link #init(View)} before invoking this method. + * * @param cujType the specific {@link InteractionJankMonitor.CujType}. * @param timeout the elapsed time in ms until firing the timeout action. * @return boolean true if the tracker is started successfully, false otherwise. */ public boolean begin(@CujType int cujType, long timeout) { - //TODO (163505250): This should be no-op if not in droid food rom. synchronized (this) { if (!mInitialized) { Log.d(TAG, "Not initialized!", new Throwable()); return false; } - Session session = new Session(cujType); - FrameTracker tracker = getTracker(session); + boolean shouldSample = ThreadLocalRandom.current().nextInt() % mSamplingInterval == 0; + if (!mEnabled || !shouldSample) { + return false; + } + FrameTracker tracker = getTracker(cujType); // Skip subsequent calls if we already have an ongoing tracing. if (tracker != null) return false; // begin a new trace session. - tracker = createFrameTracker(session); + tracker = createFrameTracker(new Session(cujType)); mRunningTrackers.put(cujType, tracker); tracker.begin(); // Cancel the trace if we don't get an end() call in specified duration. - timeout = timeout > 0L ? timeout : DEFAULT_TIMEOUT_MS; Runnable timeoutAction = () -> cancel(cujType); mTimeoutActions.put(cujType, timeoutAction); mWorker.getThreadHandler().postDelayed(timeoutAction, timeout); @@ -246,6 +290,7 @@ public class InteractionJankMonitor { /** * End a trace session, must invoke {@link #init(View)} before invoking this method. + * * @param cujType the specific {@link InteractionJankMonitor.CujType}. * @return boolean true if the tracker is ended successfully, false otherwise. */ @@ -263,18 +308,18 @@ public class InteractionJankMonitor { mTimeoutActions.remove(cujType); } - Session session = new Session(cujType); - FrameTracker tracker = getTracker(session); + FrameTracker tracker = getTracker(cujType); // Skip this call since we haven't started a trace yet. if (tracker == null) return false; tracker.end(); - mRunningTrackers.remove(session.getCuj()); + mRunningTrackers.remove(cujType); return true; } } /** * Cancel the trace session, must invoke {@link #init(View)} before invoking this method. + * * @return boolean true if the tracker is cancelled successfully, false otherwise. */ public boolean cancel(@CujType int cujType) { @@ -291,48 +336,38 @@ public class InteractionJankMonitor { mTimeoutActions.remove(cujType); } - Session session = new Session(cujType); - FrameTracker tracker = getTracker(session); + FrameTracker tracker = getTracker(cujType); // Skip this call since we haven't started a trace yet. if (tracker == null) return false; tracker.cancel(); - mRunningTrackers.remove(session.getCuj()); + mRunningTrackers.remove(cujType); return true; } } - private void destroy() { + private FrameTracker getTracker(@CujType int cuj) { synchronized (this) { - int trackers = mRunningTrackers.size(); - for (int i = 0; i < trackers; i++) { - mRunningTrackers.valueAt(i).cancel(); - } - mRunningTrackers = null; - mTimeoutActions.clear(); - mTimeoutActions = null; - mWorker.quit(); - mWorker = null; + if (!mInitialized) return null; + return mRunningTrackers.get(cuj); } } - /** - * Abandon current instance. - */ - @VisibleForTesting - public static void abandon() { - if (sInstance == null) return; - synchronized (InteractionJankMonitor.class) { - if (sInstance == null) return; - sInstance.destroy(); - sInstance = null; + private void updateProperties(DeviceConfig.Properties properties) { + synchronized (this) { + mSamplingInterval = properties.getInt(SETTINGS_SAMPLING_INTERVAL_KEY, + DEFAULT_SAMPLING_INTERVAL); + mEnabled = properties.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED); + mTraceThresholdMissedFrames = properties.getInt(SETTINGS_THRESHOLD_MISSED_FRAMES_KEY, + DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES); + mTraceThresholdFrameTimeMillis = properties.getInt( + SETTINGS_THRESHOLD_FRAME_TIME_MILLIS_KEY, + DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS); } } - private FrameTracker getTracker(Session session) { - synchronized (this) { - if (!mInitialized) return null; - return mRunningTrackers.get(session.getCuj()); - } + @VisibleForTesting + public DeviceConfig.OnPropertiesChangedListener getPropertiesChangedListener() { + return mPropertiesChangedListener; } /** @@ -402,12 +437,14 @@ public class InteractionJankMonitor { * A class to represent a session. */ public static class Session { - private @CujType int mCujType; + @CujType + private int mCujType; public Session(@CujType int cujType) { mCujType = cujType; } + @CujType public int getCuj() { return mCujType; } diff --git a/core/java/com/android/internal/notification/SystemNotificationChannels.java b/core/java/com/android/internal/notification/SystemNotificationChannels.java index 41be5c493d95..2237efc9e2b6 100644 --- a/core/java/com/android/internal/notification/SystemNotificationChannels.java +++ b/core/java/com/android/internal/notification/SystemNotificationChannels.java @@ -57,6 +57,7 @@ public class SystemNotificationChannels { public static String HEAVY_WEIGHT_APP = "HEAVY_WEIGHT_APP"; public static String SYSTEM_CHANGES = "SYSTEM_CHANGES"; public static String DO_NOT_DISTURB = "DO_NOT_DISTURB"; + public static String ACCESSIBILITY_MAGNIFICATION = "ACCESSIBILITY_MAGNIFICATION"; public static void createAll(Context context) { final NotificationManager nm = context.getSystemService(NotificationManager.class); @@ -191,6 +192,13 @@ public class SystemNotificationChannels { NotificationManager.IMPORTANCE_LOW); channelsList.add(dndChanges); + final NotificationChannel newFeaturePrompt = new NotificationChannel( + ACCESSIBILITY_MAGNIFICATION, + context.getString(R.string.notification_channel_accessibility_magnification), + NotificationManager.IMPORTANCE_HIGH); + newFeaturePrompt.setBlockable(true); + channelsList.add(newFeaturePrompt); + nm.createNotificationChannels(channelsList); } diff --git a/core/java/com/android/internal/os/BaseCommand.java b/core/java/com/android/internal/os/BaseCommand.java index c110b263e781..c85b5d7aa7a6 100644 --- a/core/java/com/android/internal/os/BaseCommand.java +++ b/core/java/com/android/internal/os/BaseCommand.java @@ -18,9 +18,10 @@ package com.android.internal.os; import android.compat.annotation.UnsupportedAppUsage; -import android.os.BasicShellCommandHandler; import android.os.Build; +import com.android.modules.utils.BasicShellCommandHandler; + import java.io.PrintStream; public abstract class BaseCommand { diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java index 267674592b02..9e59e50cffd0 100644 --- a/core/java/com/android/internal/os/BatteryStatsHelper.java +++ b/core/java/com/android/internal/os/BatteryStatsHelper.java @@ -203,7 +203,7 @@ public class BatteryStatsHelper { } } return getStats(IBatteryStats.Stub.asInterface( - ServiceManager.getService(BatteryStats.SERVICE_NAME))); + ServiceManager.getService(BatteryStats.SERVICE_NAME)), true); } @UnsupportedAppUsage @@ -223,8 +223,13 @@ public class BatteryStatsHelper { @UnsupportedAppUsage public BatteryStats getStats() { + return getStats(true /* updateAll */); + } + + /** Retrieves stats from BatteryService, optionally getting updated numbers */ + public BatteryStats getStats(boolean updateAll) { if (mStats == null) { - load(); + load(updateAll); } return mStats; } @@ -720,19 +725,23 @@ public class BatteryStatsHelper { @UnsupportedAppUsage private void load() { + load(true); + } + + private void load(boolean updateAll) { if (mBatteryInfo == null) { return; } - mStats = getStats(mBatteryInfo); + mStats = getStats(mBatteryInfo, updateAll); if (mCollectBatteryBroadcast) { mBatteryBroadcast = mContext.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); } } - private static BatteryStatsImpl getStats(IBatteryStats service) { + private static BatteryStatsImpl getStats(IBatteryStats service, boolean updateAll) { try { - ParcelFileDescriptor pfd = service.getStatisticsStream(); + ParcelFileDescriptor pfd = service.getStatisticsStream(updateAll); if (pfd != null) { if (false) { Log.d(TAG, "selinux context: " diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 7c442b408624..7571f5db8cad 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -98,6 +98,8 @@ import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidActiveTimeRead import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader; import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader; import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader; +import com.android.internal.power.MeasuredEnergyArray; +import com.android.internal.power.MeasuredEnergyStats; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastPrintWriter; import com.android.internal.util.FastXmlSerializer; @@ -155,7 +157,7 @@ public class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - static final int VERSION = 189 + (USE_OLD_HISTORY ? 1000 : 0); + static final int VERSION = 190 + (USE_OLD_HISTORY ? 1000 : 0); // The maximum number of names wakelocks we will keep track of // per uid; once the limit is reached, we batch the remaining wakelocks @@ -349,12 +351,20 @@ public class BatteryStatsImpl extends BatteryStats { } /** interface to update rail information for power monitor */ - public interface RailEnergyDataCallback { + public interface MeasuredEnergyRetriever { /** Function to fill the map for the rail data stats * Used for power monitoring feature * @param railStats */ void fillRailDataStats(RailStats railStats); + /** + * Function to get energy consumption data + * + * @return an array of measured energy (in microjoules) since boot, will be null if + * measured energy data is unavailable + */ + @Nullable + MeasuredEnergyArray getEnergyConsumptionData(); } public static abstract class UserInfoProvider { @@ -391,7 +401,7 @@ public class BatteryStatsImpl extends BatteryStats { } }; - public final RailEnergyDataCallback mRailEnergyDataCallback; + public final MeasuredEnergyRetriever mMeasuredEnergyRetriever; /** * This handler is running on {@link BackgroundThread}. @@ -624,8 +634,10 @@ public class BatteryStatsImpl extends BatteryStats { int UPDATE_WIFI = 0x02; int UPDATE_RADIO = 0x04; int UPDATE_BT = 0x08; - int UPDATE_RPM = 0x10; // 16 - int UPDATE_ALL = UPDATE_CPU | UPDATE_WIFI | UPDATE_RADIO | UPDATE_BT | UPDATE_RPM; + int UPDATE_RPM = 0x10; + int UPDATE_ENERGY = 0x20; + int UPDATE_ALL = + UPDATE_CPU | UPDATE_WIFI | UPDATE_RADIO | UPDATE_BT | UPDATE_RPM | UPDATE_ENERGY; Future<?> scheduleSync(String reason, int flags); Future<?> scheduleCpuSyncDueToRemovedUid(int uid); @@ -633,8 +645,11 @@ public class BatteryStatsImpl extends BatteryStats { long delayMillis); Future<?> scheduleCopyFromAllUidsCpuTimes(boolean onBattery, boolean onBatteryScreenOff); Future<?> scheduleCpuSyncDueToSettingChange(); - Future<?> scheduleCpuSyncDueToScreenStateChange(boolean onBattery, - boolean onBatteryScreenOff); + /** + * Schedule a sync because of a screen state change. + */ + Future<?> scheduleSyncDueToScreenStateChange(int flags, boolean onBattery, + boolean onBatteryScreenOff, int screenState); Future<?> scheduleCpuSyncDueToWakelockChange(long delayMillis); void cancelCpuSyncDueToWakelockChange(); Future<?> scheduleSyncDueToBatteryLevelChange(long delayMillis); @@ -932,6 +947,13 @@ public class BatteryStatsImpl extends BatteryStats { int mWifiRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW; /** + * Accumulated energy consumption of various consumers while on battery. + * If energy consumer data is unavailable this will be null. + */ + @GuardedBy("this") + MeasuredEnergyStats mBatteryMeasuredEnergyStats; + + /** * These provide time bases that discount the time the device is plugged * in to power. */ @@ -1123,7 +1145,7 @@ public class BatteryStatsImpl extends BatteryStats { mBatteryStatsHistory = null; mHandler = null; mPlatformIdleStateCallback = null; - mRailEnergyDataCallback = null; + mMeasuredEnergyRetriever = null; mUserInfoProvider = null; mConstants = new Constants(mHandler); clearHistoryLocked(); @@ -1424,7 +1446,8 @@ public class BatteryStatsImpl extends BatteryStats { * @param in the parcel to read from * @return the Counter or null. */ - public static @Nullable Counter readCounterFromParcel(TimeBase timeBase, Parcel in) { + @Nullable + public static Counter readCounterFromParcel(TimeBase timeBase, Parcel in) { if (in.readInt() == 0) { return null; } @@ -3949,7 +3972,7 @@ public class BatteryStatsImpl extends BatteryStats { @GuardedBy("this") public void updateTimeBasesLocked(boolean unplugged, int screenState, long uptimeUs, long realtimeUs) { - final boolean screenOff = !isScreenOn(screenState); + final boolean screenOff = !Display.isOnState(screenState); final boolean updateOnBatteryTimeBase = unplugged != mOnBatteryTimeBase.isRunning(); final boolean updateOnBatteryScreenOffTimeBase = (unplugged && screenOff) != mOnBatteryScreenOffTimeBase.isRunning(); @@ -5012,16 +5035,16 @@ public class BatteryStatsImpl extends BatteryStats { } boolean updateHistory = false; - if (isScreenDoze(state) && !isScreenDoze(oldState)) { + if (Display.isDozeState(state) && !Display.isDozeState(oldState)) { mHistoryCur.states |= HistoryItem.STATE_SCREEN_DOZE_FLAG; mScreenDozeTimer.startRunningLocked(elapsedRealtimeMs); updateHistory = true; - } else if (isScreenDoze(oldState) && !isScreenDoze(state)) { + } else if (Display.isDozeState(oldState) && !Display.isDozeState(state)) { mHistoryCur.states &= ~HistoryItem.STATE_SCREEN_DOZE_FLAG; mScreenDozeTimer.stopRunningLocked(elapsedRealtimeMs); updateHistory = true; } - if (isScreenOn(state)) { + if (Display.isOnState(state)) { mHistoryCur.states |= HistoryItem.STATE_SCREEN_ON_FLAG; if (DEBUG_HISTORY) Slog.v(TAG, "Screen on to: " + Integer.toHexString(mHistoryCur.states)); @@ -5031,7 +5054,7 @@ public class BatteryStatsImpl extends BatteryStats { .startRunningLocked(elapsedRealtimeMs); } updateHistory = true; - } else if (isScreenOn(oldState)) { + } else if (Display.isOnState(oldState)) { mHistoryCur.states &= ~HistoryItem.STATE_SCREEN_ON_FLAG; if (DEBUG_HISTORY) Slog.v(TAG, "Screen off to: " + Integer.toHexString(mHistoryCur.states)); @@ -5047,15 +5070,21 @@ public class BatteryStatsImpl extends BatteryStats { + Display.stateToString(state)); addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs); } - mExternalSync.scheduleCpuSyncDueToScreenStateChange( - mOnBatteryTimeBase.isRunning(), mOnBatteryScreenOffTimeBase.isRunning()); - if (isScreenOn(state)) { + int updateFlag = ExternalStatsSync.UPDATE_CPU; + if (mBatteryMeasuredEnergyStats != null && mBatteryMeasuredEnergyStats.hasSubsystem( + MeasuredEnergyArray.SUBSYSTEM_DISPLAY)) { + updateFlag |= ExternalStatsSync.UPDATE_ENERGY; + } + mExternalSync.scheduleSyncDueToScreenStateChange(updateFlag, + mOnBatteryTimeBase.isRunning(), mOnBatteryScreenOffTimeBase.isRunning(), + mScreenState); + if (Display.isOnState(state)) { updateTimeBasesLocked(mOnBatteryTimeBase.isRunning(), state, uptimeMs * 1000, elapsedRealtimeMs * 1000); // Fake a wake lock, so we consider the device waked as long as the screen is on. noteStartWakeLocked(-1, -1, null, "screen", null, WAKE_TYPE_PARTIAL, false, elapsedRealtimeMs, uptimeMs); - } else if (isScreenOn(oldState)) { + } else if (Display.isOnState(oldState)) { noteStopWakeLocked(-1, -1, null, "screen", "screen", WAKE_TYPE_PARTIAL, elapsedRealtimeMs, uptimeMs); updateTimeBasesLocked(mOnBatteryTimeBase.isRunning(), state, @@ -7039,6 +7068,26 @@ public class BatteryStatsImpl extends BatteryStats { } } + @Override + public long getScreenOnEnergy() { + if (mBatteryMeasuredEnergyStats == null || !mBatteryMeasuredEnergyStats.hasSubsystem( + MeasuredEnergyArray.SUBSYSTEM_DISPLAY)) { + return ENERGY_DATA_UNAVAILABLE; + } + return mBatteryMeasuredEnergyStats.getAccumulatedBucketEnergy( + MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON); + } + + @Override + public long getScreenDozeEnergy() { + if (mBatteryMeasuredEnergyStats == null || !mBatteryMeasuredEnergyStats.hasSubsystem( + MeasuredEnergyArray.SUBSYSTEM_DISPLAY)) { + return ENERGY_DATA_UNAVAILABLE; + } + return mBatteryMeasuredEnergyStats.getAccumulatedBucketEnergy( + MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_DOZE); + } + @Override public long getStartClockTime() { final long currentTimeMs = System.currentTimeMillis(); if ((currentTimeMs > MILLISECONDS_IN_YEAR @@ -10457,12 +10506,12 @@ public class BatteryStatsImpl extends BatteryStats { } public BatteryStatsImpl(File systemDir, Handler handler, PlatformIdleStateCallback cb, - RailEnergyDataCallback railStatsCb, UserInfoProvider userInfoProvider) { - this(new SystemClocks(), systemDir, handler, cb, railStatsCb, userInfoProvider); + MeasuredEnergyRetriever energyStatsCb, UserInfoProvider userInfoProvider) { + this(new SystemClocks(), systemDir, handler, cb, energyStatsCb, userInfoProvider); } private BatteryStatsImpl(Clocks clocks, File systemDir, Handler handler, - PlatformIdleStateCallback cb, RailEnergyDataCallback railStatsCb, + PlatformIdleStateCallback cb, MeasuredEnergyRetriever energyStatsCb, UserInfoProvider userInfoProvider) { init(clocks); @@ -10563,12 +10612,19 @@ public class BatteryStatsImpl extends BatteryStats { clearHistoryLocked(); updateDailyDeadlineLocked(); mPlatformIdleStateCallback = cb; - mRailEnergyDataCallback = railStatsCb; + mMeasuredEnergyRetriever = energyStatsCb; mUserInfoProvider = userInfoProvider; // Notify statsd that the system is initially not in doze. mDeviceIdleMode = DEVICE_IDLE_MODE_OFF; FrameworkStatsLog.write(FrameworkStatsLog.DEVICE_IDLE_MODE_STATE_CHANGED, mDeviceIdleMode); + + final MeasuredEnergyArray energyStats = mMeasuredEnergyRetriever.getEnergyConsumptionData(); + // If measured energy is not available, it is not supported and + // mBatteryMeasuredEnergyStats should be left null. + if (energyStats != null) { + mBatteryMeasuredEnergyStats = new MeasuredEnergyStats(energyStats, mScreenState); + } } @UnsupportedAppUsage @@ -10588,7 +10644,7 @@ public class BatteryStatsImpl extends BatteryStats { mBatteryStatsHistory = new BatteryStatsHistory(this, mHistoryBuffer); readFromParcel(p); mPlatformIdleStateCallback = null; - mRailEnergyDataCallback = null; + mMeasuredEnergyRetriever = null; } public void setPowerProfileLocked(PowerProfile profile) { @@ -11097,19 +11153,6 @@ public class BatteryStatsImpl extends BatteryStats { return mCharging; } - public boolean isScreenOn(int state) { - return state == Display.STATE_ON || state == Display.STATE_VR - || state == Display.STATE_ON_SUSPEND; - } - - public boolean isScreenOff(int state) { - return state == Display.STATE_OFF; - } - - public boolean isScreenDoze(int state) { - return state == Display.STATE_DOZE || state == Display.STATE_DOZE_SUSPEND; - } - void initTimes(long uptimeUs, long realtimeUs) { mStartClockTimeMs = System.currentTimeMillis(); mOnBatteryTimeBase.init(uptimeUs, realtimeUs); @@ -11152,11 +11195,11 @@ public class BatteryStatsImpl extends BatteryStats { mOnBatteryTimeBase.reset(uptimeUs, realtimeUs); mOnBatteryScreenOffTimeBase.reset(uptimeUs, realtimeUs); if ((mHistoryCur.states&HistoryItem.STATE_BATTERY_PLUGGED_FLAG) == 0) { - if (isScreenOn(mScreenState)) { + if (Display.isOnState(mScreenState)) { mDischargeScreenOnUnplugLevel = mHistoryCur.batteryLevel; mDischargeScreenDozeUnplugLevel = 0; mDischargeScreenOffUnplugLevel = 0; - } else if (isScreenDoze(mScreenState)) { + } else if (Display.isDozeState(mScreenState)) { mDischargeScreenOnUnplugLevel = 0; mDischargeScreenDozeUnplugLevel = mHistoryCur.batteryLevel; mDischargeScreenOffUnplugLevel = 0; @@ -11286,6 +11329,11 @@ public class BatteryStatsImpl extends BatteryStats { mTmpRailStats.reset(); + if (mBatteryMeasuredEnergyStats != null) { + mBatteryMeasuredEnergyStats.reset(); + mExternalSync.scheduleSync("reset", ExternalStatsSync.UPDATE_ENERGY); + } + resetIfNotNull(mSystemServerCpuTimesUs, false, elapsedRealtimeUs); resetIfNotNull(mSystemServerThreadCpuTimesUs, false, elapsedRealtimeUs); resetIfNotNull(mBinderThreadCpuTimesUs, false, elapsedRealtimeUs); @@ -11339,19 +11387,19 @@ public class BatteryStatsImpl extends BatteryStats { } private void updateOldDischargeScreenLevelLocked(int state) { - if (isScreenOn(state)) { + if (Display.isOnState(state)) { int diff = mDischargeScreenOnUnplugLevel - mDischargeCurrentLevel; if (diff > 0) { mDischargeAmountScreenOn += diff; mDischargeAmountScreenOnSinceCharge += diff; } - } else if (isScreenDoze(state)) { + } else if (Display.isDozeState(state)) { int diff = mDischargeScreenDozeUnplugLevel - mDischargeCurrentLevel; if (diff > 0) { mDischargeAmountScreenDoze += diff; mDischargeAmountScreenDozeSinceCharge += diff; } - } else if (isScreenOff(state)){ + } else if (Display.isOffState(state)) { int diff = mDischargeScreenOffUnplugLevel - mDischargeCurrentLevel; if (diff > 0) { mDischargeAmountScreenOff += diff; @@ -11361,15 +11409,15 @@ public class BatteryStatsImpl extends BatteryStats { } private void updateNewDischargeScreenLevelLocked(int state) { - if (isScreenOn(state)) { + if (Display.isOnState(state)) { mDischargeScreenOnUnplugLevel = mDischargeCurrentLevel; mDischargeScreenOffUnplugLevel = 0; mDischargeScreenDozeUnplugLevel = 0; - } else if (isScreenDoze(state)){ + } else if (Display.isDozeState(state)) { mDischargeScreenOnUnplugLevel = 0; mDischargeScreenDozeUnplugLevel = mDischargeCurrentLevel; mDischargeScreenOffUnplugLevel = 0; - } else if (isScreenOff(state)) { + } else if (Display.isOffState(state)) { mDischargeScreenOnUnplugLevel = 0; mDischargeScreenDozeUnplugLevel = 0; mDischargeScreenOffUnplugLevel = mDischargeCurrentLevel; @@ -12163,10 +12211,25 @@ public class BatteryStatsImpl extends BatteryStats { * Read and record Rail Energy data. */ public void updateRailStatsLocked() { - if (mRailEnergyDataCallback == null || !mTmpRailStats.isRailStatsAvailable()) { + if (mMeasuredEnergyRetriever == null || !mTmpRailStats.isRailStatsAvailable()) { return; } - mRailEnergyDataCallback.fillRailDataStats(mTmpRailStats); + mMeasuredEnergyRetriever.fillRailDataStats(mTmpRailStats); + } + + /** + * Get energy consumed (in microjoules) by a set of subsystems from the {@link + * MeasuredEnergyRetriever}, if available. + * + * @return a SparseLongArray that maps consumer id to energy consumed. Returns null if data is + * unavailable. + */ + @Nullable + public MeasuredEnergyArray getEnergyConsumptionDataLocked() { + if (mMeasuredEnergyRetriever == null) { + return null; + } + return mMeasuredEnergyRetriever.getEnergyConsumptionData(); } /** @@ -12428,6 +12491,21 @@ public class BatteryStatsImpl extends BatteryStats { } /** + * Update energy consumption data with a new snapshot of energy data. + * Generally this should only be called from BatteryExternalStatsWorker. + * + * @param energyStats latest energy data to update with. + */ + @GuardedBy("this") + public void updateMeasuredEnergyStatsLocked(@NonNull MeasuredEnergyArray energyStats, + int screenState) { + if (mBatteryMeasuredEnergyStats != null) { + mBatteryMeasuredEnergyStats.update(energyStats, screenState, + mOnBatteryTimeBase.isRunning()); + } + } + + /** * Mark the current partial timers as gone through a collection so that they will be * considered in the next cpu times distribution to wakelock holders. */ @@ -12919,11 +12997,11 @@ public class BatteryStatsImpl extends BatteryStats { } addHistoryRecordLocked(mSecRealtime, mSecUptime); mDischargeCurrentLevel = mDischargeUnplugLevel = level; - if (isScreenOn(screenState)) { + if (Display.isOnState(screenState)) { mDischargeScreenOnUnplugLevel = level; mDischargeScreenDozeUnplugLevel = 0; mDischargeScreenOffUnplugLevel = 0; - } else if (isScreenDoze(screenState)) { + } else if (Display.isDozeState(screenState)) { mDischargeScreenOnUnplugLevel = 0; mDischargeScreenDozeUnplugLevel = level; mDischargeScreenOffUnplugLevel = 0; @@ -13079,7 +13157,7 @@ public class BatteryStatsImpl extends BatteryStats { final long chargeDiff = mHistoryCur.batteryChargeUAh - chargeUAh; mDischargeCounter.addCountLocked(chargeDiff); mDischargeScreenOffCounter.addCountLocked(chargeDiff); - if (isScreenDoze(mScreenState)) { + if (Display.isDozeState(mScreenState)) { mDischargeScreenDozeCounter.addCountLocked(chargeDiff); } if (mDeviceIdleMode == DEVICE_IDLE_MODE_LIGHT) { @@ -13130,7 +13208,7 @@ public class BatteryStatsImpl extends BatteryStats { final long chargeDiff = mHistoryCur.batteryChargeUAh - chargeUAh; mDischargeCounter.addCountLocked(chargeDiff); mDischargeScreenOffCounter.addCountLocked(chargeDiff); - if (isScreenDoze(mScreenState)) { + if (Display.isDozeState(mScreenState)) { mDischargeScreenDozeCounter.addCountLocked(chargeDiff); } if (mDeviceIdleMode == DEVICE_IDLE_MODE_LIGHT) { @@ -13584,7 +13662,7 @@ public class BatteryStatsImpl extends BatteryStats { public int getDischargeAmountScreenOn() { synchronized(this) { int val = mDischargeAmountScreenOn; - if (mOnBattery && isScreenOn(mScreenState) + if (mOnBattery && Display.isOnState(mScreenState) && mDischargeCurrentLevel < mDischargeScreenOnUnplugLevel) { val += mDischargeScreenOnUnplugLevel-mDischargeCurrentLevel; } @@ -13596,7 +13674,7 @@ public class BatteryStatsImpl extends BatteryStats { public int getDischargeAmountScreenOnSinceCharge() { synchronized(this) { int val = mDischargeAmountScreenOnSinceCharge; - if (mOnBattery && isScreenOn(mScreenState) + if (mOnBattery && Display.isOnState(mScreenState) && mDischargeCurrentLevel < mDischargeScreenOnUnplugLevel) { val += mDischargeScreenOnUnplugLevel-mDischargeCurrentLevel; } @@ -13609,7 +13687,7 @@ public class BatteryStatsImpl extends BatteryStats { public int getDischargeAmountScreenOff() { synchronized(this) { int val = mDischargeAmountScreenOff; - if (mOnBattery && isScreenOff(mScreenState) + if (mOnBattery && Display.isOffState(mScreenState) && mDischargeCurrentLevel < mDischargeScreenOffUnplugLevel) { val += mDischargeScreenOffUnplugLevel-mDischargeCurrentLevel; } @@ -13622,7 +13700,7 @@ public class BatteryStatsImpl extends BatteryStats { public int getDischargeAmountScreenOffSinceCharge() { synchronized(this) { int val = mDischargeAmountScreenOffSinceCharge; - if (mOnBattery && isScreenOff(mScreenState) + if (mOnBattery && Display.isOffState(mScreenState) && mDischargeCurrentLevel < mDischargeScreenOffUnplugLevel) { val += mDischargeScreenOffUnplugLevel-mDischargeCurrentLevel; } @@ -13635,7 +13713,7 @@ public class BatteryStatsImpl extends BatteryStats { public int getDischargeAmountScreenDoze() { synchronized(this) { int val = mDischargeAmountScreenDoze; - if (mOnBattery && isScreenDoze(mScreenState) + if (mOnBattery && Display.isDozeState(mScreenState) && mDischargeCurrentLevel < mDischargeScreenDozeUnplugLevel) { val += mDischargeScreenDozeUnplugLevel-mDischargeCurrentLevel; } @@ -13647,7 +13725,7 @@ public class BatteryStatsImpl extends BatteryStats { public int getDischargeAmountScreenDozeSinceCharge() { synchronized(this) { int val = mDischargeAmountScreenDozeSinceCharge; - if (mOnBattery && isScreenDoze(mScreenState) + if (mOnBattery && Display.isDozeState(mScreenState) && mDischargeCurrentLevel < mDischargeScreenDozeUnplugLevel) { val += mDischargeScreenDozeUnplugLevel-mDischargeCurrentLevel; } @@ -14129,6 +14207,19 @@ public class BatteryStatsImpl extends BatteryStats { } } + /** + * Dump measured energy stats + */ + @GuardedBy("this") + public void dumpMeasuredEnergyStatsLocked(PrintWriter pw) { + if (mBatteryMeasuredEnergyStats == null) return; + final IndentingPrintWriter iPw = new IndentingPrintWriter(pw, " "); + iPw.println("On battery measured energy stats:"); + iPw.increaseIndent(); + mBatteryMeasuredEnergyStats.dump(iPw); + iPw.decreaseIndent(); + } + final ReentrantLock mWriteLock = new ReentrantLock(); public void writeAsyncLocked() { @@ -14504,6 +14595,8 @@ public class BatteryStatsImpl extends BatteryStats { mNextMaxDailyDeadlineMs = in.readLong(); mBatteryTimeToFullSeconds = in.readLong(); + MeasuredEnergyStats.readSummaryFromParcel(mBatteryMeasuredEnergyStats, in); + mStartCount++; mScreenState = Display.STATE_UNKNOWN; @@ -14997,6 +15090,8 @@ public class BatteryStatsImpl extends BatteryStats { out.writeLong(mNextMaxDailyDeadlineMs); out.writeLong(mBatteryTimeToFullSeconds); + MeasuredEnergyStats.writeSummaryToParcel(mBatteryMeasuredEnergyStats, out); + mScreenOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS); mScreenDozeTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS); for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) { @@ -15581,6 +15676,10 @@ public class BatteryStatsImpl extends BatteryStats { mLastWriteTimeMs = in.readLong(); mBatteryTimeToFullSeconds = in.readLong(); + if (in.readInt() != 0) { + mBatteryMeasuredEnergyStats = new MeasuredEnergyStats(in); + } + mRpmStats.clear(); int NRPMS = in.readInt(); for (int irpm = 0; irpm < NRPMS; irpm++) { @@ -15783,6 +15882,13 @@ public class BatteryStatsImpl extends BatteryStats { out.writeLong(mLastWriteTimeMs); out.writeLong(mBatteryTimeToFullSeconds); + if (mBatteryMeasuredEnergyStats != null) { + out.writeInt(1); + mBatteryMeasuredEnergyStats.writeToParcel(out); + } else { + out.writeInt(0); + } + out.writeInt(mRpmStats.size()); for (Map.Entry<String, SamplingTimer> ent : mRpmStats.entrySet()) { SamplingTimer rpmt = ent.getValue(); @@ -16036,5 +16142,8 @@ public class BatteryStatsImpl extends BatteryStats { pw.println(); dumpConstantsLocked(pw); + + pw.println(); + dumpMeasuredEnergyStatsLocked(pw); } } diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index 1fcc6b0a6d55..94e21e5a5965 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -391,16 +391,19 @@ public class ZygoteInit { SharedLibraryInfo hidlBase = new SharedLibraryInfo( "/system/framework/android.hidl.base-V1.0-java.jar", null /*packageName*/, null /*codePaths*/, null /*name*/, 0 /*version*/, SharedLibraryInfo.TYPE_BUILTIN, - null /*declaringPackage*/, null /*dependentPackages*/, null /*dependencies*/); + null /*declaringPackage*/, null /*dependentPackages*/, null /*dependencies*/, + false /*isNative*/); SharedLibraryInfo hidlManager = new SharedLibraryInfo( "/system/framework/android.hidl.manager-V1.0-java.jar", null /*packageName*/, null /*codePaths*/, null /*name*/, 0 /*version*/, SharedLibraryInfo.TYPE_BUILTIN, - null /*declaringPackage*/, null /*dependentPackages*/, null /*dependencies*/); + null /*declaringPackage*/, null /*dependentPackages*/, null /*dependencies*/, + false /*isNative*/); SharedLibraryInfo androidTestBase = new SharedLibraryInfo( "/system/framework/android.test.base.jar", null /*packageName*/, null /*codePaths*/, null /*name*/, 0 /*version*/, SharedLibraryInfo.TYPE_BUILTIN, - null /*declaringPackage*/, null /*dependentPackages*/, null /*dependencies*/); + null /*declaringPackage*/, null /*dependentPackages*/, null /*dependencies*/, + false /*isNative*/); ApplicationLoaders.getDefault().createAndCacheNonBootclasspathSystemClassLoaders( new SharedLibraryInfo[]{ @@ -625,8 +628,11 @@ public class ZygoteInit { /** * Creates a PathClassLoader for the given class path that is associated with a shared - * namespace, i.e., this classloader can access platform-private native libraries. The - * classloader will use java.library.path as the native library path. + * namespace, i.e., this classloader can access platform-private native libraries. + * + * The classloader will add java.library.path to the native library path for the classloader + * namespace. Since it includes platform locations like /system/lib, this is only appropriate + * for platform code that don't need linker namespace isolation (as opposed to APEXes and apps). */ static ClassLoader createPathClassLoader(String classPath, int targetSdkVersion) { String libraryPath = System.getProperty("java.library.path"); diff --git a/core/java/com/android/internal/power/MeasuredEnergyArray.java b/core/java/com/android/internal/power/MeasuredEnergyArray.java new file mode 100644 index 000000000000..1f6dc260a197 --- /dev/null +++ b/core/java/com/android/internal/power/MeasuredEnergyArray.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.power; + + +import android.annotation.IntDef; + +import com.android.internal.os.RailStats; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Interface to provide subsystem energy data. + * TODO: replace this and {@link RailStats} once b/173077356 is done + */ +public interface MeasuredEnergyArray { + int SUBSYSTEM_UNKNOWN = -1; + int SUBSYSTEM_DISPLAY = 0; + int NUMBER_SUBSYSTEMS = 1; + String[] SUBSYSTEM_NAMES = {"display"}; + + + @IntDef(prefix = { "SUBSYSTEM_" }, value = { + SUBSYSTEM_UNKNOWN, + SUBSYSTEM_DISPLAY, + }) + @Retention(RetentionPolicy.SOURCE) + @interface MeasuredEnergySubsystem {} + + /** + * Get the subsystem at an index in array. + * + * @param index into the array. + * @return subsystem. + */ + @MeasuredEnergySubsystem + int getSubsystem(int index); + + /** + * Get the energy (in microjoules) consumed since boot of the subsystem at an index. + * + * @param index into the array. + * @return energy (in microjoules) consumed since boot. + */ + long getEnergy(int index); + + /** + * Return number of subsystems in the array. + */ + int size(); +} diff --git a/core/java/com/android/internal/power/MeasuredEnergyStats.java b/core/java/com/android/internal/power/MeasuredEnergyStats.java new file mode 100644 index 000000000000..7b6e0790e14f --- /dev/null +++ b/core/java/com/android/internal/power/MeasuredEnergyStats.java @@ -0,0 +1,307 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.power; + + +import android.annotation.IntDef; +import android.annotation.Nullable; +import android.os.Parcel; +import android.view.Display; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.power.MeasuredEnergyArray.MeasuredEnergySubsystem; + +import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; + +/** + * MeasuredEnergyStats adds up the measured energy usage of various subsystems + */ +@VisibleForTesting +public class MeasuredEnergyStats { + private static final long UNAVAILABLE = -1; + private static final long RESET = -2; + + public static final int ENERGY_BUCKET_UNKNOWN = -1; + public static final int ENERGY_BUCKET_SCREEN_ON = 0; + public static final int ENERGY_BUCKET_SCREEN_DOZE = 1; + public static final int ENERGY_BUCKET_SCREEN_OTHER = 2; + public static final int NUMBER_ENERGY_BUCKETS = 3; + private static final String[] ENERGY_BUCKET_NAMES = + {"screen-on", "screen-doze", "screen-other"}; + + @IntDef(prefix = {"ENERGY_BUCKET_"}, value = { + ENERGY_BUCKET_UNKNOWN, + ENERGY_BUCKET_SCREEN_ON, + ENERGY_BUCKET_SCREEN_DOZE, + ENERGY_BUCKET_SCREEN_OTHER, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface EnergyBucket { + } + + /** + * Energy snapshots from the last time each {@link MeasuredEnergySubsystem} was updated. + * An energy snapshot will be set to {@link #UNAVAILABLE} if the subsystem has never been + * updated. + * An energy snapshot will be set to {@link #RESET} on a reset. A subsystems energy will + * need to be updated at least twice to start accumulating energy again. + */ + private final long[] mMeasuredEnergySnapshots = + new long[MeasuredEnergyArray.NUMBER_SUBSYSTEMS]; + + /** + * Total energy in microjoules since the last reset that an {@link EnergyBucket} has + * accumulated. + * + * Warning: Long array is used for access speed. If the number of supported subsystems + * becomes too large, consider using an alternate data structure. + */ + private final long[] mAccumulatedEnergiesMicroJoules = new long[NUMBER_ENERGY_BUCKETS]; + + /** + * Last known screen state. + */ + private int mLastScreenState; + + public MeasuredEnergyStats(MeasuredEnergyArray energyArray, int screenState) { + Arrays.fill(mMeasuredEnergySnapshots, UNAVAILABLE); + + update(energyArray, screenState, false); + } + + public MeasuredEnergyStats(Parcel in) { + in.readLongArray(mAccumulatedEnergiesMicroJoules); + } + + /** + * Constructor for creating a temp MeasuredEnergyStats + * See {@link #readSummaryFromParcel(MeasuredEnergyStats, Parcel)} + */ + private MeasuredEnergyStats() { + Arrays.fill(mMeasuredEnergySnapshots, UNAVAILABLE); + } + + /** Write to parcel */ + public void writeToParcel(Parcel out) { + out.writeLongArray(mAccumulatedEnergiesMicroJoules); + } + + /** + * Read from summary parcel. + * Note: Measured subsystem availability may be different from when the summary parcel was + * written. + */ + private void readSummaryFromParcel(Parcel in) { + final int size = in.readInt(); + for (int i = 0; i < size; i++) { + final int bucket = in.readInt(); + final long energyUJ = in.readLong(); + + final int subsystem = getSubsystem(bucket); + // Only accept the summary energy if subsystem is currently available + if (subsystem != MeasuredEnergyArray.SUBSYSTEM_UNKNOWN + && mMeasuredEnergySnapshots[subsystem] != UNAVAILABLE) { + mAccumulatedEnergiesMicroJoules[bucket] = energyUJ; + } + } + } + + /** + * Write to summary parcel. + * Note: Measured subsystem availability may be different when the summary parcel is read. + * Note: {@link com.android.internal.os.BatteryStatsImpl#VERSION} must be updated if summary + * parceling changes. + */ + private void writeSummaryToParcel(Parcel out) { + final int sizePos = out.dataPosition(); + out.writeInt(0); + int size = 0; + // Write only the buckets with reported energy + for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) { + final int subsystem = getSubsystem(i); + if (mMeasuredEnergySnapshots[subsystem] == UNAVAILABLE) continue; + + out.writeInt(i); + out.writeLong(mAccumulatedEnergiesMicroJoules[i]); + size++; + } + final int currPos = out.dataPosition(); + out.setDataPosition(sizePos); + out.writeInt(size); + out.setDataPosition(currPos); + } + + /** + * Update with the latest measured energies and device state. + * + * @param energyArray measured energy array for some subsystems. + * @param screenState screen state to attribute disaply energy to after this update. + * @param accumulate whether or not to accumulate the latest energy + */ + public void update(MeasuredEnergyArray energyArray, int screenState, boolean accumulate) { + final int size = energyArray.size(); + if (!accumulate) { + for (int i = 0; i < size; i++) { + final int subsystem = energyArray.getSubsystem(i); + mMeasuredEnergySnapshots[subsystem] = energyArray.getEnergy(i); + } + } else { + for (int i = 0; i < size; i++) { + final int subsystem = energyArray.getSubsystem(i); + final long newEnergyUJ = energyArray.getEnergy(i); + final long oldEnergyUJ = mMeasuredEnergySnapshots[subsystem]; + mMeasuredEnergySnapshots[subsystem] = newEnergyUJ; + + // This is the first valid energy, skip accumulating the delta + if (oldEnergyUJ < 0) continue; + final long deltaUJ = newEnergyUJ - oldEnergyUJ; + + final int bucket = getEnergyBucket(subsystem, mLastScreenState); + mAccumulatedEnergiesMicroJoules[bucket] += deltaUJ; + } + } + mLastScreenState = screenState; + } + + /** + * Map {@link MeasuredEnergySubsystem} and device state to an {@link EnergyBucket}. + * Keep in sync with {@link #getSubsystem} + */ + @EnergyBucket + private int getEnergyBucket(@MeasuredEnergySubsystem int subsystem, int screenState) { + switch (subsystem) { + case MeasuredEnergyArray.SUBSYSTEM_DISPLAY: + if (Display.isOnState(screenState)) { + return ENERGY_BUCKET_SCREEN_ON; + } else if (Display.isDozeState(screenState)) { + return ENERGY_BUCKET_SCREEN_DOZE; + } else { + return ENERGY_BUCKET_SCREEN_OTHER; + } + default: + return ENERGY_BUCKET_UNKNOWN; + } + } + + /** + * Map {@link EnergyBucket} to a {@link MeasuredEnergySubsystem}. + * Keep in sync with {@link #getEnergyBucket} + */ + @MeasuredEnergySubsystem + private int getSubsystem(@EnergyBucket int bucket) { + switch (bucket) { + case ENERGY_BUCKET_SCREEN_ON: //fallthrough + case ENERGY_BUCKET_SCREEN_DOZE: //fallthrough + case ENERGY_BUCKET_SCREEN_OTHER: + return MeasuredEnergyArray.SUBSYSTEM_DISPLAY; + default: + return MeasuredEnergyArray.SUBSYSTEM_UNKNOWN; + } + } + + /** + * Check if a subsystem's measured energy is available. + * @param subsystem which subsystem. + * @return true if subsystem is avaiable. + */ + public boolean hasSubsystem(@MeasuredEnergySubsystem int subsystem) { + return mMeasuredEnergySnapshots[subsystem] != UNAVAILABLE; + } + + /** + * Return accumulated energy (in microjoules) since last reset. + */ + public long getAccumulatedBucketEnergy(@EnergyBucket int bucket) { + return mAccumulatedEnergiesMicroJoules[bucket]; + } + + /** + * Populate a MeasuredEnergyStats from a parcel. If the stats is null, consume and + * ignore the parcelled data. + */ + public static void readSummaryFromParcel(@Nullable MeasuredEnergyStats stats, Parcel in) { + // Check if any MeasuredEnergyStats exists on the parcel + if (in.readInt() == 0) return; + + // If stats is null, create a placeholder MeasuredEnergyStats to consume the parcel data + final MeasuredEnergyStats mes = stats != null ? stats : new MeasuredEnergyStats(); + mes.readSummaryFromParcel(in); + } + + /** + * Write a MeasuredEnergyStats to a parcel. If the stats is null, just write a 0. + */ + public static void writeSummaryToParcel(@Nullable MeasuredEnergyStats stats, + Parcel dest) { + if (stats == null) { + dest.writeInt(0); + return; + } + dest.writeInt(1); + stats.writeSummaryToParcel(dest); + } + + /** + * Reset accumulated energy. + */ + public void reset() { + for (int i = 0; i < MeasuredEnergyArray.NUMBER_SUBSYSTEMS; i++) { + // Leave subsystems marked as unavailable alone. + if (mMeasuredEnergySnapshots[i] == UNAVAILABLE) continue; + mMeasuredEnergySnapshots[i] = RESET; + } + Arrays.fill(mAccumulatedEnergiesMicroJoules, 0); + } + + /** + * Dump debug data. + */ + public void dump(PrintWriter pw) { + pw.println("Measured energy snapshot (microjoules):"); + pw.print(" "); + for (int i = 0; i < MeasuredEnergyArray.NUMBER_SUBSYSTEMS; i++) { + final long energyUJ = mMeasuredEnergySnapshots[i]; + if (energyUJ == UNAVAILABLE) continue; + pw.print(MeasuredEnergyArray.SUBSYSTEM_NAMES[i]); + pw.print(" : "); + if (energyUJ == RESET) { + pw.print("reset"); + } else { + pw.print(energyUJ); + } + if (i != MeasuredEnergyArray.NUMBER_SUBSYSTEMS - 1) { + pw.print(", "); + } + } + pw.println(); + + pw.println("Accumulated energy since last reset (microjoules):"); + pw.print(" "); + for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) { + pw.print(ENERGY_BUCKET_NAMES[i]); + pw.print(" : "); + pw.print(mAccumulatedEnergiesMicroJoules[i]); + if (i != NUMBER_ENERGY_BUCKETS - 1) { + pw.print(", "); + } + } + pw.println(); + } +} diff --git a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl index 854fb17e692b..44dca9bae3da 100644 --- a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl +++ b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl @@ -69,6 +69,6 @@ oneway interface IPhoneStateListener { void onRegistrationFailed(in CellIdentity cellIdentity, String chosenPlmn, int domain, int causeCode, int additionalCauseCode); void onBarringInfoChanged(in BarringInfo barringInfo); - void onPhysicalChannelConfigChanged(in List<PhysicalChannelConfig> configs); - void onDataEnabledChanged(boolean enabled, int reason); + void onPhysicalChannelConfigurationChanged(in List<PhysicalChannelConfig> configs); + } diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl index ae1657b051e6..a28a66376497 100644 --- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl +++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl @@ -41,10 +41,16 @@ interface ITelephonyRegistry { IOnSubscriptionsChangedListener callback); void removeOnSubscriptionsChangedListener(String pkg, IOnSubscriptionsChangedListener callback); - - void listenWithEventList(in int subId, String pkg, String featureId, - IPhoneStateListener callback, in int[] events, boolean notifyNow); - + /** + * @deprecated Use {@link #listenWithFeature(String, String, IPhoneStateListener, int, + * boolean) instead + */ + @UnsupportedAppUsage + void listen(String pkg, IPhoneStateListener callback, int events, boolean notifyNow); + void listenWithFeature(String pkg, String featureId, IPhoneStateListener callback, long events, + boolean notifyNow); + void listenForSubscriber(in int subId, String pkg, String featureId, + IPhoneStateListener callback, long events, boolean notifyNow); @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) void notifyCallStateForAllSubs(int state, String incomingNumber); void notifyCallState(in int phoneId, in int subId, int state, String incomingNumber); @@ -93,6 +99,6 @@ interface ITelephonyRegistry { void notifyRegistrationFailed(int slotIndex, int subId, in CellIdentity cellIdentity, String chosenPlmn, int domain, int causeCode, int additionalCauseCode); void notifyBarringInfoChanged(int slotIndex, int subId, in BarringInfo barringInfo); - void notifyPhysicalChannelConfigForSubscriber(in int subId, + void notifyPhysicalChannelConfigurationForSubscriber(in int subId, in List<PhysicalChannelConfig> configs); } diff --git a/core/java/com/android/internal/util/function/pooled/OWNERS b/core/java/com/android/internal/util/function/pooled/OWNERS new file mode 100644 index 000000000000..da723b3b67da --- /dev/null +++ b/core/java/com/android/internal/util/function/pooled/OWNERS @@ -0,0 +1 @@ +eugenesusla@google.com
\ No newline at end of file diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java index 33ebe43cb23a..4deb40a0d772 100644 --- a/core/java/com/android/internal/view/IInputConnectionWrapper.java +++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java @@ -25,6 +25,7 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.RemoteException; +import android.os.Trace; import android.util.Log; import android.view.KeyEvent; import android.view.inputmethod.CompletionInfo; @@ -111,6 +112,12 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { } } + protected Looper getLooper() { + synchronized (mMainLooper) { + return mMainLooper; + } + } + protected boolean isFinished() { synchronized (mLock) { return mFinished; @@ -259,61 +266,80 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { void executeMessage(Message msg) { switch (msg.what) { case DO_GET_TEXT_AFTER_CURSOR: { - final ICharSequenceResultCallback callback = (ICharSequenceResultCallback) msg.obj; - final InputConnection ic = getInputConnection(); - final CharSequence result; - if (ic == null || !isActive()) { - Log.w(TAG, "getTextAfterCursor on inactive InputConnection"); - result = null; - } else { - result = ic.getTextAfterCursor(msg.arg1, msg.arg2); - } + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getTextAfterCursor"); try { - callback.onResult(result); - } catch (RemoteException e) { - Log.w(TAG, "Failed to return the result to getTextAfterCursor()." + final ICharSequenceResultCallback callback = + (ICharSequenceResultCallback) msg.obj; + final InputConnection ic = getInputConnection(); + final CharSequence result; + if (ic == null || !isActive()) { + Log.w(TAG, "getTextAfterCursor on inactive InputConnection"); + result = null; + } else { + result = ic.getTextAfterCursor(msg.arg1, msg.arg2); + } + try { + callback.onResult(result); + } catch (RemoteException e) { + Log.w(TAG, "Failed to return the result to getTextAfterCursor()." + " result=" + result, e); + } + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } return; } case DO_GET_TEXT_BEFORE_CURSOR: { - final ICharSequenceResultCallback callback = (ICharSequenceResultCallback) msg.obj; - final InputConnection ic = getInputConnection(); - final CharSequence result; - if (ic == null || !isActive()) { - Log.w(TAG, "getTextBeforeCursor on inactive InputConnection"); - result = null; - } else { - result = ic.getTextBeforeCursor(msg.arg1, msg.arg2); - } + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getTextBeforeCursor"); try { - callback.onResult(result); - } catch (RemoteException e) { - Log.w(TAG, "Failed to return the result to getTextBeforeCursor()." + final ICharSequenceResultCallback callback = + (ICharSequenceResultCallback) msg.obj; + final InputConnection ic = getInputConnection(); + final CharSequence result; + if (ic == null || !isActive()) { + Log.w(TAG, "getTextBeforeCursor on inactive InputConnection"); + result = null; + } else { + result = ic.getTextBeforeCursor(msg.arg1, msg.arg2); + } + try { + callback.onResult(result); + } catch (RemoteException e) { + Log.w(TAG, "Failed to return the result to getTextBeforeCursor()." + " result=" + result, e); + } + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } return; } case DO_GET_SELECTED_TEXT: { - final ICharSequenceResultCallback callback = (ICharSequenceResultCallback) msg.obj; - final InputConnection ic = getInputConnection(); - final CharSequence result; - if (ic == null || !isActive()) { - Log.w(TAG, "getSelectedText on inactive InputConnection"); - result = null; - } else { - result = ic.getSelectedText(msg.arg1); - } + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getSelectedText"); try { - callback.onResult(result); - } catch (RemoteException e) { - Log.w(TAG, "Failed to return the result to getSelectedText()." - + " result=" + result, e); + final ICharSequenceResultCallback callback = + (ICharSequenceResultCallback) msg.obj; + final InputConnection ic = getInputConnection(); + final CharSequence result; + if (ic == null || !isActive()) { + Log.w(TAG, "getSelectedText on inactive InputConnection"); + result = null; + } else { + result = ic.getSelectedText(msg.arg1); + } + try { + callback.onResult(result); + } catch (RemoteException e) { + Log.w(TAG, "Failed to return the result to getSelectedText()." + + " result=" + result, e); + } + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } return; } case DO_GET_SURROUNDING_TEXT: { final SomeArgs args = (SomeArgs) msg.obj; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getSurroundingText"); try { int beforeLength = (int) args.arg1; int afterLength = (int) args.arg2; @@ -335,30 +361,37 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { + " result=" + result, e); } } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); args.recycle(); } return; } case DO_GET_CURSOR_CAPS_MODE: { - final IIntResultCallback callback = (IIntResultCallback) msg.obj; - final InputConnection ic = getInputConnection(); - final int result; - if (ic == null || !isActive()) { - Log.w(TAG, "getCursorCapsMode on inactive InputConnection"); - result = 0; - } else { - result = ic.getCursorCapsMode(msg.arg1); - } + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getCursorCapsMode"); try { - callback.onResult(result); - } catch (RemoteException e) { - Log.w(TAG, "Failed to return the result to getCursorCapsMode()." + final IIntResultCallback callback = (IIntResultCallback) msg.obj; + final InputConnection ic = getInputConnection(); + final int result; + if (ic == null || !isActive()) { + Log.w(TAG, "getCursorCapsMode on inactive InputConnection"); + result = 0; + } else { + result = ic.getCursorCapsMode(msg.arg1); + } + try { + callback.onResult(result); + } catch (RemoteException e) { + Log.w(TAG, "Failed to return the result to getCursorCapsMode()." + " result=" + result, e); + } + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } return; } case DO_GET_EXTRACTED_TEXT: { final SomeArgs args = (SomeArgs) msg.obj; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getExtractedText"); try { final ExtractedTextRequest request = (ExtractedTextRequest) args.arg1; final IExtractedTextResultCallback callback = @@ -378,159 +411,237 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { + " result=" + result, e); } } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); args.recycle(); } return; } case DO_COMMIT_TEXT: { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "commitText on inactive InputConnection"); - return; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitText"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "commitText on inactive InputConnection"); + return; + } + ic.commitText((CharSequence) msg.obj, msg.arg1); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } - ic.commitText((CharSequence)msg.obj, msg.arg1); return; } case DO_SET_SELECTION: { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "setSelection on inactive InputConnection"); - return; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#setSelection"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "setSelection on inactive InputConnection"); + return; + } + ic.setSelection(msg.arg1, msg.arg2); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } - ic.setSelection(msg.arg1, msg.arg2); return; } case DO_PERFORM_EDITOR_ACTION: { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "performEditorAction on inactive InputConnection"); - return; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#performEditorAction"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "performEditorAction on inactive InputConnection"); + return; + } + ic.performEditorAction(msg.arg1); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } - ic.performEditorAction(msg.arg1); return; } case DO_PERFORM_CONTEXT_MENU_ACTION: { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "performContextMenuAction on inactive InputConnection"); - return; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#performContextMenuAction"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "performContextMenuAction on inactive InputConnection"); + return; + } + ic.performContextMenuAction(msg.arg1); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } - ic.performContextMenuAction(msg.arg1); return; } case DO_COMMIT_COMPLETION: { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "commitCompletion on inactive InputConnection"); - return; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitCompletion"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "commitCompletion on inactive InputConnection"); + return; + } + ic.commitCompletion((CompletionInfo) msg.obj); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } - ic.commitCompletion((CompletionInfo)msg.obj); return; } case DO_COMMIT_CORRECTION: { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "commitCorrection on inactive InputConnection"); - return; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitCorrection"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "commitCorrection on inactive InputConnection"); + return; + } + ic.commitCorrection((CorrectionInfo) msg.obj); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } - ic.commitCorrection((CorrectionInfo)msg.obj); return; } case DO_SET_COMPOSING_TEXT: { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "setComposingText on inactive InputConnection"); - return; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#setComposingText"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "setComposingText on inactive InputConnection"); + return; + } + ic.setComposingText((CharSequence) msg.obj, msg.arg1); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } - ic.setComposingText((CharSequence)msg.obj, msg.arg1); return; } case DO_SET_COMPOSING_REGION: { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "setComposingRegion on inactive InputConnection"); - return; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#setComposingRegion"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "setComposingRegion on inactive InputConnection"); + return; + } + ic.setComposingRegion(msg.arg1, msg.arg2); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } - ic.setComposingRegion(msg.arg1, msg.arg2); return; } case DO_FINISH_COMPOSING_TEXT: { - if (isFinished()) { - // In this case, #finishComposingText() is guaranteed to be called already. - // There should be no negative impact if we ignore this call silently. - if (DEBUG) { - Log.w(TAG, "Bug 35301295: Redundant finishComposingText."); + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#finishComposingText"); + try { + if (isFinished()) { + // In this case, #finishComposingText() is guaranteed to be called already. + // There should be no negative impact if we ignore this call silently. + if (DEBUG) { + Log.w(TAG, "Bug 35301295: Redundant finishComposingText."); + } + return; } - return; - } - InputConnection ic = getInputConnection(); - // Note we do NOT check isActive() here, because this is safe - // for an IME to call at any time, and we need to allow it - // through to clean up our state after the IME has switched to - // another client. - if (ic == null) { - Log.w(TAG, "finishComposingText on inactive InputConnection"); - return; + InputConnection ic = getInputConnection(); + // Note we do NOT check isActive() here, because this is safe + // for an IME to call at any time, and we need to allow it + // through to clean up our state after the IME has switched to + // another client. + if (ic == null) { + Log.w(TAG, "finishComposingText on inactive InputConnection"); + return; + } + ic.finishComposingText(); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } - ic.finishComposingText(); return; } case DO_SEND_KEY_EVENT: { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "sendKeyEvent on inactive InputConnection"); - return; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#sendKeyEvent"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "sendKeyEvent on inactive InputConnection"); + return; + } + ic.sendKeyEvent((KeyEvent) msg.obj); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } - ic.sendKeyEvent((KeyEvent)msg.obj); return; } case DO_CLEAR_META_KEY_STATES: { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "clearMetaKeyStates on inactive InputConnection"); - return; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#clearMetaKeyStates"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "clearMetaKeyStates on inactive InputConnection"); + return; + } + ic.clearMetaKeyStates(msg.arg1); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } - ic.clearMetaKeyStates(msg.arg1); return; } case DO_DELETE_SURROUNDING_TEXT: { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "deleteSurroundingText on inactive InputConnection"); - return; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#deleteSurroundingText"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "deleteSurroundingText on inactive InputConnection"); + return; + } + ic.deleteSurroundingText(msg.arg1, msg.arg2); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } - ic.deleteSurroundingText(msg.arg1, msg.arg2); return; } case DO_DELETE_SURROUNDING_TEXT_IN_CODE_POINTS: { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "deleteSurroundingTextInCodePoints on inactive InputConnection"); - return; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, + "InputConnection#deleteSurroundingTextInCodePoints"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "deleteSurroundingTextInCodePoints on inactive InputConnection"); + return; + } + ic.deleteSurroundingTextInCodePoints(msg.arg1, msg.arg2); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } - ic.deleteSurroundingTextInCodePoints(msg.arg1, msg.arg2); return; } case DO_BEGIN_BATCH_EDIT: { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "beginBatchEdit on inactive InputConnection"); - return; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#beginBatchEdit"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "beginBatchEdit on inactive InputConnection"); + return; + } + ic.beginBatchEdit(); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } - ic.beginBatchEdit(); return; } case DO_END_BATCH_EDIT: { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "endBatchEdit on inactive InputConnection"); - return; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#endBatchEdit"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "endBatchEdit on inactive InputConnection"); + return; + } + ic.endBatchEdit(); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } - ic.endBatchEdit(); return; } case DO_PERFORM_PRIVATE_COMMAND: { final SomeArgs args = (SomeArgs) msg.obj; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#performPrivateCommand"); try { final String action = (String) args.arg1; final Bundle data = (Bundle) args.arg2; @@ -541,25 +652,31 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { } ic.performPrivateCommand(action, data); } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); args.recycle(); } return; } case DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO: { - final IIntResultCallback callback = (IIntResultCallback) msg.obj; - final InputConnection ic = getInputConnection(); - final boolean result; - if (ic == null || !isActive()) { - Log.w(TAG, "requestCursorAnchorInfo on inactive InputConnection"); - result = false; - } else { - result = ic.requestCursorUpdates(msg.arg1); - } + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#requestCursorUpdates"); try { - callback.onResult(result ? 1 : 0); - } catch (RemoteException e) { - Log.w(TAG, "Failed to return the result to requestCursorUpdates()." - + " result=" + result, e); + final IIntResultCallback callback = (IIntResultCallback) msg.obj; + final InputConnection ic = getInputConnection(); + final boolean result; + if (ic == null || !isActive()) { + Log.w(TAG, "requestCursorAnchorInfo on inactive InputConnection"); + result = false; + } else { + result = ic.requestCursorUpdates(msg.arg1); + } + try { + callback.onResult(result ? 1 : 0); + } catch (RemoteException e) { + Log.w(TAG, "Failed to return the result to requestCursorUpdates()." + + " result=" + result, e); + } + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } return; } @@ -571,6 +688,7 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { if (isFinished()) { return; } + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#closeConnection"); try { InputConnection ic = getInputConnection(); // Note we do NOT check isActive() here, because this is safe @@ -590,12 +708,14 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { mInputConnection = null; mFinished = true; } + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } return; } case DO_COMMIT_CONTENT: { final int flags = msg.arg1; SomeArgs args = (SomeArgs) msg.obj; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitContent"); try { final IIntResultCallback callback = (IIntResultCallback) args.arg3; final InputConnection ic = getInputConnection(); @@ -620,6 +740,7 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { + " result=" + result, e); } } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); args.recycle(); } return; diff --git a/core/java/com/android/internal/view/IInputMethodClient.aidl b/core/java/com/android/internal/view/IInputMethodClient.aidl index 1145f5183206..ec9a0a2f4801 100644 --- a/core/java/com/android/internal/view/IInputMethodClient.aidl +++ b/core/java/com/android/internal/view/IInputMethodClient.aidl @@ -25,7 +25,7 @@ import com.android.internal.view.InputBindResult; oneway interface IInputMethodClient { void onBindMethod(in InputBindResult res); void onUnbindMethod(int sequence, int unbindReason); - void setActive(boolean active, boolean fullscreen); + void setActive(boolean active, boolean fullscreen, boolean reportToImeController); void scheduleStartInputIfNecessary(boolean fullscreen); void reportFullscreenMode(boolean fullscreen); void applyImeVisibility(boolean setVisible); diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl index 844c56bd3529..33abbe82c109 100644 --- a/core/java/com/android/internal/view/IInputMethodManager.aidl +++ b/core/java/com/android/internal/view/IInputMethodManager.aidl @@ -79,4 +79,9 @@ interface IInputMethodManager { void removeImeSurfaceFromWindow(in IBinder windowToken); void startProtoDump(in byte[] protoDump, int source, String where); boolean isImeTraceEnabled(); + + // Starts an ime trace. + void startImeTrace(); + // Stops an ime trace. + void stopImeTrace(); } diff --git a/core/java/com/android/internal/view/IInputMethodSession.aidl b/core/java/com/android/internal/view/IInputMethodSession.aidl index 0319e3637384..c6afd78ec04b 100644 --- a/core/java/com/android/internal/view/IInputMethodSession.aidl +++ b/core/java/com/android/internal/view/IInputMethodSession.aidl @@ -52,4 +52,6 @@ oneway interface IInputMethodSession { void notifyImeHidden(); void removeImeSurface(); + + void finishInput(); } diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java index ec4fe17746ee..8c763a6efe54 100644 --- a/core/java/com/android/internal/view/InputConnectionWrapper.java +++ b/core/java/com/android/internal/view/InputConnectionWrapper.java @@ -24,7 +24,6 @@ import android.inputmethodservice.AbstractInputMethodService; import android.os.Bundle; import android.os.Handler; import android.os.RemoteException; -import android.util.Log; import android.view.KeyEvent; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.CorrectionInfo; @@ -41,7 +40,6 @@ import com.android.internal.inputmethod.Completable; import com.android.internal.inputmethod.ResultCallbacks; import java.lang.ref.WeakReference; -import java.util.concurrent.TimeUnit; public class InputConnectionWrapper implements InputConnection { private static final String TAG = "InputConnectionWrapper"; @@ -73,43 +71,6 @@ public class InputConnectionWrapper implements InputConnection { mCancellationGroup = cancellationGroup; } - @AnyThread - private static void logInternal(@Nullable String methodName, boolean timedOut, - @Nullable Object defaultValue) { - if (timedOut) { - Log.w(TAG, methodName + " didn't respond in " + MAX_WAIT_TIME_MILLIS + " msec." - + " Returning default: " + defaultValue); - } else { - Log.w(TAG, methodName + " was canceled before complete. Returning default: " - + defaultValue); - } - } - - @AnyThread - private static int getResultOrZero(@NonNull Completable.Int value, @NonNull String methodName, - @Nullable CancellationGroup cancellationGroup) { - final boolean timedOut = - value.await(MAX_WAIT_TIME_MILLIS, TimeUnit.MILLISECONDS, cancellationGroup); - if (value.hasValue()) { - return value.getValue(); - } - logInternal(methodName, timedOut, 0); - return 0; - } - - @AnyThread - @Nullable - private static <T> T getResultOrNull(@NonNull Completable.Values<T> value, - @NonNull String methodName, @Nullable CancellationGroup cancellationGroup) { - final boolean timedOut = - value.await(MAX_WAIT_TIME_MILLIS, TimeUnit.MILLISECONDS, cancellationGroup); - if (value.hasValue()) { - return value.getValue(); - } - logInternal(methodName, timedOut, null); - return null; - } - /** * See {@link InputConnection#getTextAfterCursor(int, int)}. */ @@ -126,7 +87,8 @@ public class InputConnectionWrapper implements InputConnection { } catch (RemoteException e) { return null; } - return getResultOrNull(value, "getTextAfterCursor()", mCancellationGroup); + return Completable.getResultOrNull( + value, TAG, "getTextAfterCursor()", mCancellationGroup, MAX_WAIT_TIME_MILLIS); } /** @@ -145,7 +107,8 @@ public class InputConnectionWrapper implements InputConnection { } catch (RemoteException e) { return null; } - return getResultOrNull(value, "getTextBeforeCursor()", mCancellationGroup); + return Completable.getResultOrNull( + value, TAG, "getTextBeforeCursor()", mCancellationGroup, MAX_WAIT_TIME_MILLIS); } @AnyThread @@ -164,7 +127,8 @@ public class InputConnectionWrapper implements InputConnection { } catch (RemoteException e) { return null; } - return getResultOrNull(value, "getSelectedText()", mCancellationGroup); + return Completable.getResultOrNull( + value, TAG, "getSelectedText()", mCancellationGroup, MAX_WAIT_TIME_MILLIS); } /** @@ -197,7 +161,8 @@ public class InputConnectionWrapper implements InputConnection { } catch (RemoteException e) { return null; } - return getResultOrNull(value, "getSurroundingText()", mCancellationGroup); + return Completable.getResultOrNull( + value, TAG, "getSurroundingText()", mCancellationGroup, MAX_WAIT_TIME_MILLIS); } @AnyThread @@ -212,7 +177,8 @@ public class InputConnectionWrapper implements InputConnection { } catch (RemoteException e) { return 0; } - return getResultOrZero(value, "getCursorCapsMode()", mCancellationGroup); + return Completable.getResultOrZero( + value, TAG, "getCursorCapsMode()", mCancellationGroup, MAX_WAIT_TIME_MILLIS); } @AnyThread @@ -227,7 +193,8 @@ public class InputConnectionWrapper implements InputConnection { } catch (RemoteException e) { return null; } - return getResultOrNull(value, "getExtractedText()", mCancellationGroup); + return Completable.getResultOrNull( + value, TAG, "getExtractedText()", mCancellationGroup, MAX_WAIT_TIME_MILLIS); } @AnyThread @@ -438,7 +405,8 @@ public class InputConnectionWrapper implements InputConnection { } catch (RemoteException e) { return false; } - return getResultOrZero(value, "requestUpdateCursorAnchorInfo()", mCancellationGroup) != 0; + return Completable.getResultOrZero(value, TAG, "requestUpdateCursorAnchorInfo()", + mCancellationGroup, MAX_WAIT_TIME_MILLIS) != 0; } @AnyThread @@ -478,7 +446,8 @@ public class InputConnectionWrapper implements InputConnection { } catch (RemoteException e) { return false; } - return getResultOrZero(value, "commitContent()", mCancellationGroup) != 0; + return Completable.getResultOrZero( + value, TAG, "commitContent()", mCancellationGroup, MAX_WAIT_TIME_MILLIS) != 0; } @AnyThread diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java index ff3543c837eb..d30d662806d4 100644 --- a/core/java/com/android/internal/widget/EditableInputConnection.java +++ b/core/java/com/android/internal/widget/EditableInputConnection.java @@ -16,21 +16,36 @@ package com.android.internal.widget; +import static android.view.inputmethod.InputConnectionProto.CURSOR_CAPS_MODE; +import static android.view.inputmethod.InputConnectionProto.EDITABLE_TEXT; +import static android.view.inputmethod.InputConnectionProto.SELECTED_TEXT; +import static android.view.inputmethod.InputConnectionProto.SELECTED_TEXT_END; +import static android.view.inputmethod.InputConnectionProto.SELECTED_TEXT_START; + import android.compat.annotation.UnsupportedAppUsage; import android.os.Bundle; import android.text.Editable; +import android.text.Selection; import android.text.method.KeyListener; import android.util.Log; +import android.util.proto.ProtoOutputStream; import android.view.inputmethod.BaseInputConnection; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.CorrectionInfo; +import android.view.inputmethod.DumpableInputConnection; import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.InputConnection; import android.widget.TextView; -public class EditableInputConnection extends BaseInputConnection { +/** + * Base class for an editable InputConnection instance. This is created by {@link TextView} or + * {@link EditText}. + */ +public class EditableInputConnection extends BaseInputConnection + implements DumpableInputConnection { private static final boolean DEBUG = false; + private static final boolean DUMP_TEXT = false; private static final String TAG = "EditableInputConnection"; private final TextView mTextView; @@ -222,4 +237,28 @@ public class EditableInputConnection extends BaseInputConnection { } return true; } + + @Override + public void dumpDebug(ProtoOutputStream proto, long fieldId) { + final long token = proto.start(fieldId); + CharSequence editableText = mTextView.getText(); + CharSequence selectedText = getSelectedText(0 /* flags */); + if (DUMP_TEXT) { + if (editableText != null) { + proto.write(EDITABLE_TEXT, editableText.toString()); + } + if (selectedText != null) { + proto.write(SELECTED_TEXT, selectedText.toString()); + } + } + final Editable content = getEditable(); + if (content != null) { + int start = Selection.getSelectionStart(content); + int end = Selection.getSelectionEnd(content); + proto.write(SELECTED_TEXT_START, start); + proto.write(SELECTED_TEXT_END, end); + } + proto.write(CURSOR_CAPS_MODE, getCursorCapsMode(0)); + proto.end(token); + } } diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java index 58817a8a48a9..514e0b8e0ded 100644 --- a/core/java/com/android/internal/widget/FloatingToolbar.java +++ b/core/java/com/android/internal/widget/FloatingToolbar.java @@ -61,12 +61,17 @@ import android.widget.PopupWindow; import android.widget.TextView; import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; import java.util.ArrayList; +import java.util.Collection; import java.util.Comparator; +import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.Objects; /** @@ -93,7 +98,6 @@ public final class FloatingToolbar { private final Rect mPreviousContentRect = new Rect(); private Menu mMenu; - private List<MenuItem> mShowingMenuItems = new ArrayList<>(); private MenuItem.OnMenuItemClickListener mMenuItemClickListener = NO_OP_MENUITEM_CLICK_LISTENER; private int mSuggestedWidth; @@ -274,10 +278,11 @@ public final class FloatingToolbar { private void doShow() { List<MenuItem> menuItems = getVisibleAndEnabledMenuItems(mMenu); menuItems.sort(mMenuItemComparator); - if (!isCurrentlyShowing(menuItems) || mWidthChanged) { + if (mPopup.isLayoutRequired(menuItems) || mWidthChanged) { mPopup.dismiss(); mPopup.layoutMenuItems(menuItems, mMenuItemClickListener, mSuggestedWidth); - mShowingMenuItems = menuItems; + } else { + mPopup.updateMenuItems(menuItems, mMenuItemClickListener); } if (!mPopup.isShowing()) { mPopup.show(mContentRect); @@ -289,33 +294,10 @@ public final class FloatingToolbar { } /** - * Returns true if this floating toolbar is currently showing the specified menu items. - */ - private boolean isCurrentlyShowing(List<MenuItem> menuItems) { - if (mShowingMenuItems == null || menuItems.size() != mShowingMenuItems.size()) { - return false; - } - - final int size = menuItems.size(); - for (int i = 0; i < size; i++) { - final MenuItem menuItem = menuItems.get(i); - final MenuItem showingItem = mShowingMenuItems.get(i); - if (menuItem.getItemId() != showingItem.getItemId() - || !TextUtils.equals(menuItem.getTitle(), showingItem.getTitle()) - || !Objects.equals(menuItem.getIcon(), showingItem.getIcon()) - || menuItem.getGroupId() != showingItem.getGroupId()) { - return false; - } - } - - return true; - } - - /** * Returns the visible and enabled menu items in the specified menu. * This method is recursive. */ - private List<MenuItem> getVisibleAndEnabledMenuItems(Menu menu) { + private static List<MenuItem> getVisibleAndEnabledMenuItems(Menu menu) { List<MenuItem> menuItems = new ArrayList<>(); for (int i = 0; (menu != null) && (i < menu.size()); i++) { MenuItem menuItem = menu.getItem(i); @@ -427,17 +409,25 @@ public final class FloatingToolbar { private Size mOverflowPanelSize; // Should be null when there is no overflow. private Size mMainPanelSize; - /* Item click listeners */ + /* Menu items and click listeners */ + private final Map<MenuItemRepr, MenuItem> mMenuItems = new LinkedHashMap<>(); private MenuItem.OnMenuItemClickListener mOnMenuItemClickListener; private final View.OnClickListener mMenuItemButtonOnClickListener = new View.OnClickListener() { @Override public void onClick(View v) { - if (v.getTag() instanceof MenuItem) { - if (mOnMenuItemClickListener != null) { - mOnMenuItemClickListener.onMenuItemClick((MenuItem) v.getTag()); - } + if (mOnMenuItemClickListener == null) { + return; + } + final Object tag = v.getTag(); + if (!(tag instanceof MenuItemRepr)) { + return; } + final MenuItem menuItem = mMenuItems.get((MenuItemRepr) tag); + if (menuItem == null) { + return; + } + mOnMenuItemClickListener.onMenuItemClick(menuItem); } }; @@ -558,9 +548,9 @@ public final class FloatingToolbar { List<MenuItem> menuItems, MenuItem.OnMenuItemClickListener menuItemClickListener, int suggestedWidth) { - mOnMenuItemClickListener = menuItemClickListener; cancelOverflowAnimations(); clearPanels(); + updateMenuItems(menuItems, menuItemClickListener); menuItems = layoutMainPanelItems(menuItems, getAdjustedToolbarWidth(suggestedWidth)); if (!menuItems.isEmpty()) { // Add remaining items to the overflow. @@ -570,6 +560,28 @@ public final class FloatingToolbar { } /** + * Updates the popup's menu items without rebuilding the widget. + * Use in place of layoutMenuItems() when the popup's views need not be reconstructed. + * + * @see isLayoutRequired(List<MenuItem>) + */ + public void updateMenuItems( + List<MenuItem> menuItems, MenuItem.OnMenuItemClickListener menuItemClickListener) { + mMenuItems.clear(); + for (MenuItem menuItem : menuItems) { + mMenuItems.put(MenuItemRepr.of(menuItem), menuItem); + } + mOnMenuItemClickListener = menuItemClickListener; + } + + /** + * Returns true if this popup needs a relayout to properly render the specified menu items. + */ + public boolean isLayoutRequired(List<MenuItem> menuItems) { + return !MenuItemRepr.reprEquals(menuItems, mMenuItems.values()); + } + + /** * Shows this popup at the specified coordinates. * The specified coordinates may be adjusted to make sure the popup is entirely on-screen. */ @@ -1374,7 +1386,7 @@ public final class FloatingToolbar { } private void setButtonTagAndClickListener(View menuItemButton, MenuItem menuItem) { - menuItemButton.setTag(menuItem); + menuItemButton.setTag(MenuItemRepr.of(menuItem)); menuItemButton.setOnClickListener(mMenuItemButtonOnClickListener); } @@ -1656,6 +1668,85 @@ public final class FloatingToolbar { } /** + * Represents the identity of a MenuItem that is rendered in a FloatingToolbarPopup. + */ + @VisibleForTesting + public static final class MenuItemRepr { + + public final int itemId; + public final int groupId; + @Nullable public final String title; + @Nullable private final Drawable mIcon; + + private MenuItemRepr( + int itemId, int groupId, @Nullable CharSequence title, @Nullable Drawable icon) { + this.itemId = itemId; + this.groupId = groupId; + this.title = (title == null) ? null : title.toString(); + mIcon = icon; + } + + /** + * Creates an instance of MenuItemRepr for the specified menu item. + */ + public static MenuItemRepr of(MenuItem menuItem) { + return new MenuItemRepr( + menuItem.getItemId(), + menuItem.getGroupId(), + menuItem.getTitle(), + menuItem.getIcon()); + } + + /** + * Returns this object's hashcode. + */ + @Override + public int hashCode() { + return Objects.hash(itemId, groupId, title, mIcon); + } + + /** + * Returns true if this object is the same as the specified object. + */ + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (!(o instanceof MenuItemRepr)) { + return false; + } + final MenuItemRepr other = (MenuItemRepr) o; + return itemId == other.itemId + && groupId == other.groupId + && TextUtils.equals(title, other.title) + // Many Drawables (icons) do not implement equals(). Using equals() here instead + // of reference comparisons in case a Drawable subclass implements equals(). + && Objects.equals(mIcon, other.mIcon); + } + + /** + * Returns true if the two menu item collections are the same based on MenuItemRepr. + */ + public static boolean reprEquals( + Collection<MenuItem> menuItems1, Collection<MenuItem> menuItems2) { + if (menuItems1.size() != menuItems2.size()) { + return false; + } + + final Iterator<MenuItem> menuItems2Iter = menuItems2.iterator(); + for (MenuItem menuItem1 : menuItems1) { + final MenuItem menuItem2 = menuItems2Iter.next(); + if (!MenuItemRepr.of(menuItem1).equals(MenuItemRepr.of(menuItem2))) { + return false; + } + } + + return true; + } + } + + /** * Creates and returns a menu button for the specified menu item. */ private static View createMenuItemButton( diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp index e7e9c31ef90e..6337680147a5 100644 --- a/core/jni/android_view_DisplayEventReceiver.cpp +++ b/core/jni/android_view_DisplayEventReceiver.cpp @@ -41,14 +41,21 @@ static struct { jmethodID dispatchVsync; jmethodID dispatchHotplug; jmethodID dispatchConfigChanged; + jmethodID dispatchFrameRateOverrides; + + struct { + jclass clazz; + jmethodID init; + } frameRateOverrideClassInfo; + } gDisplayEventReceiverClassInfo; class NativeDisplayEventReceiver : public DisplayEventDispatcher { public: - NativeDisplayEventReceiver(JNIEnv* env, - jobject receiverWeak, const sp<MessageQueue>& messageQueue, jint vsyncSource, - jint configChanged); + NativeDisplayEventReceiver(JNIEnv* env, jobject receiverWeak, + const sp<MessageQueue>& messageQueue, jint vsyncSource, + jint eventRegistration); void dispose(); @@ -64,16 +71,17 @@ private: void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override; void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t configId, nsecs_t vsyncPeriod) override; + void dispatchFrameRateOverrides(nsecs_t timestamp, PhysicalDisplayId displayId, + std::vector<FrameRateOverride> overrides) override; void dispatchNullEvent(nsecs_t timestamp, PhysicalDisplayId displayId) override {} }; - -NativeDisplayEventReceiver::NativeDisplayEventReceiver(JNIEnv* env, - jobject receiverWeak, const sp<MessageQueue>& messageQueue, jint vsyncSource, - jint configChanged) : - DisplayEventDispatcher(messageQueue->getLooper(), - static_cast<ISurfaceComposer::VsyncSource>(vsyncSource), - static_cast<ISurfaceComposer::ConfigChanged>(configChanged)), +NativeDisplayEventReceiver::NativeDisplayEventReceiver(JNIEnv* env, jobject receiverWeak, + const sp<MessageQueue>& messageQueue, + jint vsyncSource, jint eventRegistration) + : DisplayEventDispatcher(messageQueue->getLooper(), + static_cast<ISurfaceComposer::VsyncSource>(vsyncSource), + static_cast<ISurfaceComposer::EventRegistration>(eventRegistration)), mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)), mMessageQueue(messageQueue) { ALOGV("receiver %p ~ Initializing display event receiver.", this); @@ -137,16 +145,48 @@ void NativeDisplayEventReceiver::dispatchConfigChanged( mMessageQueue->raiseAndClearException(env, "dispatchConfigChanged"); } -static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, - jobject messageQueueObj, jint vsyncSource, jint configChanged) { +void NativeDisplayEventReceiver::dispatchFrameRateOverrides( + nsecs_t timestamp, PhysicalDisplayId displayId, std::vector<FrameRateOverride> overrides) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + + ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal)); + if (receiverObj.get()) { + ALOGV("receiver %p ~ Invoking FrameRateOverride handler.", this); + const auto frameRateOverrideClass = + gDisplayEventReceiverClassInfo.frameRateOverrideClassInfo.clazz; + const auto frameRateOverrideInit = + gDisplayEventReceiverClassInfo.frameRateOverrideClassInfo.init; + auto frameRateOverrideInitObject = + env->NewObject(frameRateOverrideClass, frameRateOverrideInit, 0, 0); + auto frameRateOverrideArray = env->NewObjectArray(overrides.size(), frameRateOverrideClass, + frameRateOverrideInitObject); + for (size_t i = 0; i < overrides.size(); i++) { + auto FrameRateOverrideObject = + env->NewObject(frameRateOverrideClass, frameRateOverrideInit, overrides[i].uid, + overrides[i].frameRateHz); + env->SetObjectArrayElement(frameRateOverrideArray, i, FrameRateOverrideObject); + } + + env->CallVoidMethod(receiverObj.get(), + gDisplayEventReceiverClassInfo.dispatchFrameRateOverrides, timestamp, + displayId.value, frameRateOverrideArray); + ALOGV("receiver %p ~ Returned from FrameRateOverride handler.", this); + } + + mMessageQueue->raiseAndClearException(env, "dispatchConfigChanged"); +} + +static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, jobject messageQueueObj, + jint vsyncSource, jint eventRegistration) { sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj); if (messageQueue == NULL) { jniThrowRuntimeException(env, "MessageQueue is not initialized."); return 0; } - sp<NativeDisplayEventReceiver> receiver = new NativeDisplayEventReceiver(env, - receiverWeak, messageQueue, vsyncSource, configChanged); + sp<NativeDisplayEventReceiver> receiver = + new NativeDisplayEventReceiver(env, receiverWeak, messageQueue, vsyncSource, + eventRegistration); status_t status = receiver->initialize(); if (status) { String8 message; @@ -205,6 +245,18 @@ int register_android_view_DisplayEventReceiver(JNIEnv* env) { gDisplayEventReceiverClassInfo.clazz, "dispatchHotplug", "(JJZ)V"); gDisplayEventReceiverClassInfo.dispatchConfigChanged = GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchConfigChanged", "(JJI)V"); + gDisplayEventReceiverClassInfo.dispatchFrameRateOverrides = + GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, + "dispatchFrameRateOverrides", + "(JJ[Landroid/view/DisplayEventReceiver$FrameRateOverride;)V"); + + jclass frameRateOverrideClazz = + FindClassOrDie(env, "android/view/DisplayEventReceiver$FrameRateOverride"); + gDisplayEventReceiverClassInfo.frameRateOverrideClassInfo.clazz = + MakeGlobalRefOrDie(env, frameRateOverrideClazz); + gDisplayEventReceiverClassInfo.frameRateOverrideClassInfo.init = + GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.frameRateOverrideClassInfo.clazz, + "<init>", "(IF)V"); return res; } diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp index 1ea918a900ad..1c78750f3610 100644 --- a/core/jni/android_view_InputEventReceiver.cpp +++ b/core/jni/android_view_InputEventReceiver.cpp @@ -50,6 +50,7 @@ static struct { jmethodID dispatchInputEvent; jmethodID onFocusEvent; + jmethodID onPointerCaptureEvent; jmethodID onBatchedInputEventPending; } gInputEventReceiverClassInfo; @@ -345,6 +346,19 @@ status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env, finishInputEvent(seq, true /* handled */); continue; } + case AINPUT_EVENT_TYPE_CAPTURE: { + const CaptureEvent* captureEvent = static_cast<CaptureEvent*>(inputEvent); + if (kDebugDispatchCycle) { + ALOGD("channel '%s' ~ Received capture event: pointerCaptureEnabled=%s", + getInputChannelName().c_str(), + toString(captureEvent->getPointerCaptureEnabled())); + } + env->CallVoidMethod(receiverObj.get(), + gInputEventReceiverClassInfo.onPointerCaptureEvent, + jboolean(captureEvent->getPointerCaptureEnabled())); + finishInputEvent(seq, true /* handled */); + continue; + } default: assert(false); // InputConsumer should prevent this from ever happening @@ -489,6 +503,9 @@ int register_android_view_InputEventReceiver(JNIEnv* env) { "dispatchInputEvent", "(ILandroid/view/InputEvent;)V"); gInputEventReceiverClassInfo.onFocusEvent = GetMethodIDOrDie(env, gInputEventReceiverClassInfo.clazz, "onFocusEvent", "(ZZ)V"); + gInputEventReceiverClassInfo.onPointerCaptureEvent = + GetMethodIDOrDie(env, gInputEventReceiverClassInfo.clazz, "onPointerCaptureEvent", + "(Z)V"); gInputEventReceiverClassInfo.onBatchedInputEventPending = GetMethodIDOrDie(env, gInputEventReceiverClassInfo.clazz, "onBatchedInputEventPending", "(I)V"); diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 6ffafc16b89c..3156f71fa113 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -78,7 +78,6 @@ #include <android-base/unique_fd.h> #include <bionic/malloc.h> #include <bionic/mte.h> -#include <bionic/mte_kernel.h> #include <cutils/fs.h> #include <cutils/memory.h> #include <cutils/multiuser.h> @@ -1650,28 +1649,6 @@ static void BindMountStorageDirs(JNIEnv* env, jobjectArray pkg_data_info_list, } } -#ifdef ANDROID_EXPERIMENTAL_MTE -static void SetTagCheckingLevel(int level) { -#ifdef __aarch64__ - if (!(getauxval(AT_HWCAP2) & HWCAP2_MTE)) { - return; - } - - int tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0); - if (tagged_addr_ctrl < 0) { - ALOGE("prctl(PR_GET_TAGGED_ADDR_CTRL) failed: %s", strerror(errno)); - return; - } - - tagged_addr_ctrl = (tagged_addr_ctrl & ~PR_MTE_TCF_MASK) | level; - if (prctl(PR_SET_TAGGED_ADDR_CTRL, tagged_addr_ctrl, 0, 0, 0) < 0) { - ALOGE("prctl(PR_SET_TAGGED_ADDR_CTRL, %d) failed: %s", tagged_addr_ctrl, - strerror(errno)); - } -#endif -} -#endif - // Utility routine to specialize a zygote child process. static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, @@ -1807,22 +1784,14 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, heap_tagging_level = M_HEAP_TAGGING_LEVEL_TBI; break; case RuntimeFlags::MEMORY_TAG_LEVEL_ASYNC: -#ifdef ANDROID_EXPERIMENTAL_MTE - SetTagCheckingLevel(PR_MTE_TCF_ASYNC); -#endif heap_tagging_level = M_HEAP_TAGGING_LEVEL_ASYNC; break; case RuntimeFlags::MEMORY_TAG_LEVEL_SYNC: -#ifdef ANDROID_EXPERIMENTAL_MTE - SetTagCheckingLevel(PR_MTE_TCF_SYNC); -#endif heap_tagging_level = M_HEAP_TAGGING_LEVEL_SYNC; break; default: -#ifdef ANDROID_EXPERIMENTAL_MTE - SetTagCheckingLevel(PR_MTE_TCF_NONE); -#endif heap_tagging_level = M_HEAP_TAGGING_LEVEL_NONE; + break; } android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &heap_tagging_level, sizeof(heap_tagging_level)); // Now that we've used the flag, clear it so that we don't pass unknown flags to the ART runtime. diff --git a/core/proto/Android.bp b/core/proto/Android.bp deleted file mode 100644 index 3b891d6b4947..000000000000 --- a/core/proto/Android.bp +++ /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. - -// C++ library for Bluetooth platform wide protobuf definitions -cc_library_static { - name: "libbt-platform-protos-lite", - host_supported: true, - proto: { - export_proto_headers: true, - type: "lite", - }, - srcs: [ - "android/bluetooth/a2dp/enums.proto", - "android/bluetooth/enums.proto", - "android/bluetooth/hci/enums.proto", - "android/bluetooth/hfp/enums.proto", - "android/bluetooth/smp/enums.proto", - ], -} diff --git a/core/proto/OWNERS b/core/proto/OWNERS index 542d26fa233e..5eeeb048e6d6 100644 --- a/core/proto/OWNERS +++ b/core/proto/OWNERS @@ -25,3 +25,18 @@ hyunyoungs@google.com # Graphics stats jreck@google.com + +# Temporary Block to assist in migration +# Bug: 143080132 +per-file *enums.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com +per-file *media_output_enum.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com +per-file *networkcapabilities.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com +per-file *data_stall_event.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com +per-file *procstats_enum.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com +per-file *usb.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com +per-file *network_stack.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com +per-file *tethering.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com +per-file *dns_resolver.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com +per-file *device_policy.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com +per-file *launcher.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com +per-file *mediametrics.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com diff --git a/core/proto/android/app/enums.proto b/core/proto/android/app/enums.proto deleted file mode 100644 index 722194b322c6..000000000000 --- a/core/proto/android/app/enums.proto +++ /dev/null @@ -1,215 +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 android.app; - -option java_outer_classname = "AppProtoEnums"; -option java_multiple_files = true; - -// ActivityManagerInternal.java's APP_TRANSITION reasons. -enum AppTransitionReasonEnum { - APP_TRANSITION_REASON_UNKNOWN = 0; - // The transition was started because we drew the splash screen. - APP_TRANSITION_SPLASH_SCREEN = 1; - // The transition was started because we all app windows were drawn. - APP_TRANSITION_WINDOWS_DRAWN = 2; - // The transition was started because of a timeout. - APP_TRANSITION_TIMEOUT = 3; - // The transition was started because of a we drew a task snapshot. - APP_TRANSITION_SNAPSHOT = 4; - // The transition was started because it was a recents animation and we only needed to wait on - // the wallpaper. - APP_TRANSITION_RECENTS_ANIM = 5; -} - -// ActivityManager.java PROCESS_STATEs -// Next tag: 1021 -enum ProcessStateEnum { - // Unlike the ActivityManager PROCESS_STATE values, the ordering and numerical values - // here are completely fixed and arbitrary. Order is irrelevant. - // No attempt need be made to keep them in sync. - // The values here must not be modified. Any new process states can be appended to the end. - - // Process state that is unknown to this proto file (i.e. is not mapped - // by ActivityManager.processStateAmToProto()). Can only happen if there's a bug in the mapping. - PROCESS_STATE_UNKNOWN_TO_PROTO = 998; - // Not a real process state. - PROCESS_STATE_UNKNOWN = 999; - // Process is a persistent system process. - PROCESS_STATE_PERSISTENT = 1000; - // Process is a persistent system process and is doing UI. - PROCESS_STATE_PERSISTENT_UI = 1001; - // Process is hosting the current top activities. Note that this covers - // all activities that are visible to the user. - PROCESS_STATE_TOP = 1002; - // Process is bound to a TOP app. - PROCESS_STATE_BOUND_TOP = 1020; - // Process is hosting a foreground service. - PROCESS_STATE_FOREGROUND_SERVICE = 1003; - // Process is hosting a service bound by the system or another foreground app. - PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 1004; - // Process is important to the user, and something they are aware of. - PROCESS_STATE_IMPORTANT_FOREGROUND = 1005; - // Process is important to the user, but not something they are aware of. - PROCESS_STATE_IMPORTANT_BACKGROUND = 1006; - // Process is in the background transient so we will try to keep running. - PROCESS_STATE_TRANSIENT_BACKGROUND = 1007; - // Process is in the background running a backup/restore operation. - PROCESS_STATE_BACKUP = 1008; - // Process is in the background running a service. Unlike oom_adj, this - // level is used for both the normal running in background state and the - // executing operations state. - PROCESS_STATE_SERVICE = 1009; - // Process is in the background running a receiver. Note that from the - // perspective of oom_adj, receivers run at a higher foreground level, but - // for our prioritization here that is not necessary and putting them - // below services means many fewer changes in some process states as they - // receive broadcasts. - PROCESS_STATE_RECEIVER = 1010; - // Same as PROCESS_STATE_TOP but while device is sleeping. - PROCESS_STATE_TOP_SLEEPING = 1011; - // Process is in the background, but it can't restore its state so we want - // to try to avoid killing it. - PROCESS_STATE_HEAVY_WEIGHT = 1012; - // Process is in the background but hosts the home activity. - PROCESS_STATE_HOME = 1013; - // Process is in the background but hosts the last shown activity. - PROCESS_STATE_LAST_ACTIVITY = 1014; - // Process is being cached for later use and contains activities. - PROCESS_STATE_CACHED_ACTIVITY = 1015; - // Process is being cached for later use and is a client of another cached - // process that contains activities. - PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 1016; - // Process is being cached for later use and has an activity that corresponds - // to an existing recent task. - PROCESS_STATE_CACHED_RECENT = 1017; - // Process is being cached for later use and is empty. - PROCESS_STATE_CACHED_EMPTY = 1018; - // Process does not exist. - PROCESS_STATE_NONEXISTENT = 1019; -} - -// AppOpsManager.java - operation ids for logging -enum AppOpEnum { - APP_OP_NONE = -1; - APP_OP_COARSE_LOCATION = 0; - APP_OP_FINE_LOCATION = 1; - APP_OP_GPS = 2; - APP_OP_VIBRATE = 3; - APP_OP_READ_CONTACTS = 4; - APP_OP_WRITE_CONTACTS = 5; - APP_OP_READ_CALL_LOG = 6; - APP_OP_WRITE_CALL_LOG = 7; - APP_OP_READ_CALENDAR = 8; - APP_OP_WRITE_CALENDAR = 9; - APP_OP_WIFI_SCAN = 10; - APP_OP_POST_NOTIFICATION = 11; - APP_OP_NEIGHBORING_CELLS = 12; - APP_OP_CALL_PHONE = 13; - APP_OP_READ_SMS = 14; - APP_OP_WRITE_SMS = 15; - APP_OP_RECEIVE_SMS = 16; - APP_OP_RECEIVE_EMERGENCY_SMS = 17; - APP_OP_RECEIVE_MMS = 18; - APP_OP_RECEIVE_WAP_PUSH = 19; - APP_OP_SEND_SMS = 20; - APP_OP_READ_ICC_SMS = 21; - APP_OP_WRITE_ICC_SMS = 22; - APP_OP_WRITE_SETTINGS = 23; - APP_OP_SYSTEM_ALERT_WINDOW = 24; - APP_OP_ACCESS_NOTIFICATIONS = 25; - APP_OP_CAMERA = 26; - APP_OP_RECORD_AUDIO = 27; - APP_OP_PLAY_AUDIO = 28; - APP_OP_READ_CLIPBOARD = 29; - APP_OP_WRITE_CLIPBOARD = 30; - APP_OP_TAKE_MEDIA_BUTTONS = 31; - APP_OP_TAKE_AUDIO_FOCUS = 32; - APP_OP_AUDIO_MASTER_VOLUME = 33; - APP_OP_AUDIO_VOICE_VOLUME = 34; - APP_OP_AUDIO_RING_VOLUME = 35; - APP_OP_AUDIO_MEDIA_VOLUME = 36; - APP_OP_AUDIO_ALARM_VOLUME = 37; - APP_OP_AUDIO_NOTIFICATION_VOLUME = 38; - APP_OP_AUDIO_BLUETOOTH_VOLUME = 39; - APP_OP_WAKE_LOCK = 40; - APP_OP_MONITOR_LOCATION = 41; - APP_OP_MONITOR_HIGH_POWER_LOCATION = 42; - APP_OP_GET_USAGE_STATS = 43; - APP_OP_MUTE_MICROPHONE = 44; - APP_OP_TOAST_WINDOW = 45; - APP_OP_PROJECT_MEDIA = 46; - APP_OP_ACTIVATE_VPN = 47; - APP_OP_WRITE_WALLPAPER = 48; - APP_OP_ASSIST_STRUCTURE = 49; - APP_OP_ASSIST_SCREENSHOT = 50; - APP_OP_READ_PHONE_STATE = 51; - APP_OP_ADD_VOICEMAIL = 52; - APP_OP_USE_SIP = 53; - APP_OP_PROCESS_OUTGOING_CALLS = 54; - APP_OP_USE_FINGERPRINT = 55; - APP_OP_BODY_SENSORS = 56; - APP_OP_READ_CELL_BROADCASTS = 57; - APP_OP_MOCK_LOCATION = 58; - APP_OP_READ_EXTERNAL_STORAGE = 59; - APP_OP_WRITE_EXTERNAL_STORAGE = 60; - APP_OP_TURN_SCREEN_ON = 61; - APP_OP_GET_ACCOUNTS = 62; - APP_OP_RUN_IN_BACKGROUND = 63; - APP_OP_AUDIO_ACCESSIBILITY_VOLUME = 64; - APP_OP_READ_PHONE_NUMBERS = 65; - APP_OP_REQUEST_INSTALL_PACKAGES = 66; - APP_OP_PICTURE_IN_PICTURE = 67; - APP_OP_INSTANT_APP_START_FOREGROUND = 68; - APP_OP_ANSWER_PHONE_CALLS = 69; - APP_OP_RUN_ANY_IN_BACKGROUND = 70; - APP_OP_CHANGE_WIFI_STATE = 71; - APP_OP_REQUEST_DELETE_PACKAGES = 72; - APP_OP_BIND_ACCESSIBILITY_SERVICE = 73; - APP_OP_ACCEPT_HANDOVER = 74; - APP_OP_MANAGE_IPSEC_TUNNELS = 75; - APP_OP_START_FOREGROUND = 76; - APP_OP_BLUETOOTH_SCAN = 77; - APP_OP_USE_BIOMETRIC = 78; - APP_OP_ACTIVITY_RECOGNITION = 79; - APP_OP_SMS_FINANCIAL_TRANSACTIONS = 80; - APP_OP_READ_MEDIA_AUDIO = 81; - APP_OP_WRITE_MEDIA_AUDIO = 82; - APP_OP_READ_MEDIA_VIDEO = 83; - APP_OP_WRITE_MEDIA_VIDEO = 84; - APP_OP_READ_MEDIA_IMAGES = 85; - APP_OP_WRITE_MEDIA_IMAGES = 86; - APP_OP_LEGACY_STORAGE = 87; - APP_OP_ACCESS_ACCESSIBILITY = 88; - APP_OP_READ_DEVICE_IDENTIFIERS = 89; - APP_OP_ACCESS_MEDIA_LOCATION = 90; - APP_OP_QUERY_ALL_PACKAGES = 91; - APP_OP_MANAGE_EXTERNAL_STORAGE = 92; - APP_OP_INTERACT_ACROSS_PROFILES = 93; - APP_OP_ACTIVATE_PLATFORM_VPN = 94; - APP_OP_LOADER_USAGE_STATS = 95; - APP_OP_DEPRECATED_1 = 96 [deprecated = true]; - APP_OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED = 97; - APP_OP_AUTO_REVOKE_MANAGED_BY_INSTALLER = 98; - APP_OP_NO_ISOLATED_STORAGE = 99; - APP_OP_PHONE_CALL_MICROPHONE = 100; - APP_OP_PHONE_CALL_CAMERA = 101; - APP_OP_RECORD_AUDIO_HOTWORD = 102; - APP_OP_MANAGE_ONGOING_CALLS = 103; - APP_OP_MANAGE_CREDENTIALS = 104; -} diff --git a/core/proto/android/app/job/enums.proto b/core/proto/android/app/job/enums.proto deleted file mode 100644 index 41863bbbfbf1..000000000000 --- a/core/proto/android/app/job/enums.proto +++ /dev/null @@ -1,38 +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 android.app.job; - -// This file is for JobScheduler enums inside the app directory. If you're -// adding enums for system-server-side code, use the file in -// frameworks/base/core/proto/android/server/job. -option java_outer_classname = "JobProtoEnums"; -option java_multiple_files = true; - -// Reasons a job is stopped. -// Primarily used in android.app.job.JobParameters.java. -enum StopReasonEnum { - STOP_REASON_UNKNOWN = -1; - STOP_REASON_CANCELLED = 0; - STOP_REASON_CONSTRAINTS_NOT_SATISFIED = 1; - STOP_REASON_PREEMPT = 2; - STOP_REASON_TIMEOUT = 3; - STOP_REASON_DEVICE_IDLE = 4; - STOP_REASON_DEVICE_THERMAL = 5; - STOP_REASON_RESTRICTED_BUCKET = 6; -} diff --git a/core/proto/android/app/media_output_enum.proto b/core/proto/android/app/media_output_enum.proto deleted file mode 100644 index 0d42fb77025a..000000000000 --- a/core/proto/android/app/media_output_enum.proto +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -syntax = "proto2"; - -package android.app.settings.mediaoutput; -option java_multiple_files = true; - -/** - * The medium type specified in an output switching operation. - */ -enum MediumType { - UNKNOWN_TYPE = 0; - BUILTIN_SPEAKER = 1; - WIRED_3POINT5_MM_AUDIO = 100; - WIRED_3POINT5_MM_HEADSET = 101; - WIRED_3POINT5_MM_HEADPHONES = 102; - USB_C_AUDIO = 200; - USB_C_DEVICE = 201; - USB_C_HEADSET = 202; - USB_C_ACCESSORY = 203; - USB_C_DOCK = 204; - USB_C_HDMI = 205; - BLUETOOTH = 300; - BLUETOOTH_HEARING_AID = 301; - BLUETOOTH_A2DP = 302; - REMOTE_SINGLE = 400; - REMOTE_TV = 401; - REMOTE_SPEAKER = 402; - REMOTE_GROUP = 500; - REMOTE_DYNAMIC_GROUP = 501; -}; - -/** - * The result of an output switching operation. - */ -enum SwitchResult { - ERROR = 0; - OK = 1; -}; - -/** - * The sub result of an output switching operation. - */ -enum SubResult { - UNKNOWN_ERROR = 0; - NO_ERROR = 1; - REJECTED = 2; - NETWORK_ERROR = 3; - ROUTE_NOT_AVAILABLE = 4; - INVALID_COMMAND = 5; -} diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto deleted file mode 100644 index a2a7649645f5..000000000000 --- a/core/proto/android/app/settings_enums.proto +++ /dev/null @@ -1,2794 +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. - */ - -syntax = "proto2"; - -package android.app.settings; -option java_multiple_files = true; - -/** - * The action performed in this event - */ -enum Action { - ACTION_UNKNOWN = 0; - PAGE_VISIBLE = 1; - PAGE_HIDE = 2; - - // ACTION: Settings > Wi-Fi > [Long press network] > Connect to network - // SUBTYPE: true if connecting to a saved network, false if not - // CATEGORY: SETTINGS - // OS: 6.0 - ACTION_WIFI_CONNECT = 135; - - // ACTION: Settings > Wi-Fi > [Long press network] > Forget network - // CATEGORY: SETTINGS - // OS: 6.0 - ACTION_WIFI_FORGET = 137; - - // ACTION: Settings > Wi-Fi > Toggle off - // SUBTYPE: true if connected to network before toggle, false if not - // CATEGORY: SETTINGS - // OS: 6.0 - ACTION_WIFI_OFF = 138; - - // ACTION: Settings > Wi-Fi > Toggle on - // CATEGORY: SETTINGS - // OS: 6.0 - ACTION_WIFI_ON = 139; - - // ACTION: Settings > Bluetooth > Overflow > Rename this device - // CATEGORY: SETTINGS - // OS: 6.0 - ACTION_BLUETOOTH_RENAME = 161; - - // ACTION: Settings > Bluetooth > Overflow > Show received files - // CATEGORY: SETTINGS - // OS: 6.0 - ACTION_BLUETOOTH_FILES = 162; - - // ACTION: DND Settings > Priority only allows > Reminder toggle - // SUBTYPE: 0 is off, 1 is on - // CATEGORY: SETTINGS - // OS: 6.0 - ACTION_ZEN_ALLOW_REMINDERS = 167; - - // ACTION: DND Settings > Priority only allows > Event toggle - // SUBTYPE: 0 is off, 1 is on - // CATEGORY: SETTINGS - // OS: 6.0 - ACTION_ZEN_ALLOW_EVENTS = 168; - - // ACTION: DND Settings > Priority only allows > Messages - // SUBTYPE: 0 is off, 1 is on - // CATEGORY: SETTINGS - // OS: 6.0 - ACTION_ZEN_ALLOW_MESSAGES = 169; - - // ACTION: DND Settings > Priority only allows > Calls - // SUBTYPE: 0 is off, 1 is on - // CATEGORY: SETTINGS - // OS: 6.0 - ACTION_ZEN_ALLOW_CALLS = 170; - - // ACTION: DND Settings > Priority only allows > Repeat callers toggle - // SUBTYPE: 0 is off, 1 is on - // CATEGORY: SETTINGS - // OS: 6.0 - ACTION_ZEN_ALLOW_REPEAT_CALLS = 171; - - // ACTION: DND Settings > Automatic rules > [Rule] > Delete rule > Delete - // CATEGORY: SETTINGS - // OS: 6.0 - ACTION_ZEN_DELETE_RULE_OK = 175; - - // ACTION: Settings > More > Airplane mode toggle - // SUBTYPE: 0 is off, 1 is on - // CATEGORY: SETTINGS - // OS: 6.0 - ACTION_AIRPLANE_TOGGLE = 177; - - // ACTION: Settings > Data usage > Cellular data toggle - // SUBTYPE: 0 is off, 1 is on - // CATEGORY: SETTINGS - // OS: 6.0 - ACTION_CELL_DATA_TOGGLE = 178; - - // ACTION: Settings > Display > When device is rotated - // CATEGORY: SETTINGS - // OS: 6.0 - ACTION_ROTATION_LOCK = 203; - - // OPEN: Settings > Search > Perform search - // CATEGORY: SETTINGS - // OS: 6.0 - ACTION_SEARCH_RESULTS = 226; - - // ACTION: Settings > Security > Nexus Imprint > [Fingerprint] > Delete - // CATEGORY: SETTINGS - // OS: 6.0 - ACTION_FINGERPRINT_DELETE = 253; - - // ACTION: Settings > Security > Nexus Imprint > [Fingerprint] > Rename - // CATEGORY: SETTINGS - // OS: 6.0 - ACTION_FINGERPRINT_RENAME = 254; - - // ACTION: Settings -> Developer Options -> Take bug report -> Interactive report - // CATEGORY: SETTINGS - // OS: N - // Interactive bug report initiated from Settings. - ACTION_BUGREPORT_FROM_SETTINGS_INTERACTIVE = 294; - - // ACTION: Settings -> Developer Options -> Take bug report -> Full report - // CATEGORY: SETTINGS - // OS: N - // Interactive bug report initiated from Settings. - ACTION_BUGREPORT_FROM_SETTINGS_FULL = 295; - - // click on collapsed conditional or clicks expand button - ACTION_SETTINGS_CONDITION_EXPAND = 373; - - // click main area of expanded conditional - ACTION_SETTINGS_CONDITION_CLICK = 375; - - // click a direct button on expanded conditional - ACTION_SETTINGS_CONDITION_BUTTON = 376; - - // Action: user enable / disabled data saver using Settings - // OPEN: Settings -> Data Usage -> Data saver -> On/off toggle - // VALUE: 1 for enabled, 0 for disabled - // CATEGORY: SETTINGS - // OS: N - ACTION_DATA_SAVER_MODE = 394; - - // User whitelisted an app for Data Saver mode; action pass package name of app - // Action: user enable / disabled data saver using Settings - // OPEN: Settings -> Data Usage -> Data saver -> Unrestricted data access > APP toggle turned on - // or - // Settings -> Apps -> APP -> Data usage -> Unrestricted data usage toggle turned on - // VALUE: package name of APP - // CATEGORY: SETTINGS - // OS: N - ACTION_DATA_SAVER_WHITELIST = 395; - - // User blacklisted an app for Data Saver mode; action pass package name of app - // OPEN: Settings -> Apps -> APP -> Data usage -> Background data toggle turned off - // VALUE: package name of APP - // CATEGORY: SETTINGS - // OS: N - ACTION_DATA_SAVER_BLACKLIST = 396; - - // ACTION: Settings -> Storage -> Manage storage -> Click Storage Manager - // SUBTYPE: false is off, true is on - ACTION_TOGGLE_STORAGE_MANAGER = 489; - - // OPEN: Settings > Display -> Ambient Display - // CATEGORY: SETTINGS - ACTION_AMBIENT_DISPLAY = 495; - - // ACTION: Allow Battery optimization for an app - APP_SPECIAL_PERMISSION_BATTERY_ALLOW = 764; - - // ACTION: Deny Battery optimization for an app - APP_SPECIAL_PERMISSION_BATTERY_DENY = 765; - - // ACTION: Enable Device Admin app - APP_SPECIAL_PERMISSION_ADMIN_ALLOW = 766; - - // ACTION: Disable Device Admin app - APP_SPECIAL_PERMISSION_ADMIN_DENY = 767; - - // ACTION: Allow "Do Not Disturb access" for an app - APP_SPECIAL_PERMISSION_DND_ALLOW = 768; - - // ACTION: Deny "Do Not Disturb access" for an app - APP_SPECIAL_PERMISSION_DND_DENY = 769; - - // ACTION: Allow "Draw over other apps" for an app - APP_SPECIAL_PERMISSION_APPDRAW_ALLOW = 770; - - // ACTION: Deny "Display over other apps" for an app - APP_SPECIAL_PERMISSION_APPDRAW_DENY = 771; - - // ACTION: Allow "VR helper services" for an app - APP_SPECIAL_PERMISSION_VRHELPER_ALLOW = 772; - - // ACTION: Deny "VR helper services" for an app - APP_SPECIAL_PERMISSION_VRHELPER_DENY = 773; - - // ACTION: Allow "Modify system settings" for an app - APP_SPECIAL_PERMISSION_SETTINGS_CHANGE_ALLOW = 774; - - // ACTION: Deny "Modify system settings" for an app - APP_SPECIAL_PERMISSION_SETTINGS_CHANGE_DENY = 775; - - // ACTION: Allow "Notification access" for an app - APP_SPECIAL_PERMISSION_NOTIVIEW_ALLOW = 776; - - // ACTION: Deny "Notification access" for an app - APP_SPECIAL_PERMISSION_NOTIVIEW_DENY = 777; - - // ACTION: "Premium SMS access" for an app - "ask user" option - APP_SPECIAL_PERMISSION_PREMIUM_SMS_ASK = 778; - - // ACTION: "Premium SMS access" for an app - "never allow" option - APP_SPECIAL_PERMISSION_PREMIUM_SMS_DENY = 779; - - // ACTION: "Premium SMS access" for an app - "always allow" option - APP_SPECIAL_PERMISSION_PREMIUM_SMS_ALWAYS_ALLOW = 780; - - // ACTION: Allow "Unrestricted data access" for an app - APP_SPECIAL_PERMISSION_UNL_DATA_ALLOW = 781; - - // ACTION: Deny "Unrestricted data access" for an app - APP_SPECIAL_PERMISSION_UNL_DATA_DENY = 782; - - // ACTION: Allow "Usage access" for an app - APP_SPECIAL_PERMISSION_USAGE_VIEW_ALLOW = 783; - - // ACTION: Deny "Usage access" for an app - APP_SPECIAL_PERMISSION_USAGE_VIEW_DENY = 784; - - // ACTION: "Force stop" action on an app - ACTION_APP_FORCE_STOP = 807; - - // ACTION: Allow "Enable picture-in-picture" for an app - APP_PICTURE_IN_PICTURE_ALLOW = 813; - - // ACTION: Create a Settings shortcut item. - ACTION_SETTINGS_CREATE_SHORTCUT = 829; - - // ACTION: A tile in Settings information architecture is clicked - ACTION_SETTINGS_TILE_CLICK = 830; - - // ACTION: Settings advanced button is expanded - ACTION_SETTINGS_ADVANCED_BUTTON_EXPAND = 834; - - // ACTION: Deny "Enable picture-in-picture" for an app - APP_PICTURE_IN_PICTURE_DENY = 814; - - // ACTION: Settings -> Display -> Theme - ACTION_THEME = 816; - - // ACTION: Settings > About device > Build number - ACTION_SETTINGS_BUILD_NUMBER_PREF = 847; - - // ACTION: Settings > Battery > Menu > Optimization - ACTION_SETTINGS_MENU_BATTERY_OPTIMIZATION = 851; - - // ACTION: Settings > Battery > Menu > Apps Toggle - ACTION_SETTINGS_MENU_BATTERY_APPS_TOGGLE = 852; - - // ACTION: Settings > Any preference is changed - ACTION_SETTINGS_PREFERENCE_CHANGE = 853; - - // ACTION: Settings > Connected devices > Bluetooth -> Available devices - ACTION_SETTINGS_BLUETOOTH_PAIR = 866; - - // ACTION: Settings > Connected devices > Bluetooth -> Paired devices - ACTION_SETTINGS_BLUETOOTH_CONNECT = 867; - - // ACTION: Settings > Connected devices > Bluetooth -> Connected device - ACTION_SETTINGS_BLUETOOTH_DISCONNECT = 868; - - // ACTION: Settings > Connected devices > Bluetooth -> Error dialog - ACTION_SETTINGS_BLUETOOTH_CONNECT_ERROR = 869; - - // ACTION: Settings > Connected devices > Bluetooth master switch Toggle - ACTION_SETTINGS_MASTER_SWITCH_BLUETOOTH_TOGGLE = 870; - - // ACTION: Settings > App detail > Uninstall - ACTION_SETTINGS_UNINSTALL_APP = 872; - - // ACTION: Settings > App detail > Uninstall Device admin app - ACTION_SETTINGS_UNINSTALL_DEVICE_ADMIN = 873; - - // ACTION: Settings > App detail > Disable app - ACTION_SETTINGS_DISABLE_APP = 874; - - // ACTION: Settings > App detail > Enable app - ACTION_SETTINGS_ENABLE_APP = 875; - - // ACTION: Settings > App detail > Clear data - ACTION_SETTINGS_CLEAR_APP_DATA = 876; - - // ACTION: Settings > App detail > Clear cache - ACTION_SETTINGS_CLEAR_APP_CACHE = 877; - - // ACTION: Logs pressing the "Clear app" button in the app info settings page for an instant - // app. - // VALUE: The package name of the app - ACTION_SETTINGS_CLEAR_INSTANT_APP = 923; - - // OPEN: Assist Gesture training intro in Settings - // CATEGORY: SETTINGS - // OS: O DR - SETTINGS_ASSIST_GESTURE_TRAINING_INTRO = 991; - - // OPEN: Assist Gesture training enrolling in Settings - // CATEGORY: SETTINGS - // OS: O DR - SETTINGS_ASSIST_GESTURE_TRAINING_ENROLLING = 992; - - // OPEN: Assist Gesture training finished in Settings - // CATEGORY: SETTINGS - // OS: O DR - SETTINGS_ASSIST_GESTURE_TRAINING_FINISHED = 993; - - // ACTION: Update default app from Settings - ACTION_SETTINGS_UPDATE_DEFAULT_APP = 1000; - - // ACTION: Settings > Wi-Fi > [Long press network] > Sign in to network - // CATEGORY: SETTINGS - // OS: O DR - ACTION_WIFI_SIGNIN = 1008; - - // ACTION: Settings > Notification Settings > Open application notification - // CATEGORY: SETTINGS - // OS: O DR - ACTION_OPEN_APP_NOTIFICATION_SETTING = 1016; - - // ACTION: Settings > App Info > Open app settings - // CATEGORY: SETTINGS - // OS: O DR - ACTION_OPEN_APP_SETTING = 1017; - - // ACTION: Collect PSD Signals - // CATEGORY: SETTINGS - // OS: O DR - ACTION_PSD_LOADER = 1019; - - // OPEN: Settings > Trampoline Intent > Settings page - // CATEGORY: SETTINGS - // OS: O DR - TRAMPOLINE_SETTINGS_EVENT = 1033; - - // ACTION: Logged when user tries to pair a Bluetooth device without name from Settings app - // CATEGORY: SETTINGS - // OS: O MR - ACTION_SETTINGS_BLUETOOTH_PAIR_DEVICES_WITHOUT_NAMES = 1096; - - // ACTION: Settings > Network & Internet > Mobile network > Network - // CATEGORY: SETTINGS - ACTION_MOBILE_NETWORK_MANUAL_SELECT_NETWORK = 1210; - - // ACTION: DND Settings > Priority only allows > Alarms toggle - // SUBTYPE: 0 is off, 1 is on - // CATEGORY: SETTINGS - // OS: P - ACTION_ZEN_ALLOW_ALARMS = 1226; - - // ACTION: DND Settings > Priority only allows > Media toggle - // SUBTYPE: 0 is off, 1 is on - // CATEGORY: SETTINGS - // OS: P - ACTION_ZEN_ALLOW_MEDIA = 1227; - - // ACTION: A private dns mode been selected by user - // CATEGORY: SETTINGS - // OS: P - ACTION_PRIVATE_DNS_MODE = 1249; - - // OPEN: Settings > Sound > Do Not Disturb > Turn on automatically > Select rule ("Event") > Rule name > OK - // CATEGORY: SETTINGS - // OS: P - ACTION_ZEN_MODE_RULE_NAME_CHANGE_OK = 1267; - - // OPEN: Settings > Sound > Do Not Disturb > TURN ON NOW/TURN OFF NOW - // CATEGORY: SETTINGS - // OS: P - ACTION_ZEN_TOGGLE_DND_BUTTON = 1268; - - // ACTION: DND Settings > What to block > full screen intents - // SUBTYPE: false is allowed, true is blocked - // CATEGORY: SETTINGS - // OS: 6.0 - ACTION_ZEN_BLOCK_FULL_SCREEN_INTENTS = 1332; - - // ACTION: DND Settings > What to block - // SUBTYPE: false is allowed, true is blocked - // OS: P - ACTION_ZEN_BLOCK_LIGHT = 1333; - - // ACTION: DND Settings > What to block - // SUBTYPE: false is allowed, true is blocked - // OS: P - ACTION_ZEN_BLOCK_PEEK = 1334; - - // ACTION: DND Settings > What to block - // SUBTYPE: false is allowed, true is blocked - // OS: P - ACTION_ZEN_BLOCK_STATUS = 1335; - - // ACTION: DND Settings > What to block - // SUBTYPE: false is allowed, true is blocked - // OS: P - ACTION_ZEN_BLOCK_BADGE = 1336; - - // ACTION: DND Settings > What to block - // SUBTYPE: false is allowed, true is blocked - // OS: P - ACTION_ZEN_BLOCK_AMBIENT = 1337; - - // ACTION: DND Settings > What to block - // SUBTYPE: false is allowed, true is blocked - // OS: P - ACTION_ZEN_BLOCK_NOTIFICATION_LIST = 1338; - - // ACTION: DND Settings > Priority only allows > System toggle - // SUBTYPE: 0 is off, 1 is on - // CATEGORY: SETTINGS - // OS: P - ACTION_ZEN_ALLOW_SYSTEM = 1340; - - // ACTION: Settings > Battery settings > Battery tip > App restriction tip - // OS: P - ACTION_APP_RESTRICTION_TIP = 1347; - - // ACTION: Settings > Battery settings > Battery tip > High usage tip - // OS: P - ACTION_HIGH_USAGE_TIP = 1348; - - // ACTION: Settings > Battery settings > Battery tip > Summary tip - // OS: P - ACTION_SUMMARY_TIP = 1349; - - // ACTION: Settings > Battery settings > Battery tip > Smart battery tip - // OS: P - ACTION_SMART_BATTERY_TIP = 1350; - - // ACTION: Settings > Battery settings > Battery tip > Early warning tip - // OS: P - ACTION_EARLY_WARNING_TIP = 1351; - - // ACTION: Settings > Battery settings > Battery tip > Low battery tip - // OS: P - ACTION_LOW_BATTERY_TIP = 1352; - - // ACTION: Settings > Battery settings > Battery tip > App restriction list shown - // OS: P - ACTION_APP_RESTRICTION_TIP_LIST = 1353; - - // ACTION: Settings > Battery settings > Battery tip > High usage list shown - // OS: P - ACTION_HIGH_USAGE_TIP_LIST = 1354; - - // ACTION: Settings > Battery settings > Battery tip > Open app restriction page - // CATEGORY: SETTINGS - // OS: P - ACTION_TIP_OPEN_APP_RESTRICTION_PAGE = 1361; - - // ACTION: Settings > Battery settings > Battery tip > Restrict app - // CATEGORY: SETTINGS - // OS: P - ACTION_TIP_RESTRICT_APP = 1362; - - // ACTION: Settings > Battery settings > Battery tip > Unrestrict app - // CATEGORY: SETTINGS - // OS: P - ACTION_TIP_UNRESTRICT_APP = 1363; - - // ACTION: Settings > Battery settings > Battery tip > Open smart battery page - // CATEGORY: SETTINGS - // OS: P - ACTION_TIP_OPEN_SMART_BATTERY = 1364; - - // ACTION: Settings > Battery settings > Battery tip > Turn on battery saver - // CATEGORY: SETTINGS - // OS: P - ACTION_TIP_TURN_ON_BATTERY_SAVER = 1365; - - // ACTION: Settings > Anomaly receiver > Anomaly received - // CATEGORY: SETTINGS - // OS: P - ACTION_ANOMALY_TRIGGERED = 1367; - - // ACTION: A Settings Slice is requested - // CATEGORY: SETTINGS - // OS: P - ACTION_SETTINGS_SLICE_REQUESTED = 1371; - - // ACTION: A Settings Slice is updated with new value - // CATEGORY: SETTINGS - // OS: P - ACTION_SETTINGS_SLICE_CHANGED = 1372; - - // OPEN: DND onboarding activity > Ok button - // CATEGORY: SETTINGS - // OS: P - ACTION_ZEN_ONBOARDING_OK = 1378; - - // OPEN: DND onboarding activity > Settings link - // CATEGORY: SETTINGS - // OS: P - ACTION_ZEN_ONBOARDING_SETTINGS = 1379; - - // ACTION: Settings > Anomaly receiver > Anomaly ignored, don't show up in battery settings - // CATEGORY: SETTINGS - // OS: P - ACTION_ANOMALY_IGNORED = 1387; - - // ACTION: Settings > Battery settings > Battery tip > Open battery saver page - // CATEGORY: SETTINGS - // OS: P - ACTION_TIP_OPEN_BATTERY_SAVER_PAGE = 1388; - - // ACTION: DND Settings > What to block - // OS: P - ACTION_ZEN_SOUND_ONLY = 1396; - - // ACTION: DND Settings > Notifications - // OS: P - ACTION_ZEN_SOUND_AND_VIS_EFFECTS = 1397; - - // ACTION: DND Settings > Notifications - // OS: P - ACTION_ZEN_SHOW_CUSTOM = 1398; - - // ACTION: DND Settings > Notifications - // OS: P - ACTION_ZEN_CUSTOM = 1399; - - // OPEN: DND onboarding activity > don't update button - // CATEGORY: SETTINGS - // OS: P - ACTION_ZEN_ONBOARDING_KEEP_CURRENT_SETTINGS = 1406; - - // ACTION: Storage initialization wizard initialization choice of external/portable - // CATEGORY: SETTINGS - // OS: P - ACTION_STORAGE_INIT_EXTERNAL = 1407; - - // ACTION: Storage initialization wizard initialization choice of internal/adoptable - // CATEGORY: SETTINGS - // OS: P - ACTION_STORAGE_INIT_INTERNAL = 1408; - - // ACTION: Storage initialization wizard benchmark fast choice of continue - // CATEGORY: SETTINGS - // OS: P - ACTION_STORAGE_BENCHMARK_FAST_CONTINUE = 1409; - - // ACTION: Storage initialization wizard benchmark slow choice of continue - // CATEGORY: SETTINGS - // OS: P - ACTION_STORAGE_BENCHMARK_SLOW_CONTINUE = 1410; - - // ACTION: Storage initialization wizard benchmark slow choice of abort - // CATEGORY: SETTINGS - // OS: P - ACTION_STORAGE_BENCHMARK_SLOW_ABORT = 1411; - - // ACTION: Storage initialization wizard migration choice of now - // CATEGORY: SETTINGS - // OS: P - ACTION_STORAGE_MIGRATE_NOW = 1412; - - // ACTION: Storage initialization wizard migration choice of later - // CATEGORY: SETTINGS - // OS: P - ACTION_STORAGE_MIGRATE_LATER = 1413; - - // OPEN: Settings > Sound > Switch a2dp devices dialog - // CATEGORY: SETTINGS - // OS: P - DIALOG_SWITCH_A2DP_DEVICES = 1415; - - - // OPEN: Settings > Sound > Switch hfp devices dialog - // CATEGORY: SETTINGS - // OS: P - DIALOG_SWITCH_HFP_DEVICES = 1416; - - // OPEN: QS Sensor Privacy Mode tile shown - // ACTION: QS Sensor Privacy Mode tile tapped - // SUBTYPE: 0 is off, 1 is on - // CATEGORY: QUICK_SETTINGS - // OS: Q - QS_SENSOR_PRIVACY = 1598; - - // ACTION: Tap & Pay -> Default Application Setting -> Use Forground - ACTION_NFC_PAYMENT_FOREGROUND_SETTING = 1622; - - // ACTION: Tap & Pay -> Default Application Setting -> Use Default - ACTION_NFC_PAYMENT_ALWAYS_SETTING = 1623; - - // ACTION: Settings > Search Bar > Avatar - // CATEGORY: SETTINGS - // OS: Q - CLICK_ACCOUNT_AVATAR = 1643; - - // ACTION: Set new password (action intent android.app.action.SET_NEW_PASSWORD) - // CATEGORY: SETTINGS - // OS: Q - ACTION_SET_NEW_PASSWORD = 1645; - - // ACTION: Set new password (action intent android.app.action.SET_NEW_PARENT_PROFILE_PASSWORD) - // CATEGORY: SETTINGS - // OS: Q - ACTION_SET_NEW_PARENT_PROFILE_PASSWORD = 1646; - - // ACTION: An interaction with a Slice or other component in the Panel. - // CATEGORY: SETTINGS - // OS: Q - ACTION_PANEL_INTERACTION = 1658; - - // ACTION: Show Contextual homepage. Log total loading latency. - ACTION_CONTEXTUAL_HOME_SHOW = 1662; - - // ACTION: Contextual card displays - ACTION_CONTEXTUAL_CARD_SHOW = 1663; - - // ACTION: Contextual cards are eligible to be shown, but don't rank high - ACTION_CONTEXTUAL_CARD_NOT_SHOW = 1664; - - // ACTION: Settings > long press a card, and click dismiss - // Contextual card is dismissed - ACTION_CONTEXTUAL_CARD_DISMISS = 1665; - - // ACTION: Settings > click a card - // Contextual card is clicked - ACTION_CONTEXTUAL_CARD_CLICK = 1666; - - // Mapping: go/at-mapping - ACTION_ATSG = 1674; - - ACTION_ATPG = 1675; - - ACTION_ATCLPB = 1676; - - ACTION_ATCGIB = 1677; - - ACTION_ATCPAB = 1678; - - ACTION_ATCSAUC = 1679; - - ACTION_ATCSCUC = 1680; - - ACTION_ATCHNUC = 1681; - - // ACTION: Individual contextual card loading time - ACTION_CONTEXTUAL_CARD_LOAD = 1684; - - //ACTION: Contextual card loading timeout - ACTION_CONTEXTUAL_CARD_LOAD_TIMEOUT = 1685; - - //ACTION: Log result for each card's eligibility check - ACTION_CONTEXTUAL_CARD_ELIGIBILITY = 1686; - - // ACTION: Display white balance setting enabled or disabled. - // CATEGORY: SETTINGS - // OS: Q - ACTION_DISPLAY_WHITE_BALANCE_SETTING_CHANGED = 1703; - - // ACTION: Share a Wi-Fi network by generating a QR code - ACTION_SETTINGS_SHARE_WIFI_QR_CODE = 1710; - - // ACTION: Connect to a Wi-Fi network by scanning a QR code - ACTION_SETTINGS_ENROLL_WIFI_QR_CODE = 1711; - - // ACTION: Share Wi-Fi hotspot by generating a QR code - ACTION_SETTINGS_SHARE_WIFI_HOTSPOT_QR_CODE = 1712; - - // ACTION: Settings > Initialize Search bar > Verify Slice > Invalid data - ACTION_VERIFY_SLICE_ERROR_INVALID_DATA = 1725; - - // ACTION: Settings > Initialize Search bar > Verify Slice > Parsing error - ACTION_VERIFY_SLICE_PARSING_ERROR = 1726; - - // ACTION: Settings > Initialize Search bar > Verify Slice > Other exception - ACTION_VERIFY_SLICE_OTHER_EXCEPTION = 1727; - - // Custom tag to evaluate the consuming time of the Controller.updateState. - // CATEGORY: SETTINGS - // OS: R - ACTION_CONTROLLER_UPDATE_STATE = 1728; - - // Custom tag to evaluate the consuming time from onAttach to - // DashboardFragment.updatePreferenceStates. - // CATEGORY: SETTINGS - // OS: R - ACTION_DASHBOARD_VISIBLE_TIME = 1729; - - // ACTION: Allow "Access all files" for an app - APP_SPECIAL_PERMISSION_MANAGE_EXT_STRG_ALLOW = 1730; - - // ACTION: Deny "Access all files" for an app - APP_SPECIAL_PERMISSION_MANAGE_EXT_STRG_DENY = 1731; - - // ACTION: Battery feature usage - ACTION_BATTERY_OPTION_FEATURE_USAGE = 1732; - - // ACTION: Battery feature runtime event - ACTION_BATTERY_OPTION_RUNTIME_EVENT = 1733; - - // ACTION: Settings > Developer Options > Toggle on Wireless debugging - // CATEGORY: SETTINGS - // OS: R - ACTION_ADB_WIRELESS_ON = 1734; - - // ACTION: Settings > Developer Options > Toggle off Wireless debugging - // CATEGORY: SETTINGS - // OS: R - ACTION_ADB_WIRELESS_OFF = 1735; - - // ACTION: Change Wi-Fi hotspot name - // CATEGORY: SETTINGS - // OS: R - ACTION_SETTINGS_CHANGE_WIFI_HOTSPOT_NAME = 1736; - - // ACTION: Change Wi-Fi hotspot password - // CATEGORY: SETTINGS - // OS: R - ACTION_SETTINGS_CHANGE_WIFI_HOTSPOT_PASSWORD = 1737; - - // ACTION: Settings > Security > Toggle on Confirm Sim deletion - // CATEGORY: SETTINGS - // OS: R - ACTION_CONFIRM_SIM_DELETION_ON = 1738; - - // ACTION: Settings > Security > Toggle off Confirm Sim deletion - // CATEGORY: SETTINGS - // OS: R - ACTION_CONFIRM_SIM_DELETION_OFF = 1739; - - // ACTION: Settings > System > Gestures > Double tap > Toggle on Double tap - // CATEGORY: SETTINGS - // OS: S - ACTION_COLUMBUS_ENABLED = 1740; - - // ACTION: Settings > System > Gestures > Double tap > Toggle off Double tap - // CATEGORY: SETTINGS - // OS: S - ACTION_COLUMBUS_DISABLED = 1741; - - // ACTION: Settings > System > Gestures > Double tap > Invoke Assistant - // CATEGORY: SETTINGS - // OS: S - ACTION_COLUMBUS_ACTION_ASSISTANT = 1742; - - // ACTION: Settings > System > Gestures > Double tap > Take screenshot - // CATEGORY: SETTINGS - // OS: S - ACTION_COLUMBUS_ACTION_SCREENSHOT = 1743; - - // ACTION: Settings > System > Gestures > Double tap > Play and pause - // CATEGORY: SETTINGS - // OS: S - ACTION_COLUMBUS_ACTION_PLAY_PAUSE = 1744; - - // ACTION: Settings > System > Gestures > Double tap > Open app overview - // CATEGORY: SETTINGS - // OS: S - ACTION_COLUMBUS_ACTION_OVERVIEW = 1745; - - // ACTION: Settings > System > Gestures > Double tap > Open notification shade - // CATEGORY: SETTINGS - // OS: S - ACTION_COLUMBUS_ACTION_NOTIFICATION_SHADE = 1746; - - // ACTION: Settings > System > Gestures > Double tap > Require harder taps - // CATEGORY: SETTINGS - // OS: S - ACTION_COLUMBUS_LOW_SENSITIVITY = 1747; - - // OPEN: Columbus Gesture training intro in Settings - // CATEGORY: SETTINGS - // OS: S - SETTINGS_COLUMBUS_GESTURE_TRAINING_INTRO = 1748; - - // OPEN: Columbus Gesture training enrolling in Settings - // CATEGORY: SETTINGS - // OS: S - SETTINGS_COLUMBUS_GESTURE_TRAINING_ENROLLING = 1749; - - // OPEN: Columbus Gesture training finished in Settings - // CATEGORY: SETTINGS - // OS: S - SETTINGS_COLUMBUS_GESTURE_TRAINING_FINISHED = 1750; -} - -/** - * Id for Settings pages. Each page must have its own unique Id. - */ -enum PageId { - // Unknown page. Should not be used in production code. - PAGE_UNKNOWN = 0; - - // OPEN: Settings > Accessibility - // CATEGORY: SETTINGS - // OS: 6.0 - ACCESSIBILITY = 2; - - // OPEN: Settings > Accessibility > Captions preference - // CATEGORY: SETTINGS - // OS: 6.0 - ACCESSIBILITY_CAPTION_PROPERTIES = 3; - - // OPEN: Settings > Accessibility > [Service] - // CATEGORY: SETTINGS - // OS: 6.0 - ACCESSIBILITY_SERVICE = 4; - - // OPEN: Settings > Accessibility > Color correction - // CATEGORY: SETTINGS - // OS: 6.0 - ACCESSIBILITY_TOGGLE_DALTONIZER = 5; - - // OPEN: Settings > Accessibility > Accessibility shortcut - // CATEGORY: SETTINGS - // OS: 6.0 - ACCESSIBILITY_TOGGLE_GLOBAL_GESTURE = 6; - - // OPEN: Settings > Accessibility > Magnification gestures (Renamed in O) - // OPEN: Settings > Accessibility > Magnification > Magnify with triple-tap - // OPEN: Settings > Accessibility > Magnification > Magnify with button - // CATEGORY: SETTINGS - // OS: 6.0 - ACCESSIBILITY_TOGGLE_SCREEN_MAGNIFICATION = 7; - - // OPEN: Settings > Accounts - // CATEGORY: SETTINGS - // OS: 6.0 - ACCOUNT = 8; - - // OPEN: Settings > Accounts > [Single Account Sync Settings] - // CATEGORY: SETTINGS - // OS: 6.0 - ACCOUNTS_ACCOUNT_SYNC = 9; - - // OPEN: Settings > Accounts > Add an account - // CATEGORY: SETTINGS - // OS: 6.0 - ACCOUNTS_CHOOSE_ACCOUNT_ACTIVITY = 10; - - // OPEN: Settings > Cellular network settings > APNs - // CATEGORY: SETTINGS - // OS: 6.0 - APN = 12; - - // OPEN: Settings > More > Cellular network settings > APNs > [Edit APN] - // CATEGORY: SETTINGS - // OS: 6.0 - APN_EDITOR = 13; - - // OPEN: Settings > Apps > Configure apps > App links > [App] - // CATEGORY: SETTINGS - // OS: 6.0 - APPLICATIONS_APP_LAUNCH = 17; - - // OPEN: Settings > Internal storage > Apps storage > [App] - // CATEGORY: SETTINGS - // OS: 6.0 - APPLICATIONS_APP_STORAGE = 19; - - // OPEN: Settings > Apps > [App info] - // CATEGORY: SETTINGS - // OS: 6.0 - APPLICATIONS_INSTALLED_APP_DETAILS = 20; - - // OPEN: Settings > Memory > App usage > [App Memory usage] - // CATEGORY: SETTINGS - // OS: 6.0 - APPLICATIONS_PROCESS_STATS_DETAIL = 21; - - // OPEN: Settings > Memory > App usage - // CATEGORY: SETTINGS - // OS: 6.0 - APPLICATIONS_PROCESS_STATS_UI = 23; - - // OPEN: Choose Bluetooth device (ex: when sharing) - // CATEGORY: SETTINGS - // OS: 6.0 - BLUETOOTH_DEVICE_PICKER = 25; - - // OPEN: Settings > Security > Choose screen lock - // CATEGORY: SETTINGS - // OS: 6.0 - CHOOSE_LOCK_GENERIC = 27; - - // OPEN: Settings > Security > Choose screen lock > Choose your password - // CATEGORY: SETTINGS - // OS: 6.0 - CHOOSE_LOCK_PASSWORD = 28; - - // OPEN: Settings > Security > Choose screen lock > Choose your pattern - // CATEGORY: SETTINGS - // OS: 6.0 - CHOOSE_LOCK_PATTERN = 29; - - // OPEN: Settings > Security > Choose screen lock > Confirm your password - // CATEGORY: SETTINGS - // OS: 6.0 - CONFIRM_LOCK_PASSWORD = 30; - - // OPEN: Settings > Security > Choose screen lock > Confirm your pattern - // CATEGORY: SETTINGS - // OS: 6.0 - CONFIRM_LOCK_PATTERN = 31; - - // OPEN: Settings > Security > Encrypt phone - // CATEGORY: SETTINGS - // OS: 6.0 - CRYPT_KEEPER = 32; - - // OPEN: Settings > Security > Encrypt phone > Confirm - // CATEGORY: SETTINGS - // OS: 6.0 - CRYPT_KEEPER_CONFIRM = 33; - - // OPEN: Settings (Root page) - // CATEGORY: SETTINGS - // OS: 6.0 - DASHBOARD_SUMMARY = 35; - - // OPEN: Settings > Data usage - // CATEGORY: SETTINGS - // OS: 6.0 - DATA_USAGE_SUMMARY = 37; - - // OPEN: Settings > Date & time - // CATEGORY: SETTINGS - // OS: 6.0 - DATE_TIME = 38; - - // OPEN: Settings > Developer options - // CATEGORY: SETTINGS - // OS: 6.0 - DEVELOPMENT = 39; - - // OPEN: Settings > About phone - // CATEGORY: SETTINGS - // OS: 6.0 - DEVICEINFO = 40; - - // OPEN: Settings > Internal storage - // CATEGORY: SETTINGS - // OS: 6.0 - DEVICEINFO_STORAGE = 42; - - // OPEN: Settings > Display - // CATEGORY: SETTINGS - // OS: 6.0 - DISPLAY = 46; - - // OPEN: Settings > Display > Daydream - // CATEGORY: SETTINGS - // OS: 6.0 - DREAM = 47; - - // OPEN: Settings > Security > Screen lock > Secure start-up - // CATEGORY: SETTINGS - // OS: 6.0 - ENCRYPTION = 48; - - // OPEN: Settings > Security > Nexus Imprint - // CATEGORY: SETTINGS - // OS: 6.0 - FINGERPRINT = 49; - - // OPEN: Settings > Battery > History details - // CATEGORY: SETTINGS - // OS: 6.0 - FUELGAUGE_BATTERY_HISTORY_DETAIL = 51; - - // OPEN: Settings > Battery > Battery saver - // CATEGORY: SETTINGS - // OS: 6.0 - FUELGAUGE_BATTERY_SAVER = 52; - - // OPEN: Settings > Battery > [App Use details] - // CATEGORY: SETTINGS - // OS: 6.0 - FUELGAUGE_POWER_USAGE_DETAIL = 53; - - // OPEN: Settings > Security > SIM card lock settings - // CATEGORY: SETTINGS - // OS: 6.0 - ICC_LOCK = 56; - - // OPEN: Settings > Language & input > Physical keyboard - // CATEGORY: SETTINGS - // OS: 6.0 - INPUTMETHOD_KEYBOARD = 58; - - // OPEN: Settings > Language & input > Spell checker - // CATEGORY: SETTINGS - // OS: 6.0 - INPUTMETHOD_SPELL_CHECKERS = 59; - - // OBSOLETE - INPUTMETHOD_SUBTYPE_ENABLER = 60; - - // OPEN: Settings > Language & input > Personal dictionary - // CATEGORY: SETTINGS - // OS: 6.0 - INPUTMETHOD_USER_DICTIONARY = 61; - - // OPEN: Settings > Language & input > Add word - // CATEGORY: SETTINGS - // OS: 6.0 - INPUTMETHOD_USER_DICTIONARY_ADD_WORD = 62; - - // OPEN: Settings > Location - // CATEGORY: SETTINGS - // OS: 6.0 - LOCATION = 63; - - // OPEN: Settings > Apps - // CATEGORY: SETTINGS - // OS: 6.0 - MANAGE_APPLICATIONS = 65; - - // OPEN: Settings > Backup & reset > Factory data reset - // CATEGORY: SETTINGS - // OS: 6.0 - MASTER_CLEAR = 66; - - // OPEN: Settings > Backup & reset > Factory data reset > Confirm - // CATEGORY: SETTINGS - // OS: 6.0 - MASTER_CLEAR_CONFIRM = 67; - - // OPEN: Settings > More > Android Beam - // CATEGORY: SETTINGS - // OS: 6.0 - NFC_BEAM = 69; - - // OPEN: Settings > Tap & pay - // CATEGORY: SETTINGS - // OS: 6.0 - NFC_PAYMENT = 70; - - // OPEN: Settings > Sound & notification > App notifications > [App] - // CATEGORY: SETTINGS - // OS: 6.0 - NOTIFICATION_APP_NOTIFICATION = 72; - - // OBSOLETE - NOTIFICATION_REDACTION = 74; - - // OPEN: Settings Widget > Notification log - // CATEGORY: SETTINGS - // OS: 6.0 - NOTIFICATION_STATION = 75; - - // OPEN: Settings > Sound & notification > Do not disturb - // CATEGORY: SETTINGS - // OS: 6.0 - NOTIFICATION_ZEN_MODE = 76; - - - // OPEN: Print job notification > Print job settings - // CATEGORY: SETTINGS - // OS: 6.0 - PRINT_JOB_SETTINGS = 78; - - // OPEN: Settings > Printing > [Print Service] - // CATEGORY: SETTINGS - // OS: 6.0 - PRINT_SERVICE_SETTINGS = 79; - - // OPEN: Settings > Printing - // CATEGORY: SETTINGS - // OS: 6.0 - PRINT_SETTINGS = 80; - - // OPEN: Settings > Backup & reset - // CATEGORY: SETTINGS - // OS: 6.0 - PRIVACY = 81; - - //OBSOLETE - PROXY_SELECTOR = 82; - - // OPEN: Settings > Backup & reset > Network settings reset - // CATEGORY: SETTINGS - // OS: 6.0 - RESET_NETWORK = 83; - - // OPEN: Settings > Backup & reset > Network settings reset > Confirm - // CATEGORY: SETTINGS - // OS: 6.0 - RESET_NETWORK_CONFIRM = 84; - - // OPEN: Settings > Developer Options > Running Services - // CATEGORY: SETTINGS - // OS: 6.0 - RUNNING_SERVICE_DETAILS = 85; - - // OPEN: Settings > Security > Screen pinning - // CATEGORY: SETTINGS - // OS: 6.0 - SCREEN_PINNING = 86; - - // OPEN: Settings > Security - // CATEGORY: SETTINGS - // OS: 6.0 - SECURITY = 87; - - // OPEN: Settings > SIM cards - // CATEGORY: SETTINGS - // OS: 6.0 - SIM = 88; - - // OBSOLETE - TESTING = 89; - - // OPEN: Settings > More > Tethering & portable hotspot - // CATEGORY: SETTINGS - // OS: 6.0 - TETHER = 90; - - // OPEN: Settings > Security > Trust agents - // CATEGORY: SETTINGS - // OS: 6.0 - TRUST_AGENT = 91; - - // OPEN: Settings > Security > Trusted credentials - // CATEGORY: SETTINGS - // OS: 6.0 - TRUSTED_CREDENTIALS = 92; - - // OPEN: Settings > Language & input > TTS output > [Engine] > Settings - // CATEGORY: SETTINGS - // OS: 6.0 - TTS_ENGINE_SETTINGS = 93; - - // OPEN: Settings > Language & input > Text-to-speech output - // CATEGORY: SETTINGS - // OS: 6.0 - TTS_TEXT_TO_SPEECH = 94; - - // OPEN: Settings > Security > Apps with usage access - // CATEGORY: SETTINGS - // OS: 6.0 - USAGE_ACCESS = 95; - - // OPEN: Settings > Users - // CATEGORY: SETTINGS - // OS: 6.0 - USER = 96; - - // OPEN: Settings > Users > [Restricted profile app & content access] - // CATEGORY: SETTINGS - // OS: 6.0 - USERS_APP_RESTRICTIONS = 97; - - // OPEN: Settings > Users > [User settings] - // CATEGORY: SETTINGS - // OS: 6.0 - USER_DETAILS = 98; - - // OPEN: Settings > More > VPN - // CATEGORY: SETTINGS - // OS: 6.0 - VPN = 100; - - // OPEN: Settings > Display > Choose wallpaper from - // CATEGORY: SETTINGS - // OS: 6.0 - WALLPAPER_TYPE = 101; - - // OPEN: Settings > Display > Cast - // CATEGORY: SETTINGS - // OS: 6.0 - WFD_WIFI_DISPLAY = 102; - - // OPEN: Settings > Wi-Fi - // CATEGORY: SETTINGS - // OS: 6.0 - WIFI = 103; - - // OPEN: Settings > More > Wi-Fi Calling - // CATEGORY: SETTINGS - // OS: 6.0 - WIFI_CALLING = 105; - - // OPEN: Settings > Wi-Fi > Saved networks - // CATEGORY: SETTINGS - // OS: 6.0 - WIFI_SAVED_ACCESS_POINTS = 106; - - // OPEN: Settings > Wi-Fi > Advanced Wi-Fi > Wi-Fi Direct - // CATEGORY: SETTINGS - // OS: 6.0 - WIFI_P2P = 109; - - // OPEN: Settings > Apps > Configure apps > App permissions - // CATEGORY: SETTINGS - // OS: 6.0 - APPLICATIONS_ADVANCED = 130; - - // OPEN: Settings > Location > Scanning - // CATEGORY: SETTINGS - // OS: 6.0 - LOCATION_SCANNING = 131; - - // OPEN: Settings > Sound & notification > App notifications - // CATEGORY: SETTINGS - // OS: 6.0 - MANAGE_APPLICATIONS_NOTIFICATIONS = 133; - - // OPEN: Settings > Sound & notification > DND > Priority only allows - // CATEGORY: SETTINGS - // OS: 6.0 - NOTIFICATION_ZEN_MODE_PRIORITY = 141; - - // OPEN: Settings > Sound & notification > DND > Automatic rules - // CATEGORY: SETTINGS - // OS: 6.0 - NOTIFICATION_ZEN_MODE_AUTOMATION = 142; - - // OPEN: Settings > Sound & notification > DND > [Time based rule] - // CATEGORY: SETTINGS - // OS: 6.0 - NOTIFICATION_ZEN_MODE_SCHEDULE_RULE = 144; - - // OPEN: Settings > Apps > Configure apps > App links - // CATEGORY: SETTINGS - // OS: 6.0 - MANAGE_DOMAIN_URLS = 143; - - // OPEN: Settings > Sound & notification > DND > [Event rule] - // CATEGORY: SETTINGS - // OS: 6.0 - NOTIFICATION_ZEN_MODE_EVENT_RULE = 146; - - // OPEN: Settings > Sound & notification > Notification access - // CATEGORY: SETTINGS - // OS: 6.0 - NOTIFICATION_ACCESS = 179; - - // OPEN: Settings > Sound & notification > Do Not Disturb access - // CATEGORY: SETTINGS - // OS: 6.0 - NOTIFICATION_ZEN_MODE_ACCESS = 180; - - // OPEN: Settings > Internal storage > Apps storage - // CATEGORY: SETTINGS - // OS: 6.0 - APPLICATIONS_STORAGE_APPS = 182; - - // OPEN: Settings > Security > Usage access - // CATEGORY: SETTINGS - // OS: 6.0 - APPLICATIONS_USAGE_ACCESS_DETAIL = 183; - - // OPEN: Settings > Battery > Battery optimization - // CATEGORY: SETTINGS - // OS: 6.0 - APPLICATIONS_HIGH_POWER_APPS = 184; - - // OPEN: Settings > Apps > Configure > Default apps > Assist & voice input - // CATEGORY: SETTINGS - // OS: 6.0 - APPLICATIONS_MANAGE_ASSIST = 201; - - // OPEN: Settings > Memory - // CATEGORY: SETTINGS - // OS: 6.0 - PROCESS_STATS_SUMMARY = 202; - - // OPEN: Settings > Apps > Configure Apps > Display over other apps - // CATEGORY: SETTINGS - // OS: 6.0 - SYSTEM_ALERT_WINDOW_APPS = 221; - - // OPEN: Settings > About phone > Legal information - // CATEGORY: SETTINGS - // OS: 6.0 - ABOUT_LEGAL_SETTINGS = 225; - - - // OPEN: Settings > Developer options > Inactive apps - // CATEGORY: SETTINGS - // OS: 6.0 - FUELGAUGE_INACTIVE_APPS = 238; - - // OPEN: Settings > Security > Nexus Imprint > Add Fingerprint - // CATEGORY: SETTINGS - // OS: 6.0 - FINGERPRINT_ENROLLING = 240; - // OPEN: Fingerprint Enroll > Find Sensor - // CATEGORY: SETTINGS - // OS: 6.0 - FINGERPRINT_FIND_SENSOR = 241; - - // OPEN: Fingerprint Enroll > Fingerprint Enrolled! - // CATEGORY: SETTINGS - // OS: 6.0 - FINGERPRINT_ENROLL_FINISH = 242; - - // OPEN: Fingerprint Enroll introduction - // CATEGORY: SETTINGS - // OS: 6.0 - FINGERPRINT_ENROLL_INTRO = 243; - - // OPEN: Fingerprint Enroll > Let's Start! - // CATEGORY: SETTINGS - // OS: 6.0 - FINGERPRINT_ENROLL_SIDECAR = 245; - - // OPEN: Fingerprint Enroll SUW > Let's Start! - // CATEGORY: SETTINGS - // OS: 6.0 - FINGERPRINT_ENROLLING_SETUP = 246; - - // OPEN: Fingerprint Enroll SUW > Find Sensor - // CATEGORY: SETTINGS - // OS: 6.0 - FINGERPRINT_FIND_SENSOR_SETUP = 247; - - // OPEN: Fingerprint Enroll SUW > Fingerprint Enrolled! - // CATEGORY: SETTINGS - // OS: 6.0 - FINGERPRINT_ENROLL_FINISH_SETUP = 248; - - // OPEN: Fingerprint Enroll SUW introduction - // CATEGORY: SETTINGS - // OS: 6.0 - FINGERPRINT_ENROLL_INTRO_SETUP = 249; - - // OPEN: Settings > Developer Options > Background Check - // CATEGORY: SETTINGS - // OS: N - BACKGROUND_CHECK_SUMMARY = 258; - - // OPEN: Settings > Notifications > [App] > Channel Notifications - // CATEGORY: SETTINGS - // OS: N - NOTIFICATION_TOPIC_NOTIFICATION = 265; - - // OPEN: Settings > Security > User credentials - // CATEGORY: Settings - // OS: N - USER_CREDENTIALS = 285; - - // Logs that the user has edited the enabled VR listeners. - // CATEGORY: SETTINGS - // OS: N - VR_MANAGE_LISTENERS = 334; - - // Settings -> Accessibility -> Click after pointer stops moving - // CATEGORY: SETTINGS - // OS: N - ACCESSIBILITY_TOGGLE_AUTOCLICK = 335; - - // Settings -> Sound - // CATEGORY: SETTINGS - // OS: N - SOUND = 336; - - // Settings -> Notifications -> Gear - // CATEGORY: SETTINGS - // OS: N - CONFIGURE_NOTIFICATION = 337; - - // Settings -> Wi-Fi -> Gear - // CATEGORY: SETTINGS - // OS: N - CONFIGURE_WIFI = 338; - - // Settings -> Display -> Display size - // OS: N - DISPLAY_SCREEN_ZOOM = 339; - - // Settings -> Display -> Font size - // CATEGORY: SETTINGS - // OS: N - ACCESSIBILITY_FONT_SIZE = 340; - - // Settings -> Data usage -> Cellular/Wi-Fi data usage - // CATEGORY: SETTINGS - // OS: N - DATA_USAGE_LIST = 341; - - // Settings -> Data usage -> Billing cycle or DATA_USAGE_LIST -> Gear - // CATEGORY: SETTINGS - // OS: N - BILLING_CYCLE = 342; - - // DATA_USAGE_LIST -> Any item or App info -> Data usage - // CATEGORY: SETTINGS - // OS: N - APP_DATA_USAGE = 343; - - // Settings -> Language & input -> Language - // CATEGORY: SETTINGS - // OS: N - USER_LOCALE_LIST = 344; - - // Settings -> Language & input -> Virtual keyboard - // CATEGORY: SETTINGS - // OS: N - VIRTUAL_KEYBOARDS = 345; - - // Settings -> Language & input -> Physical keyboard - // CATEGORY: SETTINGS - // OS: N - PHYSICAL_KEYBOARDS = 346; - - // Settings -> Language & input -> Virtual keyboard -> Add a virtual keyboard - // CATEGORY: SETTINGS - // OS: N - ENABLE_VIRTUAL_KEYBOARDS = 347; - - // Settings -> Data usage -> Data Saver - // CATEGORY: SETTINGS - // OS: N - DATA_SAVER_SUMMARY = 348; - - // Settings -> Data usage -> Data Saver -> Unrestricted data access - // CATEGORY: SETTINGS - // OS: N - DATA_USAGE_UNRESTRICTED_ACCESS = 349; - - // Settings -> Apps -> Gear -> Special access - SPECIAL_ACCESS = 351; - - // OPEN: SUW Welcome Screen -> Vision Settings - // CATEGORY: SETTINGS - // OS: N - SUW_ACCESSIBILITY = 367; - - // OPEN: SUW Welcome Screen -> Vision Settings -> Magnification gestures (Renamed in O) - // OPEN: SUW Welcome Screen -> Vision Settings -> Magnification -> Magnify with triple-tap - // OPEN: SUW Welcome Screen -> Vision Settings -> Magnification -> Magnify with button - // ACTION: New magnification gesture configuration is chosen - // SUBTYPE: 0 is off, 1 is on - // CATEGORY: SETTINGS - // OS: N - SUW_ACCESSIBILITY_TOGGLE_SCREEN_MAGNIFICATION = 368; - - // OPEN: SUW Welcome Screen -> Vision Settings -> Font size - // ACTION: New font size is chosen - // SUBTYPE: 0 is small, 1 is default, 2 is large, 3 is largest - // CATEGORY: SETTINGS - // OS: N - SUW_ACCESSIBILITY_FONT_SIZE = 369; - - // OPEN: SUW Welcome Screen -> Vision Settings -> Display size - // ACTION: New display size is chosen - // SUBTYPE: 0 is small, 1 is default, 2 is large, 3 is larger, 4 is largest - // CATEGORY: SETTINGS - // OS: N - SUW_ACCESSIBILITY_DISPLAY_SIZE = 370; - - // OPEN: SUW Welcome Screen -> Vision Settings -> TalkBack - // ACTION: New screen reader configuration is chosen - // SUBTYPE: 0 is off, 1 is on - // CATEGORY: SETTINGS - // OS: N - SUW_ACCESSIBILITY_TOGGLE_SCREEN_READER = 371; - - // Airplane mode on - SETTINGS_CONDITION_AIRPLANE_MODE = 377; - // AKA Data saver on - SETTINGS_CONDITION_BACKGROUND_DATA = 378; - // Battery saver on - SETTINGS_CONDITION_BATTERY_SAVER = 379; - // Cellular data off - SETTINGS_CONDITION_CELLULAR_DATA = 380; - // Do not disturb on - SETTINGS_CONDITION_DND = 381; - // Hotspot on - SETTINGS_CONDITION_HOTSPOT = 382; - // Work profile off - SETTINGS_CONDITION_WORK_MODE = 383; - - // Settings > Apps > Gear > Special Access > Premium SMS access - PREMIUM_SMS_ACCESS = 388; - - // OPEN: Settings > Accounts > Work profile settings - // CATEGORY: SETTINGS - ACCOUNTS_WORK_PROFILE_SETTINGS = 401; - - // Settings -> Dev options -> Convert to file encryption - CONVERT_FBE = 402; - - // Settings -> Dev options -> Convert to file encryption -> WIPE AND CONVERT... - CONVERT_FBE_CONFIRM = 403; - - // Settings -> Dev options -> Running services - RUNNING_SERVICES = 404; - - // The dialog shown by 3P intent to change current webview implementation. - WEBVIEW_IMPLEMENTATION = 405; - - // OPEN: Settings > Internal storage > Storage manager - // CATEGORY: SETTINGS - STORAGE_MANAGER_SETTINGS = 458; - - // OPEN: Settings -> Gestures - // CATEGORY: SETTINGS - SETTINGS_GESTURES = 459; - - // OPEN: Settings > Display > Night Light - // CATEGORY: SETTINGS - NIGHT_DISPLAY_SETTINGS = 488; - - // Night Light on - SETTINGS_CONDITION_NIGHT_DISPLAY = 492; - - // OPEN: Settings > Language & input > Personal dictionary (single locale) - USER_DICTIONARY_SETTINGS = 514; - - // OPEN: Settings > Date & time > Select time zone - ZONE_PICKER = 515; - - // OPEN: Settings > Security > Device administrators - DEVICE_ADMIN_SETTINGS = 516; - - // OPEN: Settings > Security > Factory Reset Protection dialog - DIALOG_FRP = 528; - - // OPEN: Settings > Custom list preference with confirmation message - DIALOG_CUSTOM_LIST_CONFIRMATION = 529; - - // OPEN: Settings > APN Editor > Error dialog - DIALOG_APN_EDITOR_ERROR = 530; - - // OPEN: Settings > Users > Edit owner info dialog - DIALOG_OWNER_INFO_SETTINGS = 531; - - // OPEN: Settings > Security > Use one lock dialog - DIALOG_UNIFICATION_CONFIRMATION = 532; - - // OPEN: Settings > Security > User Credential - DIALOG_USER_CREDENTIAL = 533; - - // OPEN: Settings > Accounts > Remove account - DIALOG_REMOVE_USER = 534; - - // OPEN: Settings > Accounts > Confirm auto sync dialog - DIALOG_CONFIRM_AUTO_SYNC_CHANGE = 535; - - // OPEN: Settings > Apps > Dialog for running service details - DIALOG_RUNNIGN_SERVICE = 536; - - // OPEN: Settings > Bluetooth > Rename this device - DIALOG_BLUETOOTH_RENAME = 538; - - // OPEN: Settings > Battery optimization > details for app - DIALOG_HIGH_POWER_DETAILS = 540; - - // OPEN: Settings > Keyboard > Show keyboard layout dialog - DIALOG_KEYBOARD_LAYOUT = 541; - - // OPEN: Settings > WIFI Scan permission dialog - DIALOG_WIFI_SCAN_MODE = 543; - - // OPEN: Settings > Wireless > VPN > Config dialog - DIALOG_LEGACY_VPN_CONFIG = 545; - - // OPEN: Settings > Wireless > VPN > Config dialog for app - DIALOG_VPN_APP_CONFIG = 546; - - // OPEN: Settings > Wireless > VPN > Cannot connect dialog - DIALOG_VPN_CANNOT_CONNECT = 547; - - // OPEN: Settings > Wireless > VPN > Replace existing VPN dialog - DIALOG_VPN_REPLACE_EXISTING = 548; - - // OPEN: Settings > Billing cycle > Edit billing cycle dates dialog - DIALOG_BILLING_CYCLE = 549; - - // OPEN: Settings > Billing cycle > Edit data limit/warning dialog - DIALOG_BILLING_BYTE_LIMIT = 550; - - // OPEN: Settings > Billing cycle > turn on data limit dialog - DIALOG_BILLING_CONFIRM_LIMIT = 551; - - // OPEN: Settings > Service > Turn off notification access dialog - DIALOG_DISABLE_NOTIFICATION_ACCESS = 552; - - // OPEN: Settings > Sound > Use personal sound for work profile dialog - DIALOG_UNIFY_SOUND_SETTINGS = 553; - - // OPEN: Settings > Zen mode > Dialog warning about the zen access privileges being granted. - DIALOG_ZEN_ACCESS_GRANT = 554; - - // OPEN: Settings > Zen mode > Dialog warning about the zen access privileges being revoked. - DIALOG_ZEN_ACCESS_REVOKE = 555; - - // OPEN: Settings > Zen mode > Dialog that picks time for zen mode. - DIALOG_ZEN_TIMEPICKER = 556; - - // OPEN: Settings > Apps > Dialog that informs user to allow service access for app. - DIALOG_SERVICE_ACCESS_WARNING = 557; - - // OPEN: Settings > Apps > Dialog for app actions (such as force stop/clear data) - DIALOG_APP_INFO_ACTION = 558; - - // OPEN: Settings > Storage > Dialog for forgetting a storage device - DIALOG_VOLUME_FORGET = 559; - - // OPEN: Settings > Storage > Dialog for initializing a volume - DIALOG_VOLUME_INIT = 561; - - // OPEN: Settings > Storage > Dialog for unmounting a volume - DIALOG_VOLUME_UNMOUNT = 562; - - // OPEN: Settings > Storage > Dialog for renaming a volume - DIALOG_VOLUME_RENAME = 563; - - // OPEN: Settings > Storage > Dialog for clear cache - DIALOG_STORAGE_CLEAR_CACHE = 564; - - // OPEN: Settings > Storage > Dialog for system info - DIALOG_STORAGE_SYSTEM_INFO = 565; - - // OPEN: Settings > Storage > Dialog for other info - DIALOG_STORAGE_OTHER_INFO = 566; - - // OPEN: Settings > Storage > Dialog for user info - DIALOG_STORAGE_USER_INFO = 567; - // OPEN: Settings > Add fingerprint > Dialog when user touches fingerprint icon. - DIALOG_FINGERPRINT_ICON_TOUCH = 568; - - // OPEN: Settings > Add fingerprint > Error dialog - DIALOG_FINGERPINT_ERROR = 569; - - // OPEN: Settings > Fingerprint > Rename or delete dialog - DIALOG_FINGERPINT_EDIT = 570; - - // OPEN: Settings > Fingerprint > Dialog for deleting last fingerprint - DIALOG_FINGERPINT_DELETE_LAST = 571; - - // OPEN: SUW > Fingerprint > Dialog to confirm skip fingerprint setup entirely. - DIALOG_FINGERPRINT_SKIP_SETUP = 573; - - // OPEN: Settings > Proxy Selector error dialog - DIALOG_PROXY_SELECTOR_ERROR = 574; - - // OPEN: Settings > Wifi > P2P Settings > Disconnect dialog - DIALOG_WIFI_P2P_DISCONNECT = 575; - - // OPEN: Settings > Wifi > P2P Settings > Cancel connection dialog - DIALOG_WIFI_P2P_CANCEL_CONNECT = 576; - - // OPEN: Settings > Wifi > P2P Settings > Rename dialog - DIALOG_WIFI_P2P_RENAME = 577; - - // OPEN: Settings > Wifi > P2P Settings > Forget group dialog - DIALOG_WIFI_P2P_DELETE_GROUP = 578; - - // OPEN: Settings > APN > Restore default dialog - DIALOG_APN_RESTORE_DEFAULT = 579; - - // OPEN: Settings > Encryption interstitial accessibility warning dialog - DIALOG_ENCRYPTION_INTERSTITIAL_ACCESSIBILITY = 581; - - // OPEN: Settings > Acessibility > Enable accessiblity service dialog - DIALOG_ACCESSIBILITY_SERVICE_ENABLE = 583; - - // OPEN: Settings > Acessibility > Disable accessiblity service dialog - DIALOG_ACCESSIBILITY_SERVICE_DISABLE = 584; - - // OPEN: Settings > Account > Remove account dialog - DIALOG_ACCOUNT_SYNC_REMOVE = 585; - - // OPEN: Settings > Account > Remove account failed dialog - DIALOG_ACCOUNT_SYNC_FAILED_REMOVAL = 586; - - // OPEN: Settings > Account > Cannot do onetime sync dialog - DIALOG_ACCOUNT_SYNC_CANNOT_ONETIME_SYNC = 587; - - // OPEN: Settings > Display > Night light > Set start time dialog - DIALOG_NIGHT_DISPLAY_SET_START_TIME = 588; - - // OPEN: Settings > Display > Night light > Set end time dialog - DIALOG_NIGHT_DISPLAY_SET_END_TIME = 589; - - - - // OPEN: Settings > User > Edit info dialog - DIALOG_USER_EDIT = 590; - - // OPEN: Settings > User > Confirm remove dialog - DIALOG_USER_REMOVE = 591; - - // OPEN: Settings > User > Enable calling dialog - DIALOG_USER_ENABLE_CALLING = 592; - - // OPEN: Settings > User > Enable calling and sms dialog - DIALOG_USER_ENABLE_CALLING_AND_SMS = 593; - - // OPEN: Settings > User > Cannot manage device message dialog - DIALOG_USER_CANNOT_MANAGE = 594; - - // OPEN: Settings > User > Add user dialog - DIALOG_USER_ADD = 595; - - // OPEN: Settings > User > Setup user dialog - DIALOG_USER_SETUP = 596; - - // OPEN: Settings > User > Setup profile dialog - DIALOG_USER_SETUP_PROFILE = 597; - - // OPEN: Settings > User > Choose user type dialog - DIALOG_USER_CHOOSE_TYPE = 598; - - // OPEN: Settings > User > Need lockscreen dialog - DIALOG_USER_NEED_LOCKSCREEN = 599; - - // OPEN: Settings > User > Confirm exit guest mode dialog - DIALOG_USER_CONFIRM_EXIT_GUEST = 600; - - // OPEN: Settings > User > Edit user profile dialog - DIALOG_USER_EDIT_PROFILE = 601; - - - // OPEN: Settings > Wifi > Saved AP > Edit dialog - DIALOG_WIFI_SAVED_AP_EDIT = 602; - - // OPEN: Settings > Wifi > Edit AP dialog - DIALOG_WIFI_AP_EDIT = 603; - - // OPEN: Settings > Wifi > Write config to NFC dialog - DIALOG_WIFI_WRITE_NFC = 606; - - // OPEN: Settings > Date > Date picker dialog - DIALOG_DATE_PICKER = 607; - - // OPEN: Settings > Date > Time picker dialog - DIALOG_TIME_PICKER = 608; - - // OPEN: Settings > Wireless > Manage wireless plan dialog - DIALOG_MANAGE_MOBILE_PLAN = 609; - - // OPEN Settings > Bluetooth > Attempt to connect to device that shows dialog - BLUETOOTH_DIALOG_FRAGMENT = 613; - - // OPEN: Settings > Security - // CATEGORY: SETTINGS - // OS: O - ENTERPRISE_PRIVACY_SETTINGS = 628; - - // OPEN: Settings > System - SETTINGS_SYSTEM_CATEGORY = 744; - - // OPEN: Settings > Storage - SETTINGS_STORAGE_CATEGORY = 745; - - // OPEN: Settings > Network & Internet - SETTINGS_NETWORK_CATEGORY = 746; - - // OPEN: Settings > Connected Device - SETTINGS_CONNECTED_DEVICE_CATEGORY = 747; - - // OPEN: Settings > App & Notification - SETTINGS_APP_NOTIF_CATEGORY = 748; - - // OPEN: Settings > System > Language & Region - SETTINGS_LANGUAGE_CATEGORY = 750; - - // OPEN: Settings > System > Input & Gesture > Swipe fingerprint for notifications - SETTINGS_GESTURE_SWIPE_TO_NOTIFICATION = 751; - - // OPEN: Settings > System > Input & Gesture > Double tap power button gesture - SETTINGS_GESTURE_DOUBLE_TAP_POWER = 752; - - // OPEN: Settings > System > Input & Gesture > Pick up gesture - SETTINGS_GESTURE_PICKUP = 753; - - // OPEN: Settings > System > Input & Gesture > Double tap screen gesture - SETTINGS_GESTURE_DOUBLE_TAP_SCREEN = 754; - - // OPEN: Settings > System > Input & Gesture > Double twist gesture - SETTINGS_GESTURE_DOUBLE_TWIST = 755; - - // OPEN: Settings > Apps > Default Apps > Default browser - DEFAULT_BROWSER_PICKER = 785; - // OPEN: Settings > Apps > Default Apps > Default emergency app - DEFAULT_EMERGENCY_APP_PICKER = 786; - - // OPEN: Settings > Apps > Default Apps > Default home - DEFAULT_HOME_PICKER = 787; - - // OPEN: Settings > Apps > Default Apps > Default phone - DEFAULT_PHONE_PICKER = 788; - - // OPEN: Settings > Apps > Default Apps > Default sms - DEFAULT_SMS_PICKER = 789; - - // OPEN: Settings > Apps > Notification > Notification Assistant - DEFAULT_NOTIFICATION_ASSISTANT = 790; - - - // OPEN: Settings > Apps > Default Apps > Warning dialog to confirm selection - DEFAULT_APP_PICKER_CONFIRMATION_DIALOG = 791; - - // OPEN: Settings > Apps > Default Apps > Default autofill app - DEFAULT_AUTOFILL_PICKER = 792; - - // OPEN: Settings > Apps > Gear > Special Access > Install other apps - // CATEGORY: SETTINGS - // OS: 8.0 - MANAGE_EXTERNAL_SOURCES = 808; - - // Logs that the user has edited the picture-in-picture settings. - // CATEGORY: SETTINGS - SETTINGS_MANAGE_PICTURE_IN_PICTURE = 812; - - // OPEN: SUW Welcome Screen -> Vision Settings -> Select to Speak - // ACTION: Select to Speak configuration is chosen - // SUBTYPE: 0 is off, 1 is on - // CATEGORY: SETTINGS - // OS: N - SUW_ACCESSIBILITY_TOGGLE_SELECT_TO_SPEAK = 817; - - // OPEN: Settings > System > Backup - // CATEGORY: SETTINGS - // OS: O - BACKUP_SETTINGS = 818; - - // OPEN: Settings > Storage > Games - // CATEGORY: SETTINGS - // OS: O - APPLICATIONS_STORAGE_GAMES = 838; - - // OPEN: Settings > Storage > Audio and Music - // CATEGORY: SETTINGS - // OS: O - APPLICATIONS_STORAGE_MUSIC = 839; - - // ACTION: Settings > Storage > Free Up Space to launch Deletion Helper - // CATEGORY: SETTINGS - // OS: O - STORAGE_FREE_UP_SPACE_NOW = 840; - - // ACTION: Settings > Storage > Files to open the File Manager - // CATEGORY: SETTINGS - // OS: O - STORAGE_FILES = 841; - - // OPEN: Settings > Apps > Default Apps > Assist > Default assist - DEFAULT_ASSIST_PICKER = 843; - - // OPEN: Settings > Apps > Default Apps > Assist > Default voice input - DEFAULT_VOICE_INPUT_PICKER = 844; - - // OPEN: Settings > Storage > [Profile] - SETTINGS_STORAGE_PROFILE = 845; - - // OPEN: Settings > Security & screen lock -> Encryption & crendentials - // CATEGORY: SETTINGS - // OS: O - ENCRYPTION_AND_CREDENTIAL = 846; - - // OPEN: Settings > Wi-Fi > Network Details (click on Access Point) - // CATEGORY: SETTINGS - // OS: O - WIFI_NETWORK_DETAILS = 849; - - // OPEN: Settings > Wi-Fi > Wifi Preferences -> Advanced -> Network Scorer - // CATEGORY: SETTINGS - // OS: O - SETTINGS_NETWORK_SCORER = 861; - - // OPEN: Settings > About device > Model > Hardware info dialog - DIALOG_SETTINGS_HARDWARE_INFO = 862; - - // OPEN: Settings > Security & screen lock -> Lock screen preferences - // CATEGORY: SETTINGS - SETTINGS_LOCK_SCREEN_PREFERENCES = 882; - - - // OPEN: Settings -> Display -> When in VR Mode - VR_DISPLAY_PREFERENCE = 921; - - // OPEN: Settings > Accessibility > Magnification - // CATEGORY: SETTINGS - // OS: O - ACCESSIBILITY_SCREEN_MAGNIFICATION_SETTINGS = 922; - - // OPEN: Settings -> System -> Reset options - RESET_DASHBOARD = 924; - - // OPEN: Settings > Security > Nexus Imprint > [Fingerprint] > Delete - // CATEGORY: SETTINGS - // OS: O - FINGERPRINT_REMOVE_SIDECAR = 934; - - // OPEN: Settings > Storage > Movies & TV - // CATEGORY: SETTINGS - // OS: O - APPLICATIONS_STORAGE_MOVIES = 935; - - // OPEN: Settings > Security > Managed Device Info > Apps installed - // CATEGORY: SETTINGS - // OS: O - ENTERPRISE_PRIVACY_INSTALLED_APPS = 938; - - // OPEN: Settings > Security > Managed Device Info > nnn permissions - // CATEGORY: SETTINGS - // OS: O - ENTERPRISE_PRIVACY_PERMISSIONS = 939; - - - // OPEN: Settings > Security > Managed Device Info > Default apps - // CATEGORY: SETTINGS - // OS: O - ENTERPRISE_PRIVACY_DEFAULT_APPS = 940; - - // OPEN: Choose screen lock dialog in Settings - // CATEGORY: SETTINGS - // OS: O DR - SETTINGS_CHOOSE_LOCK_DIALOG = 990; - - // OPEN: Settings > System > Languages & input > Assist gesture - // CATEGORY: SETTINGS - // OS: O DR - SETTINGS_ASSIST_GESTURE = 996; - - // OPEN: Settings > Connected Devices > Bluetooth > (click on details link for a paired device) - BLUETOOTH_DEVICE_DETAILS = 1009; - - // OPEN: Settings > credential pages - prompt for key guard configuration confirmation - CONFIGURE_KEYGUARD_DIALOG = 1010; - - // OPEN: Settings > Network > Tether > Wi-Fi hotspot - WIFI_TETHER_SETTINGS = 1014; - - // OPEN: Settings->Connected Devices->Bluetooth->(click on details link for a paired device) - // -> Edit name button. - // CATEGORY: SETTINGS - // OS: O DR - DIALOG_BLUETOOTH_PAIRED_DEVICE_RENAME = 1015; - - // OPEN: Settings > Connected devices > Bluetooth > Pair new device - // CATEGORY: SETTINGS - // OS: O DR - BLUETOOTH_PAIRING = 1018; - - // OPEN: Settings->Connected Devices->Bluetooth->(click on details link for a paired device) - // -> Forget button. - // CATEGORY: SETTINGS - // OS: O DR - DIALOG_BLUETOOTH_PAIRED_DEVICE_FORGET = 1031; - - // OPEN: Settings > Storage > Photos & Videos - // CATEGORY: SETTINGS - // OS: O MR - APPLICATIONS_STORAGE_PHOTOS = 1092; - - // OPEN: Settings > Display > Colors - // CATEGORY: SETTINGS - // OS: O MR - COLOR_MODE_SETTINGS = 1143; - - // OPEN: Settings > Developer Options > Experiment dashboard - // CATEGORY: SETTINGS - SETTINGS_FEATURE_FLAGS_DASHBOARD = 1217; - - // OPEN: Settings > Notifications > [App] > Topic Notifications - // CATEGORY: SETTINGS - // OS: P - NOTIFICATION_CHANNEL_GROUP = 1218; - - // OPEN: Settings > Developer options > Enable > Info dialog - // CATEGORY: SETTINGS - // OS: P - DIALOG_ENABLE_DEVELOPMENT_OPTIONS = 1219; - - // OPEN: Settings > Developer options > OEM unlocking > Info dialog - // CATEGORY: SETTINGS - // OS: P - DIALOG_ENABLE_OEM_UNLOCKING = 1220; - - // OPEN: Settings > Developer options > USB debugging > Info dialog - // CATEGORY: SETTINGS - // OS: P - DIALOG_ENABLE_ADB = 1222; - - // OPEN: Settings > Security > Nexus Imprint > [Fingerprint] - // CATEGORY: SETTINGS - // OS: P - FINGERPRINT_AUTHENTICATE_SIDECAR = 1221; - - // OPEN: Settings > Developer options > Revoke USB debugging authorizations > Info dialog - // CATEGORY: SETTINGS - // OS: P - DIALOG_CLEAR_ADB_KEYS = 1223; - - // Open: Settings > Developer options > Quick setting tile config - // CATEGORY: SETTINGS - // OS: P - DEVELOPMENT_QS_TILE_CONFIG = 1224; - - // OPEN: Settings > Developer options > Store logger data persistently on device > Info dialog - // CATEGORY: SETTINGS - // OS: P - DIALOG_LOG_PERSIST = 1225; - - // OPEN: Settings > Network & Internet > Mobile network > Wi-Fi calling - // CATEGORY: SETTINGS - // OS: P - WIFI_CALLING_FOR_SUB = 1230; - - // Open: Settings > Dev options > Oem unlock > lock it > warning dialog. - // OS: P - DIALOG_OEM_LOCK_INFO = 1238; - - // Open: Settings > System > About phone > IMEI - // CATEGORY: SETTINGS - // OS: P - DIALOG_IMEI_INFO = 1240; - - // OPEN: Settings > System > About Phone > Sim status - // CATEGORY: SETTINGS - // OS: P - DIALOG_SIM_STATUS = 1246; - - // OPEN: Settings > System > About Phone > Android Version - // CATEGORY: SETTINGS - // OS: P - DIALOG_FIRMWARE_VERSION = 1247; - - // OPEN: Settings > Battery(version 2) - // CATEGORY: SETTINGS - // OS: P - FUELGAUGE_POWER_USAGE_SUMMARY_V2 = 1263; - - // OPEN: Settings > Connected devices > Connection preferences - // CATEGORY: SETTINGS - // OS: P - CONNECTION_DEVICE_ADVANCED = 1264; - - // OPEN: Settings > Security > Screen lock gear icon - // CATEGORY: SETTINGS - // OS: P - SCREEN_LOCK_SETTINGS = 1265; - - // OPEN: Settings > Sound > Do Not Disturb > Turn on automatically > Delete rule (trash can icon) - // CATEGORY: SETTINGS - // OS: P - NOTIFICATION_ZEN_MODE_DELETE_RULE_DIALOG = 1266; - - // OPEN: Settings > Sound > Do Not Disturb > Turn on automatically > Add rule > Event/Time - // OPEN: Settings > Sound > Do Not Disturb > Turn on automatically > Select rule ("Event") > Rule name - // CATEGORY: SETTINGS - // OS: P - NOTIFICATION_ZEN_MODE_RULE_NAME_DIALOG = 1269; - - // OPEN: Settings > Sound > Do Not Disturb > Turn on automatically > Add rule - // CATEGORY: SETTINGS - // OS: P - NOTIFICATION_ZEN_MODE_RULE_SELECTION_DIALOG = 1270; - - // OPEN: Settings > Battery > Smart Battery - // CATEGORY: SETTINGS - // OS: P - FUELGAUGE_SMART_BATTERY = 1281; - - // OPEN: Settings > Battery > Smart Battery > Restricted apps - // CATEGORY: SETTINGS - // OS: P - FUELGAUGE_RESTRICTED_APP_DETAILS = 1285; - - // OPEN: Settings > Sound & notification > Do Not Disturb > Turn on now - // CATEGORY: SETTINGS - // OS: P - NOTIFICATION_ZEN_MODE_ENABLE_DIALOG = 1286; - - // OPEN: Settings->Connected Devices->USB->(click on details link) - // CATEGORY: SETTINGS - // OS: P - USB_DEVICE_DETAILS = 1291; - - // OPEN: Settings > Accessibility > Vibration - // CATEGORY: SETTINGS - // OS: P - ACCESSIBILITY_VIBRATION = 1292; - - // OPEN: Settings > Accessibility > Vibration > Notification vibration - // CATEGORY: SETTINGS - // OS: P - ACCESSIBILITY_VIBRATION_NOTIFICATION = 1293; - - // OPEN: Settings > Accessibility > Vibration > Touch vibration - // CATEGORY: SETTINGS - // OS: P - ACCESSIBILITY_VIBRATION_TOUCH = 1294; - - // OPEN: Settings->Developer Options->Default USB - // CATEGORY: SETTINGS - // OS: P - USB_DEFAULT = 1312; - - // OPEN: Settings > Battery > Battery tip > Battery tip Dialog - // CATEGORY: SETTINGS - // OS: P - FUELGAUGE_BATTERY_TIP_DIALOG = 1323; - - // OPEN: DND Settings > What to block - // OS: P - ZEN_WHAT_TO_BLOCK = 1339; - - // OPEN: Settings > Sounds > Do Not Disturb > Duration - // CATEGORY: SETTINGS - // OS: P - NOTIFICATION_ZEN_MODE_DURATION_DIALOG = 1341; - - // OPEN: Settings > Date & time > Select time zone -> Region - // CATEGORY: SETTINGS - // OS: P - SETTINGS_ZONE_PICKER_REGION = 1355; - - // OPEN: Settings > Date & time > Select time zone -> Time Zone - // CATEGORY: SETTINGS - // OS: P - SETTINGS_ZONE_PICKER_TIME_ZONE = 1356; - // OPEN: Settings > Date & time > Select time zone -> Select UTC Offset - // CATEGORY: SETTINGS - // OS: P - SETTINGS_ZONE_PICKER_FIXED_OFFSET = 1357; - - // OPEN: Settings > Gestures > Prevent Ringing - // OS: P - SETTINGS_PREVENT_RINGING = 1360; - - // Settings > Condition > Device muted - // CATEGORY: SETTINGS - // OS: P - SETTINGS_CONDITION_DEVICE_MUTED = 1368; - - // Settings > Condition > Device vibrate - // CATEGORY: SETTINGS - // OS: P - SETTINGS_CONDITION_DEVICE_VIBRATE = 1369; - - // OPEN: Settings > Connected devices > previously connected devices - // CATEGORY: SETTINGS - // OS: P - PREVIOUSLY_CONNECTED_DEVICES = 1370; - - // OPEN: Settings > Network & Internet > Wi-Fi > Wi-Fi Preferences > Turn on Wi-Fi automatically - // note: Wifi Scanning must be off for this dialog to show - // CATEGORY: SETTINGS - // OS: P - WIFI_SCANNING_NEEDED_DIALOG = 1373; - - // OPEN: Settings > System > Gestures > System navigation - // CATEGORY: SETTINGS - // OS: P - SETTINGS_GESTURE_SWIPE_UP = 1374; - - // OPEN: Settings > Storage > Dialog to format a storage volume - // CATEGORY: SETTINGS - // OS: P - DIALOG_VOLUME_FORMAT = 1375; - - // OPEN: DND onboarding activity - // CATEGORY: SETTINGS - // OS: P - SETTINGS_ZEN_ONBOARDING = 1380; - - // OPEN: Settings > Display > Auto brightness - // CATEGORY: SETTINGS - // OS: P - SETTINGS_AUTO_BRIGHTNESS = 1381; - - // OPEN: Settings > Connected Devices > Bluetooth - // CATEGORY: SETTINGS - // OS: P - BLUETOOTH_FRAGMENT = 1390; - - // Screen: DND Settings > Notifications - // OS: P - SETTINGS_ZEN_NOTIFICATIONS = 1400; - - // An event category for slices. - // OPEN: Slice became visible. - // CLOSE: Slice became invisible. - // ACTION: Slice was tapped. - SLICE = 1401; - - // OPEN: Settings -> Developer Options -> Disable Bluetooth A2DP hardware - // offload - // CATEGORY: SETTINGS - // OS: P - DIALOG_BLUETOOTH_DISABLE_A2DP_HW_OFFLOAD = 1441; - - // OPEN: Settings homepage - SETTINGS_HOMEPAGE = 1502; - - // OPEN: Settings > Create shortcut(widget) - // CATEGORY: SETTINGS - // OS: Q - SETTINGS_CREATE_SHORTCUT = 1503; - - // OPEN: Face Enroll introduction - // CATEGORY: SETTINGS - // OS: Q - FACE_ENROLL_INTRO = 1506; - - // OPEN: Face Enroll introduction - // CATEGORY: SETTINGS - // OS: Q - FACE_ENROLL_ENROLLING = 1507; - - // OPEN: Face Enroll introduction - // CATEGORY: SETTINGS - // OS: Q - FACE_ENROLL_FINISHED = 1508; - - // OPEN: Face Enroll sidecar - // CATEGORY: SETTINGS - // OS: Q - FACE_ENROLL_SIDECAR = 1509; - - // OPEN: Settings > Add face > Error dialog - // OS: Q - DIALOG_FACE_ERROR = 1510; - - // OPEN: Settings > Security > Face - // CATEGORY: SETTINGS - // OS: Q - FACE = 1511; - - // OPEN: Settings > Acessibility > HearingAid pairing instructions dialog - // CATEGORY: SETTINGS - // OS: Q - DIALOG_ACCESSIBILITY_HEARINGAID = 1512; - - // OPEN: Settings > Add face - // OS: Q - FACE_ENROLL_PREVIEW = 1554; - - // OPEN: Settings > Network & Internet > Wi-Fi > Add network - // CATEGORY: SETTINGS - // OS: Q - SETTINGS_WIFI_ADD_NETWORK = 1556; - - // OPEN: Settings > System > Input & Gesture > Reach up gesture - // OS: Q - SETTINGS_GESTURE_WAKE_LOCK_SCREEN = 1557; - - // OPEN: Settings > System > Input & Gesture > Wake screen - SETTINGS_GESTURE_WAKE_SCREEN = 1570; - - // OPEN: Settings > Network & internet > Mobile network - MOBILE_NETWORK = 1571; - - // OPEN: Settings > Network & internet > Mobile network > Choose network - MOBILE_NETWORK_SELECT = 1581; - - // OPEN: Settings > Network & internet > Mobile network > Mobile Data > Dialog - MOBILE_DATA_DIALOG = 1582; - - // OPEN: Settings > Network & internet > Mobile network > Data roaming > Dialog - MOBILE_ROAMING_DIALOG = 1583; - - // Settings > Display > Lock screen display > On lock screen - LOCK_SCREEN_NOTIFICATION_CONTENT = 1584; - - // ConfirmDeviceCredentials > BiometricPrompt - BIOMETRIC_FRAGMENT = 1585; - - // OPEN: Biometric Enrollment (android.settings.BIOMETRIC_ENROLL action intent) - BIOMETRIC_ENROLL_ACTIVITY = 1586; - - // OPEN: Settings > Privacy - TOP_LEVEL_PRIVACY = 1587; - - // OPEN: Settings > Sound & notification > Do Not Disturb > See all exceptions > - // Allow apps to override - // CATEGORY: SETTINGS - // OS: Q - NOTIFICATION_ZEN_MODE_OVERRIDING_APPS = 1588; - - - // OPEN: Settings > Sound & notification > Do Not Disturb > See all exceptions > - // Allow apps to override > Choose app - // CATEGORY: SETTINGS - // OS: Q - NOTIFICATION_ZEN_MODE_OVERRIDING_APP = 1589; - - // OPEN: Settings > Developer options > Disable > Info dialog - DIALOG_DISABLE_DEVELOPMENT_OPTIONS = 1591; - - // OPEN: WifiDppConfiguratorActivity (android.settings.WIFI_DPP_CONFIGURATOR_XXX action intents) - SETTINGS_WIFI_DPP_CONFIGURATOR = 1595; - - // OPEN: WifiDppEnrolleeActivity (android.settings.WIFI_DPP_ENROLLEE_XXX action intents) - SETTINGS_WIFI_DPP_ENROLLEE = 1596; - - // OPEN: Settings > Apps & Notifications -> Special app access -> Financial Apps Sms Access - SETTINGS_FINANCIAL_APPS_SMS_ACCESS = 1597; - - - - // OPEN: Settings > Sound > Do Not Disturb > Schedules > (Click on system rule) - // > Do Not Disturb behavior - // CATEGORY: SETTINGS - // OS: Q - ZEN_CUSTOM_RULE_SETTINGS = 1604; - - // OPEN: Settings > Sound > Do Not Disturb > Schedules > (Click on system rule) - // > Do Not Disturb behavior > Custom - // CATEGORY: SETTINGS - // OS: Q - ZEN_CUSTOM_RULE_SOUND_SETTINGS = 1605; - - // OPEN: Settings > Sound > Do Not Disturb > Schedules > (Click on system rule) - // > Do Not Disturb behavior > Use default Do Not Disturb behavior - // CATEGORY: SETTINGS - // OS: Q - ZEN_CUSTOM_RULE_DEFAULT_SETTINGS = 1606; - - // OPEN: Settings > Sound > Do Not Disturb > Schedules > (Click on system rule) - // > Do Not Disturb behavior > Use default Do Not Disturb behavior - // > Notification restriction - // CATEGORY: SETTINGS - // OS: Q - ZEN_CUSTOM_RULE_NOTIFICATION_RESTRICTIONS = 1608; - - // OPEN: Settings > Sound > Do Not Disturb > Schedules > (Click on system rule) - // > Do Not Disturb behavior > Use default Do Not Disturb behavior - // > Notification restriction > Custom - // CATEGORY: SETTINGS - // OS: Q - ZEN_CUSTOM_RULE_VIS_EFFECTS = 1609; - - // OPEN: Settings > Sound > Do Not Disturb > Schedules > (Click on system rule) - // > Do Not Disturb behavior > Use default Do Not Disturb behavior - // > Notification restriction > Custom > Allow messages - // CATEGORY: SETTINGS - // OS: Q - ZEN_CUSTOM_RULE_MESSAGES = 1610; - - // OPEN: Settings > Sound > Do Not Disturb > Schedules > (Click on system rule) - // > Do Not Disturb behavior > Use default Do Not Disturb behavior - // > Notification restriction > Custom > Allow calls - // CATEGORY: SETTINGS - // OS: Q - ZEN_CUSTOM_RULE_CALLS = 1611; - - // OPEN: Settings > Sound > Do Not Disturb > Click footer link if custom settings applied - // CATEGORY: SETTINGS - // OS: Q - ZEN_CUSTOM_SETTINGS_DIALOG = 1612; - - // OPEN: Settings > Developer Options > Graphics Driver Preferences - // CATEGORY: SETTINGS - // OS: Q - SETTINGS_GRAPHICS_DRIVER_DASHBOARD = 1613; - - // OPEN: Settings > Accessibility > Vibration > Ring vibration - // CATEGORY: SETTINGS - // OS: Q - ACCESSIBILITY_VIBRATION_RING = 1620; - - // OPEN: Settings > System > Input & Gesture > Skip songs - SETTINGS_GESTURE_SKIP = 1624; - - // OPEN: Settings > System > Input & Gesture > Silence alerts - SETTINGS_GESTURE_SILENCE = 1625; - - // OPEN: Settings > System > Input & Gesture > Tap to check - SETTINGS_GESTURE_TAP_SCREEN = 1626; - - // OPEN: Settings > Network & internet > Click Mobile network to land on a page with a list of - // SIM/eSIM subscriptions. - MOBILE_NETWORK_LIST = 1627; - - // OPEN: Settings > Display > Adaptive sleep - // OS: Q - SETTINGS_ADAPTIVE_SLEEP = 1628; - - // OPEN: Settings > System > Aware - SETTINGS_AWARE = 1632; - - // OPEN: Settings > System > Aware > Disable > Dialog - DIALOG_AWARE_DISABLE = 1633; - - // OPEN: Settings > Settings > Network & internet > Click Mobile network to land on page with - // details for a SIM/eSIM mobile network > Click edit icon to bring up a rename dialog. - // OS: Q - MOBILE_NETWORK_RENAME_DIALOG = 1642; - - // OPEN: Set new password (android.app.action.SET_NEW_PASSWORD action intent) - // CATEGORY: SETTINGS - // OS: Q - SET_NEW_PASSWORD_ACTIVITY = 1644; - - // Panel for Internet Connectivity - PANEL_INTERNET_CONNECTIVITY = 1654; - - // Panel for Volume - PANEL_VOLUME = 1655; - - // Panel for NFC - PANEL_NFC = 1656; - - // Panel for Media Output - PANEL_MEDIA_OUTPUT = 1657; - - // Mapping: go/at-mapping - PAGE_ATSSI = 1667; - - PAGE_ATSII = 1668; - - PAGE_ATUS = 1669; - - PAGE_ATSSP = 1670; - - PAGE_ATSAP = 1671; - - PAGE_ATSCP = 1672; - - PAGE_ATHNP = 1673; - - // OPEN: Accessibility detail settings (android.settings.ACCESSIBILITY_DETAILS_SETTINGS intent) - ACCESSIBILITY_DETAILS_SETTINGS = 1682; - - // Open: Settings will show the conditional when Grayscale mode is on - SETTINGS_CONDITION_GRAYSCALE_MODE = 1683; - - // Panel for Wifi - PANEL_WIFI = 1687; - - // Open: Settings > Special App Access > Do not disturb control for app - ZEN_ACCESS_DETAIL = 1692; - - // OPEN: Settings > Face > Remove face - // OS: Q - DIALOG_FACE_REMOVE = 1693; - - // Settings > Display > Theme - DARK_UI_SETTINGS = 1698; - - // Settings > global bubble settings - BUBBLE_SETTINGS = 1699; - - // Settings > app > bubble settings - APP_BUBBLE_SETTINGS = 1700; - - // OPEN: Settings > System > Aware > Info dialog - DIALOG_AWARE_STATUS = 1701; - - // Open: Settings > app > bubble settings > confirmation dialog - DIALOG_APP_BUBBLE_SETTINGS = 1702; - - // OPEN: Settings > Pick SIM dialog - DIALOG_SIM_LIST = 1707; - - // OPEN: Settings > Pick SIM (that supports calling) dialog - DIALOG_CALL_SIM_LIST = 1708; - - // OPEN: Settings > Pick preferred SIM dialog - DIALOG_PREFERRED_SIM_PICKER = 1709; - - // OPEN: Settings > Network & internet > Mobile network > Delete sim - DIALOG_DELETE_SIM_CONFIRMATION = 1713; - - // OPEN: Settings > Network & internet > Mobile network > Delete sim > (answer yes to - // confirmation) - DIALOG_DELETE_SIM_PROGRESS = 1714; - - // Settings > Apps and notifications > Notifications > Gentle notifications - GENTLE_NOTIFICATIONS_SCREEN = 1715; - - // OPEN: Settings > System > Gestures > Global Actions Panel - // CATEGORY: SETTINGS - // OS: Q - GLOBAL_ACTIONS_PANEL_SETTINGS = 1728; - - // OPEN: Settings > Display > Dark Theme - // CATEGORY: SETTINGS - // OS: Q - // Note: Only shows up on first time toggle - DIALOG_DARK_UI_INFO = 1740; - - // OPEN: Settings > About phone > Legal information > Google Play system update licenses - // CATEGORY: SETTINGS - // OS: Q - MODULE_LICENSES_DASHBOARD = 1746; - - // OPEN: Settings > System > Gestures > System navigation > Info icon - // CATEGORY: SETTINGS - // OS: Q - // Note: Info icon is visible only when gesture navigation is not available and disabled - SETTINGS_GESTURE_NAV_NOT_AVAILABLE_DLG = 1747; - - // OPEN: Settings > System > Gestures > System navigation > Gear icon - // CATEGORY: SETTINGS - // OS: Q - // Note: Gear icon is shown next to gesture navigation preference and opens sensitivity dialog - SETTINGS_GESTURE_NAV_BACK_SENSITIVITY_DLG = 1748; - - // OPEN: Settings > System > Aware > Aware Display - // CATEGORY: SETTINGS - // OS: Q - SETTINGS_AWARE_DISPLAY = 1750; - - // OPEN: Settings > System > Input & Gesture > tap gesture - // CATEGORY: SETTINGS - // OS: Q - SETTINGS_GESTURE_TAP = 1751; - // ---- End Q Constants, all Q constants go above this line ---- - // OPEN: Settings > Network & Internet > Wi-Fi > Click new network - // CATEGORY: SETTINGS - // OS: R - SETTINGS_WIFI_CONFIGURE_NETWORK = 1800; - - // OPEN: Settings > Accessibility > Magnification - // CATEGORY: SETTINGS - // OS: R - // Note: Shows up only when Magnify with shortcut is enabled - // and under accessibility button mode. - DIALOG_TOGGLE_SCREEN_MAGNIFICATION_ACCESSIBILITY_BUTTON = 1801; - - // OPEN: Settings > Accessibility > Magnification - // CATEGORY: SETTINGS - // OS: R - // Note: Shows up only when Magnify with shortcut is enabled. - // and under gesture navigation mode. - DIALOG_TOGGLE_SCREEN_MAGNIFICATION_GESTURE_NAVIGATION = 1802; - - // OPEN: Settings > Security & screen lock -> Encryption & credentials > Install a certificate - // CATEGORY: SETTINGS - // OS: R - INSTALL_CERTIFICATE_FROM_STORAGE = 1803; - - // OPEN: Settings > Apps and notifications > Special app access > notification access > - // an app - // CATEGORY: SETTINGS - // OS: R - NOTIFICATION_ACCESS_DETAIL = 1804; - - // OPEN: Settings > Developer Options > Platform Compat - // CATEGORY: SETTINGS - // OS: R - SETTINGS_PLATFORM_COMPAT_DASHBOARD = 1805; - - // OPEN: Settings > Location -> Work profile tab - // CATEGORY: SETTINGS - // OS: R - LOCATION_WORK = 1806; - - // OPEN: Settings > Account -> Work profile tab - // CATEGORY: SETTINGS - // OS: R - ACCOUNT_WORK = 1807; - - // OPEN: Settings > Developer Options > Bug report handler - // CATEGORY: SETTINGS - // OS: R - SETTINGS_BUGREPORT_HANDLER = 1808; - - // Panel for adding Wi-Fi networks - // CATEGORY: SETTINGS - // OS: R - PANEL_ADD_WIFI_NETWORKS = 1809; - - // OPEN: Settings > Accessibility > Enable the feature or shortcut > Show tutorial dialog - // CATEGORY: SETTINGS - // OS: R - DIALOG_ACCESSIBILITY_TUTORIAL = 1810; - - // OPEN: Settings > Accessibility > Edit shortcut dialog - // CATEGORY: SETTINGS - // OS: R - DIALOG_ACCESSIBILITY_SERVICE_EDIT_SHORTCUT = 1812; - - // OPEN: Settings > Accessibility > Magnification > Edit shortcut dialog - // CATEGORY: SETTINGS - // OS: R - DIALOG_MAGNIFICATION_EDIT_SHORTCUT = 1813; - - // OPEN: Settings > Accessibility > Color correction > Edit shortcut dialog - // CATEGORY: SETTINGS - // OS: R - DIALOG_DALTONIZER_EDIT_SHORTCUT = 1814; - - // OPEN: Settings > Accessibility > Magnification > Settings - // CATEGORY: SETTINGS - // OS: R - ACCESSIBILITY_MAGNIFICATION_SETTINGS = 1815; - - // OPEN: Settings > Accessibility > Magnification > Settings > Magnification area dialog - // CATEGORY: SETTINGS - // OS: R - DIALOG_MAGNIFICATION_CAPABILITY = 1816; - - // OPEN: Settings > Accessibility > Color inversion - // CATEGORY: SETTINGS - // OS: R - ACCESSIBILITY_COLOR_INVERSION_SETTINGS = 1817; - - // OPEN: Settings > Accessibility > Color inversion > Edit shortcut dialog - // CATEGORY: SETTINGS - // OS: R - DIALOG_COLOR_INVERSION_EDIT_SHORTCUT = 1818; - - // OPEN: Settings > Accessibility > Captions preference > Captions appearance - // CATEGORY: SETTINGS - // OS: R - ACCESSIBILITY_CAPTION_APPEARANCE = 1819; - - // OPEN: Settings > Accessibility > Captions preference > More options - // CATEGORY: SETTINGS - // OS: R - ACCESSIBILITY_CAPTION_MORE_OPTIONS = 1820; - - // OPEN: Settings > Battery > Battery share - // CATEGORY: SETTINGS - // OS: R - FUELGAUGE_BATTERY_SHARE = 1821; - - // OPEN: Settings -> Apps & Notifications -> Special App Access - // CATEGORY: SETTINGS - // OS: R - MANAGE_EXTERNAL_STORAGE = 1822; - - // Open: Settings > DND > People - // OS: R - DND_PEOPLE = 1823; - - // OPEN: Settings > Apps and notifications > App info > one of any app > Open by default - // > Open supported links - // CATEGORY: SETTINGS - // OS: R - OPEN_SUPPORTED_LINKS = 1824; - - // OPEN: Settings > Display > Dark theme > Set start time dialog - DIALOG_DARK_THEME_SET_START_TIME = 1825; - - // OPEN: Settings > Display > Dark theme > Set end time dialog - DIALOG_DARK_THEME_SET_END_TIME = 1826; - - // OPEN: Settings -> Sound -> Vibrate for calls - // CATEGORY: SETTINGS - // OS: R - VIBRATE_FOR_CALLS = 1827; - - // OPEN: Settings > Connected devices > Connection preferences > NFC - // CATEGORY: SETTINGS - // OS: R - CONNECTION_DEVICE_ADVANCED_NFC = 1828; - - // OPEN: Settings -> Apps & Notifications -> Special App Access - INTERACT_ACROSS_PROFILES = 1829; - - // OPEN: Settings > Notifications > (app or conversations) > conversation - NOTIFICATION_CONVERSATION_SETTINGS = 1830; - - // OPEN: Settings > Developer Options > Wireless debugging - // CATEGORY: SETTINGS - // OS: R - SETTINGS_ADB_WIRELESS = 1831; - - // OPEN: Settings > Developer Options > Wireless debugging - // > Pair device with pairing code > Pairing code dialog - // CATEGORY: SETTINGS - // OS: R - ADB_WIRELESS_DEVICE_PAIRING_DIALOG = 1832; - - // OPEN: Settings > Developer Options > Wireless debugging - // > Pair device with QR code > Scan QR code > Pairing device dialog - // CATEGORY: SETTINGS - // OS: R - ADB_WIRELESS_DEVICE_QR_PAIRING_DIALOG = 1833; - - // OPEN: Settings > apps & notifications > notifications > conversations - // CATEGORY: SETTINGS - // OS: R - NOTIFICATION_CONVERSATION_LIST_SETTINGS = 1834; - - // Panel for Media Output Group operation - // CATEGORY: SETTINGS - // OS: R - PANEL_MEDIA_OUTPUT_GROUP = 1835; - - // OPEN: Settings > Developer Options > Wireless debugging - // > Click on paired device - // CATEGORY: SETTINGS - // OS: R - ADB_WIRELESS_DEVICE_DETAILS = 1836; - - // Open: Settings > Sound > Do Not Disturb > People > Conversations - // OS: R - DND_CONVERSATIONS = 1837; - - // Open: Settings > Sound > Do Not Disturb > People > Calls - // OS: R - DND_CALLS = 1838; - - // Open: Settings > Sound > Do Not Disturb > People > Messages - // OS: R - DND_MESSAGES = 1839; - - // Open: Settings > Sound > Do Not Disturb > Apps > <Choose App> - // OS: R - DND_APPS_BYPASSING = 1840; - - // OPEN: Settings > System > Gestures > One-Handed - // CATEGORY: SETTINGS - // OS: R QPR - SETTINGS_ONE_HANDED = 1841; - - // OPEN: Settings > Battery > Advanced battery option - // CATEGORY: SETTINGS - // OS: R - FUELGAUGE_ADVANCED_BATTERY_OPTION = 1842; - - // OPEN: Settings > System > Gestures > Power menu - // CATEGORY: SETTINGS - // OS: R - POWER_MENU_SETTINGS = 1843; - - // OPEN: Settings > System > Gestures > Power menu > Device controls - // CATEGORY: SETTINGS - // OS: R - DEVICE_CONTROLS_SETTINGS = 1844; - - // OPEN: Settings > Sound > Media - // CATEGORY: SETTINGS - // OS: R - MEDIA_CONTROLS_SETTINGS = 1845; - - // OPEN: Settings > System > Gestures > Swipe for notification - // CATEGORY: SETTINGS - // OS: R QPR - SETTINGS_SWIPE_BOTTOM_TO_NOTIFICATION = 1846; - - // OPEN: Settings > System > Gestures > Emergency SOS Gesture - // CATEGORY: SETTINGS - // OS: S - EMERGENCY_SOS_GESTURE_SETTINGS = 1847; - - // OPEN: Settings > System > Gestures > Double tap - // CATEGORY: SETTINGS - // OS: S - SETTINGS_COLUMBUS = 1848; - - // OPEN: Settings > Accessibility > Magnification > Settings > Magnification area > Magnification switch shortcut dialog - // CATEGORY: SETTINGS - // OS: S - DIALOG_MAGNIFICATION_SWITCH_SHORTCUT = 1849; - - // OPEN: Settings > Network & internet > Adaptive connectivity - // CATEGORY: SETTINGS - // OS: R QPR - ADAPTIVE_CONNECTIVITY_CATEGORY = 1850; - - // OS: R QPR2 - BLUETOOTH_PAIRING_RECEIVER = 1851; - - // OPEN: Settings > Display > Screen timeout - // CATEGORY: SETTINGS - // OS: S - SCREEN_TIMEOUT = 1852; - - // OPEN: Settings > Accessibility > Reduce Bright Colors - // CATEGORY: SETTINGS - // OS: S - REDUCE_BRIGHT_COLORS_SETTINGS = 1853; - - // OPEN: Settings > Location > Time Zone Detection - // CATEGORY: SETTINGS - // OS: S - LOCATION_TIME_ZONE_DETECTION = 1854; - - // OPEN: Settings > Developer options > Media transcode settings - // CATEGORY: SETTINGS - // OS: S - TRANSCODE_SETTINGS = 1855; -} diff --git a/core/proto/android/app/tvsettings_enums.proto b/core/proto/android/app/tvsettings_enums.proto deleted file mode 100644 index 77bf98f1c15e..000000000000 --- a/core/proto/android/app/tvsettings_enums.proto +++ /dev/null @@ -1,1132 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -syntax = "proto2"; - -package android.app.tvsettings; -option java_multiple_files = true; -option java_outer_classname = "TvSettingsEnums"; - -/** The performed action types */ -enum Action { - - /** - * Denotes an unknown action. It is a filler that should generally be - * avoided. - */ - ACTION_UNKNOWN = 0; - - /** - * Denotes that a TvSettings page is being focused. (Previewing a page in - * two panel settings should NOT be considered as focusing on the page.) - */ - PAGE_FOCUSED = 1; - - /** - * Denotes that an entry (typically a leaf node of settings tree) is - * selected by a user. - */ - ENTRY_SELECTED = 2; - - /** Denotes that a toggle is clicked by a user. */ - TOGGLE_INTERACTED = 3; - - /** - * Denotes that a TvSettings page is being focused in the forward direction - * into the settings tree. - */ - PAGE_FOCUSED_FORWARD = 4; - - /** - * Denotes that a TvSettings page is being focused in the backward direction - * up the settings tree. - */ - PAGE_FOCUSED_BACKWARD = 5; - - /** Denotes that a toggle is turned on by a user. */ - TOGGLED_ON = 6; - - /** Denotes that a toggle is turned off by a user. */ - TOGGLED_OFF = 7; - -} - -/** - * Ids for TvSettings focusable pages or actionable entries - * - * For details of the scheme, please refer to the "Definition of item_id" and - * "Evolve of item_id" sections in go/atv-settings-ww-logging-design. - */ -enum ItemId { - - option allow_alias = true; - - // Filler that should be avoided - UNKNOWN = 0x00000000; - - // TvSettings - TV_SETTINGS_ROOT = 0x00000001; - - // TvSettings unknown/default classic page - PAGE_CLASSIC_DEFAULT = 0x00000002; - - // TvSettings unknown/default slice page - PAGE_SLICE_DEFAULT = 0x00000003; - - // TvSettings unknown/default entry - ENTRY_DEFAULT = 0x00000004; - - // TvSettings > Suggested settings entry - SUGGESTED_SETTINGS = 0x00000010; - - // TvSettings > Quick Settings - QUICK_SETTINGS = 0x00000011; - - // VERSION 1: Starting with Q - // These are ordered in depth-first search manner. - - // TvSettings > Network & Internet - NETWORK = 0x11000000; - - // TvSettings > Network & Internet > Wi-Fi (toggle) - NETWORK_WIFI_ON_OFF = 0x11100000; - - // TvSettings > Network & Internet > - // [A connected network entry in available networks list] - NETWORK_AP_INFO = 0x11200000; - - // TvSettings > Network & Internet > - // [A connected network entry in available networks list] > Proxy settings - NETWORK_AP_INFO_PROXY_SETTINGS = 0x11210000; - - // TvSettings > Network & Internet > - // [A connected network entry in available networks list] > IP settings - NETWORK_AP_INFO_IP_SETTINGS = 0x11220000; - - // TvSettings > Network & Internet > - // [A connected network entry in available networks list] > Forget network - NETWORK_AP_INFO_FORGET_NETWORK = 0x11230000; - - // TvSettings > Network & Internet > - // [A not connected network entry in available networks list] - NETWORK_NOT_CONNECTED_AP = 0x11300000; - - // TvSettings > Network & Internet > See all - NETWORK_SEE_ALL = 0x11400000; - - // TvSettings > Network & Internet > See fewer - NETWORK_SEE_FEWER = 0x11500000; - - // TvSettings > Network & Internet > Add new network - NETWORK_ADD_NEW_NETWORK = 0x11600000; - - // TvSettings > Network & Internet > Scanning always available (toggle) - NETWORK_ALWAYS_SCANNING_NETWORKS = 0x11700000; - - // TvSettings > Network & Internet > Proxy settings (in Ethernet category) - NETWORK_ETHERNET_PROXY_SETTINGS = 0x11800000; - - // TvSettings > Network & Internet > IP settings (in Ethernet category) - NETWORK_ETHERNET_IP_SETTINGS = 0x11900000; - - // TvSettings > Account & Sign In (Slice) - ACCOUNT_SLICE = 0x12000000; - - // TvSettings > Account & Sign In (Slice) > [A regular account] - ACCOUNT_SLICE_REG_ACCOUNT = 0x12100000; - - // TvSettings > Account & Sign In (Slice) > [A regular account] > - // Services - ACCOUNT_SLICE_REG_ACCOUNT_SERVICES = 0x12110000; - - // TvSettings > Account & Sign In (Slice) > [A regular account] > - // Payment & Purchases - ACCOUNT_SLICE_REG_ACCOUNT_PAYMENT = 0x12120000; - - // TvSettings > Account & Sign In (Slice) > [A regular account] > - // Payment & Purchases > - // Require authentication for purchases (reauth interval) - ACCOUNT_SLICE_REG_ACCOUNT_PAYMENT_REAUTH = 0x12121000; - - // TvSettings > Account & Sign In (Slice) > [A regular account] > - // Payment & Purchases > - // Require authentication for purchases (reauth interval) > Always - ACCOUNT_SLICE_REG_ACCOUNT_PAYMENT_REAUTH_ALWAYS = 0x12121100; - - // TvSettings > Account & Sign In (Slice) > [A regular account] > - // Payment & Purchases > - // Require authentication for purchases (reauth interval) > Every 30 minutes - ACCOUNT_SLICE_REG_ACCOUNT_PAYMENT_REAUTH_30MINS = 0x12121200; - - // TvSettings > Account & Sign In (Slice) > [A regular account] > - // Payment & Purchases > - // Require authentication for purchases (reauth interval) > Never - ACCOUNT_SLICE_REG_ACCOUNT_PAYMENT_REAUTH_NEVER = 0x12121300; - - // TvSettings > Account & Sign In (Slice) > [A regular account] > - // Google Assistant - ACCOUNT_SLICE_REG_ACCOUNT_ASSISTANT = 0x12130000; - - // TvSettings > Account & Sign In (Slice) > [A regular account] > - // Google Assistant > SafeSearch filter (toggle) - ACCOUNT_SLICE_REG_ACCOUNT_ASSISTANT_SAFE_SEARCH = 0x12131000; - - // TvSettings > Account & Sign In (Slice) > [A regular account] > - // Google Assistant > Block offensive words (toggle) - ACCOUNT_SLICE_REG_ACCOUNT_ASSISTANT_BLOCK_OFFENSIVE = 0x12132000; - - // TvSettings > Account & Sign In (Slice) > [A regular account] > - // Google Assistant > Searchable apps - ACCOUNT_SLICE_REG_ACCOUNT_ASSISTANT_SEARCHABLE_APPS = 0x12133000; - - // TvSettings > Account & Sign In (Slice) > [A regular account] > - // Google Assistant > Personal results (toggle) - ACCOUNT_SLICE_REG_ACCOUNT_ASSISTANT_PERSONAL_RESULTS = 0x12134000; - - // TvSettings > Account & Sign In (Slice) > [A regular account] > - // Apps only mode (toggle) - ACCOUNT_SLICE_REG_ACCOUNT_APPS_ONLY_MODE = 0x12140000; - - // Reserving [0x12150000, 0x12190000] for possible future settings - - // TvSettings > Account & Sign In (Slice) > [A regular account] > Remove - ACCOUNT_SLICE_REG_ACCOUNT_REMOVE = 0x121A0000; - - // Reserving [0x12200000, 0x12900000] for possible future settings - - // TvSettings > Account & Sign In (Slice) > Add account... - ACCOUNT_SLICE_ADD_ACCOUNT = 0x12A00000; - - // TvSettings > Account & Sign In (Classic) - ACCOUNT_CLASSIC = 0x13000000; - - // TvSettings > Account & Sign In (Classic) > [A regular account] - ACCOUNT_CLASSIC_REG_ACCOUNT = 0x13100000; - - // TvSettings > Account & Sign In (Classic) > [A regular account] > Sync now - ACCOUNT_CLASSIC_REG_ACCOUNT_SYNC_NOW = 0x13110000; - - // TvSettings > Account & Sign In (Classic) > [A regular account] > - // Remove account - ACCOUNT_CLASSIC_REG_ACCOUNT_REMOVE_ACCOUNT = 0x13120000; - - // TvSettings > Account & Sign In (Classic) > [A regular account] > - // [Choose synced apps] Calendar (toggle) - ACCOUNT_CLASSIC_REG_ACCOUNT_SYNC_CALENDAR = 0x13130000; - - // TvSettings > Account & Sign In (Classic) > [A regular account] > - // [Choose synced apps] Contacts (toggle) - ACCOUNT_CLASSIC_REG_ACCOUNT_SYNC_CONTACTS = 0x13140000; - - // TvSettings > Account & Sign In (Classic) > [A regular account] > - // [Choose synced apps] Google Play Movies & TV (toggle) - ACCOUNT_CLASSIC_REG_ACCOUNT_SYNC_GPMT = 0x13150000; - - // TvSettings > Account & Sign In (Classic) > [A regular account] > - // [Choose synced apps] Google Play Music (toggle) - ACCOUNT_CLASSIC_REG_ACCOUNT_SYNC_GPM = 0x13160000; - - // TvSettings > Account & Sign In (Classic) > [A regular account] > - // [Choose synced apps] People details (toggle) - ACCOUNT_CLASSIC_REG_ACCOUNT_SYNC_PEOPLE = 0x13170000; - - // Reserving [0x13200000, 0x13900000] for possible future settings - - // TvSettings > Account & Sign In (Classic) > Add account - ACCOUNT_CLASSIC_ADD_ACCOUNT = 0x13A00000; - - // TvSettings > Privacy - PRIVACY = 0x14000000; - - // TvSettings > Privacy > Location - PRIVACY_LOCATION = 0x14100000; - - // TvSettings > Privacy > Location > Location status (radio button) - PRIVACY_LOCATION_STATUS = 0x14110000; - - // TvSettings > Privacy > Location > Location status (radio button) > - // Use Wi-Fi to estimate location - PRIVACY_LOCATION_STATUS_USE_WIFI = 0x14111000; - - // TvSettings > Privacy > Location > Location status (radio button) > Off - PRIVACY_LOCATION_STATUS_OFF = 0x14112000; - - // TvSettings > Privacy > Location > Scanning always available (toggle) - PRIVACY_LOCATION_ALWAYS_SCANNING_NETWORKS = 0x14120000; - - // TvSettings > Privacy > Location > [An app that had recent requests] - PRIVACY_LOCATION_REQUESTED_APP = 0x14130000; - - // TvSettings > Privacy > Usage & Diagnostics - PRIVACY_DIAGNOSTICS = 0x14200000; - - // TvSettings > Privacy > Usage & Diagnostics > On (Toggle) - PRIVACY_DIAGNOSTICS_ON_OFF = 0x14210000; - - // TvSettings > Privacy > Ads - PRIVACY_ADS = 0x14300000; - - // The following three IDs may not actually be logged as they are within a - // GMSCore Activity but we reserve IDs for them. - // TvSettings > Privacy > Ads > Reset advertising ID - PRIVACY_ADS_RESET_AD_ID = 0x14310000; - - // TvSettings > Privacy > Ads > Opt out of Ads Personalization - PRIVACY_ADS_OPT_OUT_PERSONALIZATION = 0x14320000; - - // TvSettings > Privacy > Ads > Ads by Google (WebView) - PRIVACY_ADS_ADS_BY_GOOGLE = 0x14330000; - - // TvSettings > Display & Sound - DISPLAY_SOUND = 0x15000000; - - // TvSettings > Display & Sound > Advanced display settings - DISPLAY_SOUND_ADVANCED_DISPLAY = 0x15100000; - - // TvSettings > Display & Sound > Advanced display settings > - // Allow game mode (toggle) - DISPLAY_SOUND_ADVANCED_DISPLAY_GAME_MODE = 0x15110000; - - // TvSettings > Display & Sound > System sounds (toggle) - DISPLAY_SOUND_SYSTEM_SOUNDS = 0x15200000; - - // TvSettings > Display & Sound > Advanced sound settings - DISPLAY_SOUND_ADVANCED_SOUNDS = 0x15300000; - - // TvSettings > Display & Sound > Advanced sound settings > Select formats - DISPLAY_SOUND_ADVANCED_SOUNDS_SELECT_FORMATS = 0x15310000; - - // TvSettings > Display & Sound > Advanced sound settings > Select formats > - // Auto... - DISPLAY_SOUND_ADVANCED_SOUNDS_SELECT_FORMATS_AUTO = 0x15311000; - - // TvSettings > Display & Sound > Advanced sound settings > Select formats > - // None... - DISPLAY_SOUND_ADVANCED_SOUNDS_SELECT_FORMATS_NONE = 0x15312000; - - // TvSettings > Display & Sound > Advanced sound settings > Select formats > - // Manual... - DISPLAY_SOUND_ADVANCED_SOUNDS_SELECT_FORMATS_MANUAL = 0x15313000; - - // TvSettings > Display & Sound > Advanced sound settings > - // Dolby AC-4 (toggle) - DISPLAY_SOUND_ADVANCED_SOUNDS_DAC4 = 0x15320000; - - // TvSettings > Display & Sound > Advanced sound settings > - // Dolby Atmos in Dolby Digital Plus (toggle) - DISPLAY_SOUND_ADVANCED_SOUNDS_DADDP = 0x15330000; - - // TvSettings > Display & Sound > Advanced sound settings > - // Dolby Digital (toggle) - DISPLAY_SOUND_ADVANCED_SOUNDS_DD = 0x15340000; - - // TvSettings > Display & Sound > Advanced sound settings > - // Dolby Digital Plus (toggle) - DISPLAY_SOUND_ADVANCED_SOUNDS_DDP = 0x15350000; - - // TvSettings > Display & Sound > Advanced sound settings > DTS (toggle) - DISPLAY_SOUND_ADVANCED_SOUNDS_DTS = 0x15360000; - - // TvSettings > Display & Sound > Advanced sound settings > DTS-HD (toggle) - DISPLAY_SOUND_ADVANCED_SOUNDS_DTSHD = 0x15370000; - - // TvSettings > Display & Sound > Advanced sound settings > AAC (toggle) - DISPLAY_SOUND_ADVANCED_SOUNDS_AAC = 0x15380000; - - // TvSettings > Display & Sound > Advanced sound settings > - // Dolby TrueHD (toggle) - DISPLAY_SOUND_ADVANCED_SOUNDS_DTHD = 0x15390000; - - // TvSettings > Apps - APPS = 0x16000000; - - // TvSettings > Apps > See all apps - APPS_ALL_APPS = 0x16100000; - - // TvSettings > Apps > See all apps > [An app entry] - APPS_ALL_APPS_APP_ENTRY = 0x16110000; - - // TvSettings > Apps > See all apps > [An app entry] > Open - APPS_ALL_APPS_APP_ENTRY_OPEN = 0x16111000; - - // TvSettings > Apps > See all apps > [An app entry] > Force stop - APPS_ALL_APPS_APP_ENTRY_FORCE_STOP = 0x16112000; - - // TvSettings > Apps > See all apps > [An app entry] > Uninstall - APPS_ALL_APPS_APP_ENTRY_UNINSTALL = 0x16113000; - - // TvSettings > Apps > See all apps > [An app entry] > Uninstall updates - APPS_ALL_APPS_APP_ENTRY_UNINSTALL_UPDATES = 0x16114000; - - // TvSettings > Apps > See all apps > [An app entry] > Disable - APPS_ALL_APPS_APP_ENTRY_DISABLE = 0x16115000; - - // TvSettings > Apps > See all apps > [An app entry] > Clear data - APPS_ALL_APPS_APP_ENTRY_CLEAR_DATA = 0x16116000; - - // TvSettings > Apps > See all apps > [An app entry] > Clear cache - APPS_ALL_APPS_APP_ENTRY_CLEAR_CACHE = 0x16117000; - - // TvSettings > Apps > See all apps > [An app entry] > Clear defaults - APPS_ALL_APPS_APP_ENTRY_CLEAR_DEFAULTS = 0x16118000; - - // TvSettings > Apps > See all apps > [An app entry] > - // Notifications (toggle) - APPS_ALL_APPS_APP_ENTRY_NOTIFICATIONS = 0x16119000; - - // TvSettings > Apps > See all apps > [An app entry] > Permissions - APPS_ALL_APPS_APP_ENTRY_PERMISSIONS = 0x1611A000; - - // TvSettings > Apps > See all apps > [An app entry] > Enable - APPS_ALL_APPS_APP_ENTRY_ENABLE = 0x1611B000; - - // TvSettings > Apps > See all apps > [An app entry] > Open source licenses - APPS_ALL_APPS_APP_ENTRY_LICENSES = 0x1611C000; - - // TvSettings > Apps > See all apps > Show system apps - APPS_ALL_APPS_SHOW_SYSTEM_APPS = 0x16120000; - - // TvSettings > Apps > App permissions - APPS_APP_PERMISSIONS = 0x16200000; - - // TvSettings > Apps > App permission > Body sensors - APPS_APP_PERMISSIONS_BODY_SENSORS = 0x16210000; - - // TvSettings > Apps > App permission > Calendar - APPS_APP_PERMISSIONS_CALENDAR = 0x16220000; - - // TvSettings > Apps > App permission > Call logs - APPS_APP_PERMISSIONS_CALL_LOGS = 0x16230000; - - // TvSettings > Apps > App permission > Camera - APPS_APP_PERMISSIONS_CAMERA = 0x16240000; - - // TvSettings > Apps > App permission > Contacts - APPS_APP_PERMISSIONS_CONTACTS = 0x16250000; - - // TvSettings > Apps > App permission > Location - APPS_APP_PERMISSIONS_LOCATION = 0x16260000; - - // TvSettings > Apps > App permission > Microphone - APPS_APP_PERMISSIONS_MICROPHONE = 0x16270000; - - // TvSettings > Apps > App permission > Phone - APPS_APP_PERMISSIONS_PHONE = 0x16280000; - - // TvSettings > Apps > App permission > Physical activity - APPS_APP_PERMISSIONS_PHYSICAL_ACTIVITY = 0x16290000; - - // TvSettings > Apps > App permission > SMS - APPS_APP_PERMISSIONS_SMS = 0x162A0000; - - // TvSettings > Apps > App permission > Storage - APPS_APP_PERMISSIONS_STORAGE = 0x162B0000; - - // TvSettings > Apps > App permission > Additional permissions - APPS_APP_PERMISSIONS_ADDITIONAL = 0x162C0000; - - // TvSettings > Apps > App permission > Additional permissions > - // real all TV listings - APPS_APP_PERMISSIONS_ADDITIONAL_READ_TV_LISTINGS = 0x162C1000; - - // TvSettings > Apps > App permission > Additional permissions > - // real instant messages - APPS_APP_PERMISSIONS_ADDITIONAL_READ_INSTANT_MESSAGES = 0x162C2000; - - // TvSettings > Apps > App permission > Additional permissions > - // write instant messages - APPS_APP_PERMISSIONS_ADDITIONAL_WRITE_INSTANT_MESSAGES = 0x162C3000; - - // TvSettings > Apps > Special app access - APPS_SPECIAL_APP_ACCESS = 0x16300000; - - // TvSettings > Apps > Special app access > Energy optimization - APPS_SPECIAL_APP_ACCESS_ENERGY_OPTIMIZATION = 0x16310000; - - // TvSettings > Apps > Special app access > Usage access - APPS_SPECIAL_APP_ACCESS_USAGE_ACCESS = 0x16320000; - - // TvSettings > Apps > Special app access > Notification access - APPS_SPECIAL_APP_ACCESS_NOTIFICATION_ACCESS = 0x16330000; - - // TvSettings > Apps > Special app access > Display over other apps - APPS_SPECIAL_APP_ACCESS_DISPLAY_OVER_OTHERS = 0x16340000; - - // TvSettings > Apps > Special app access > Modify system settings - APPS_SPECIAL_APP_ACCESS_MODIFY_SYSTEM_SETTINGS = 0x16350000; - - // TvSettings > Apps > Special app access > Picture-in-picture - APPS_SPECIAL_APP_ACCESS_PICTURE_IN_PICTURE = 0x16360000; - - // TvSettings > Apps > Security & restrictions - APPS_SECURITY_RESTRICTIONS = 0x16400000; - - // TvSettings > Apps > Security & restrictions > Unknown sources - APPS_SECURITY_RESTRICTIONS_UNKNOWN_SOURCES = 0x16410000; - - // TvSettings > Apps > Security & restrictions > Verify apps (toggle) - APPS_SECURITY_RESTRICTIONS_VERIFY_APPS = 0x16420000; - - // TvSettings > Apps > Security & restrictions > Create restricted profile - APPS_SECURITY_RESTRICTIONS_CREATE_PROFILE = 0x16430000; - - // TvSettings > Apps > Security & restrictions > Enter restricted profile - APPS_SECURITY_RESTRICTIONS_ENTER_PROFILE = 0x16440000; - - // TvSettings > Apps > Security & restrictions > - // Allowed apps (Restricted Profile) - APPS_SECURITY_RESTRICTIONS_PROFILE_ALLOWED_APPS = 0x16450000; - - // TvSettings > Apps > Security & restrictions > - // Change pin (Restricted Profile) - APPS_SECURITY_RESTRICTIONS_PROFILE_CHANGE_PIN = 0x16460000; - - // TvSettings > Apps > Security & restrictions > - // Delete restricted profile - APPS_SECURITY_RESTRICTIONS_DELETE_PROFILE = 0x16470000; - - // TvSettings > Apps > Security & restrictions > - // Exit restricted profile - APPS_SECURITY_RESTRICTIONS_EXIT_PROFILE = 0x16480000; - - // TvSettings > System (same as TvSettings > Device Preferences) - SYSTEM = 0x17000000; - - // TvSettings > System > About - SYSTEM_ABOUT = 0x17100000; - - // TvSettings > System > System update - SYSTEM_ABOUT_SYSTEM_UPDATE = 0x17110000; - - // TvSettings > System > Device name - SYSTEM_ABOUT_DEVICE_NAME = 0x17120000; - - // TvSettings > System > Factory reset - SYSTEM_ABOUT_FACTORY_RESET = 0x17130000; - - // TvSettings > System > Status - SYSTEM_ABOUT_STATUS = 0x17140000; - - // TvSettings > System > Legal information - SYSTEM_ABOUT_LEGAL_INFO = 0x17150000; - - // TvSettings > System > Legal information > Open source licenses - SYSTEM_ABOUT_LEGAL_INFO_OPEN_SOURCE = 0x17151000; - - // TvSettings > System > Legal information > Google legal - SYSTEM_ABOUT_LEGAL_INFO_GOOGLE_LEGAL = 0x17152000; - - // TvSettings > System > Legal information > System WebView licenses - SYSTEM_ABOUT_LEGAL_INFO_SYSTEM_WEBVIEW = 0x17153000; - - // TvSettings > System > Build - SYSTEM_ABOUT_BUILD = 0x17160000; - - // TvSettings > System > Date & time - SYSTEM_DATE_TIME = 0x17200000; - - // TvSettings > System > Date & time > Automatic data & time - SYSTEM_DATE_TIME_AUTOMATIC = 0x17210000; - - // TvSettings > System > Date & time > Automatic data & time > - // Use network-provided time - SYSTEM_DATE_TIME_AUTOMATIC_USE_NETWORK_TIME = 0x17211000; - - // TvSettings > System > Date & time > Automatic data & time > Off - SYSTEM_DATE_TIME_AUTOMATIC_OFF = 0x17212000; - - // TvSettings > System > Date & time > Set date - SYSTEM_DATE_TIME_SET_DATE = 0x17220000; - - // TvSettings > System > Date & time > Set time - SYSTEM_DATE_TIME_SET_TIME = 0x17230000; - - // TvSettings > System > Date & time > Set time zone - SYSTEM_DATE_TIME_SET_TIME_ZONE = 0x17240000; - - // TvSettings > System > Date & time > Set time zone > [A time zone button] - SYSTEM_DATE_TIME_SET_TIME_ZONE_BUTTON = 0x17241000; - - // TvSettings > System > Date & time > Use 24-hour format (toggle) - SYSTEM_DATE_TIME_USE_24_HOUR_FORMAT = 0x17250000; - - // TvSettings > System > Language - SYSTEM_LANGUAGE = 0x17300000; - - // TvSettings > System > Language > [A language button] - SYSTEM_LANGUAGE_BUTTON = 0x17310000; - - // TvSettings > System > Keyboard - SYSTEM_KEYBOARD = 0x17400000; - - // TvSettings > System > Keyboard > Current keyboard - SYSTEM_KEYBOARD_CURRENT_KEYBOARD = 0x17410000; - - // TvSettings > System > Keyboard > Gboard Settings - SYSTEM_KEYBOARD_GBOARD_SETTINGS = 0x17420000; - - // TvSettings > System > Keyboard > Gboard Settings > Languages - SYSTEM_KEYBOARD_GBOARD_SETTINGS_LANGUAGES = 0x17421000; - - // TvSettings > System > Keyboard > Gboard Settings > Terms of services - SYSTEM_KEYBOARD_GBOARD_SETTINGS_TOS = 0x17422000; - - // TvSettings > System > Keyboard > Gboard Settings > Privacy policy - SYSTEM_KEYBOARD_GBOARD_SETTINGS_PRIVACY_POLICY = 0x17423000; - - // TvSettings > System > Keyboard > Gboard Settings > Open source licenses - SYSTEM_KEYBOARD_GBOARD_SETTINGS_OPEN_SOURCE = 0x17424000; - - // TvSettings > System > Keyboard > Gboard Settings > - // Share usage statistics (toggle) - SYSTEM_KEYBOARD_GBOARD_SETTINGS_SHARE_USAGE_STATS = 0x17425000; - - // TvSettings > System > Keyboard > Manage keyboards - SYSTEM_KEYBOARD_MANAGE_KEYBOARDS = 0x17430000; - - // TvSettings > System > Storage - SYSTEM_STORAGE = 0x17500000; - - // TvSettings > System > Internal shared storage - SYSTEM_STORAGE_INTERNAL_STORAGE = 0x17510000; - - // TvSettings > System > Internal shared storage > Apps - SYSTEM_STORAGE_INTERNAL_STORAGE_APPS = 0x17511000; - - // TvSettings > System > Internal shared storage > - // Cached data (brings up "Clear cached data?" dialog upon click) - SYSTEM_STORAGE_INTERNAL_STORAGE_CACHED = 0x17512000; - - // TvSettings > System > Ambient mode - SYSTEM_AMBIENT = 0x17600000; - - // TvSettings > System > Ambient mode > Start now - SYSTEM_AMBIENT_START = 0x17610000; - - // TvSettings > System > Ambient mode > Settings - SYSTEM_AMBIENT_SETTINGS = 0x17620000; - - // TvSettings > System > Ambient mode > Settings > Google Photos (Channels) - SYSTEM_AMBIENT_SETTINGS_CHANNEL_GP = 0x17621000; - - // TvSettings > System > Ambient mode > Settings > Art gallery (Channels) - SYSTEM_AMBIENT_SETTINGS_CHANNEL_AG = 0x17622000; - - // TvSettings > System > Ambient mode > Settings > - // Cinematic videos (Channels) - SYSTEM_AMBIENT_SETTINGS_CHANNEL_CV = 0x17623000; - - // TvSettings > System > Ambient mode > Settings > Experimental (Channels) - SYSTEM_AMBIENT_SETTINGS_CHANNEL_EXP = 0x17624000; - - // TvSettings > System > Ambient mode > Settings > Weather - SYSTEM_AMBIENT_SETTINGS_WEATHER = 0x17625000; - - // TvSettings > System > Ambient mode > Settings > Weather > Hide - SYSTEM_AMBIENT_SETTINGS_WEATHER_HIDE = 0x17625100; - - // TvSettings > System > Ambient mode > Settings > Weather > Celsius (Unit) - SYSTEM_AMBIENT_SETTINGS_WEATHER_UNIT_C = 0x17625200; - - // TvSettings > System > Ambient mode > Settings > Weather > - // Fahrenheit (Unit) - SYSTEM_AMBIENT_SETTINGS_WEATHER_UNIT_F = 0x17625300; - - // TvSettings > System > Ambient mode > Settings > Weather > Both (Unit) - SYSTEM_AMBIENT_SETTINGS_WEATHER_UNIT_BOTH = 0x17625400; - - // TvSettings > System > Ambient mode > Settings > Time - SYSTEM_AMBIENT_SETTINGS_TIME = 0x17626000; - - // TvSettings > System > Ambient mode > Settings > Time > Hide - SYSTEM_AMBIENT_SETTINGS_TIME_HIDE = 0x17626100; - - // TvSettings > System > Ambient mode > Settings > Time > Show - SYSTEM_AMBIENT_SETTINGS_TIME_SHOW = 0x17626200; - - // TvSettings > System > Ambient mode > Settings > Device information - SYSTEM_AMBIENT_SETTINGS_DEVICE_INFO = 0x17627000; - - // TvSettings > System > Ambient mode > Settings > Device information > Hide - SYSTEM_AMBIENT_SETTINGS_DEVICE_INFO_HIDE = 0x17627100; - - // TvSettings > System > Ambient mode > Settings > Device information > Show - SYSTEM_AMBIENT_SETTINGS_DEVICE_INFO_SHOW = 0x17627200; - - // TvSettings > System > Ambient mode > Settings > Personal photo data - SYSTEM_AMBIENT_SETTINGS_PPD = 0x17628000; - - // TvSettings > System > Ambient mode > Settings > Personal photo data > - // Hide - SYSTEM_AMBIENT_SETTINGS_PPD_HIDE = 0x17628100; - - // TvSettings > System > Ambient mode > Settings > Personal photo data > - // Show - SYSTEM_AMBIENT_SETTINGS_PPD_SHOW = 0x17628200; - - // TvSettings > System > Ambient mode > Settings > Portrait Google Photos - SYSTEM_AMBIENT_SETTINGS_PGP = 0x17629000; - - // TvSettings > System > Ambient mode > Settings > Portrait Google Photos > - // Hide - SYSTEM_AMBIENT_SETTINGS_PGP_HIDE = 0x17629100; - - // TvSettings > System > Ambient mode > Settings > Portrait Google Photos > - // Show - SYSTEM_AMBIENT_SETTINGS_PGP_SHOW = 0x17629200; - - // TvSettings > System > Ambient mode > Settings > Portrait Google Photos > - // Show pairs - SYSTEM_AMBIENT_SETTINGS_PGP_SHOW_PAIRS = 0x17629300; - - // TvSettings > System > Ambient mode > Settings > Personal photo curation - SYSTEM_AMBIENT_SETTINGS_PPC = 0x1762A000; - - // TvSettings > System > Ambient mode > Settings > Personal photo curation > - // All albums - SYSTEM_AMBIENT_SETTINGS_PPC_ALL_ALBUMS = 0x1762A100; - - // TvSettings > System > Ambient mode > Settings > Personal photo curation > - // Live albums only - SYSTEM_AMBIENT_SETTINGS_PPC_LIVE_ALBUMS = 0x1762A200; - - // TvSettings > System > Ambient mode > Settings > Slideshow speed - SYSTEM_AMBIENT_SETTINGS_SLIDE_SPEED = 0x1762B000; - - // TvSettings > System > Ambient mode > Settings > Slideshow speed > 5s - SYSTEM_AMBIENT_SETTINGS_SLIDE_SPEED_5S = 0x1762B100; - - // TvSettings > System > Ambient mode > Settings > Slideshow speed > 10s - SYSTEM_AMBIENT_SETTINGS_SLIDE_SPEED_10S = 0x1762B200; - - // TvSettings > System > Ambient mode > Settings > Slideshow speed > 30s - SYSTEM_AMBIENT_SETTINGS_SLIDE_SPEED_30S = 0x1762B300; - - // TvSettings > System > Ambient mode > Settings > Slideshow speed > 1m - SYSTEM_AMBIENT_SETTINGS_SLIDE_SPEED_1M = 0x1762B400; - - // TvSettings > System > Ambient mode > Settings > Slideshow speed > 3m - SYSTEM_AMBIENT_SETTINGS_SLIDE_SPEED_3M = 0x1762B500; - - // TvSettings > System > Ambient mode > Settings > Slideshow speed > 5m - SYSTEM_AMBIENT_SETTINGS_SLIDE_SPEED_5M = 0x1762B600; - - // TvSettings > System > Ambient mode > Settings > Slideshow speed > 10m - SYSTEM_AMBIENT_SETTINGS_SLIDE_SPEED_10M = 0x1762B700; - - // TvSettings > System > Energy saver - SYSTEM_ENERGYSAVER = 0x17700000; - - // TvSettings > System > Energy saver > Turn off display after - SYSTEM_ENERGYSAVER_START_DELAY = 0x17710000; - - // TvSettings > System > Energy saver > Turn off display after > 15 minutes - SYSTEM_ENERGYSAVER_START_DELAY_15M = 0x17711000; - - // TvSettings > System > Energy saver > Turn off display after > 30 minutes - SYSTEM_ENERGYSAVER_START_DELAY_30M = 0x17712000; - - // TvSettings > System > Energy saver > Turn off display after > 1 hour - SYSTEM_ENERGYSAVER_START_DELAY_1H = 0x17713000; - - // TvSettings > System > Energy saver > Turn off display after > 3 hours - SYSTEM_ENERGYSAVER_START_DELAY_3H = 0x17714000; - - // TvSettings > System > Energy saver > Turn off display after > 6 hours - SYSTEM_ENERGYSAVER_START_DELAY_6H = 0x17715000; - - // TvSettings > System > Energy saver > Turn off display after > 12 hours - SYSTEM_ENERGYSAVER_START_DELAY_12H = 0x17716000; - - // TvSettings > System > Energy saver > Turn off display after > Never - SYSTEM_ENERGYSAVER_START_DELAY_NEVER = 0x17717000; - - // TvSettings > System > Accessibility - SYSTEM_A11Y = 0x17800000; - - // TvSettings > System > Accessibility > Captions - SYSTEM_A11Y_CAPTIONS = 0x17810000; - - // TvSettings > System > Accessibility > Captions > Display (toggle) - SYSTEM_A11Y_CAPTIONS_DISPLAY_ON_OFF = 0x17811000; - - // TvSettings > System > Accessibility > Captions > Language - SYSTEM_A11Y_CAPTIONS_LANGUAGE = 0x17812000; - - // TvSettings > System > Accessibility > Captions > Language > [A language] - SYSTEM_A11Y_CAPTIONS_LANGUAGE_BUTTON = 0x17812100; - - // TvSettings > System > Accessibility > Captions > Text size - SYSTEM_A11Y_CAPTIONS_TEXT_SIZE = 0x17813000; - - // TvSettings > System > Accessibility > Captions > Text size > Very small - SYSTEM_A11Y_CAPTIONS_TEXT_SIZE_VERY_SMALL = 0x17813100; - - // TvSettings > System > Accessibility > Captions > Text size > Small - SYSTEM_A11Y_CAPTIONS_TEXT_SIZE_SMALL = 0x17813200; - - // TvSettings > System > Accessibility > Captions > Text size > Normal - SYSTEM_A11Y_CAPTIONS_TEXT_SIZE_NORMAL = 0x17813300; - - // TvSettings > System > Accessibility > Captions > Text size > Large - SYSTEM_A11Y_CAPTIONS_TEXT_SIZE_LARGE = 0x17813400; - - // TvSettings > System > Accessibility > Captions > Text size > Very large - SYSTEM_A11Y_CAPTIONS_TEXT_SIZE_VERY_LARGE = 0x17813500; - - // TvSettings > System > Accessibility > Captions > - // White on black (radio button) - SYSTEM_A11Y_CAPTIONS_WHITE_ON_BLACK = 0x17814000; - - // TvSettings > System > Accessibility > Captions > - // Black on white (radio button) - SYSTEM_A11Y_CAPTIONS_BLACK_ON_WHITE = 0x17815000; - - // TvSettings > System > Accessibility > Captions > - // Yellow on black (radio button) - SYSTEM_A11Y_CAPTIONS_YELLOW_ON_BLACK = 0x17816000; - - // TvSettings > System > Accessibility > Captions > - // Yellow on blue (radio button) - SYSTEM_A11Y_CAPTIONS_YELLOW_ON_BLUE = 0x17817000; - - // TvSettings > System > Accessibility > Captions > Custom - SYSTEM_A11Y_CAPTIONS_CUSTOM = 0x17818000; - - // TvSettings > System > Accessibility > Captions > Custom > Font family - SYSTEM_A11Y_CAPTIONS_CUSTOM_FONT = 0x17818100; - - // TvSettings > System > Accessibility > Captions > Custom > Text color - SYSTEM_A11Y_CAPTIONS_CUSTOM_TEXT_COLOR = 0x17818200; - - // TvSettings > System > Accessibility > Captions > Custom > Text opacity - SYSTEM_A11Y_CAPTIONS_CUSTOM_TEXT_OPACITY = 0x17818300; - - // TvSettings > System > Accessibility > Captions > Custom > Edge type - SYSTEM_A11Y_CAPTIONS_CUSTOM_EDGE_TYPE = 0x17818400; - - // TvSettings > System > Accessibility > Captions > Custom > Edge color - SYSTEM_A11Y_CAPTIONS_CUSTOM_EDGE_COLOR = 0x17818500; - - // TvSettings > System > Accessibility > Captions > Custom > - // Show background (toggle) - SYSTEM_A11Y_CAPTIONS_SHOW_BACKGROUND = 0x17818600; - - // TvSettings > System > Accessibility > Captions > Custom > - // Background color - SYSTEM_A11Y_CAPTIONS_BACKGROUND_COLOR = 0x17818700; - - // TvSettings > System > Accessibility > Captions > Custom > - // Background opacity - SYSTEM_A11Y_CAPTIONS_BACKGROUND_OPACITY = 0x17818800; - - // TvSettings > System > Accessibility > Captions > Custom > - // Show window (toggle) - SYSTEM_A11Y_CAPTIONS_SHOW_WINDOW = 0x17818900; - - // TvSettings > System > Accessibility > Captions > Custom > Window color - SYSTEM_A11Y_CAPTIONS_WINDOW_COLOR = 0x17818A00; - - // TvSettings > System > Accessibility > Captions > Custom > Window opacity - SYSTEM_A11Y_CAPTIONS_WINDOW_OPACITY = 0x17818B00; - - // TvSettings > System > Accessibility > High contrast text (toggle) - SYSTEM_A11Y_HIGH_CONTRAST_TEXT = 0x17820000; - - // TvSettings > System > Accessibility > Text to speech - SYSTEM_A11Y_TTS = 0x17830000; - - // TvSettings > System > Accessibility > Text to speech > [Select an engine] - SYSTEM_A11Y_TTS_ENGINE_SELECT = 0x17831000; - - // TvSettings > System > Accessibility > Text to speech > - // Engine configuration - SYSTEM_A11Y_TTS_ENGINE_CONFIG = 0x17832000; - - // TvSettings > System > Accessibility > Text to speech > - // Engine configuration > Language - SYSTEM_A11Y_TTS_ENGINE_CONFIG_LANGUAGE = 0x17832100; - - // TvSettings > System > Accessibility > Text to speech > - // Engine configuration > Language > Button - SYSTEM_A11Y_TTS_ENGINE_CONFIG_LANGUAGE_CHOOSE_LANGUAGE = 0x17832110; - - // TvSettings > System > Accessibility > Text to speech > - // Engine configuration > Settings for Google Text-to-speech Engine - SYSTEM_A11Y_TTS_ENGINE_CONFIG_SETTINGS_GTTS_ENGINE = 0x17832200; - - // TvSettings > System > Accessibility > Text to speech > - // Engine configuration > Install voice data - SYSTEM_A11Y_TTS_ENGINE_CONFIG_INSTALL_VOICE_DATA = 0x17832300; - - // TvSettings > System > Accessibility > Text to speech > Speech rate - SYSTEM_A11Y_TTS_SPEECH_RATE = 0x17833000; - - // TvSettings > System > Accessibility > Text to speech > - // Listen to an example - SYSTEM_A11Y_TTS_LISTEN_EXAMPLE = 0x17834000; - - // TvSettings > System > Accessibility > Accessibility shortcut - SYSTEM_A11Y_SHORTCUT = 0x17840000; - - // TvSettings > System > Accessibility > Accessibility shortcut > - // Enable (toggle) - SYSTEM_A11Y_SHORTCUT_ON_OFF = 0x17841000; - - // TvSettings > System > Accessibility > Accessibility shortcut > - // Shortcut services - SYSTEM_A11Y_SHORTCUT_SERVICE = 0x17842000; - - // TvSettings > System > Accessibility > TalkBack - SYSTEM_A11Y_TALKBACK = 0x17850000; - - // TvSettings > System > Accessibility > TalkBack > Enable (toggle) - SYSTEM_A11Y_TALKBACK_ON_OFF = 0x17851000; - - // TvSettings > System > Accessibility > TalkBack > Configuration - SYSTEM_A11Y_TALKBACK_CONFIG = 0x17852000; - - // TvSettings > System > Accessibility > Accessibility Menu - SYSTEM_A11Y_A11Y_MENU = 0x17860000; - - // TvSettings > System > Accessibility > Accessibility Menu > - // Enable (toggle) - SYSTEM_A11Y_A11Y_MENU_ON_OFF = 0x17861000; - - // TvSettings > System > Accessibility > Accessibility Menu > Configuration - SYSTEM_A11Y_A11Y_MENU_CONFIG = 0x17862000; - - // TvSettings > System > Accessibility > Select to Speak - SYSTEM_A11Y_STS = 0x17870000; - - // TvSettings > System > Accessibility > Select to Speak > Enable (toggle) - SYSTEM_A11Y_STS_ON_OFF = 0x17871000; - - // TvSettings > System > Accessibility > Select to Speak > Configuration - SYSTEM_A11Y_STS_CONFIG = 0x17872000; - - // TvSettings > System > Accessibility > Switch Access - SYSTEM_A11Y_SWITCH_ACCESS = 0x17880000; - - // TvSettings > System > Accessibility > Switch Access > Enable (Toggle) - SYSTEM_A11Y_SWITCH_ACCESS_ON_OFF = 0x17881000; - - // TvSettings > System > Accessibility > Switch Access > Configuration - SYSTEM_A11Y_SWITCH_ACCESS_CONFIG = 0x17882000; - - // TvSettings > System > Reboot - SYSTEM_REBOOT = 0x17900000; - - // TvSettings > Device Preferences > Home screen (in classic TvSettings) - PREFERENCES_HOME_SCREEN = 0x17A00000; - - // TvSettings > Device Preferences > Home screen (in classic TvSettings) > - // Customize channels - PREFERENCES_HOME_SCREEN_CUSTOMIZE_CHANNELS = 0x17A10000; - - // TvSettings > Device Preferences > Home screen (in classic TvSettings) > - // Customize channels > Play Next - PREFERENCES_HOME_SCREEN_CUSTOMIZE_CHANNELS_PN = 0x17A11000; - - // TvSettings > Device Preferences > Home screen (in classic TvSettings) > - // Customize channels > Play Next > On (toggle) - PREFERENCES_HOME_SCREEN_CUSTOMIZE_CHANNELS_PN_ON_OFF = 0x17A11100; - - // TvSettings > Device Preferences > Home screen (in classic TvSettings) > - // Customize channels > Play Next > Google Play Movies & TV (toggle) - PREFERENCES_HOME_SCREEN_CUSTOMIZE_CHANNELS_PN_GPMT = 0x17A11200; - - // TvSettings > Device Preferences > Home screen (in classic TvSettings) > - // Customize channels > Play Next > Google Play Music (toggle) - PREFERENCES_HOME_SCREEN_CUSTOMIZE_CHANNELS_PN_GPM = 0x17A11300; - - // TvSettings > Device Preferences > Home screen (in classic TvSettings) > - // Customize channels > Play Next > Promotional channels (toggle) - PREFERENCES_HOME_SCREEN_CUSTOMIZE_CHANNELS_PN_PROMOTIONAL = 0x17A11400; - - // TvSettings > Device Preferences > Home screen (in classic TvSettings) > - // Customize channels > Home screen channels - PREFERENCES_HOME_SCREEN_CUSTOMIZE_CHANNELS_HOME_SCREEN = 0x17A12000; - - // TvSettings > Device Preferences > Home screen (in classic TvSettings) > - // Customize channels > Promotional channels - PREFERENCES_HOME_SCREEN_CUSTOMIZE_CHANNELS_PROMOTIONAL = 0x17A13000; - - // TvSettings > Device Preferences > Home screen (in classic TvSettings) > - // Enable video previews (toggle) - PREFERENCES_HOME_SCREEN_VIDEO_PREVIEWS = 0x17A20000; - - // TvSettings > Device Preferences > Home screen (in classic TvSettings) > - // Enable audio previews (toggle) - PREFERENCES_HOME_SCREEN_AUDIO_PREVIEWS = 0x17A30000; - - // TvSettings > Device Preferences > Home screen (in classic TvSettings) > - // Reorder apps - PREFERENCES_HOME_SCREEN_REORDER_APPS = 0x17A40000; - - // TvSettings > Device Preferences > Home screen (in classic TvSettings) > - // Reorder games - PREFERENCES_HOME_SCREEN_REORDER_GAMES = 0x17A50000; - - // TvSettings > Device Preferences > Home screen (in classic TvSettings) > - // Android TV Home open source licenses - PREFERENCES_HOME_SCREEN_ATVH_OPEN_SOURCE = 0x17A60000; - - // TvSettings > Device Preferences > Home screen (in classic TvSettings) > - // Android TV Core Services open source licenses - PREFERENCES_HOME_SCREEN_ATVCS_OPEN_SOURCE = 0x17A70000; - - // TvSettings > Device Preferences > Google Assistant - PREFERENCES_ASSISTANT = 0x17B00000; - - // TvSettings > Device Preferences > Google Assistant > Accounts - PREFERENCES_ASSISTANT_ACCOUNTS = 0x17B10000; - - // TvSettings > Device Preferences > Google Assistant > Accept permissions - PREFERENCES_ASSISTANT_ACCEPT_PERMISSIONS = 0x17B20000; - - // TvSettings > Device Preferences > Google Assistant > View permissions - PREFERENCES_ASSISTANT_VIEW_PERMISSIONS = 0x17B30000; - - // TvSettings > Device Preferences > Google Assistant > Searchable apps - // (aliasing ACCOUNT_SLICE_REG_ACCOUNT_ASSISTANT_SEARCHABLE_APPS) - PREFERENCES_ASSISTANT_SEARCHABLE_APPS = 0x12133000; - - // TvSettings > Device Preferences > Google Assistant > SafeSearch filter - // (aliasing ACCOUNT_SLICE_REG_ACCOUNT_ASSISTANT_SAFE_SEARCH) - PREFERENCES_ASSISTANT_SAFESEARCH_FILTER = 0x12131000; - - // TvSettings > Device Preferences > Google Assistant > - // Block offensive words - // (aliasing ACCOUNT_SLICE_REG_ACCOUNT_ASSISTANT_BLOCK_OFFENSIVE) - PREFERENCES_ASSISTANT_BLOCK_OFFENSIVE = 0x12132000; - - // TvSettings > Device Preferences > Google Assistant > Open source licenses - PREFERENCES_ASSISTANT_OPEN_SOURCE = 0x17B40000; - - // TvSettings > Device Preferences > Chromecast Android Shell - PREFERENCES_CHROMECAST_SHELL = 0x17C00000; - - // TvSettings > Device Preferences > Chromecast Android Shell > - // Open source licenses - PREFERENCES_CHROMECAST_SHELL_OPEN_SOURCE = 0x17C10000; - - // TvSettings > Device Preferences > Screen saver - PREFERENCES_SCREENSAVER = 0x17D00000; - - // TvSettings > Device Preferences > Screen saver > Screen saver (chooser) - PREFERENCES_SCREENSAVER_CHOOSER = 0x17D10000; - - // TvSettings > Device Preferences > Screen saver > Screen saver (chooser) > - // Turn screen off - PREFERENCES_SCREENSAVER_CHOOSER_SCREEN_OFF = 0x17D11000; - - // TvSettings > Device Preferences > Screen saver > Screen saver (chooser) > - // Backdrop - PREFERENCES_SCREENSAVER_CHOOSER_BACKDROP = 0x17D12000; - - // TvSettings > Device Preferences > Screen saver > Screen saver (chooser) > - // Colors - PREFERENCES_SCREENSAVER_CHOOSER_COLORS = 0x17D13000; - - // TvSettings > Device Preferences > Screen saver > When to start - PREFERENCES_SCREENSAVER_START_DELAY = 0x17D20000; - - // TvSettings > Device Preferences > Screen saver > When to start > - // 5 minutes - PREFERENCES_SCREENSAVER_START_DELAY_5M = 0x17D21000; - - // TvSettings > Device Preferences > Screen saver > When to start > - // 15 minutes - PREFERENCES_SCREENSAVER_START_DELAY_15M = 0x17D22000; - - // TvSettings > Device Preferences > Screen saver > When to start > - // 30 minutes - PREFERENCES_SCREENSAVER_START_DELAY_30M = 0x17D23000; - - // TvSettings > Device Preferences > Screen saver > When to start > - // 1 hour - PREFERENCES_SCREENSAVER_START_DELAY_1H = 0x17D24000; - - // TvSettings > Device Preferences > Screen saver > When to start > - // 2 hours - PREFERENCES_SCREENSAVER_START_DELAY_2H = 0x17D25000; - - // TvSettings > Device Preferences > Screen saver > Start now - PREFERENCES_SCREENSAVER_START_NOW = 0x17D30000; - - // TvSettings > Connected Devices (Slice) - CONNECTED_SLICE = 0x18000000; - - // TvSettings > Connected Devices (Slice) > Connect remote or headphones - CONNECTED_SLICE_CONNECT_NEW_DEVICES = 0x18100000; - - // TvSettings > Connected Devices (Slice) > [A connected device] - CONNECTED_SLICE_DEVICE_ENTRY = 0x18200000; - - // TvSettings > Connected Devices (Slice) > [A connected device] > - // Remote update - CONNECTED_SLICE_DEVICE_ENTRY_UPDATE = 0x18210000; - - // TvSettings > Connected Devices (Slice) > [A connected device] > Rename - CONNECTED_SLICE_DEVICE_ENTRY_RENAME = 0x18220000; - - // TvSettings > Connected Devices (Slice) > [A connected device] > Forget - CONNECTED_SLICE_DEVICE_ENTRY_FORGET = 0x18230000; - - // TvSettings > Connected Devices (Slice) > HDMI-CEC - CONNECTED_SLICE_HDMICEC = 0x18300000; - - // TvSettings > Connected Devices (Slice) > HDMI-CEC > Enable (toggle) - CONNECTED_SLICE_HDMICEC_ON_OFF = 0x18310000; - - // TvSettings > Connected Devices (aliasing CONNECTED_SLICE) - CONNECTED_CLASSIC = 0x18000000; - - // TvSettings > Connected Devices > Connect remote - // (aliasing CONNECTED_SLICE_CONNECT_NEW_DEVICES) - CONNECTED_CLASSIC_CONNECT_REMOTE = 0x18100000; - - // TvSettings > Connected Devices > [A connected device] - // (aliasing CONNECTED_SLICE_DEVICE_ENTRY) - CONNECTED_CLASSIC_DEVICE_ENTRY = 0x18200000; - - // TvSettings > Connected Devices > [A connected device] > Update - // (aliasing CONNECTED_SLICE_DEVICE_ENTRY_UPDATE) - CONNECTED_CLASSIC_DEVICE_ENTRY_UPDATE = 0x18210000; - - // TvSettings > Connected Devices > [A connected device] > Rename - // (aliasing CONNECTED_SLICE_DEVICE_ENTRY_RENAME) - CONNECTED_CLASSIC_DEVICE_ENTRY_RENAME = 0x18220000; - - // TvSettings > Connected Devices > [A connected device] > Forget - // (aliasing CONNECTED_SLICE_DEVICE_ENTRY_FORGET) - CONNECTED_CLASSIC_DEVICE_ENTRY_FORGET = 0x18230000; - - // TvSettings > Connected Devices > HDMI-CEC - // (aliasing CONNECTED_SLICE_HDMICEC) - CONNECTED_CLASSIC_HDMICEC = 0x18300000; - - // TvSettings > Connected Devices > HDMI-CEC > Enable (toggle) - // (aliasing CONNECTED_SLICE_HDMICEC_ON_OFF) - CONNECTED_CLASSIC_HDMICEC_ON_OFF = 0x18310000; - - // TvSettings > Help & Feedback - FEEDBACK = 0x19000000; - - // TvSettings > Help & Feedback > Send feedback - FEEDBACK_SEND = 0x19100000; -} diff --git a/core/proto/android/bluetooth/enums.proto b/core/proto/android/bluetooth/enums.proto deleted file mode 100644 index dc60ededf965..000000000000 --- a/core/proto/android/bluetooth/enums.proto +++ /dev/null @@ -1,142 +0,0 @@ -/* - * 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. - */ - -syntax = "proto2"; -package android.bluetooth; - -option java_outer_classname = "BluetoothProtoEnums"; -option java_multiple_files = true; - -// Bluetooth connection states. -enum ConnectionStateEnum { - CONNECTION_STATE_DISCONNECTED = 0; - CONNECTION_STATE_CONNECTING = 1; - CONNECTION_STATE_CONNECTED = 2; - CONNECTION_STATE_DISCONNECTING = 3; -} - -// Bluetooth Adapter Enable and Disable Reasons -enum EnableDisableReasonEnum { - ENABLE_DISABLE_REASON_UNSPECIFIED = 0; - ENABLE_DISABLE_REASON_APPLICATION_REQUEST = 1; - ENABLE_DISABLE_REASON_AIRPLANE_MODE = 2; - ENABLE_DISABLE_REASON_DISALLOWED = 3; - ENABLE_DISABLE_REASON_RESTARTED = 4; - ENABLE_DISABLE_REASON_START_ERROR = 5; - ENABLE_DISABLE_REASON_SYSTEM_BOOT = 6; - ENABLE_DISABLE_REASON_CRASH = 7; - ENABLE_DISABLE_REASON_USER_SWITCH = 8; - ENABLE_DISABLE_REASON_RESTORE_USER_SETTING = 9; - ENABLE_DISABLE_REASON_FACTORY_RESET = 10; - ENABLE_DISABLE_REASON_INIT_FLAGS_CHANGED = 11; -} - -enum DirectionEnum { - DIRECTION_UNKNOWN = 0; - DIRECTION_OUTGOING = 1; - DIRECTION_INCOMING = 2; -} - -// First item is the default value, other values follow Bluetooth spec definition -enum LinkTypeEnum { - // Link type is at most 1 byte (0xFF), thus 0xFFF must not be a valid value - LINK_TYPE_UNKNOWN = 0xFFF; - LINK_TYPE_SCO = 0x00; - LINK_TYPE_ACL = 0x01; - LINK_TYPE_ESCO = 0x02; -} - -enum DeviceInfoSrcEnum { - DEVICE_INFO_SRC_UNKNOWN = 0; - // Within Android Bluetooth stack - DEVICE_INFO_INTERNAL = 1; - // Outside Android Bluetooth stack - DEVICE_INFO_EXTERNAL = 2; -} - -enum DeviceTypeEnum { - DEVICE_TYPE_UNKNOWN = 0; - DEVICE_TYPE_CLASSIC = 1; - DEVICE_TYPE_LE = 2; - DEVICE_TYPE_DUAL = 3; -} - -// Defined in frameworks/base/core/java/android/bluetooth/BluetoothDevice.java -enum TransportTypeEnum { - TRANSPORT_TYPE_AUTO = 0; - TRANSPORT_TYPE_BREDR = 1; - TRANSPORT_TYPE_LE = 2; -} - -// Bond state enum -// Defined in frameworks/base/core/java/android/bluetooth/BluetoothDevice.java -enum BondStateEnum { - BOND_STATE_UNKNOWN = 0; - BOND_STATE_NONE = 10; - BOND_STATE_BONDING = 11; - BOND_STATE_BONDED = 12; -} - -// Sub states within the bonding general state -enum BondSubStateEnum { - BOND_SUB_STATE_UNKNOWN = 0; - BOND_SUB_STATE_LOCAL_OOB_DATA_PROVIDED = 1; - BOND_SUB_STATE_LOCAL_PIN_REQUESTED = 2; - BOND_SUB_STATE_LOCAL_PIN_REPLIED = 3; - BOND_SUB_STATE_LOCAL_SSP_REQUESTED = 4; - BOND_SUB_STATE_LOCAL_SSP_REPLIED = 5; -} - -enum UnbondReasonEnum { - UNBOND_REASON_UNKNOWN = 0; - UNBOND_REASON_AUTH_FAILED = 1; - UNBOND_REASON_AUTH_REJECTED = 2; - UNBOND_REASON_AUTH_CANCELED = 3; - UNBOND_REASON_REMOTE_DEVICE_DOWN = 4; - UNBOND_REASON_DISCOVERY_IN_PROGRESS = 5; - UNBOND_REASON_AUTH_TIMEOUT = 6; - UNBOND_REASON_REPEATED_ATTEMPTS = 7; - UNBOND_REASON_REMOTE_AUTH_CANCELED = 8; - UNBOND_REASON_REMOVED = 9; -} - -enum SocketTypeEnum { - SOCKET_TYPE_UNKNOWN = 0; - SOCKET_TYPE_RFCOMM = 1; - SOCKET_TYPE_SCO = 2; - SOCKET_TYPE_L2CAP_BREDR = 3; - SOCKET_TYPE_L2CAP_LE = 4; -} - -enum SocketConnectionstateEnum { - SOCKET_CONNECTION_STATE_UNKNOWN = 0; - // Socket acts as a server waiting for connection - SOCKET_CONNECTION_STATE_LISTENING = 1; - // Socket acts as a client trying to connect - SOCKET_CONNECTION_STATE_CONNECTING = 2; - // Socket is connected - SOCKET_CONNECTION_STATE_CONNECTED = 3; - // Socket tries to disconnect from remote - SOCKET_CONNECTION_STATE_DISCONNECTING = 4; - // This socket is closed - SOCKET_CONNECTION_STATE_DISCONNECTED = 5; -} - -enum SocketRoleEnum { - SOCKET_ROLE_UNKNOWN = 0; - SOCKET_ROLE_LISTEN = 1; - SOCKET_ROLE_CONNECTION = 2; -} diff --git a/core/proto/android/bluetooth/hci/enums.proto b/core/proto/android/bluetooth/hci/enums.proto deleted file mode 100644 index ef894e548351..000000000000 --- a/core/proto/android/bluetooth/hci/enums.proto +++ /dev/null @@ -1,559 +0,0 @@ -/* - * 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. - */ - -syntax = "proto2"; -package android.bluetooth.hci; - -option java_outer_classname = "BluetoothHciProtoEnums"; -option java_multiple_files = true; - -// HCI command opcodes (OCF+OGF) from Bluetooth 5.0 specification Vol 2, Part E, Section 7 -// Original definition: system/bt/stack/include/hcidefs.h -enum CommandEnum { - // Opcode is at most 2 bytes (0xFFFF), thus 0xFFFFF must not be a valid value - CMD_UNKNOWN = 0xFFFFF; - // Link control commands 0x0400 - CMD_INQUIRY = 0x0401; - CMD_INQUIRY_CANCEL = 0x0402; - CMD_PERIODIC_INQUIRY_MODE = 0x0403; - CMD_EXIT_PERIODIC_INQUIRY_MODE = 0x0404; - CMD_CREATE_CONNECTION = 0x0405; - CMD_DISCONNECT = 0x0406; - CMD_ADD_SCO_CONNECTION = 0x0407; // Deprecated since Bluetooth 1.2 - CMD_CREATE_CONNECTION_CANCEL = 0x0408; - CMD_ACCEPT_CONNECTION_REQUEST = 0x0409; - CMD_REJECT_CONNECTION_REQUEST = 0x040A; - CMD_LINK_KEY_REQUEST_REPLY = 0x040B; - CMD_LINK_KEY_REQUEST_NEG_REPLY = 0x040C; - CMD_PIN_CODE_REQUEST_REPLY = 0x040D; - CMD_PIN_CODE_REQUEST_NEG_REPLY = 0x040E; - CMD_CHANGE_CONN_PACKET_TYPE = 0x040F; - CMD_AUTHENTICATION_REQUESTED = 0x0411; - CMD_SET_CONN_ENCRYPTION = 0x0413; - CMD_CHANGE_CONN_LINK_KEY = 0x0415; - CMD_MASTER_LINK_KEY = 0x0417; - CMD_RMT_NAME_REQUEST = 0x0419; - CMD_RMT_NAME_REQUEST_CANCEL = 0x041A; - CMD_READ_RMT_FEATURES = 0x041B; - CMD_READ_RMT_EXT_FEATURES = 0x041C; - CMD_READ_RMT_VERSION_INFO = 0x041D; - CMD_READ_RMT_CLOCK_OFFSET = 0x041F; - CMD_READ_LMP_HANDLE = 0x0420; - CMD_SETUP_ESCO_CONNECTION = 0x0428; - CMD_ACCEPT_ESCO_CONNECTION = 0x0429; - CMD_REJECT_ESCO_CONNECTION = 0x042A; - CMD_IO_CAPABILITY_REQUEST_REPLY = 0x042B; - CMD_USER_CONF_REQUEST_REPLY = 0x042C; - CMD_USER_CONF_VALUE_NEG_REPLY = 0x042D; - CMD_USER_PASSKEY_REQ_REPLY = 0x042E; - CMD_USER_PASSKEY_REQ_NEG_REPLY = 0x042F; - CMD_REM_OOB_DATA_REQ_REPLY = 0x0430; - CMD_REM_OOB_DATA_REQ_NEG_REPLY = 0x0433; - CMD_IO_CAP_REQ_NEG_REPLY = 0x0434; - // BEGIN: AMP commands (not used in system/bt) - CMD_CREATE_PHYSICAL_LINK = 0x0435; - CMD_ACCEPT_PHYSICAL_LINK = 0x0436; - CMD_DISCONNECT_PHYSICAL_LINK = 0x0437; - CMD_CREATE_LOGICAL_LINK = 0x0438; - CMD_ACCEPT_LOGICAL_LINK = 0x0439; - CMD_DISCONNECT_LOGICAL_LINK = 0x043A; - CMD_LOGICAL_LINK_CANCEL = 0x043B; - CMD_FLOW_SPEC_MODIFY = 0x043C; - // END: AMP commands - CMD_ENH_SETUP_ESCO_CONNECTION = 0x043D; - CMD_ENH_ACCEPT_ESCO_CONNECTION = 0x043E; - CMD_TRUNCATED_PAGE = 0x043F; - CMD_TRUNCATED_PAGE_CANCEL = 0x0440; - CMD_SET_CLB = 0x0441; - CMD_RECEIVE_CLB = 0x0442; - CMD_START_SYNC_TRAIN = 0x0443; - CMD_RECEIVE_SYNC_TRAIN = 0x0444; - CMD_REM_OOB_EXTENDED_DATA_REQ_REPLY = 0x0445; // Not currently used in system/bt - // Link policy commands 0x0800 - CMD_HOLD_MODE = 0x0801; - CMD_SNIFF_MODE = 0x0803; - CMD_EXIT_SNIFF_MODE = 0x0804; - CMD_PARK_MODE = 0x0805; - CMD_EXIT_PARK_MODE = 0x0806; - CMD_QOS_SETUP = 0x0807; - CMD_ROLE_DISCOVERY = 0x0809; - CMD_SWITCH_ROLE = 0x080B; - CMD_READ_POLICY_SETTINGS = 0x080C; - CMD_WRITE_POLICY_SETTINGS = 0x080D; - CMD_READ_DEF_POLICY_SETTINGS = 0x080E; - CMD_WRITE_DEF_POLICY_SETTINGS = 0x080F; - CMD_FLOW_SPECIFICATION = 0x0810; - CMD_SNIFF_SUB_RATE = 0x0811; - // Host controller baseband commands 0x0C00 - CMD_SET_EVENT_MASK = 0x0C01; - CMD_RESET = 0x0C03; - CMD_SET_EVENT_FILTER = 0x0C05; - CMD_FLUSH = 0x0C08; - CMD_READ_PIN_TYPE = 0x0C09; - CMD_WRITE_PIN_TYPE = 0x0C0A; - CMD_CREATE_NEW_UNIT_KEY = 0x0C0B; - CMD_GET_MWS_TRANS_LAYER_CFG = 0x0C0C; // Deprecated (not used in spec) - CMD_READ_STORED_LINK_KEY = 0x0C0D; - CMD_WRITE_STORED_LINK_KEY = 0x0C11; - CMD_DELETE_STORED_LINK_KEY = 0x0C12; - CMD_CHANGE_LOCAL_NAME = 0x0C13; - CMD_READ_LOCAL_NAME = 0x0C14; - CMD_READ_CONN_ACCEPT_TOUT = 0x0C15; - CMD_WRITE_CONN_ACCEPT_TOUT = 0x0C16; - CMD_READ_PAGE_TOUT = 0x0C17; - CMD_WRITE_PAGE_TOUT = 0x0C18; - CMD_READ_SCAN_ENABLE = 0x0C19; - CMD_WRITE_SCAN_ENABLE = 0x0C1A; - CMD_READ_PAGESCAN_CFG = 0x0C1B; - CMD_WRITE_PAGESCAN_CFG = 0x0C1C; - CMD_READ_INQUIRYSCAN_CFG = 0x0C1D; - CMD_WRITE_INQUIRYSCAN_CFG = 0x0C1E; - CMD_READ_AUTHENTICATION_ENABLE = 0x0C1F; - CMD_WRITE_AUTHENTICATION_ENABLE = 0x0C20; - CMD_READ_ENCRYPTION_MODE = 0x0C21; // Deprecated - CMD_WRITE_ENCRYPTION_MODE = 0x0C22; // Deprecated - CMD_READ_CLASS_OF_DEVICE = 0x0C23; - CMD_WRITE_CLASS_OF_DEVICE = 0x0C24; - CMD_READ_VOICE_SETTINGS = 0x0C25; - CMD_WRITE_VOICE_SETTINGS = 0x0C26; - CMD_READ_AUTOMATIC_FLUSH_TIMEOUT = 0x0C27; - CMD_WRITE_AUTOMATIC_FLUSH_TIMEOUT = 0x0C28; - CMD_READ_NUM_BCAST_REXMITS = 0x0C29; - CMD_WRITE_NUM_BCAST_REXMITS = 0x0C2A; - CMD_READ_HOLD_MODE_ACTIVITY = 0x0C2B; - CMD_WRITE_HOLD_MODE_ACTIVITY = 0x0C2C; - CMD_READ_TRANSMIT_POWER_LEVEL = 0x0C2D; - CMD_READ_SCO_FLOW_CTRL_ENABLE = 0x0C2E; - CMD_WRITE_SCO_FLOW_CTRL_ENABLE = 0x0C2F; - CMD_SET_HC_TO_HOST_FLOW_CTRL = 0x0C31; - CMD_HOST_BUFFER_SIZE = 0x0C33; - CMD_HOST_NUM_PACKETS_DONE = 0x0C35; - CMD_READ_LINK_SUPER_TOUT = 0x0C36; - CMD_WRITE_LINK_SUPER_TOUT = 0x0C37; - CMD_READ_NUM_SUPPORTED_IAC = 0x0C38; - CMD_READ_CURRENT_IAC_LAP = 0x0C39; - CMD_WRITE_CURRENT_IAC_LAP = 0x0C3A; - CMD_READ_PAGESCAN_PERIOD_MODE = 0x0C3B; // Deprecated - CMD_WRITE_PAGESCAN_PERIOD_MODE = 0x0C3C; // Deprecated - CMD_READ_PAGESCAN_MODE = 0x0C3D; // Deprecated - CMD_WRITE_PAGESCAN_MODE = 0x0C3E; // Deprecated - CMD_SET_AFH_CHANNELS = 0x0C3F; - CMD_READ_INQSCAN_TYPE = 0x0C42; - CMD_WRITE_INQSCAN_TYPE = 0x0C43; - CMD_READ_INQUIRY_MODE = 0x0C44; - CMD_WRITE_INQUIRY_MODE = 0x0C45; - CMD_READ_PAGESCAN_TYPE = 0x0C46; - CMD_WRITE_PAGESCAN_TYPE = 0x0C47; - CMD_READ_AFH_ASSESSMENT_MODE = 0x0C48; - CMD_WRITE_AFH_ASSESSMENT_MODE = 0x0C49; - CMD_READ_EXT_INQ_RESPONSE = 0x0C51; - CMD_WRITE_EXT_INQ_RESPONSE = 0x0C52; - CMD_REFRESH_ENCRYPTION_KEY = 0x0C53; - CMD_READ_SIMPLE_PAIRING_MODE = 0x0C55; - CMD_WRITE_SIMPLE_PAIRING_MODE = 0x0C56; - CMD_READ_LOCAL_OOB_DATA = 0x0C57; - CMD_READ_INQ_TX_POWER_LEVEL = 0x0C58; - CMD_WRITE_INQ_TX_POWER_LEVEL = 0x0C59; - CMD_READ_ERRONEOUS_DATA_RPT = 0x0C5A; - CMD_WRITE_ERRONEOUS_DATA_RPT = 0x0C5B; - CMD_ENHANCED_FLUSH = 0x0C5F; - CMD_SEND_KEYPRESS_NOTIF = 0x0C60; - CMD_READ_LOGICAL_LINK_ACCEPT_TIMEOUT = 0x0C61; - CMD_WRITE_LOGICAL_LINK_ACCEPT_TIMEOUT = 0x0C62; - CMD_SET_EVENT_MASK_PAGE_2 = 0x0C63; - CMD_READ_LOCATION_DATA = 0x0C64; - CMD_WRITE_LOCATION_DATA = 0x0C65; - CMD_READ_FLOW_CONTROL_MODE = 0x0C66; - CMD_WRITE_FLOW_CONTROL_MODE = 0x0C67; - CMD_READ_ENHANCED_TX_PWR_LEVEL = 0x0C68; // Not currently used in system/bt - CMD_READ_BE_FLUSH_TOUT = 0x0C69; - CMD_WRITE_BE_FLUSH_TOUT = 0x0C6A; - CMD_SHORT_RANGE_MODE = 0x0C6B; - CMD_READ_BLE_HOST_SUPPORT = 0x0C6C; - CMD_WRITE_BLE_HOST_SUPPORT = 0x0C6D; - CMD_SET_MWS_CHANNEL_PARAMETERS = 0x0C6E; - CMD_SET_EXTERNAL_FRAME_CONFIGURATION = 0x0C6F; - CMD_SET_MWS_SIGNALING = 0x0C70; - CMD_SET_MWS_TRANSPORT_LAYER = 0x0C71; - CMD_SET_MWS_SCAN_FREQUENCY_TABLE = 0x0C72; - CMD_SET_MWS_PATTERN_CONFIGURATION = 0x0C73; - CMD_SET_RESERVED_LT_ADDR = 0x0C74; - CMD_DELETE_RESERVED_LT_ADDR = 0x0C75; - CMD_WRITE_CLB_DATA = 0x0C76; - CMD_READ_SYNC_TRAIN_PARAM = 0x0C77; - CMD_WRITE_SYNC_TRAIN_PARAM = 0x0C78; - CMD_READ_SECURE_CONNS_SUPPORT = 0x0C79; - CMD_WRITE_SECURE_CONNS_SUPPORT = 0x0C7A; - CMD_READ_AUTHED_PAYLOAD_TIMEOUT = 0x0C7B; // Not currently used in system/bt - CMD_WRITE_AUTHED_PAYLOAD_TIMEOUT = 0x0C7C; // Not currently used in system/bt - CMD_READ_LOCAL_OOB_EXTENDED_DATA = 0x0C7D; // Not currently used in system/bt - CMD_READ_EXTENDED_PAGE_TIMEOUT = 0x0C7E; // Not currently used in system/bt - CMD_WRITE_EXTENDED_PAGE_TIMEOUT = 0x0C7F; // Not currently used in system/bt - CMD_READ_EXTENDED_INQUIRY_LENGTH = 0x0C80; // Not currently used in system/bt - CMD_WRITE_EXTENDED_INQUIRY_LENGTH = 0x0C81; // Not currently used in system/bt - // Informational parameter commands 0x1000 - CMD_READ_LOCAL_VERSION_INFO = 0x1001; - CMD_READ_LOCAL_SUPPORTED_CMDS = 0x1002; - CMD_READ_LOCAL_FEATURES = 0x1003; - CMD_READ_LOCAL_EXT_FEATURES = 0x1004; - CMD_READ_BUFFER_SIZE = 0x1005; - CMD_READ_COUNTRY_CODE = 0x1007; // Deprecated - CMD_READ_BD_ADDR = 0x1009; - CMD_READ_DATA_BLOCK_SIZE = 0x100A; - CMD_READ_LOCAL_SUPPORTED_CODECS = 0x100B; - // Status parameter commands 0x1400 - CMD_READ_FAILED_CONTACT_COUNTER = 0x1401; - CMD_RESET_FAILED_CONTACT_COUNTER = 0x1402; - CMD_GET_LINK_QUALITY = 0x1403; - CMD_READ_RSSI = 0x1405; - CMD_READ_AFH_CH_MAP = 0x1406; - CMD_READ_CLOCK = 0x1407; - CMD_READ_ENCR_KEY_SIZE = 0x1408; - CMD_READ_LOCAL_AMP_INFO = 0x1409; - CMD_READ_LOCAL_AMP_ASSOC = 0x140A; - CMD_WRITE_REMOTE_AMP_ASSOC = 0x140B; - CMD_GET_MWS_TRANSPORT_CFG = 0x140C; // Not currently used in system/bt - CMD_SET_TRIGGERED_CLK_CAPTURE = 0x140D; // Not currently used in system/bt - // Testing commands 0x1800 - CMD_READ_LOOPBACK_MODE = 0x1801; - CMD_WRITE_LOOPBACK_MODE = 0x1802; - CMD_ENABLE_DEV_UNDER_TEST_MODE = 0x1803; - CMD_WRITE_SIMP_PAIR_DEBUG_MODE = 0x1804; - CMD_ENABLE_AMP_RCVR_REPORTS = 0x1807; - CMD_AMP_TEST_END = 0x1808; - CMD_AMP_TEST = 0x1809; - CMD_WRITE_SECURE_CONN_TEST_MODE = 0x180A; // Not currently used in system/bt - // BLE commands 0x2000 - CMD_BLE_SET_EVENT_MASK = 0x2001; - CMD_BLE_READ_BUFFER_SIZE = 0x2002; - CMD_BLE_READ_LOCAL_SPT_FEAT = 0x2003; - CMD_BLE_WRITE_LOCAL_SPT_FEAT = 0x2004; - CMD_BLE_WRITE_RANDOM_ADDR = 0x2005; - CMD_BLE_WRITE_ADV_PARAMS = 0x2006; - CMD_BLE_READ_ADV_CHNL_TX_POWER = 0x2007; - CMD_BLE_WRITE_ADV_DATA = 0x2008; - CMD_BLE_WRITE_SCAN_RSP_DATA = 0x2009; - CMD_BLE_WRITE_ADV_ENABLE = 0x200A; - CMD_BLE_WRITE_SCAN_PARAMS = 0x200B; - CMD_BLE_WRITE_SCAN_ENABLE = 0x200C; - CMD_BLE_CREATE_LL_CONN = 0x200D; - CMD_BLE_CREATE_CONN_CANCEL = 0x200E; - CMD_BLE_READ_WHITE_LIST_SIZE = 0x200F; - CMD_BLE_CLEAR_WHITE_LIST = 0x2010; - CMD_BLE_ADD_WHITE_LIST = 0x2011; - CMD_BLE_REMOVE_WHITE_LIST = 0x2012; - CMD_BLE_UPD_LL_CONN_PARAMS = 0x2013; - CMD_BLE_SET_HOST_CHNL_CLASS = 0x2014; - CMD_BLE_READ_CHNL_MAP = 0x2015; - CMD_BLE_READ_REMOTE_FEAT = 0x2016; - CMD_BLE_ENCRYPT = 0x2017; - CMD_BLE_RAND = 0x2018; - CMD_BLE_START_ENC = 0x2019; - CMD_BLE_LTK_REQ_REPLY = 0x201A; - CMD_BLE_LTK_REQ_NEG_REPLY = 0x201B; - CMD_BLE_READ_SUPPORTED_STATES = 0x201C; - CMD_BLE_RECEIVER_TEST = 0x201D; - CMD_BLE_TRANSMITTER_TEST = 0x201E; - CMD_BLE_TEST_END = 0x201F; - CMD_BLE_RC_PARAM_REQ_REPLY = 0x2020; - CMD_BLE_RC_PARAM_REQ_NEG_REPLY = 0x2021; - CMD_BLE_SET_DATA_LENGTH = 0x2022; - CMD_BLE_READ_DEFAULT_DATA_LENGTH = 0x2023; - CMD_BLE_WRITE_DEFAULT_DATA_LENGTH = 0x2024; - CMD_BLE_GENERATE_DHKEY = 0x2026; // Not currently used in system/bt - CMD_BLE_ADD_DEV_RESOLVING_LIST = 0x2027; - CMD_BLE_RM_DEV_RESOLVING_LIST = 0x2028; - CMD_BLE_CLEAR_RESOLVING_LIST = 0x2029; - CMD_BLE_READ_RESOLVING_LIST_SIZE = 0x202A; - CMD_BLE_READ_RESOLVABLE_ADDR_PEER = 0x202B; - CMD_BLE_READ_RESOLVABLE_ADDR_LOCAL = 0x202C; - CMD_BLE_SET_ADDR_RESOLUTION_ENABLE = 0x202D; - CMD_BLE_SET_RAND_PRIV_ADDR_TIMOUT = 0x202E; - CMD_BLE_READ_MAXIMUM_DATA_LENGTH = 0x202F; - CMD_BLE_READ_PHY = 0x2030; - CMD_BLE_SET_DEFAULT_PHY = 0x2031; - CMD_BLE_SET_PHY = 0x2032; - CMD_BLE_ENH_RECEIVER_TEST = 0x2033; - CMD_BLE_ENH_TRANSMITTER_TEST = 0x2034; - CMD_BLE_SET_EXT_ADVERTISING_RANDOM_ADDRESS = 0x2035; - CMD_BLE_SET_EXT_ADVERTISING_PARAM = 0x2036; - CMD_BLE_SET_EXT_ADVERTISING_DATA = 0x2037; - CMD_BLE_SET_EXT_ADVERTISING_SCAN_RESP = 0x2038; - CMD_BLE_SET_EXT_ADVERTISING_ENABLE = 0x2039; - CMD_BLE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH = 0x203A; - CMD_BLE_READ_NUMBER_OF_SUPPORTED_ADVERTISING_SETS = 0x203B; - CMD_BLE_REMOVE_ADVERTISING_SET = 0x203C; - CMD_BLE_CLEAR_ADVERTISING_SETS = 0x203D; - CMD_BLE_SET_PERIODIC_ADVERTISING_PARAM = 0x203E; - CMD_BLE_SET_PERIODIC_ADVERTISING_DATA = 0x203F; - CMD_BLE_SET_PERIODIC_ADVERTISING_ENABLE = 0x2040; - CMD_BLE_SET_EXTENDED_SCAN_PARAMETERS = 0x2041; - CMD_BLE_SET_EXTENDED_SCAN_ENABLE = 0x2042; - CMD_BLE_EXTENDED_CREATE_CONNECTION = 0x2043; - CMD_BLE_PERIODIC_ADVERTISING_CREATE_SYNC = 0x2044; - CMD_BLE_PERIODIC_ADVERTISING_CREATE_SYNC_CANCEL = 0x2045; - CMD_BLE_PERIODIC_ADVERTISING_TERMINATE_SYNC = 0x2046; - CMD_BLE_ADD_DEVICE_TO_PERIODIC_ADVERTISING_LIST = 0x2047; - CMD_BLE_RM_DEVICE_FROM_PERIODIC_ADVERTISING_LIST = 0x2048; - CMD_BLE_CLEAR_PERIODIC_ADVERTISING_LIST = 0x2049; - CMD_BLE_READ_PERIODIC_ADVERTISING_LIST_SIZE = 0x204A; - CMD_BLE_READ_TRANSMIT_POWER = 0x204B; - CMD_BLE_READ_RF_COMPENS_POWER = 0x204C; - CMD_BLE_WRITE_RF_COMPENS_POWER = 0x204D; - CMD_BLE_SET_PRIVACY_MODE = 0x204E; - // Vendor specific commands 0xFC00 and above - // Android vendor specific commands defined in - // https://source.android.com/devices/bluetooth/hci_requirements#vendor-specific-capabilities - CMD_BLE_VENDOR_CAP = 0xFD53; - CMD_BLE_MULTI_ADV = 0xFD54; - CMD_BLE_BATCH_SCAN = 0xFD56; - CMD_BLE_ADV_FILTER = 0xFD57; - CMD_BLE_TRACK_ADV = 0xFD58; - CMD_BLE_ENERGY_INFO = 0xFD59; - CMD_BLE_EXTENDED_SCAN_PARAMS = 0xFD5A; - CMD_CONTROLLER_DEBUG_INFO = 0xFD5B; - CMD_CONTROLLER_A2DP_OPCODE = 0xFD5D; - CMD_BRCM_SET_ACL_PRIORITY = 0xFC57; - // Other vendor specific commands below here -} - -// HCI event codes from the Bluetooth 5.0 specification Vol 2, Part 7, Section 7 -// Original definition: system/bt/stack/include/hcidefs.h -enum EventEnum { - // Event is at most 1 byte (0xFF), thus 0xFFF must not be a valid value - EVT_UNKNOWN = 0xFFF; - EVT_INQUIRY_COMP = 0x01; - EVT_INQUIRY_RESULT = 0x02; - EVT_CONNECTION_COMP = 0x03; - EVT_CONNECTION_REQUEST = 0x04; - EVT_DISCONNECTION_COMP = 0x05; - EVT_AUTHENTICATION_COMP = 0x06; - EVT_RMT_NAME_REQUEST_COMP = 0x07; - EVT_ENCRYPTION_CHANGE = 0x08; - EVT_CHANGE_CONN_LINK_KEY = 0x09; - EVT_MASTER_LINK_KEY_COMP = 0x0A; - EVT_READ_RMT_FEATURES_COMP = 0x0B; - EVT_READ_RMT_VERSION_COMP = 0x0C; - EVT_QOS_SETUP_COMP = 0x0D; - EVT_COMMAND_COMPLETE = 0x0E; - EVT_COMMAND_STATUS = 0x0F; - EVT_HARDWARE_ERROR = 0x10; - EVT_FLUSH_OCCURRED = 0x11; - EVT_ROLE_CHANGE = 0x12; - EVT_NUM_COMPL_DATA_PKTS = 0x13; - EVT_MODE_CHANGE = 0x14; - EVT_RETURN_LINK_KEYS = 0x15; - EVT_PIN_CODE_REQUEST = 0x16; - EVT_LINK_KEY_REQUEST = 0x17; - EVT_LINK_KEY_NOTIFICATION = 0x18; - EVT_LOOPBACK_COMMAND = 0x19; - EVT_DATA_BUF_OVERFLOW = 0x1A; - EVT_MAX_SLOTS_CHANGED = 0x1B; - EVT_READ_CLOCK_OFF_COMP = 0x1C; - EVT_CONN_PKT_TYPE_CHANGE = 0x1D; - EVT_QOS_VIOLATION = 0x1E; - EVT_PAGE_SCAN_MODE_CHANGE = 0x1F; // Deprecated - EVT_PAGE_SCAN_REP_MODE_CHNG = 0x20; - EVT_FLOW_SPECIFICATION_COMP = 0x21; - EVT_INQUIRY_RSSI_RESULT = 0x22; - EVT_READ_RMT_EXT_FEATURES_COMP = 0x23; - EVT_ESCO_CONNECTION_COMP = 0x2C; - EVT_ESCO_CONNECTION_CHANGED = 0x2D; - EVT_SNIFF_SUB_RATE = 0x2E; - EVT_EXTENDED_INQUIRY_RESULT = 0x2F; - EVT_ENCRYPTION_KEY_REFRESH_COMP = 0x30; - EVT_IO_CAPABILITY_REQUEST = 0x31; - EVT_IO_CAPABILITY_RESPONSE = 0x32; - EVT_USER_CONFIRMATION_REQUEST = 0x33; - EVT_USER_PASSKEY_REQUEST = 0x34; - EVT_REMOTE_OOB_DATA_REQUEST = 0x35; - EVT_SIMPLE_PAIRING_COMPLETE = 0x36; - EVT_LINK_SUPER_TOUT_CHANGED = 0x38; - EVT_ENHANCED_FLUSH_COMPLETE = 0x39; - EVT_USER_PASSKEY_NOTIFY = 0x3B; - EVT_KEYPRESS_NOTIFY = 0x3C; - EVT_RMT_HOST_SUP_FEAT_NOTIFY = 0x3D; - EVT_BLE_META = 0x3E; - EVT_PHYSICAL_LINK_COMP = 0x40; - EVT_CHANNEL_SELECTED = 0x41; - EVT_DISC_PHYSICAL_LINK_COMP = 0x42; - EVT_PHY_LINK_LOSS_EARLY_WARNING = 0x43; - EVT_PHY_LINK_RECOVERY = 0x44; - EVT_LOGICAL_LINK_COMP = 0x45; - EVT_DISC_LOGICAL_LINK_COMP = 0x46; - EVT_FLOW_SPEC_MODIFY_COMP = 0x47; - EVT_NUM_COMPL_DATA_BLOCKS = 0x48; - EVT_AMP_TEST_START = 0x49; // Not currently used in system/bt - EVT_AMP_TEST_END = 0x4A; // Not currently used in system/bt - EVT_AMP_RECEIVER_RPT = 0x4B; // Not currently used in system/bt - EVT_SHORT_RANGE_MODE_COMPLETE = 0x4C; - EVT_AMP_STATUS_CHANGE = 0x4D; - EVT_SET_TRIGGERED_CLOCK_CAPTURE = 0x4E; - EVT_SYNC_TRAIN_CMPL = 0x4F; // Not currently used in system/bt - EVT_SYNC_TRAIN_RCVD = 0x50; // Not currently used in system/bt - EVT_CONNLESS_SLAVE_BROADCAST_RCVD = 0x51; // Not currently used in system/bt - EVT_CONNLESS_SLAVE_BROADCAST_TIMEOUT = 0x52; // Not currently used in system/bt - EVT_TRUNCATED_PAGE_CMPL = 0x53; // Not currently used in system/bt - EVT_SLAVE_PAGE_RES_TIMEOUT = 0x54; // Not currently used in system/bt - EVT_CONNLESS_SLAVE_BROADCAST_CHNL_MAP_CHANGE = 0x55; // Not currently used in system/bt - EVT_INQUIRY_RES_NOTIFICATION = 0x56; // Not currently used in system/bt - EVT_AUTHED_PAYLOAD_TIMEOUT = 0x57; // Not currently used in system/bt - EVT_SAM_STATUS_CHANGE = 0x58; // Not currently used in system/bt -} - -// Bluetooth low energy related meta event codes -// from the Bluetooth 5.0 specification Vol 2, Part E, Section 7.7.65 -// Original definition: system/bt/stack/include/hcidefs.h -enum BleMetaEventEnum { - // BLE meta event code is at most 1 byte (0xFF), thus 0xFFF must not be a valid value - BLE_EVT_UNKNOWN = 0xFFF; - BLE_EVT_CONN_COMPLETE_EVT = 0x01; - BLE_EVT_ADV_PKT_RPT_EVT = 0x02; - BLE_EVT_LL_CONN_PARAM_UPD_EVT = 0x03; - BLE_EVT_READ_REMOTE_FEAT_CMPL_EVT = 0x04; - BLE_EVT_LTK_REQ_EVT = 0x05; - BLE_EVT_RC_PARAM_REQ_EVT = 0x06; - BLE_EVT_DATA_LENGTH_CHANGE_EVT = 0x07; - BLE_EVT_READ_LOCAL_P256_PUB_KEY = 0x08; // Not currently used in system/bt - BLE_EVT_GEN_DHKEY_CMPL = 0x09; // Not currently used in system/bt - BLE_EVT_ENHANCED_CONN_COMPLETE_EVT = 0x0a; - BLE_EVT_DIRECT_ADV_EVT = 0x0b; - BLE_EVT_PHY_UPDATE_COMPLETE_EVT = 0x0c; - BLE_EVT_EXTENDED_ADVERTISING_REPORT_EVT = 0x0D; - BLE_EVT_PERIODIC_ADV_SYNC_EST_EVT = 0x0E; - BLE_EVT_PERIODIC_ADV_REPORT_EVT = 0x0F; - BLE_EVT_PERIODIC_ADV_SYNC_LOST_EVT = 0x10; - BLE_EVT_SCAN_TIMEOUT_EVT = 0x11; - BLE_EVT_ADVERTISING_SET_TERMINATED_EVT = 0x12; - BLE_EVT_SCAN_REQ_RX_EVT = 0x13; - BLE_EVT_CHNL_SELECTION_ALGORITHM = 0x14; // Not currently used in system/bt -} - -// HCI status code from the Bluetooth 5.0 specification Vol 2, Part D. -// Original definition: system/bt/stack/include/hcidefs.h -enum StatusEnum { - // Status is at most 1 byte (0xFF), thus 0xFFF must not be a valid value - STATUS_UNKNOWN = 0xFFF; - STATUS_SUCCESS = 0x00; - STATUS_ILLEGAL_COMMAND = 0x01; - STATUS_NO_CONNECTION = 0x02; - STATUS_HW_FAILURE = 0x03; - STATUS_PAGE_TIMEOUT = 0x04; - STATUS_AUTH_FAILURE = 0x05; - STATUS_KEY_MISSING = 0x06; - STATUS_MEMORY_FULL = 0x07; - STATUS_CONNECTION_TOUT = 0x08; - STATUS_MAX_NUM_OF_CONNECTIONS = 0x09; - STATUS_MAX_NUM_OF_SCOS = 0x0A; - STATUS_CONNECTION_EXISTS = 0x0B; - STATUS_COMMAND_DISALLOWED = 0x0C; - STATUS_HOST_REJECT_RESOURCES = 0x0D; - STATUS_HOST_REJECT_SECURITY = 0x0E; - STATUS_HOST_REJECT_DEVICE = 0x0F; - STATUS_HOST_TIMEOUT = 0x10; - STATUS_UNSUPPORTED_VALUE = 0x11; - STATUS_ILLEGAL_PARAMETER_FMT = 0x12; - STATUS_PEER_USER = 0x13; - STATUS_PEER_LOW_RESOURCES = 0x14; - STATUS_PEER_POWER_OFF = 0x15; - STATUS_CONN_CAUSE_LOCAL_HOST = 0x16; - STATUS_REPEATED_ATTEMPTS = 0x17; - STATUS_PAIRING_NOT_ALLOWED = 0x18; - STATUS_UNKNOWN_LMP_PDU = 0x19; - STATUS_UNSUPPORTED_REM_FEATURE = 0x1A; - STATUS_SCO_OFFSET_REJECTED = 0x1B; - STATUS_SCO_INTERVAL_REJECTED = 0x1C; - STATUS_SCO_AIR_MODE = 0x1D; - STATUS_INVALID_LMP_PARAM = 0x1E; - STATUS_UNSPECIFIED = 0x1F; - STATUS_UNSUPPORTED_LMP_FEATURE = 0x20; - STATUS_ROLE_CHANGE_NOT_ALLOWED = 0x21; - STATUS_LMP_RESPONSE_TIMEOUT = 0x22; - STATUS_LMP_STATUS_TRANS_COLLISION = 0x23; - STATUS_LMP_PDU_NOT_ALLOWED = 0x24; - STATUS_ENCRY_MODE_NOT_ACCEPTABLE = 0x25; - STATUS_UNIT_KEY_USED = 0x26; - STATUS_QOS_NOT_SUPPORTED = 0x27; - STATUS_INSTANT_PASSED = 0x28; - STATUS_PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED = 0x29; - STATUS_DIFF_TRANSACTION_COLLISION = 0x2A; - STATUS_UNDEFINED_0x2B = 0x2B; // Not used - STATUS_QOS_UNACCEPTABLE_PARAM = 0x2C; - STATUS_QOS_REJECTED = 0x2D; - STATUS_CHAN_CLASSIF_NOT_SUPPORTED = 0x2E; - STATUS_INSUFFCIENT_SECURITY = 0x2F; - STATUS_PARAM_OUT_OF_RANGE = 0x30; - STATUS_UNDEFINED_0x31 = 0x31; // Not used - STATUS_ROLE_SWITCH_PENDING = 0x32; - STATUS_UNDEFINED_0x33 = 0x33; - STATUS_RESERVED_SLOT_VIOLATION = 0x34; - STATUS_ROLE_SWITCH_FAILED = 0x35; - STATUS_INQ_RSP_DATA_TOO_LARGE = 0x36; - STATUS_SIMPLE_PAIRING_NOT_SUPPORTED = 0x37; - STATUS_HOST_BUSY_PAIRING = 0x38; - STATUS_REJ_NO_SUITABLE_CHANNEL = 0x39; - STATUS_CONTROLLER_BUSY = 0x3A; - STATUS_UNACCEPT_CONN_INTERVAL = 0x3B; - STATUS_ADVERTISING_TIMEOUT = 0x3C; - STATUS_CONN_TOUT_DUE_TO_MIC_FAILURE = 0x3D; - STATUS_CONN_FAILED_ESTABLISHMENT = 0x3E; - STATUS_MAC_CONNECTION_FAILED = 0x3F; - STATUS_LT_ADDR_ALREADY_IN_USE = 0x40; - STATUS_LT_ADDR_NOT_ALLOCATED = 0x41; - STATUS_CLB_NOT_ENABLED = 0x42; - STATUS_CLB_DATA_TOO_BIG = 0x43; - STATUS_OPERATION_CANCELED_BY_HOST = 0x44; // Not currently used in system/bt -} - -enum BqrIdEnum { - BQR_ID_UNKNOWN = 0x00; - BQR_ID_MONITOR_MODE = 0x01; - BQR_ID_APPROACH_LSTO = 0x02; - BQR_ID_A2DP_AUDIO_CHOPPY = 0x03; - BQR_ID_SCO_VOICE_CHOPPY = 0x04; -} - -enum BqrPacketTypeEnum { - BQR_PACKET_TYPE_UNKNOWN = 0x00; - BQR_PACKET_TYPE_ID = 0x01; - BQR_PACKET_TYPE_NULL = 0x02; - BQR_PACKET_TYPE_POLL = 0x03; - BQR_PACKET_TYPE_FHS = 0x04; - BQR_PACKET_TYPE_HV1 = 0x05; - BQR_PACKET_TYPE_HV2 = 0x06; - BQR_PACKET_TYPE_HV3 = 0x07; - BQR_PACKET_TYPE_DV = 0x08; - BQR_PACKET_TYPE_EV3 = 0x09; - BQR_PACKET_TYPE_EV4 = 0x0A; - BQR_PACKET_TYPE_EV5 = 0x0B; - BQR_PACKET_TYPE_2EV3 = 0x0C; - BQR_PACKET_TYPE_2EV5 = 0x0D; - BQR_PACKET_TYPE_3EV3 = 0x0E; - BQR_PACKET_TYPE_3EV5 = 0x0F; - BQR_PACKET_TYPE_DM1 = 0x10; - BQR_PACKET_TYPE_DH1 = 0x11; - BQR_PACKET_TYPE_DM3 = 0x12; - BQR_PACKET_TYPE_DH3 = 0x13; - BQR_PACKET_TYPE_DM5 = 0x14; - BQR_PACKET_TYPE_DH5 = 0x15; - BQR_PACKET_TYPE_AUX1 = 0x16; - BQR_PACKET_TYPE_2DH1 = 0x17; - BQR_PACKET_TYPE_2DH3 = 0x18; - BQR_PACKET_TYPE_2DH5 = 0x19; - BQR_PACKET_TYPE_3DH1 = 0x1A; - BQR_PACKET_TYPE_3DH3 = 0x1B; - BQR_PACKET_TYPE_3DH5 = 0x1C; -} diff --git a/core/proto/android/bluetooth/smp/enums.proto b/core/proto/android/bluetooth/smp/enums.proto deleted file mode 100644 index c6747b78dc29..000000000000 --- a/core/proto/android/bluetooth/smp/enums.proto +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -syntax = "proto2"; -package android.bluetooth.smp; - -option java_outer_classname = "BluetoothSmpProtoEnums"; -option java_multiple_files = true; - -// SMP Pairing command codes -enum CommandEnum { - CMD_UNKNOWN = 0x00; - CMD_PAIRING_REQUEST = 0x01; - CMD_PAIRING_RESPONSE = 0x02; - CMD_PAIRING_CONFIRM = 0x03; - CMD_PAIRING_RANDOM = 0x04; - CMD_PAIRING_FAILED = 0x05; - CMD_ENCRYPTION_INFON = 0x06; - CMD_MASTER_IDENTIFICATION = 0x07; - CMD_IDENTITY_INFO = 0x08; - CMD_IDENTITY_ADDR_INFO = 0x09; - CMD_SIGNING_INFO = 0x0A; - CMD_SECURITY_REQUEST = 0x0B; - CMD_PAIRING_PUBLIC_KEY = 0x0C; - CMD_PAIRING_DHKEY_CHECK = 0x0D; - CMD_PAIRING_KEYPRESS_INFO = 0x0E; -} - -enum PairingFailReasonEnum { - PAIRING_FAIL_REASON_RESERVED = 0x00; - PAIRING_FAIL_REASON_PASSKEY_ENTRY = 0x01; - PAIRING_FAIL_REASON_OOB = 0x02; - PAIRING_FAIL_REASON_AUTH_REQ = 0x03; - PAIRING_FAIL_REASON_CONFIRM_VALUE = 0x04; - PAIRING_FAIL_REASON_PAIR_NOT_SUPPORT = 0x05; - PAIRING_FAIL_REASON_ENC_KEY_SIZE = 0x06; - PAIRING_FAIL_REASON_INVALID_CMD = 0x07; - PAIRING_FAIL_REASON_UNSPECIFIED = 0x08; - PAIRING_FAIL_REASON_REPEATED_ATTEMPTS = 0x09; - PAIRING_FAIL_REASON_INVALID_PARAMETERS = 0x0A; - PAIRING_FAIL_REASON_DHKEY_CHK = 0x0B; - PAIRING_FAIL_REASON_NUMERIC_COMPARISON = 0x0C; - PAIRING_FAIL_REASON_CLASSIC_PAIRING_IN_PROGR = 0x0D; - PAIRING_FAIL_REASON_XTRANS_DERIVE_NOT_ALLOW = 0x0E; -}
\ No newline at end of file diff --git a/core/proto/android/debug/enums.proto b/core/proto/android/debug/enums.proto deleted file mode 100644 index 6747bb7276b3..000000000000 --- a/core/proto/android/debug/enums.proto +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -syntax = "proto2"; -package android.debug; - -option java_outer_classname = "AdbProtoEnums"; -option java_multiple_files = true; - -/** - * adb connection state used to track adb connection changes in AdbDebuggingManager.java. - */ -enum AdbConnectionStateEnum { - UNKNOWN = 0; - - /** - * The adb connection is waiting for approval from the user. - */ - AWAITING_USER_APPROVAL = 1; - - /** - * The user allowed the adb connection from the system. - */ - USER_ALLOWED = 2; - - /** - * The user denied the adb connection from the system. - */ - USER_DENIED = 3; - - /** - * The adb connection was automatically allowed without user interaction due to the system - * being previously allowed by the user with the 'always allow' option selected, and the adb - * grant has not yet expired. - */ - AUTOMATICALLY_ALLOWED = 4; - - /** - * An empty or invalid base64 encoded key was provided to the framework; the connection was - * automatically denied. - */ - DENIED_INVALID_KEY = 5; - - /** - * vold decrypt has not yet occurred; the connection was automatically denied. - */ - DENIED_VOLD_DECRYPT = 6; - - /** - * The adb session has been disconnected. - */ - DISCONNECTED = 7; -} - diff --git a/core/proto/android/hardware/biometrics/enums.proto b/core/proto/android/hardware/biometrics/enums.proto deleted file mode 100644 index f2e06383b5b1..000000000000 --- a/core/proto/android/hardware/biometrics/enums.proto +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -syntax = "proto2"; - -package android.hardware.biometrics; - -option java_outer_classname = "BiometricsProtoEnums"; -option java_multiple_files = true; - -// Logging constants for <Biometric>Service and BiometricService - -enum ModalityEnum { - MODALITY_UNKNOWN = 0; - MODALITY_FINGERPRINT = 1; // 1 << 0 - MODALITY_IRIS = 2; // 1 << 1 - MODALITY_FACE = 4; // 1 << 2 -} - -enum ClientEnum { - CLIENT_UNKNOWN = 0; - CLIENT_KEYGUARD = 1; - CLIENT_BIOMETRIC_PROMPT = 2; - CLIENT_FINGERPRINT_MANAGER = 3; // Deprecated API before BiometricPrompt was introduced -} - -enum ActionEnum { - ACTION_UNKNOWN = 0; - ACTION_ENROLL = 1; - ACTION_AUTHENTICATE = 2; - ACTION_ENUMERATE = 3; - ACTION_REMOVE = 4; -} - -enum IssueEnum { - ISSUE_UNKNOWN = 0; - // When a biometric HAL has crashed. - ISSUE_HAL_DEATH = 1; - // When Android Framework has a template that doesn't exist in the HAL. The framework - // is expected to remove its template to stay in sync with the HAL. - ISSUE_UNKNOWN_TEMPLATE_ENROLLED_FRAMEWORK = 2; - // When the HAL has a template that doesn't exist in Android Framework. The framework - // is expected to notify the HAL to remove this template to stay in sync with the framework. - ISSUE_UNKNOWN_TEMPLATE_ENROLLED_HAL = 3; - // When the HAL has not sent ERROR_CANCELED within the specified timeout. - ISSUE_CANCEL_TIMED_OUT = 4; -}
\ No newline at end of file diff --git a/core/proto/android/net/networkcapabilities.proto b/core/proto/android/net/networkcapabilities.proto deleted file mode 100644 index be0cad18a24d..000000000000 --- a/core/proto/android/net/networkcapabilities.proto +++ /dev/null @@ -1,133 +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 android.net; - -option java_multiple_files = true; - -import "frameworks/base/core/proto/android/privacy.proto"; - -/** - * An android.net.NetworkCapabilities object. - */ -message NetworkCapabilitiesProto { - option (.android.msg_privacy).dest = DEST_AUTOMATIC; - - enum Transport { - // Indicates this network uses a Cellular transport. - TRANSPORT_CELLULAR = 0; - // Indicates this network uses a Wi-Fi transport. - TRANSPORT_WIFI = 1; - // Indicates this network uses a Bluetooth transport. - TRANSPORT_BLUETOOTH = 2; - // Indicates this network uses an Ethernet transport. - TRANSPORT_ETHERNET = 3; - // Indicates this network uses a VPN transport. - TRANSPORT_VPN = 4; - // Indicates this network uses a Wi-Fi Aware transport. - TRANSPORT_WIFI_AWARE = 5; - // Indicates this network uses a LoWPAN transport. - TRANSPORT_LOWPAN = 6; - } - repeated Transport transports = 1; - - enum NetCapability { - // Indicates this is a network that has the ability to reach the - // carrier's MMSC for sending and receiving MMS messages. - NET_CAPABILITY_MMS = 0; - // Indicates this is a network that has the ability to reach the - // carrier's SUPL server, used to retrieve GPS information. - NET_CAPABILITY_SUPL = 1; - // Indicates this is a network that has the ability to reach the - // carrier's DUN or tethering gateway. - NET_CAPABILITY_DUN = 2; - // Indicates this is a network that has the ability to reach the - // carrier's FOTA portal, used for over the air updates. - NET_CAPABILITY_FOTA = 3; - // Indicates this is a network that has the ability to reach the - // carrier's IMS servers, used for network registration and signaling. - NET_CAPABILITY_IMS = 4; - // Indicates this is a network that has the ability to reach the - // carrier's CBS servers, used for carrier specific services. - NET_CAPABILITY_CBS = 5; - // Indicates this is a network that has the ability to reach a Wi-Fi - // direct peer. - NET_CAPABILITY_WIFI_P2P = 6; - // Indicates this is a network that has the ability to reach a carrier's - // Initial Attach servers. - NET_CAPABILITY_IA = 7; - // Indicates this is a network that has the ability to reach a carrier's - // RCS servers, used for Rich Communication Services. - NET_CAPABILITY_RCS = 8; - // Indicates this is a network that has the ability to reach a carrier's - // XCAP servers, used for configuration and control. - NET_CAPABILITY_XCAP = 9; - // Indicates this is a network that has the ability to reach a carrier's - // Emergency IMS servers or other services, used for network signaling - // during emergency calls. - NET_CAPABILITY_EIMS = 10; - // Indicates that this network is unmetered. - NET_CAPABILITY_NOT_METERED = 11; - // Indicates that this network should be able to reach the internet. - NET_CAPABILITY_INTERNET = 12; - // Indicates that this network is available for general use. If this is - // not set applications should not attempt to communicate on this - // network. Note that this is simply informative and not enforcement - - // enforcement is handled via other means. Set by default. - NET_CAPABILITY_NOT_RESTRICTED = 13; - // Indicates that the user has indicated implicit trust of this network. - // This generally means it's a sim-selected carrier, a plugged in - // ethernet, a paired BT device or a wifi the user asked to connect to. - // Untrusted networks are probably limited to unknown wifi AP. Set by - // default. - NET_CAPABILITY_TRUSTED = 14; - // Indicates that this network is not a VPN. This capability is set by - // default and should be explicitly cleared for VPN networks. - NET_CAPABILITY_NOT_VPN = 15; - // Indicates that connectivity on this network was successfully - // validated. For example, for a network with NET_CAPABILITY_INTERNET, - // it means that Internet connectivity was successfully detected. - NET_CAPABILITY_VALIDATED = 16; - // Indicates that this network was found to have a captive portal in - // place last time it was probed. - NET_CAPABILITY_CAPTIVE_PORTAL = 17; - // Indicates that this network is not roaming. - NET_CAPABILITY_NOT_ROAMING = 18; - // Indicates that this network is available for use by apps, and not a - // network that is being kept up in the background to facilitate fast - // network switching. - NET_CAPABILITY_FOREGROUND = 19; - } - repeated NetCapability capabilities = 2; - - // Passive link bandwidth. This is a rough guide of the expected peak - // bandwidth for the first hop on the given transport. It is not measured, - // but may take into account link parameters (Radio technology, allocated - // channels, etc). - optional int32 link_up_bandwidth_kbps = 3; - optional int32 link_down_bandwidth_kbps = 4; - - optional string network_specifier = 5 [ (.android.privacy).dest = DEST_EXPLICIT ]; - - // True if this object specifies a signal strength. - optional bool can_report_signal_strength = 6; - // This is a signed integer, and higher values indicate better signal. The - // exact units are bearer-dependent. For example, Wi-Fi uses RSSI. - // Only valid if can_report_signal_strength is true. - optional sint32 signal_strength = 7; -} diff --git a/core/proto/android/net/networkrequest.proto b/core/proto/android/net/networkrequest.proto index b35a0203ff02..6794c8cd8acb 100644 --- a/core/proto/android/net/networkrequest.proto +++ b/core/proto/android/net/networkrequest.proto @@ -20,8 +20,8 @@ package android.net; option java_multiple_files = true; -import "frameworks/base/core/proto/android/net/networkcapabilities.proto"; import "frameworks/base/core/proto/android/privacy.proto"; +import "frameworks/proto_logging/stats/enums/net/networkcapabilities.proto"; /** * An android.net.NetworkRequest object. diff --git a/core/proto/android/os/batterystats.proto b/core/proto/android/os/batterystats.proto index 892ebf70ca75..7d68a0df23d5 100644 --- a/core/proto/android/os/batterystats.proto +++ b/core/proto/android/os/batterystats.proto @@ -19,10 +19,10 @@ option java_multiple_files = true; package android.os; -import "frameworks/base/core/proto/android/app/job/enums.proto"; import "frameworks/base/core/proto/android/os/powermanager.proto"; -import "frameworks/base/core/proto/android/telephony/enums.proto"; import "frameworks/base/core/proto/android/privacy.proto"; +import "frameworks/proto_logging/stats/enums/app/job/enums.proto"; +import "frameworks/proto_logging/stats/enums/telephony/enums.proto"; message BatteryStatsProto { option (android.msg_privacy).dest = DEST_AUTOMATIC; diff --git a/core/proto/android/os/enums.proto b/core/proto/android/os/enums.proto deleted file mode 100644 index 566861b6e836..000000000000 --- a/core/proto/android/os/enums.proto +++ /dev/null @@ -1,169 +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. - */ - -syntax = "proto2"; -package android.os; - -option java_outer_classname = "OsProtoEnums"; -option java_multiple_files = true; - -// These constants are defined in hardware/interfaces/health/1.0/types.hal -// They are primarily used by android/os/BatteryManager.java. -enum BatteryHealthEnum { - BATTERY_HEALTH_INVALID = 0; - BATTERY_HEALTH_UNKNOWN = 1; - BATTERY_HEALTH_GOOD = 2; - BATTERY_HEALTH_OVERHEAT = 3; - BATTERY_HEALTH_DEAD = 4; - BATTERY_HEALTH_OVER_VOLTAGE = 5; - BATTERY_HEALTH_UNSPECIFIED_FAILURE = 6; - BATTERY_HEALTH_COLD = 7; -} - -// Plug states, primarily used by android/os/BatteryManager.java. -enum BatteryPluggedStateEnum { - // Note that NONE is not in BatteryManager.java's constants. - BATTERY_PLUGGED_NONE = 0; - // Power source is an AC charger. - BATTERY_PLUGGED_AC = 1; - // Power source is a USB port. - BATTERY_PLUGGED_USB = 2; - // Power source is wireless. - BATTERY_PLUGGED_WIRELESS = 4; -} - -// These constants are defined in hardware/interfaces/health/1.0/types.hal -// They are primarily used by android/os/BatteryManager.java. -enum BatteryStatusEnum { - BATTERY_STATUS_INVALID = 0; - BATTERY_STATUS_UNKNOWN = 1; - BATTERY_STATUS_CHARGING = 2; - BATTERY_STATUS_DISCHARGING = 3; - BATTERY_STATUS_NOT_CHARGING = 4; - BATTERY_STATUS_FULL = 5; -} - -// These constants are defined in hardware/interfaces/thermal/1.0/types.hal -// and in hardware/interfaces/thermal/2.0/types.hal -// They are primarily used by android/os/HardwarePropertiesManager.java. -// Any change to the types in the thermal hal should be made here as well. -enum TemperatureTypeEnum { - TEMPERATURE_TYPE_UNKNOWN = -1; - TEMPERATURE_TYPE_CPU = 0; - TEMPERATURE_TYPE_GPU = 1; - TEMPERATURE_TYPE_BATTERY = 2; - TEMPERATURE_TYPE_SKIN = 3; - TEMPERATURE_TYPE_USB_PORT = 4; - TEMPERATURE_TYPE_POWER_AMPLIFIER = 5; - - // Battery Charge Limit - virtual thermal sensors. - TEMPERATURE_TYPE_BCL_VOLTAGE = 6; - TEMPERATURE_TYPE_BCL_CURRENT = 7; - TEMPERATURE_TYPE_BCL_PERCENTAGE = 8; - - // Neural Processing Unit. - TEMPERATURE_TYPE_NPU = 9; -} - -// Device throttling severity -// These constants are defined in hardware/interfaces/thermal/2.0/types.hal. -// Any change to the types in the thermal hal should be made here as well. -enum ThrottlingSeverityEnum { - // Not under throttling. - NONE = 0; - // Light throttling where UX is not impacted. - LIGHT = 1; - // Moderate throttling where UX is not largely impacted. - MODERATE = 2; - // Severe throttling where UX is largely impacted. - // Similar to 1.0 throttlingThreshold. - SEVERE = 3; - // Platform has done everything to reduce power. - CRITICAL = 4; - // Key components in platform are shutting down due to thermal condition. - // Device functionalities will be limited. - EMERGENCY = 5; - // Need shutdown immediately. - SHUTDOWN = 6; -}; - -// Device cooling device types. -// These constants are defined in hardware/interfaces/thermal/2.0/types.hal. -// Any change to the types in the thermal hal should be made here as well. -enum CoolingTypeEnum { - FAN = 0; - BATTERY = 1; - CPU = 2; - GPU = 3; - MODEM = 4; - NPU = 5; - COMPONENT = 6; -}; - -// Wakelock types, primarily used by android/os/PowerManager.java. -enum WakeLockLevelEnum { - // NOTE: Wake lock levels were previously defined as a bit field, except - // that only a few combinations were actually supported so the bit field - // was removed. This explains why the numbering scheme is so odd. If - // adding a new wake lock level, any unused value can be used. - - // Ensures that the CPU is running; the screen and keyboard backlight - // will be allowed to go off. - PARTIAL_WAKE_LOCK = 1; - - // Ensures that the screen is on (but may be dimmed); the keyboard - // backlight will be allowed to go off. If the user presses the power - // button, then the SCREEN_DIM_WAKE_LOCK will be implicitly released by - // the system, causing both the screen and the CPU to be turned off. - SCREEN_DIM_WAKE_LOCK = 6 [deprecated = true]; - - // Ensures that the screen is on at full brightness; the keyboard - // backlight will be allowed to go off. If the user presses the power - // button, then the SCREEN_BRIGHT_WAKE_LOCK will be implicitly released - // by the system, causing both the screen and the CPU to be turned off. - SCREEN_BRIGHT_WAKE_LOCK = 10 [deprecated = true]; - - // Ensures that the screen and keyboard backlight are on at full - // brightness. If the user presses the power button, then the - // FULL_WAKE_LOCK will be implicitly released by the system, causing - // both the screen and the CPU to be turned off. - FULL_WAKE_LOCK = 26 [deprecated = true]; - - // Turns the screen off when the proximity sensor activates. If the - // proximity sensor detects that an object is nearby, the screen turns - // off immediately. Shortly after the object moves away, the screen - // turns on again. - // A proximity wake lock does not prevent the device from falling asleep - // unlike FULL_WAKE_LOCK, SCREEN_BRIGHT_WAKE_LOCK and - // SCREEN_DIM_WAKE_LOCK. If there is no user activity and no other wake - // locks are held, then the device will fall asleep (and lock) as usual. - // However, the device will not fall asleep while the screen has been - // turned off by the proximity sensor because it effectively counts as - // ongoing user activity. - PROXIMITY_SCREEN_OFF_WAKE_LOCK = 32; - - // Put the screen in a low power state and allow the CPU to suspend if - // no other wake locks are held. This is used by the dream manager to - // implement doze mode. It currently has no effect unless the power - // manager is in the dozing state. - DOZE_WAKE_LOCK = 64; - - // Keep the device awake enough to allow drawing to occur. This is used - // by the window manager to allow applications to draw while the system - // is dozing. It currently has no effect unless the power manager is in - // the dozing state. - DRAW_WAKE_LOCK = 128; -} diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto index fe65bda365af..8de30f8547a7 100644 --- a/core/proto/android/os/incident.proto +++ b/core/proto/android/os/incident.proto @@ -56,13 +56,13 @@ import "frameworks/base/core/proto/android/service/print.proto"; import "frameworks/base/core/proto/android/service/procstats.proto"; import "frameworks/base/core/proto/android/service/restricted_image.proto"; import "frameworks/base/core/proto/android/service/sensor_service.proto"; -import "frameworks/base/core/proto/android/service/usb.proto"; import "frameworks/base/core/proto/android/util/event_log_tags.proto"; import "frameworks/base/core/proto/android/util/log.proto"; import "frameworks/base/core/proto/android/util/textdump.proto"; import "frameworks/base/core/proto/android/privacy.proto"; import "frameworks/base/core/proto/android/section.proto"; import "frameworks/base/proto/src/ipconnectivity.proto"; +import "frameworks/proto_logging/stats/enums/service/usb.proto"; package android.os; diff --git a/core/proto/android/bluetooth/hfp/enums.proto b/core/proto/android/server/accessibility.proto index d286e4b64d67..7fe7f0d5f3cc 100644 --- a/core/proto/android/bluetooth/hfp/enums.proto +++ b/core/proto/android/server/accessibility.proto @@ -1,5 +1,5 @@ /* - * Copyright 2018 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,14 +15,13 @@ */ syntax = "proto2"; -package android.bluetooth.hfp; -option java_outer_classname = "BluetoothHfpProtoEnums"; +import "frameworks/base/core/proto/android/typedef.proto"; + +package com.android.server.accessibility; + option java_multiple_files = true; -enum ScoCodec { - SCO_CODEC_UNKNOWN = 0; - SCO_CODEC_CVSD = 1; - // Default codec behind Wide Band Speech - SCO_CODEC_MSBC = 2; -}
\ No newline at end of file +/* The proto format trace entry for accessibility service */ +message AccessibilityDumpProto { +} diff --git a/core/proto/android/server/accessibilitytrace.proto b/core/proto/android/server/accessibilitytrace.proto new file mode 100644 index 000000000000..1fc4a01936b1 --- /dev/null +++ b/core/proto/android/server/accessibilitytrace.proto @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; + +import "frameworks/base/core/proto/android/server/accessibility.proto"; +import "frameworks/base/core/proto/android/server/windowmanagerservice.proto"; + +package com.android.server.accessibility; + +option java_multiple_files = true; + +/* represents a file full of accessibility trace entries. + Encoded, it should start with 0x9 0x41 0x31 0x31 0x59 0x54 0x52 0x41 0x43 (.A11YTRAC), such + that they can be easily identified. */ +message AccessibilityTraceFileProto { + + /* constant; MAGIC_NUMBER = (long) MAGIC_NUMBER_H << 32 | MagicNumber.MAGIC_NUMBER_L + (this is needed because enums have to be 32 bits and there's no nice way to put 64bit + constants into .proto files. */ + enum MagicNumber { + INVALID = 0; + MAGIC_NUMBER_L = 0x59313141; /* A11Y (little-endian ASCII) */ + MAGIC_NUMBER_H = 0x43415254; /* TRAC (little-endian ASCII) */ + } + + optional fixed64 magic_number = 1; /* Must be the first field, set to value in MagicNumber */ + repeated AccessibilityTraceProto entry = 2; +} + +/* one accessibility trace entry. */ +message AccessibilityTraceProto { + /* required: elapsed realtime in nanos since boot of when this entry was logged */ + optional fixed64 elapsed_realtime_nanos = 1; + optional string calendar_time = 2; + + optional string process_name = 3; + optional string thread_id_name = 4; + + /* where the trace originated */ + optional string where = 5; + + optional string calling_pkg = 6; + optional string calling_params = 7; + optional string calling_stacks = 8; + + optional AccessibilityDumpProto accessibility_service = 9; + optional com.android.server.wm.WindowManagerServiceDumpProto window_manager_service = 10; +} diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto index 2d2ead455a4d..fa046c6593af 100644 --- a/core/proto/android/server/activitymanagerservice.proto +++ b/core/proto/android/server/activitymanagerservice.proto @@ -20,7 +20,6 @@ package com.android.server.am; import "frameworks/base/core/proto/android/app/activitymanager.proto"; import "frameworks/base/core/proto/android/app/appexitinfo.proto"; -import "frameworks/base/core/proto/android/app/enums.proto"; import "frameworks/base/core/proto/android/app/notification.proto"; import "frameworks/base/core/proto/android/app/profilerinfo.proto"; import "frameworks/base/core/proto/android/content/component_name.proto"; @@ -35,6 +34,7 @@ import "frameworks/base/core/proto/android/server/intentresolver.proto"; import "frameworks/base/core/proto/android/server/windowmanagerservice.proto"; import "frameworks/base/core/proto/android/util/common.proto"; import "frameworks/base/core/proto/android/privacy.proto"; +import "frameworks/proto_logging/stats/enums/app/enums.proto"; option java_multiple_files = true; diff --git a/core/proto/android/server/bluetooth_manager_service.proto b/core/proto/android/server/bluetooth_manager_service.proto index 998413f05ebe..c33f66a9aeca 100644 --- a/core/proto/android/server/bluetooth_manager_service.proto +++ b/core/proto/android/server/bluetooth_manager_service.proto @@ -17,8 +17,8 @@ syntax = "proto2"; package com.android.server; -import "frameworks/base/core/proto/android/bluetooth/enums.proto"; import "frameworks/base/core/proto/android/privacy.proto"; +import "frameworks/proto_logging/stats/enums/bluetooth/enums.proto"; option java_multiple_files = true; diff --git a/core/proto/android/server/connectivity/Android.bp b/core/proto/android/server/connectivity/Android.bp deleted file mode 100644 index 50c238b96307..000000000000 --- a/core/proto/android/server/connectivity/Android.bp +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (C) 2019 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -java_library_static { - name: "datastallprotosnano", - proto: { - type: "nano", - }, - srcs: [ - "data_stall_event.proto", - ], - sdk_version: "system_current", - // this is part of updatable modules(NetworkStack) which targets 29(Q) - min_sdk_version: "29", -} diff --git a/core/proto/android/server/connectivity/data_stall_event.proto b/core/proto/android/server/connectivity/data_stall_event.proto deleted file mode 100644 index 787074ba494e..000000000000 --- a/core/proto/android/server/connectivity/data_stall_event.proto +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -syntax = "proto2"; - -package com.android.server.connectivity; -option java_multiple_files = true; -option java_outer_classname = "DataStallEventProto"; - -enum ProbeResult { - UNKNOWN = 0; - VALID = 1; - INVALID = 2; - PORTAL = 3; - PARTIAL = 4; -} - -enum ApBand { - AP_BAND_UNKNOWN = 0; - AP_BAND_2GHZ = 1; - AP_BAND_5GHZ = 2; - AP_BAND_6GHZ = 3; -} - -// Refer to definition in TelephonyManager.java. -enum RadioTech { - RADIO_TECHNOLOGY_UNKNOWN = 0; - RADIO_TECHNOLOGY_GPRS = 1; - RADIO_TECHNOLOGY_EDGE = 2; - RADIO_TECHNOLOGY_UMTS = 3; - RADIO_TECHNOLOGY_IS95A = 4; - RADIO_TECHNOLOGY_IS95B = 5; - RADIO_TECHNOLOGY_1XRTT = 6; - RADIO_TECHNOLOGY_EVDO_0 = 7; - RADIO_TECHNOLOGY_EVDO_A = 8; - RADIO_TECHNOLOGY_HSDPA = 9; - RADIO_TECHNOLOGY_HSUPA = 10; - RADIO_TECHNOLOGY_HSPA = 11; - RADIO_TECHNOLOGY_EVDO_B = 12; - RADIO_TECHNOLOGY_LTE = 13; - RADIO_TECHNOLOGY_EHRPD = 14; - RADIO_TECHNOLOGY_HSPAP = 15; - RADIO_TECHNOLOGY_GSM = 16; - RADIO_TECHNOLOGY_TD_SCDMA = 17; - RADIO_TECHNOLOGY_IWLAN = 18; - RADIO_TECHNOLOGY_LTE_CA = 19; - RADIO_TECHNOLOGY_NR = 20; -} - -// Cellular specific information. -message CellularData { - // Indicate the radio technology at the time of data stall suspected. - optional RadioTech rat_type = 1; - // True if device is in roaming network at the time of data stall suspected. - optional bool is_roaming = 2; - // Registered network MccMnc when data stall happen - optional string network_mccmnc = 3; - // Indicate the SIM card carrier. - optional string sim_mccmnc = 4; - // Signal strength level at the time of data stall suspected. - optional int32 signal_strength = 5; -} - -// Wifi specific information. -message WifiData { - // Signal strength at the time of data stall suspected. - // RSSI range is between -55 to -110. - optional int32 signal_strength = 1; - // AP band. - optional ApBand wifi_band = 2; -} - -message DnsEvent { - // The dns return code. - repeated int32 dns_return_code = 1; - // Indicate the timestamp of the dns event. - repeated int64 dns_time = 2; -} diff --git a/core/proto/android/server/enums.proto b/core/proto/android/server/enums.proto deleted file mode 100644 index 89f7010e8d81..000000000000 --- a/core/proto/android/server/enums.proto +++ /dev/null @@ -1,40 +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. - */ - -syntax = "proto2"; -package android.server; - -option java_outer_classname = "ServerProtoEnums"; -option java_multiple_files = true; - -enum DeviceIdleModeEnum { - // Device idle mode - not active. - DEVICE_IDLE_MODE_OFF = 0; - // Device idle mode - active in lightweight mode. - DEVICE_IDLE_MODE_LIGHT = 1; - // Device idle mode - active in full mode. - DEVICE_IDLE_MODE_DEEP = 2; -} - -enum ErrorSource { - ERROR_SOURCE_UNKNOWN = 0; - // Data app - DATA_APP = 1; - // System app - SYSTEM_APP = 2; - // System server. - SYSTEM_SERVER = 3; -} diff --git a/core/proto/android/server/job/enums.proto b/core/proto/android/server/job/enums.proto deleted file mode 100644 index 50fc0310ad99..000000000000 --- a/core/proto/android/server/job/enums.proto +++ /dev/null @@ -1,43 +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. - */ - -syntax = "proto2"; - -package com.android.server.job; - -// This file is for JobScheduler enums inside the server directory. If you're -// adding enums for app-side code, use the file in -// frameworks/base/core/proto/android/app/job. -option java_outer_classname = "JobServerProtoEnums"; -option java_multiple_files = true; - -// Set of constraints that a job potentially needs satisfied before it can run. -// Defined in -// frameworks/base/services/core/java/com/android/server/job/controllers/JobStatus.java -enum ConstraintEnum { - CONSTRAINT_UNKNOWN = 0; - CONSTRAINT_CHARGING = 1; - CONSTRAINT_BATTERY_NOT_LOW = 2; - CONSTRAINT_STORAGE_NOT_LOW = 3; - CONSTRAINT_TIMING_DELAY = 4; - CONSTRAINT_DEADLINE = 5; - CONSTRAINT_IDLE = 6; - CONSTRAINT_CONNECTIVITY = 7; - CONSTRAINT_CONTENT_TRIGGER = 8; - CONSTRAINT_DEVICE_NOT_DOZING = 9; - CONSTRAINT_WITHIN_QUOTA = 10; - CONSTRAINT_BACKGROUND_NOT_RESTRICTED = 11; -} diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto index 0e2bd2605836..d18722049109 100644 --- a/core/proto/android/server/jobscheduler.proto +++ b/core/proto/android/server/jobscheduler.proto @@ -20,7 +20,6 @@ package com.android.server.job; option java_multiple_files = true; -import "frameworks/base/core/proto/android/app/job/enums.proto"; import "frameworks/base/core/proto/android/content/clipdata.proto"; import "frameworks/base/core/proto/android/content/component_name.proto"; import "frameworks/base/core/proto/android/content/intent.proto"; @@ -29,10 +28,11 @@ import "frameworks/base/core/proto/android/net/networkrequest.proto"; import "frameworks/base/core/proto/android/os/bundle.proto"; import "frameworks/base/core/proto/android/os/persistablebundle.proto"; import "frameworks/base/core/proto/android/server/appstatetracker.proto"; -import "frameworks/base/core/proto/android/server/job/enums.proto"; import "frameworks/base/core/proto/android/server/statlogger.proto"; import "frameworks/base/core/proto/android/privacy.proto"; import "frameworks/base/core/proto/android/util/quotatracker.proto"; +import "frameworks/proto_logging/stats/enums/app/job/enums.proto"; +import "frameworks/proto_logging/stats/enums/server/job/enums.proto"; message JobSchedulerServiceDumpProto { option (.android.msg_privacy).dest = DEST_AUTOMATIC; diff --git a/core/proto/android/server/location/enums.proto b/core/proto/android/server/location/enums.proto deleted file mode 100644 index 943ff181fe14..000000000000 --- a/core/proto/android/server/location/enums.proto +++ /dev/null @@ -1,132 +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. - */ - -syntax = "proto2"; - -package android.server.location; - -option java_outer_classname = "ServerLocationProtoEnums"; -option java_multiple_files = true; - -// GPS Signal Quality levels, -// primarily used by location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java -enum GpsSignalQualityEnum { - GPS_SIGNAL_QUALITY_UNKNOWN = -1; - GPS_SIGNAL_QUALITY_POOR = 0; - GPS_SIGNAL_QUALITY_GOOD = 1; -} - -// A type which distinguishes different categories of NI request, such as VOICE, UMTS_SUPL etc. -enum GnssNiType { - VOICE = 1; - UMTS_SUPL = 2; - UMTS_CTRL_PLANE = 3; - EMERGENCY_SUPL = 4; -}; - -// GNSS NI responses, used to define the response in NI structures. -enum GnssUserResponseType { - RESPONSE_ACCEPT = 1; - RESPONSE_DENY = 2; - RESPONSE_NORESP = 3; -}; - -// GNSS NI data encoding scheme. -enum GnssNiEncodingType { - ENC_NONE = 0; - ENC_SUPL_GSM_DEFAULT = 1; - ENC_SUPL_UTF8 = 2; - ENC_SUPL_UCS2 = 3; - ENC_UNKNOWN = -1; -}; - -// Protocol stack that initiated the non-framework location request. -enum NfwProtocolStack { - // Cellular control plane requests. - CTRL_PLANE = 0; - // All types of SUPL requests. - SUPL = 1; - // All types of requests from IMS. - IMS = 10; - // All types of requests from SIM. - SIM = 11; - // Requests from other protocol stacks. - OTHER_PROTOCOL_STACK = 100; -}; - -// Source initiating/receiving the location information. -enum NfwRequestor { - // Wireless service provider. - CARRIER = 0; - // Device manufacturer. - OEM = 10; - // Modem chipset vendor. - MODEM_CHIPSET_VENDOR = 11; - // GNSS chipset vendor. - GNSS_CHIPSET_VENDOR = 12; - // Other chipset vendor. - OTHER_CHIPSET_VENDOR = 13; - // Automobile client. - AUTOMOBILE_CLIENT = 20; - // Other sources. - OTHER_REQUESTOR = 100; -}; - -// Indicates whether location information was provided for this request. -enum NfwResponseType { - // Request rejected because framework has not given permission for this use case. - REJECTED = 0; - // Request accepted but could not provide location because of a failure. - ACCEPTED_NO_LOCATION_PROVIDED = 1; - // Request accepted and location provided. - ACCEPTED_LOCATION_PROVIDED = 2; -}; - -// The SUPL mode. -enum SuplMode { - // Mobile Station Based. - MSB = 0x01; - // Mobile Station Assisted. - MSA = 0x02; -}; - -// Enum that hold the bit masks for various LTE Positioning Profile settings (LPP_PROFILE -// configuration parameter). If none of the bits in the enum are set, the default setting is -// Radio Resource Location Protocol(RRLP). -enum LppProfile { - // Enable LTE Positioning Protocol user plane. - USER_PLANE = 0x01; - // Enable LTE Positioning Protocol Control plane. - CONTROL_PLANE = 0x02; -}; - -// Positioning protocol on A-Glonass system. -enum GlonassPosProtocol { - // Radio Resource Control(RRC) control-plane. - RRC_CPLANE = 0x01; - // Radio Resource Location user-plane. - RRLP_CPLANE = 0x02; - // LTE Positioning Protocol User plane. - LPP_UPLANE = 0x04; -}; - -// Configurations of how GPS functionalities should be locked when user turns off GPS On setting. -enum GpsLock { - // Lock Mobile Originated GPS functionalitues. - MO = 0x01; - // Lock Network Initiated GPS functionalities. - NI = 0x02; -}; diff --git a/core/proto/android/server/powermanagerservice.proto b/core/proto/android/server/powermanagerservice.proto index a2f2c46cba6a..0d23946ebbb8 100644 --- a/core/proto/android/server/powermanagerservice.proto +++ b/core/proto/android/server/powermanagerservice.proto @@ -19,16 +19,16 @@ package com.android.server.power; option java_multiple_files = true; -import "frameworks/base/core/proto/android/app/enums.proto"; import "frameworks/base/core/proto/android/content/intent.proto"; -import "frameworks/base/core/proto/android/os/enums.proto"; import "frameworks/base/core/proto/android/os/looper.proto"; import "frameworks/base/core/proto/android/os/powermanager.proto"; import "frameworks/base/core/proto/android/os/worksource.proto"; import "frameworks/base/core/proto/android/providers/settings.proto"; import "frameworks/base/core/proto/android/server/wirelesschargerdetector.proto"; -import "frameworks/base/core/proto/android/view/enums.proto"; import "frameworks/base/core/proto/android/privacy.proto"; +import "frameworks/proto_logging/stats/enums/app/enums.proto"; +import "frameworks/proto_logging/stats/enums/os/enums.proto"; +import "frameworks/proto_logging/stats/enums/view/enums.proto"; message PowerManagerServiceDumpProto { option (.android.msg_privacy).dest = DEST_AUTOMATIC; diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto index 0453d3f3763d..c4c007d8113b 100644 --- a/core/proto/android/server/windowmanagerservice.proto +++ b/core/proto/android/server/windowmanagerservice.proto @@ -24,7 +24,6 @@ import "frameworks/base/core/proto/android/server/windowcontainerthumbnail.proto import "frameworks/base/core/proto/android/server/surfaceanimator.proto"; import "frameworks/base/core/proto/android/view/displaycutout.proto"; import "frameworks/base/core/proto/android/view/displayinfo.proto"; -import "frameworks/base/core/proto/android/view/enums.proto"; import "frameworks/base/core/proto/android/view/surface.proto"; import "frameworks/base/core/proto/android/view/windowlayoutparams.proto"; import "frameworks/base/core/proto/android/privacy.proto"; @@ -34,6 +33,8 @@ import "frameworks/base/core/proto/android/view/surfacecontrol.proto"; import "frameworks/base/core/proto/android/view/insetssource.proto"; import "frameworks/base/core/proto/android/view/insetssourcecontrol.proto"; +import "frameworks/proto_logging/stats/enums/view/enums.proto"; + package com.android.server.wm; option java_multiple_files = true; @@ -206,9 +207,10 @@ message DisplayContentProto { optional WindowStateProto input_method_control_target = 29; optional WindowStateProto current_focus = 30; optional ImeInsetsSourceProviderProto ime_insets_source_provider = 31; - optional bool can_show_ime = 32; + optional bool can_show_ime = 32 [deprecated=true]; optional DisplayRotationProto display_rotation = 33; + optional int32 ime_policy = 34; } /* represents DisplayArea object */ diff --git a/core/proto/android/service/battery.proto b/core/proto/android/service/battery.proto index 586411f8ad96..3a112e7c85f0 100644 --- a/core/proto/android/service/battery.proto +++ b/core/proto/android/service/battery.proto @@ -20,8 +20,8 @@ package android.service.battery; option java_multiple_files = true; option java_outer_classname = "BatteryServiceProto"; -import "frameworks/base/core/proto/android/os/enums.proto"; import "frameworks/base/core/proto/android/privacy.proto"; +import "frameworks/proto_logging/stats/enums/os/enums.proto"; message BatteryServiceDumpProto { option (android.msg_privacy).dest = DEST_AUTOMATIC; diff --git a/core/proto/android/service/procstats.proto b/core/proto/android/service/procstats.proto index 7a4c0706e119..57051f07d124 100644 --- a/core/proto/android/service/procstats.proto +++ b/core/proto/android/service/procstats.proto @@ -21,8 +21,8 @@ option java_multiple_files = true; option java_outer_classname = "ProcessStatsServiceProto"; import "frameworks/base/core/proto/android/util/common.proto"; -import "frameworks/base/core/proto/android/service/procstats_enum.proto"; import "frameworks/base/core/proto/android/privacy.proto"; +import "frameworks/proto_logging/stats/enums/service/procstats_enum.proto"; /** * Data from ProcStatsService Dumpsys diff --git a/core/proto/android/service/procstats_enum.proto b/core/proto/android/service/procstats_enum.proto deleted file mode 100644 index 2abf3730aa9f..000000000000 --- a/core/proto/android/service/procstats_enum.proto +++ /dev/null @@ -1,102 +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. - */ - -syntax = "proto2"; -package android.service.procstats; - -option java_multiple_files = true; -option java_outer_classname = "ProcessStatsEnums"; - -enum ScreenState { - SCREEN_STATE_UNKNOWN = 0; - SCREEN_STATE_OFF = 1; - SCREEN_STATE_ON = 2; -} - -enum MemoryState { - MEMORY_STATE_UNKNOWN = 0; - MEMORY_STATE_NORMAL = 1; // normal. - MEMORY_STATE_MODERATE = 2; // moderate memory pressure. - MEMORY_STATE_LOW = 3; // low memory. - MEMORY_STATE_CRITICAL = 4; // critical memory. -} - -// this enum list is from frameworks/base/core/java/com/android/internal/app/procstats/ProcessStats.java -// and not frameworks/base/core/java/android/app/ActivityManager.java -enum ProcessState { - PROCESS_STATE_UNKNOWN = 0; - // Persistent system process. - PROCESS_STATE_PERSISTENT = 1; - // Top activity; actually any visible activity. - PROCESS_STATE_TOP = 2; - // Important foreground process (ime, wallpaper, etc). - PROCESS_STATE_IMPORTANT_FOREGROUND = 3; - // Important background process. - PROCESS_STATE_IMPORTANT_BACKGROUND = 4; - // Performing backup operation. - PROCESS_STATE_BACKUP = 5; - // Background process running a service. - PROCESS_STATE_SERVICE = 6; - // Process not running, but would be if there was enough RAM. - PROCESS_STATE_SERVICE_RESTARTING = 7; - // Process running a receiver. - PROCESS_STATE_RECEIVER = 8; - // Heavy-weight process (currently not used). - PROCESS_STATE_HEAVY_WEIGHT = 9; - // Process hosting home/launcher app when not on top. - PROCESS_STATE_HOME = 10; - // Process hosting the last app the user was in. - PROCESS_STATE_LAST_ACTIVITY = 11; - // Cached process hosting a previous activity. - PROCESS_STATE_CACHED_ACTIVITY = 12; - // Cached process hosting a client activity. - PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 13; - // Cached process that is empty. - PROCESS_STATE_CACHED_EMPTY = 14; -} - -enum ServiceOperationState { - SERVICE_OPERATION_STATE_UNKNOWN = 0; - SERVICE_OPERATION_STATE_RUNNING = 1; - SERVICE_OPERATION_STATE_STARTED = 2; - SERVICE_OPERATION_STATE_FOREGROUND = 3; - SERVICE_OPERATION_STATE_BOUND = 4; - SERVICE_OPERATION_STATE_EXECUTING = 5; -} - -// this enum list is from frameworks/base/core/java/com/android/internal/app/procstats/ProcessStats.java -// and not frameworks/base/core/java/android/app/ActivityManager.java -enum AggregatedProcessState { - AGGREGATED_PROCESS_STATE_UNKNOWN = 0; - // Persistent system process; PERSISTENT or PERSISTENT_UI in ActivityManager - AGGREGATED_PROCESS_STATE_PERSISTENT = 1; - // Top activity; actually any visible activity; TOP or TOP_SLEEPING in ActivityManager - AGGREGATED_PROCESS_STATE_TOP = 2; - // Bound top foreground process; BOUND_TOP or BOUND_FOREGROUND_SERVICE in ActivityManager - AGGREGATED_PROCESS_STATE_BOUND_TOP_OR_FGS = 3; - // Important foreground process; FOREGROUND_SERVICE in ActivityManager - AGGREGATED_PROCESS_STATE_FGS = 4; - // Important foreground process ; IMPORTANT_FOREGROUND in ActivityManager - AGGREGATED_PROCESS_STATE_IMPORTANT_FOREGROUND = 5; - // Various background processes; IMPORTANT_BACKGROUND, TRANSIENT_BACKGROUND, BACKUP, SERVICE, - // HEAVY_WEIGHT in ActivityManager - AGGREGATED_PROCESS_STATE_BACKGROUND = 6; - // Process running a receiver; RECEIVER in ActivityManager - AGGREGATED_PROCESS_STATE_RECEIVER = 7; - // Various cached processes; HOME, LAST_ACTIVITY, CACHED_ACTIVITY, CACHED_RECENT, - // CACHED_ACTIVITY_CLIENT, CACHED_EMPTY in ActivityManager - AGGREGATED_PROCESS_STATE_CACHED = 8; -}
\ No newline at end of file diff --git a/core/proto/android/service/usb.proto b/core/proto/android/service/usb.proto deleted file mode 100644 index 40c5a85e1f24..000000000000 --- a/core/proto/android/service/usb.proto +++ /dev/null @@ -1,440 +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. - */ - -syntax = "proto2"; -package android.service.usb; - -option java_multiple_files = true; -option java_outer_classname = "UsbServiceProto"; - -import "frameworks/base/core/proto/android/content/component_name.proto"; -import "frameworks/base/core/proto/android/service/enums.proto"; -import "frameworks/base/core/proto/android/privacy.proto"; - -message UsbServiceDumpProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - - optional UsbDeviceManagerProto device_manager = 1; - optional UsbHostManagerProto host_manager = 2; - optional UsbPortManagerProto port_manager = 3; - optional UsbAlsaManagerProto alsa_manager = 4; - optional UsbSettingsManagerProto settings_manager = 5; - optional UsbPermissionsManagerProto permissions_manager = 6; -} - -message UsbDeviceManagerProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - - optional UsbHandlerProto handler = 1; - optional UsbDebuggingManagerProto debugging_manager = 2; -} - -message UsbHandlerProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - - /* Same as android.hardware.usb.gadget.V1_0.GadgetFunction.* */ - enum Function { - FUNCTION_ADB = 1; - FUNCTION_ACCESSORY = 2; - FUNCTION_MTP = 4; - FUNCTION_MIDI = 8; - FUNCTION_PTP = 16; - FUNCTION_RNDIS = 32; - FUNCTION_AUDIO_SOURCE = 64; - } - - repeated Function current_functions = 1; - optional bool current_functions_applied = 2; - repeated Function screen_unlocked_functions = 3; - optional bool screen_locked = 4; - optional bool connected = 5; - optional bool configured = 6; - optional UsbAccessoryProto current_accessory = 7; - optional bool host_connected = 8; - optional bool source_power = 9; - optional bool sink_power = 10; - optional bool usb_charging = 11; - optional bool hide_usb_notification = 12; - optional bool audio_accessory_connected = 13; - optional bool adb_enabled = 14; - optional string kernel_state = 15; - optional string kernel_function_list = 16; -} - -message UsbAccessoryProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - - optional string manufacturer = 1; - optional string model = 2; - // For "classical" USB-accessories the manufacturer bakes this into the - // firmware of the device. If an Android phone is configured as accessory, the - // app that sets up the accessory side of the connection set this. Either way, - // these are part of the detection protocol, and so they cannot be user set or - // unique. - optional string description = 3; - optional string version = 4; - optional string uri = 5 [ (android.privacy).dest = DEST_EXPLICIT ]; - // Non-resettable hardware ID. - optional string serial = 6 [ (android.privacy).dest = DEST_LOCAL ]; -} - -message UsbDebuggingManagerProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - - optional bool connected_to_adb = 1; - // A workstation that connects to the phone for debugging is identified by - // this key. - optional string last_key_received = 2 [ (android.privacy).dest = DEST_EXPLICIT ]; - optional string user_keys = 3 [ (android.privacy).dest = DEST_LOCAL ]; - optional string system_keys = 4 [ (android.privacy).dest = DEST_LOCAL ]; -} - -message UsbHostManagerProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - - optional android.content.ComponentNameProto default_usb_host_connection_handler = 1; - repeated UsbDeviceProto devices = 2; - optional int32 num_connects = 3; - repeated UsbConnectionRecordProto connections = 4; -} - -message UsbDeviceProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - - // Generic USB name, not user-provided. - optional string name = 1; - // ID specific to the vendor, not the device. - optional int32 vendor_id = 2; - // ID of this product type: Each vendor gives each product a unique ID. E.g. - // all mice of the same model would have the same ID. - optional int32 product_id = 3; - optional int32 class = 4; - optional int32 subclass = 5; - optional int32 protocol = 6; - optional string manufacturer_name = 7; - optional string product_name = 8; - optional string version = 9; - // Non-resettable hardware ID. - optional string serial_number = 10 [ (android.privacy).dest = DEST_LOCAL ]; - repeated UsbConfigurationProto configurations = 11; -} - -message UsbConfigurationProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - - // A single USB device can have several configurations and the app accessing - // the USB device can switch between them. At any time only one can be active. - // Each configuration can present completely different interfaces end - // endpoints, i.e. a completely different behavior. - optional int32 id = 1; - // Hardware-defined name, not set by the user. - optional string name = 2; - optional uint32 attributes = 3; - optional int32 max_power = 4; - repeated UsbInterfaceProto interfaces = 5; -} - -message UsbInterfaceProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - - // Hardware defined. This is the id used by the app to identify the interface. - optional int32 id = 1; - optional int32 alternate_settings = 2; - optional string name = 3; - optional int32 class = 4; - optional int32 subclass = 5; - optional int32 protocol = 6; - repeated UsbEndPointProto endpoints = 7; -} - -message UsbEndPointProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - - optional int32 endpoint_number = 1; - optional android.service.UsbEndPointDirection direction = 2; - // The address of the endpoint. Needed to read and write to the endpoint. - optional int32 address = 3; - optional android.service.UsbEndPointType type = 4; - optional uint32 attributes = 5; - optional int32 max_packet_size = 6; - optional int32 interval = 7; -} - -message UsbConnectionRecordProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - - // usb device's address, e.g. 001/002, nothing about the phone - optional string device_address = 1; - optional android.service.UsbConnectionRecordMode mode = 2; - optional int64 timestamp = 3; - optional int32 manufacturer = 4; - optional int32 product = 5; - optional UsbIsHeadsetProto is_headset = 6; -} - -message UsbIsHeadsetProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - - optional bool in = 1; - optional bool out = 2; -} - -message UsbPortManagerProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - - optional bool is_simulation_active = 1; - repeated UsbPortInfoProto usb_ports = 2; -} - -message UsbPortInfoProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - - optional UsbPortProto port = 1; - optional UsbPortStatusProto status = 2; - optional bool can_change_mode = 3; - optional bool can_change_power_role = 4; - optional bool can_change_data_role = 5; - optional int64 connected_at_millis = 6; - optional int64 last_connect_duration_millis = 7; -} - -message UsbPortProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - - /* Same as android.hardware.usb.V1_1.Constants.PortMode_1_1 */ - enum Mode { - MODE_NONE = 0; - MODE_UFP = 1; - MODE_DFP = 2; - MODE_DRP = 3; - MODE_AUDIO_ACCESSORY = 4; - MODE_DEBUG_ACCESSORY = 8; - } - - // ID of the port. A device (eg: Chromebooks) might have multiple ports. - optional string id = 1; - repeated Mode supported_modes = 2; -} - -/* Same as android.hardware.usb.V1_2.Constants.ContaminantPresenceStatus */ -enum ContaminantPresenceStatus { - CONTAMINANT_STATUS_UNKNOWN = 0; - CONTAMINANT_STATUS_NOT_SUPPORTED = 1; - CONTAMINANT_STATUS_DISABLED = 2; - CONTAMINANT_STATUS_NOT_DETECTED = 3; - CONTAMINANT_STATUS_DETECTED = 4; -} - -message UsbPortStatusProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - - /* Same as android.hardware.usb.V1_0.Constants.PortPowerRole */ - enum PowerRole { - POWER_ROLE_NONE = 0; - POWER_ROLE_SOURCE = 1; - POWER_ROLE_SINK = 2; - } - - /* Same as android.hardware.usb.V1_0.Constants.PortDataRole */ - enum DataRole { - DATA_ROLE_NONE = 0; - DATA_ROLE_HOST = 1; - DATA_ROLE_DEVICE = 2; - } - - optional bool connected = 1; - optional UsbPortProto.Mode current_mode = 2; - optional PowerRole power_role = 3; - optional DataRole data_role = 4; - repeated UsbPortStatusRoleCombinationProto role_combinations = 5; - optional ContaminantPresenceStatus contaminant_presence_status = 6; -} - -message UsbPortStatusRoleCombinationProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - - optional UsbPortStatusProto.PowerRole power_role = 1; - optional UsbPortStatusProto.DataRole data_role = 2; -} - -message UsbAlsaManagerProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - - optional int32 cards_parser = 1; - repeated UsbAlsaDeviceProto alsa_devices = 2; - repeated UsbMidiDeviceProto midi_devices = 3; -} - -message UsbAlsaDeviceProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - - optional int32 card = 1; - optional int32 device = 2; - optional string name = 3; - optional bool has_playback = 4; - optional bool has_capture = 5; - // usb device's address, e.g. 001/002, nothing about the phone - optional string address = 6; -} - -message UsbMidiDeviceProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - - optional int32 card = 1; - optional int32 device = 2; - // usb device's address, e.g. 001/002, nothing about the phone - optional string device_address = 3; -} - -message UsbSettingsManagerProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - - repeated UsbUserSettingsManagerProto user_settings = 1; - repeated UsbProfileGroupSettingsManagerProto profile_group_settings = 2; -} - -message UsbUserSettingsManagerProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - - optional int32 user_id = 1; - reserved 2; // previously device_permissions, now unused - reserved 3; // previously accessory_permissions, now unused - repeated UsbDeviceAttachedActivities device_attached_activities = 4; - repeated UsbAccessoryAttachedActivities accessory_attached_activities = 5; -} - -message UsbProfileGroupSettingsManagerProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - - // The user id of the personal profile if the device has a work profile. - optional int32 parent_user_id = 1; - repeated UsbSettingsDevicePreferenceProto device_preferences = 2; - repeated UsbSettingsAccessoryPreferenceProto accessory_preferences = 3; -} - -message UsbSettingsDevicePreferenceProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - - optional UsbDeviceFilterProto filter = 1; - optional UserPackageProto user_package = 2; -} - -message UsbPermissionsManagerProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - - repeated UsbUserPermissionsManagerProto user_permissions = 1; -} - -message UsbUserPermissionsManagerProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - - optional int32 user_id = 1; - - repeated UsbDevicePermissionProto device_permissions = 2; - repeated UsbAccessoryPermissionProto accessory_permissions = 3; - - repeated UsbDevicePersistentPermissionProto device_persistent_permissions = 4; - repeated UsbAccessoryPersistentPermissionProto accessory_persistent_permissions = 5; -} - -message UsbDevicePermissionProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - - // Name of device set by manufacturer - // All devices of the same model have the same name - optional string device_name = 1; - repeated int32 uids = 2; -} - -message UsbAccessoryPermissionProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - - // Description of accessory set by manufacturer - // All accessories of the same model have the same description - optional string accessory_description = 1; - repeated int32 uids = 2; -} - -message UsbDevicePersistentPermissionProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - - optional UsbDeviceFilterProto device_filter = 1; - repeated UsbUidPermissionProto permission_values = 2; -} - -message UsbAccessoryPersistentPermissionProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - - optional UsbAccessoryFilterProto accessory_filter = 1; - repeated UsbUidPermissionProto permission_values = 2; -} - -message UsbUidPermissionProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - - optional int32 uid = 1; - optional bool is_granted = 2; -} - -message UsbDeviceFilterProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - - // Mirrors the vendor_id of UsbDeviceProto. - optional int32 vendor_id = 1; - optional int32 product_id = 2; - optional int32 class = 3; - optional int32 subclass = 4; - optional int32 protocol = 5; - optional string manufacturer_name = 6; - optional string product_name = 7; - optional string serial_number = 8 [ (android.privacy).dest = DEST_EXPLICIT ]; -} - -message UserPackageProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - - optional int32 user_id = 1; - optional string package_name =2; -} - -message UsbSettingsAccessoryPreferenceProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - - optional UsbAccessoryFilterProto filter = 1; - optional UserPackageProto user_package = 2; -} - -message UsbAccessoryFilterProto { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - - optional string manufacturer = 1; - optional string model = 2; - optional string version = 3; -} - -message UsbDeviceAttachedActivities { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - - optional android.content.ComponentNameProto activity = 1; - repeated UsbDeviceFilterProto filters = 2; -} - -message UsbAccessoryAttachedActivities { - option (android.msg_privacy).dest = DEST_AUTOMATIC; - - optional android.content.ComponentNameProto activity = 1; - repeated UsbAccessoryFilterProto filters = 2; -} diff --git a/core/proto/android/stats/connectivity/Android.bp b/core/proto/android/stats/connectivity/Android.bp deleted file mode 100644 index 5e6ac3cd3ca1..000000000000 --- a/core/proto/android/stats/connectivity/Android.bp +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (C) 2019 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -java_library_static { - name: "networkstackprotos", - proto: { - type: "lite", - }, - srcs: [ - "network_stack.proto", - ], - sdk_version: "system_29", -} - -java_library_static { - name: "tetheringprotos", - proto: { - type: "lite", - }, - srcs: [ - "tethering.proto", - ], - apex_available: [ - "com.android.tethering", - ], - sdk_version: "system_current", -} diff --git a/core/proto/android/stats/connectivity/network_stack.proto b/core/proto/android/stats/connectivity/network_stack.proto deleted file mode 100644 index e9726d7ce195..000000000000 --- a/core/proto/android/stats/connectivity/network_stack.proto +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -syntax = "proto2"; - -package android.stats.connectivity; -option java_multiple_files = true; -option java_outer_classname = "NetworkStackProto"; - -enum DhcpRenewResult { - RR_UNKNOWN = 0; - RR_SUCCESS = 1; - RR_ERROR_NAK = 2; - RR_ERROR_IP_MISMATCH = 3; - RR_ERROR_IP_EXPIRE = 4; -} - -enum DisconnectCode { - DC_NONE = 0; - DC_NORMAL_TERMINATION = 1; - DC_PROVISIONING_FAIL = 2; - DC_ERROR_STARTING_IPV4 = 4; - DC_ERROR_STARTING_IPV6 = 5; - DC_ERROR_STARTING_IPREACHABILITYMONITOR = 6; - DC_INVALID_PROVISIONING = 7; - DC_INTERFACE_NOT_FOUND = 8; - DC_PROVISIONING_TIMEOUT = 9; -} - -enum TransportType { - TT_UNKNOWN = 0; - // Indicates this network uses a Cellular transport - TT_CELLULAR = 1; - // Indicates this network uses a Wi-Fi transport - TT_WIFI = 2; - // Indicates this network uses a Bluetooth transport - TT_BLUETOOTH = 3; - // Indicates this network uses an Ethernet transport - TT_ETHERNET = 4; - // Indicates this network uses a Wi-Fi Aware transport - TT_WIFI_AWARE = 5; - // Indicates this network uses a LoWPAN transport - TT_LOWPAN = 6; - // Indicates this network uses a Cellular+VPN transport - TT_CELLULAR_VPN = 7; - // Indicates this network uses a Wi-Fi+VPN transport - TT_WIFI_VPN = 8; - // Indicates this network uses a Bluetooth+VPN transport - TT_BLUETOOTH_VPN = 9; - // Indicates this network uses an Ethernet+VPN transport - TT_ETHERNET_VPN = 10; - // Indicates this network uses a Wi-Fi+Cellular+VPN transport - TT_WIFI_CELLULAR_VPN = 11; - // Indicates this network uses for test only - TT_TEST = 12; -} - -enum DhcpFeature { - DF_UNKNOWN = 0; - // DHCP INIT-REBOOT state - DF_INITREBOOT = 1; - // DHCP rapid commit option - DF_RAPIDCOMMIT = 2; - // Duplicate address detection - DF_DAD = 3; - // Fast initial Link setup - DF_FILS = 4; -} - -enum HostnameTransResult { - HTR_UNKNOWN = 0; - HTR_SUCCESS = 1; - HTR_FAILURE = 2; - HTR_DISABLE = 3; -} - -enum ProbeResult { - PR_UNKNOWN = 0; - PR_SUCCESS = 1; - PR_FAILURE = 2; - PR_PORTAL = 3; - // DNS query for the probe host returned a private IP address - PR_PRIVATE_IP_DNS = 4; -} - -enum ValidationResult { - VR_UNKNOWN = 0; - VR_SUCCESS = 1; - VR_FAILURE = 2; - VR_PORTAL = 3; - VR_PARTIAL = 4; -} - -enum ProbeType { - PT_UNKNOWN = 0; - PT_DNS = 1; - PT_HTTP = 2; - PT_HTTPS = 3; - PT_PAC = 4; - PT_FALLBACK = 5; - PT_PRIVDNS = 6; - PT_CAPPORT_API = 7; -} - -// The Dhcp error code is defined in android.net.metrics.DhcpErrorEvent -enum DhcpErrorCode { - ET_UNKNOWN = 0; - ET_L2_ERROR = 1; - ET_L3_ERROR = 2; - ET_L4_ERROR = 3; - ET_DHCP_ERROR = 4; - ET_MISC_ERROR = 5; - /* Reserve for error type - // ET_L2_ERROR_TYPE = ET_L2_ERROR << 8; - ET_L2_ERROR_TYPE = 256; - // ET_L3_ERROR_TYPE = ET_L3_ERROR << 8; - ET_L3_ERROR_TYPE = 512; - // ET_L4_ERROR_TYPE = ET_L4_ERROR << 8; - ET_L4_ERROR_TYPE = 768; - // ET_DHCP_ERROR_TYPE = ET_DHCP_ERROR << 8; - ET_DHCP_ERROR_TYPE = 1024; - // ET_MISC_ERROR_TYPE = ET_MISC_ERROR << 8; - ET_MISC_ERROR_TYPE = 1280; - */ - // ET_L2_TOO_SHORT = (ET_L2_ERROR_TYPE | 0x1) << 16; - ET_L2_TOO_SHORT = 16842752; - // ET_L2_WRONG_ETH_TYPE = (ET_L2_ERROR_TYPE | 0x2) << 16; - ET_L2_WRONG_ETH_TYPE = 16908288; - // ET_L3_TOO_SHORT = (ET_L3_ERROR_TYPE | 0x1) << 16; - ET_L3_TOO_SHORT = 33619968; - // ET_L3_NOT_IPV4 = (ET_L3_ERROR_TYPE | 0x2) << 16; - ET_L3_NOT_IPV4 = 33685504; - // ET_L3_INVALID_IP = (ET_L3_ERROR_TYPE | 0x3) << 16; - ET_L3_INVALID_IP = 33751040; - // ET_L4_NOT_UDP = (ET_L4_ERROR_TYPE | 0x1) << 16; - ET_L4_NOT_UDP = 50397184; - // ET_L4_WRONG_PORT = (ET_L4_ERROR_TYPE | 0x2) << 16; - ET_L4_WRONG_PORT = 50462720; - // ET_BOOTP_TOO_SHORT = (ET_DHCP_ERROR_TYPE | 0x1) << 16; - ET_BOOTP_TOO_SHORT = 67174400; - // ET_DHCP_BAD_MAGIC_COOKIE = (ET_DHCP_ERROR_TYPE | 0x2) << 16; - ET_DHCP_BAD_MAGIC_COOKIE = 67239936; - // ET_DHCP_INVALID_OPTION_LENGTH = (ET_DHCP_ERROR_TYPE | 0x3) << 16; - ET_DHCP_INVALID_OPTION_LENGTH = 67305472; - // ET_DHCP_NO_MSG_TYPE = (ET_DHCP_ERROR_TYPE | 0x4) << 16; - ET_DHCP_NO_MSG_TYPE = 67371008; - // ET_DHCP_UNKNOWN_MSG_TYPE = (ET_DHCP_ERROR_TYPE | 0x5) << 16; - ET_DHCP_UNKNOWN_MSG_TYPE = 67436544; - // ET_DHCP_NO_COOKIE = (ET_DHCP_ERROR_TYPE | 0x6) << 16; - ET_DHCP_NO_COOKIE = 67502080; - // ET_BUFFER_UNDERFLOW = (ET_MISC_ERROR_TYPE | 0x1) << 16; - ET_BUFFER_UNDERFLOW = 83951616; - // ET_RECEIVE_ERROR = (ET_MISC_ERROR_TYPE | 0x2) << 16; - ET_RECEIVE_ERROR = 84017152; - // ET_PARSING_ERROR = (ET_MISC_ERROR_TYPE | 0x3) << 16; - ET_PARSING_ERROR = 84082688; -} - -enum NetworkQuirkEvent { - QE_UNKNOWN = 0; - QE_IPV6_PROVISIONING_ROUTER_LOST = 1; -} - -message NetworkStackEventData { - -} - diff --git a/core/proto/android/stats/connectivity/tethering.proto b/core/proto/android/stats/connectivity/tethering.proto deleted file mode 100644 index 13f0b8c44fb5..000000000000 --- a/core/proto/android/stats/connectivity/tethering.proto +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -syntax = "proto2"; -package android.stats.connectivity; -option java_multiple_files = true; -option java_outer_classname = "TetheringProto"; - -enum ErrorCode { - EC_NO_ERROR = 0; - EC_UNKNOWN_IFACE = 1; - EC_SERVICE_UNAVAIL = 2; - EC_UNSUPPORTED = 3; - EC_UNAVAIL_IFACE = 4; - EC_INTERNAL_ERROR = 5; - EC_TETHER_IFACE_ERROR = 6; - EC_UNTETHER_IFACE_ERROR = 7; - EC_ENABLE_FORWARDING_ERROR = 8; - EC_DISABLE_FORWARDING_ERROR = 9; - EC_IFACE_CFG_ERROR = 10; - EC_PROVISIONING_FAILED = 11; - EC_DHCPSERVER_ERROR = 12; - EC_ENTITLEMENT_UNKNOWN = 13; - EC_NO_CHANGE_TETHERING_PERMISSION = 14; - EC_NO_ACCESS_TETHERING_PERMISSION = 15; - EC_UNKNOWN_TYPE = 16; -} - -enum DownstreamType { - // Unspecific tethering type. - DS_UNSPECIFIED = 0; - // Wifi tethering type. - DS_TETHERING_WIFI = 1; - // USB tethering type. - DS_TETHERING_USB = 2; - // Bluetooth tethering type. - DS_TETHERING_BLUETOOTH = 3; - // Wifi P2p tethering type. - DS_TETHERING_WIFI_P2P = 4; - // NCM (Network Control Model) local tethering type. - DS_TETHERING_NCM = 5; - // Ethernet tethering type. - DS_TETHERING_ETHERNET = 6; -} - -enum UpstreamType { - UT_UNKNOWN = 0; - // Indicates upstream using a Cellular transport. - UT_CELLULAR = 1; - // Indicates upstream using a Wi-Fi transport. - UT_WIFI = 2; - // Indicates upstream using a Bluetooth transport. - UT_BLUETOOTH = 3; - // Indicates upstream using an Ethernet transport. - UT_ETHERNET = 4; - // Indicates upstream using a Wi-Fi Aware transport. - UT_WIFI_AWARE = 5; - // Indicates upstream using a LoWPAN transport. - UT_LOWPAN = 6; - // Indicates upstream using a Cellular+VPN transport. - UT_CELLULAR_VPN = 7; - // Indicates upstream using a Wi-Fi+VPN transport. - UT_WIFI_VPN = 8; - // Indicates upstream using a Bluetooth+VPN transport. - UT_BLUETOOTH_VPN = 9; - // Indicates upstream using an Ethernet+VPN transport. - UT_ETHERNET_VPN = 10; - // Indicates upstream using a Wi-Fi+Cellular+VPN transport. - UT_WIFI_CELLULAR_VPN = 11; - // Indicates upstream using for test only. - UT_TEST = 12; - // Indicates upstream using DUN capability + Cellular transport. - UT_DUN_CELLULAR = 13; -} - -enum UserType { - // Unknown. - USER_UNKNOWN = 0; - // Settings. - USER_SETTINGS = 1; - // System UI. - USER_SYSTEMUI = 2; - // Google mobile service. - USER_GMS = 3; -} diff --git a/core/proto/android/stats/devicepolicy/Android.bp b/core/proto/android/stats/devicepolicy/Android.bp deleted file mode 100644 index 5fb278a34dae..000000000000 --- a/core/proto/android/stats/devicepolicy/Android.bp +++ /dev/null @@ -1,33 +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. - -java_library_static { - name: "devicepolicyprotosnano", - proto: { - type: "nano", - }, - srcs: [ - "*.proto", - ], - java_version: "1.8", - target: { - android: { - jarjar_rules: "jarjar-rules.txt", - }, - host: { - static_libs: ["libprotobuf-java-nano"], - } - }, - sdk_version: "core_platform", -} diff --git a/core/proto/android/stats/devicepolicy/device_policy_enums.proto b/core/proto/android/stats/devicepolicy/device_policy_enums.proto deleted file mode 100644 index 7c1a04944d68..000000000000 --- a/core/proto/android/stats/devicepolicy/device_policy_enums.proto +++ /dev/null @@ -1,204 +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. - */ - -syntax = "proto2"; - -package android.stats.devicepolicy; -option java_multiple_files = true; - -/** - * Id for device policy features. - */ -enum EventId { - SET_PASSWORD_QUALITY = 1; - SET_PASSWORD_MINIMUM_LENGTH = 2; - SET_PASSWORD_MINIMUM_NUMERIC = 3; - SET_PASSWORD_MINIMUM_NON_LETTER = 4; - SET_PASSWORD_MINIMUM_LETTERS = 5; - SET_PASSWORD_MINIMUM_LOWER_CASE = 6; - SET_PASSWORD_MINIMUM_UPPER_CASE = 7; - SET_PASSWORD_MINIMUM_SYMBOLS = 8; - SET_KEYGUARD_DISABLED_FEATURES = 9; - LOCK_NOW = 10; - WIPE_DATA_WITH_REASON = 11; - ADD_USER_RESTRICTION = 12; - REMOVE_USER_RESTRICTION = 13; - SET_SECURE_SETTING = 14; - SET_SECURITY_LOGGING_ENABLED = 15; - RETRIEVE_SECURITY_LOGS = 16; - RETRIEVE_PRE_REBOOT_SECURITY_LOGS = 17; - SET_PERMISSION_POLICY = 18; - SET_PERMISSION_GRANT_STATE = 19; - INSTALL_KEY_PAIR = 20; - INSTALL_CA_CERT = 21; - CHOOSE_PRIVATE_KEY_ALIAS = 22; - REMOVE_KEY_PAIR = 23; - UNINSTALL_CA_CERTS = 24; - SET_CERT_INSTALLER_PACKAGE = 25; - SET_ALWAYS_ON_VPN_PACKAGE = 26; - SET_PERMITTED_INPUT_METHODS = 27; - SET_PERMITTED_ACCESSIBILITY_SERVICES = 28; - SET_SCREEN_CAPTURE_DISABLED = 29; - SET_CAMERA_DISABLED = 30; - QUERY_SUMMARY_FOR_USER = 31; - QUERY_SUMMARY = 32; - QUERY_DETAILS = 33; - REBOOT = 34; - SET_MASTER_VOLUME_MUTED = 35; - SET_AUTO_TIME_REQUIRED = 36; - SET_KEYGUARD_DISABLED = 37; - SET_STATUS_BAR_DISABLED = 38; - SET_ORGANIZATION_COLOR = 39; - SET_PROFILE_NAME = 40; - SET_USER_ICON = 41; - SET_DEVICE_OWNER_LOCK_SCREEN_INFO = 42; - SET_SHORT_SUPPORT_MESSAGE = 43; - SET_LONG_SUPPORT_MESSAGE = 44; - SET_CROSS_PROFILE_CONTACTS_SEARCH_DISABLED = 45; - SET_CROSS_PROFILE_CALLER_ID_DISABLED = 46; - SET_BLUETOOTH_CONTACT_SHARING_DISABLED = 47; - ADD_CROSS_PROFILE_INTENT_FILTER = 48; - ADD_CROSS_PROFILE_WIDGET_PROVIDER = 49; - SET_SYSTEM_UPDATE_POLICY = 50; - SET_LOCKTASK_MODE_ENABLED = 51; - ADD_PERSISTENT_PREFERRED_ACTIVITY = 52; - REQUEST_BUGREPORT = 53; - GET_WIFI_MAC_ADDRESS = 54; - REQUEST_QUIET_MODE_ENABLED = 55; - WORK_PROFILE_LOCATION_CHANGED = 56; - DO_USER_INFO_CLICKED = 57; - TRANSFER_OWNERSHIP = 58; - GENERATE_KEY_PAIR = 59; - SET_KEY_PAIR_CERTIFICATE = 60; - SET_KEEP_UNINSTALLED_PACKAGES = 61; - SET_APPLICATION_RESTRICTIONS = 62; - SET_APPLICATION_HIDDEN = 63; - ENABLE_SYSTEM_APP = 64; - ENABLE_SYSTEM_APP_WITH_INTENT = 65; - INSTALL_EXISTING_PACKAGE = 66; - SET_UNINSTALL_BLOCKED = 67; - SET_PACKAGES_SUSPENDED = 68; - ON_LOCK_TASK_MODE_ENTERING = 69; - SET_CROSS_PROFILE_CALENDAR_PACKAGES = 70; - GET_USER_PASSWORD_COMPLEXITY_LEVEL = 72; - INSTALL_SYSTEM_UPDATE = 73; - INSTALL_SYSTEM_UPDATE_ERROR = 74; - IS_MANAGED_KIOSK = 75; - IS_UNATTENDED_MANAGED_KIOSK = 76; - PROVISIONING_MANAGED_PROFILE_ON_FULLY_MANAGED_DEVICE = 77; - PROVISIONING_PERSISTENT_DEVICE_OWNER = 78; - - // existing Tron logs to be migrated to statsd - PROVISIONING_ENTRY_POINT_NFC = 79; - PROVISIONING_ENTRY_POINT_QR_CODE = 80; - PROVISIONING_ENTRY_POINT_CLOUD_ENROLLMENT = 81; - PROVISIONING_ENTRY_POINT_ADB = 82; - PROVISIONING_ENTRY_POINT_TRUSTED_SOURCE = 83; - PROVISIONING_DPC_PACKAGE_NAME = 84; - PROVISIONING_DPC_INSTALLED_BY_PACKAGE = 85; - PROVISIONING_PROVISIONING_ACTIVITY_TIME_MS = 86; - PROVISIONING_PREPROVISIONING_ACTIVITY_TIME_MS = 87; - PROVISIONING_ENCRYPT_DEVICE_ACTIVITY_TIME_MS = 88; - PROVISIONING_WEB_ACTIVITY_TIME_MS = 89; - PROVISIONING_TRAMPOLINE_ACTIVITY_TIME_MS = 90 [deprecated=true]; - PROVISIONING_POST_ENCRYPTION_ACTIVITY_TIME_MS = 91 [deprecated=true]; - PROVISIONING_FINALIZATION_ACTIVITY_TIME_MS = 92 [deprecated=true]; - PROVISIONING_NETWORK_TYPE = 93; - PROVISIONING_ACTION = 94; - PROVISIONING_EXTRAS = 95; - PROVISIONING_COPY_ACCOUNT_TASK_MS = 96; - PROVISIONING_CREATE_PROFILE_TASK_MS = 97; - PROVISIONING_START_PROFILE_TASK_MS = 98; - PROVISIONING_DOWNLOAD_PACKAGE_TASK_MS = 99; - PROVISIONING_INSTALL_PACKAGE_TASK_MS = 100; - PROVISIONING_CANCELLED = 101; - PROVISIONING_ERROR = 102; - PROVISIONING_COPY_ACCOUNT_STATUS = 103; - PROVISIONING_TOTAL_TASK_TIME_MS = 104; - PROVISIONING_SESSION_STARTED = 105; - PROVISIONING_SESSION_COMPLETED = 106; - PROVISIONING_TERMS_ACTIVITY_TIME_MS = 107; - PROVISIONING_TERMS_COUNT = 108; - PROVISIONING_TERMS_READ = 109; - - SEPARATE_PROFILE_CHALLENGE_CHANGED = 110; - SET_GLOBAL_SETTING = 111; - INSTALL_PACKAGE = 112; - UNINSTALL_PACKAGE = 113; - WIFI_SERVICE_ADD_NETWORK_SUGGESTIONS = 114; - WIFI_SERVICE_ADD_OR_UPDATE_NETWORK = 115; - QUERY_SUMMARY_FOR_DEVICE = 116; - REMOVE_CROSS_PROFILE_WIDGET_PROVIDER = 117; - ESTABLISH_VPN = 118; - SET_NETWORK_LOGGING_ENABLED = 119; - RETRIEVE_NETWORK_LOGS = 120; - PROVISIONING_PREPARE_TOTAL_TIME_MS = 121; - PROVISIONING_PREPARE_STARTED = 122; - PROVISIONING_PREPARE_COMPLETED = 123; - PROVISIONING_FLOW_TYPE = 124; - CROSS_PROFILE_APPS_GET_TARGET_USER_PROFILES = 125; - CROSS_PROFILE_APPS_START_ACTIVITY_AS_USER = 126; - SET_AUTO_TIME = 127; - SET_AUTO_TIME_ZONE = 128; - SET_USER_CONTROL_DISABLED_PACKAGES = 129; - SET_FACTORY_RESET_PROTECTION = 130; - SET_COMMON_CRITERIA_MODE = 131; - ALLOW_MODIFICATION_OF_ADMIN_CONFIGURED_NETWORKS = 132; - SET_TIME = 133; - SET_TIME_ZONE = 134; - SET_PERSONAL_APPS_SUSPENDED = 135; - SET_MANAGED_PROFILE_MAXIMUM_TIME_OFF = 136; - COMP_TO_ORG_OWNED_PO_MIGRATED = 137; - SET_CROSS_PROFILE_PACKAGES = 138; - SET_INTERACT_ACROSS_PROFILES_APP_OP = 139; - GET_CROSS_PROFILE_PACKAGES = 140; - CAN_REQUEST_INTERACT_ACROSS_PROFILES_TRUE = 141; - CAN_REQUEST_INTERACT_ACROSS_PROFILES_FALSE_NO_PROFILES = 142; - CAN_REQUEST_INTERACT_ACROSS_PROFILES_FALSE_WHITELIST = 143; - CAN_REQUEST_INTERACT_ACROSS_PROFILES_FALSE_PERMISSION = 144; - CAN_INTERACT_ACROSS_PROFILES_TRUE = 145; - CAN_INTERACT_ACROSS_PROFILES_FALSE_PERMISSION = 146; - CAN_INTERACT_ACROSS_PROFILES_FALSE_NO_PROFILES = 147; - CREATE_CROSS_PROFILE_INTENT = 148; - IS_MANAGED_PROFILE = 149; - START_ACTIVITY_BY_INTENT = 150; - BIND_CROSS_PROFILE_SERVICE = 151; - PROVISIONING_DPC_SETUP_STARTED = 152; - PROVISIONING_DPC_SETUP_COMPLETED = 153; - PROVISIONING_ORGANIZATION_OWNED_MANAGED_PROFILE = 154; - RESOLVER_CROSS_PROFILE_TARGET_OPENED = 155; - RESOLVER_SWITCH_TABS = 156; - RESOLVER_EMPTY_STATE_WORK_APPS_DISABLED = 157; - RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL= 158; - RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK= 159; - RESOLVER_EMPTY_STATE_NO_APPS_RESOLVED= 160; - RESOLVER_AUTOLAUNCH_CROSS_PROFILE_TARGET = 161; - CROSS_PROFILE_SETTINGS_PAGE_LAUNCHED_FROM_APP = 162; - CROSS_PROFILE_SETTINGS_PAGE_LAUNCHED_FROM_SETTINGS = 163; - CROSS_PROFILE_SETTINGS_PAGE_ADMIN_RESTRICTED = 164; - CROSS_PROFILE_SETTINGS_PAGE_MISSING_WORK_APP = 165; - CROSS_PROFILE_SETTINGS_PAGE_MISSING_PERSONAL_APP = 166; - CROSS_PROFILE_SETTINGS_PAGE_MISSING_INSTALL_BANNER_INTENT = 167; - CROSS_PROFILE_SETTINGS_PAGE_INSTALL_BANNER_CLICKED = 168; - CROSS_PROFILE_SETTINGS_PAGE_INSTALL_BANNER_NO_INTENT_CLICKED = 169; - CROSS_PROFILE_SETTINGS_PAGE_USER_CONSENTED = 170; - CROSS_PROFILE_SETTINGS_PAGE_USER_DECLINED_CONSENT = 171; - CROSS_PROFILE_SETTINGS_PAGE_PERMISSION_REVOKED = 172; - DOCSUI_EMPTY_STATE_NO_PERMISSION = 173; - DOCSUI_EMPTY_STATE_QUIET_MODE = 174; - DOCSUI_LAUNCH_OTHER_APP = 175; - DOCSUI_PICK_RESULT = 176; -} diff --git a/core/proto/android/stats/devicepolicy/jarjar-rules.txt b/core/proto/android/stats/devicepolicy/jarjar-rules.txt deleted file mode 100644 index 40043a861ceb..000000000000 --- a/core/proto/android/stats/devicepolicy/jarjar-rules.txt +++ /dev/null @@ -1 +0,0 @@ -rule com.google.protobuf.nano.** com.android.framework.protobuf.nano.@1 diff --git a/core/proto/android/stats/dnsresolver/dns_resolver.proto b/core/proto/android/stats/dnsresolver/dns_resolver.proto deleted file mode 100644 index b17d12c9c315..000000000000 --- a/core/proto/android/stats/dnsresolver/dns_resolver.proto +++ /dev/null @@ -1,375 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -syntax = "proto2"; -package android.stats.dnsresolver; - -enum EventType { - EVENT_UNKNOWN = 0; - EVENT_GETADDRINFO = 1; - EVENT_GETHOSTBYNAME = 2; - EVENT_GETHOSTBYADDR = 3; - EVENT_RES_NSEND = 4; -} - -// The return value of the DNS resolver for each DNS lookups. -// bionic/libc/include/netdb.h -// system/netd/resolv/include/netd_resolv/resolv.h -enum ReturnCode { - RC_EAI_NO_ERROR = 0; - RC_EAI_ADDRFAMILY = 1; - RC_EAI_AGAIN = 2; - RC_EAI_BADFLAGS = 3; - RC_EAI_FAIL = 4; - RC_EAI_FAMILY = 5; - RC_EAI_MEMORY = 6; - RC_EAI_NODATA = 7; - RC_EAI_NONAME = 8; - RC_EAI_SERVICE = 9; - RC_EAI_SOCKTYPE = 10; - RC_EAI_SYSTEM = 11; - RC_EAI_BADHINTS = 12; - RC_EAI_PROTOCOL = 13; - RC_EAI_OVERFLOW = 14; - RC_RESOLV_INTERNAL_ERROR = 254; - RC_RESOLV_TIMEOUT = 255; - RC_EAI_MAX = 256; -} - -enum NsRcode { - NS_R_NO_ERROR = 0; // No error occurred. - NS_R_FORMERR = 1; // Format error. - NS_R_SERVFAIL = 2; // Server failure. - NS_R_NXDOMAIN = 3; // Name error. - NS_R_NOTIMPL = 4; // Unimplemented. - NS_R_REFUSED = 5; // Operation refused. - // these are for BIND_UPDATE - NS_R_YXDOMAIN = 6; // Name exists - NS_R_YXRRSET = 7; // RRset exists - NS_R_NXRRSET = 8; // RRset does not exist - NS_R_NOTAUTH = 9; // Not authoritative for zone - NS_R_NOTZONE = 10; // Zone of record different from zone section - NS_R_MAX = 11; - // Define rcode=12~15(UNASSIGNED) in rcode enum type. - // Some DNS Servers might return undefined code to devices. - // Without the enum definition, that would be noise for our dashboard. - NS_R_UNASSIGNED12 = 12; // Unassigned - NS_R_UNASSIGNED13 = 13; // Unassigned - NS_R_UNASSIGNED14 = 14; // Unassigned - NS_R_UNASSIGNED15 = 15; // Unassigned - // The following are EDNS extended rcodes - NS_R_BADVERS = 16; - // The following are TSIG errors - // NS_R_BADSIG = 16, - NS_R_BADKEY = 17; - NS_R_BADTIME = 18; - NS_R_INTERNAL_ERROR = 254; - NS_R_TIMEOUT = 255; -} - -// Currently defined type values for resources and queries. -enum NsType { - NS_T_INVALID = 0; // Cookie. - NS_T_A = 1; // Host address. - NS_T_NS = 2; // Authoritative server. - NS_T_MD = 3; // Mail destination. - NS_T_MF = 4; // Mail forwarder. - NS_T_CNAME = 5; // Canonical name. - NS_T_SOA = 6; // Start of authority zone. - NS_T_MB = 7; // Mailbox domain name. - NS_T_MG = 8; // Mail group member. - NS_T_MR = 9; // Mail rename name. - NS_T_NULL = 10; // Null resource record. - NS_T_WKS = 11; // Well known service. - NS_T_PTR = 12; // Domain name pointer. - NS_T_HINFO = 13; // Host information. - NS_T_MINFO = 14; // Mailbox information. - NS_T_MX = 15; // Mail routing information. - NS_T_TXT = 16; // Text strings. - NS_T_RP = 17; // Responsible person. - NS_T_AFSDB = 18; // AFS cell database. - NS_T_X25 = 19; // X_25 calling address. - NS_T_ISDN = 20; // ISDN calling address. - NS_T_RT = 21; // Router. - NS_T_NSAP = 22; // NSAP address. - NS_T_NSAP_PTR = 23; // Reverse NSAP lookup (deprecated). - NS_T_SIG = 24; // Security signature. - NS_T_KEY = 25; // Security key. - NS_T_PX = 26; // X.400 mail mapping. - NS_T_GPOS = 27; // Geographical position (withdrawn). - NS_T_AAAA = 28; // IPv6 Address. - NS_T_LOC = 29; // Location Information. - NS_T_NXT = 30; // Next domain (security). - NS_T_EID = 31; // Endpoint identifier. - NS_T_NIMLOC = 32; // Nimrod Locator. - NS_T_SRV = 33; // Server Selection. - NS_T_ATMA = 34; // ATM Address - NS_T_NAPTR = 35; // Naming Authority PoinTeR - NS_T_KX = 36; // Key Exchange - NS_T_CERT = 37; // Certification record - NS_T_A6 = 38; // IPv6 address (experimental) - NS_T_DNAME = 39; // Non-terminal DNAME - NS_T_SINK = 40; // Kitchen sink (experimentatl) - NS_T_OPT = 41; // EDNS0 option (meta-RR) - NS_T_APL = 42; // Address prefix list (RFC 3123) - NS_T_DS = 43; // Delegation Signer - NS_T_SSHFP = 44; // SSH Fingerprint - NS_T_IPSECKEY = 45; // IPSEC Key - NS_T_RRSIG = 46; // RRset Signature - NS_T_NSEC = 47; // Negative security - NS_T_DNSKEY = 48; // DNS Key - NS_T_DHCID = 49; // Dynamic host configuratin identifier - NS_T_NSEC3 = 50; // Negative security type 3 - NS_T_NSEC3PARAM = 51; // Negative security type 3 parameters - NS_T_HIP = 55; // Host Identity Protocol - NS_T_SPF = 99; // Sender Policy Framework - NS_T_TKEY = 249; // Transaction key - NS_T_TSIG = 250; // Transaction signature. - NS_T_IXFR = 251; // Incremental zone transfer. - NS_T_AXFR = 252; // Transfer zone of authority. - NS_T_MAILB = 253; // Transfer mailbox records. - NS_T_MAILA = 254; // Transfer mail agent records. - NS_T_ANY = 255; // Wildcard match. - NS_T_ZXFR = 256; // BIND-specific, nonstandard. - NS_T_DLV = 32769; // DNSSEC look-aside validatation. - NS_T_MAX = 65536; -} - -enum IpVersion { - IV_UNKNOWN = 0; - IV_IPV4 = 1; - IV_IPV6 = 2; -} - -enum Protocol { - PROTO_UNKNOWN = 0; - PROTO_UDP = 1; - PROTO_TCP = 2; - PROTO_DOT = 3; -} - -enum PrivateDnsModes { - PDM_UNKNOWN = 0; - PDM_OFF = 1; - PDM_OPPORTUNISTIC = 2; - PDM_STRICT = 3; -} - -enum NetworkType { - NT_UNKNOWN = 0; - // Indicates this network uses a Cellular transport. - NT_CELLULAR = 1; - // Indicates this network uses a Wi-Fi transport. - NT_WIFI = 2; - // Indicates this network uses a Bluetooth transport. - NT_BLUETOOTH = 3; - // Indicates this network uses an Ethernet transport. - NT_ETHERNET = 4; - // Indicates this network uses a VPN transport, now deprecated. - NT_VPN = 5 [deprecated=true]; - // Indicates this network uses a Wi-Fi Aware transport. - NT_WIFI_AWARE = 6; - // Indicates this network uses a LoWPAN transport. - NT_LOWPAN = 7; - // Indicates this network uses a Cellular+VPN transport. - NT_CELLULAR_VPN = 8; - // Indicates this network uses a Wi-Fi+VPN transport. - NT_WIFI_VPN = 9; - // Indicates this network uses a Bluetooth+VPN transport. - NT_BLUETOOTH_VPN = 10; - // Indicates this network uses an Ethernet+VPN transport. - NT_ETHERNET_VPN = 11; - // Indicates this network uses a Wi-Fi+Cellular+VPN transport. - NT_WIFI_CELLULAR_VPN = 12; -} - -enum CacheStatus{ - // the cache can't handle that kind of queries. - // or the answer buffer is too small. - CS_UNSUPPORTED = 0; - // the cache doesn't know about this query. - CS_NOTFOUND = 1; - // the cache found the answer. - CS_FOUND = 2; - // Don't do anything on cache. - CS_SKIP = 3; -} - -// The enum LinuxErrno is defined in the following 2 files. -// 1. bionic/libc/kernel/uapi/asm-generic/errno-base.h -// 2. bionic/libc/kernel/uapi/asm-generic/errno.h -enum LinuxErrno { - SYS_NO_ERROR = 0; - SYS_EPERM = 1; // Not super-user - SYS_ENOENT = 2; // No such file or directory - SYS_ESRCH = 3; // No such process - SYS_EINTR = 4; // Interrupted system call - SYS_EIO = 5; // I/O error - SYS_ENXIO = 6; // No such device or address - SYS_E2BIG = 7; // Arg list too long - SYS_ENOEXEC = 8; // Exec format error - SYS_EBADF = 9; // Bad file number - SYS_ECHILD = 10; // No children - SYS_EAGAIN = 11; // No more processes - SYS_ENOMEM = 12; // Not enough core - SYS_EACCES = 13; // Permission denied - SYS_EFAULT = 14; // Bad address - SYS_ENOTBLK = 15; // Block device required - SYS_EBUSY = 16; // Mount device busy - SYS_EEXIST = 17; // File exists - SYS_EXDEV = 18; // Cross-device link - SYS_ENODEV = 19; // No such device - SYS_ENOTDIR = 20; // Not a directory - SYS_EISDIR = 21; // Is a directory - SYS_EINVAL = 22; // Invalid argument - SYS_ENFILE = 23; // Too many open files in system - SYS_EMFILE = 24; // Too many open files - SYS_ENOTTY = 25; // Not a typewriter - SYS_ETXTBSY = 26; // Text file busy - SYS_EFBIG = 27; // File too large - SYS_ENOSPC = 28; // No space left on device - SYS_ESPIPE = 29; // Illegal seek - SYS_EROFS = 30; // Read only file system - SYS_EMLINK = 31; // Too many links - SYS_EPIPE = 32; // Broken pipe - SYS_EDOM = 33; // Math arg out of domain of func - SYS_ERANGE = 34; // Math result not representable - SYS_EDEADLOCK = 35; // File locking deadlock error - SYS_ENAMETOOLONG = 36; // File or path name too long - SYS_ENOLCK = 37; // No record locks available - SYS_ENOSYS = 38; // Function not implemented - SYS_ENOTEMPTY = 39; // Directory not empty - SYS_ELOOP = 40; // Too many symbolic links - SYS_ENOMSG = 42; // No message of desired type - SYS_EIDRM = 43; // Identifier removed - SYS_ECHRNG = 44; // Channel number out of range - SYS_EL2NSYNC = 45; // Level 2 not synchronized - SYS_EL3HLT = 46; // Level 3 halted - SYS_EL3RST = 47; // Level 3 reset - SYS_ELNRNG = 48; // Link number out of range - SYS_EUNATCH = 49; // rotocol driver not attached - SYS_ENOCSI = 50; // No CSI structure available - SYS_EL2HLT = 51; // Level 2 halted - SYS_EBADE = 52; // Invalid exchange - SYS_EBADR = 53; // Invalid request descriptor - SYS_EXFULL = 54; // Exchange full - SYS_ENOANO = 55; // No anode - SYS_EBADRQC = 56; // Invalid request code - SYS_EBADSLT = 57; // Invalid slot - SYS_EBFONT = 59; // Bad font file fmt - SYS_ENOSTR = 60; // Device not a stream - SYS_ENODATA = 61; // No data (for no delay io) - SYS_ETIME = 62; // Timer expired - SYS_ENOSR = 63; // Out of streams resources - SYS_ENONET = 64; // Machine is not on the network - SYS_ENOPKG = 65; // Package not installed - SYS_EREMOTE = 66; // The object is remote - SYS_ENOLINK = 67; // The link has been severed - SYS_EADV = 68; // Advertise error - SYS_ESRMNT = 69; // Srmount error - SYS_ECOMM = 70; // Communication error on send - SYS_EPROTO = 71; // Protocol error - SYS_EMULTIHOP = 72; // Multihop attempted - SYS_EDOTDOT = 73; // Cross mount point (not really error) - SYS_EBADMSG = 74; // Trying to read unreadable message - SYS_EOVERFLOW = 75; // Value too large for defined data type - SYS_ENOTUNIQ = 76; // Given log. name not unique - SYS_EBADFD = 77; // f.d. invalid for this operation - SYS_EREMCHG = 78; // Remote address changed - SYS_ELIBACC = 79; // Can't access a needed shared lib - SYS_ELIBBAD = 80; // Accessing a corrupted shared lib - SYS_ELIBSCN = 81; // .lib section in a.out corrupted - SYS_ELIBMAX = 82; // Attempting to link in too many libs - SYS_ELIBEXEC = 83; // Attempting to exec a shared library - SYS_EILSEQ = 84; - SYS_ERESTART = 85; - SYS_ESTRPIPE = 86; - SYS_EUSERS = 87; - SYS_ENOTSOCK = 88; // Socket operation on non-socket - SYS_EDESTADDRREQ = 89; // Destination address required - SYS_EMSGSIZE = 90; // Message too long - SYS_EPROTOTYPE = 91; // Protocol wrong type for socket - SYS_ENOPROTOOPT = 92; // Protocol not available - SYS_EPROTONOSUPPORT = 93; // Unknown protocol - SYS_ESOCKTNOSUPPORT = 94; // Socket type not supported - SYS_EOPNOTSUPP = 95; // Operation not supported on transport endpoint - SYS_EPFNOSUPPORT = 96; // Protocol family not supported - SYS_EAFNOSUPPORT = 97; // Address family not supported by protocol family - SYS_EADDRINUSE = 98; // Address already in use - SYS_EADDRNOTAVAIL = 99; // Address not available - SYS_ENETDOWN = 100; // Network interface is not configured - SYS_ENETUNREACH = 101; // Network is unreachable - SYS_ENETRESET = 102; - SYS_ECONNABORTED = 103; // Connection aborted - SYS_ECONNRESET = 104; // Connection reset by peer - SYS_ENOBUFS = 105; // No buffer space available - SYS_EISCONN = 106; // Socket is already connected - SYS_ENOTCONN = 107; // Socket is not connected - SYS_ESHUTDOWN = 108; // Can't send after socket shutdown - SYS_ETOOMANYREFS = 109; - SYS_ETIMEDOUT = 110; // Connection timed out - SYS_ECONNREFUSED = 111; // Connection refused - SYS_EHOSTDOWN = 112; // Host is down - SYS_EHOSTUNREACH = 113; // Host is unreachable - SYS_EALREADY = 114; // Socket already connected - SYS_EINPROGRESS = 115; // Connection already in progress - SYS_ESTALE = 116; - SYS_EUCLEAN = 117; - SYS_ENOTNAM = 118; - SYS_ENAVAIL = 119; - SYS_EISNAM = 120; - SYS_EREMOTEIO = 121; - SYS_EDQUOT = 122; - SYS_ENOMEDIUM = 123; // No medium (in tape drive) - SYS_EMEDIUMTYPE = 124; - SYS_ECANCELED = 125; - SYS_ENOKEY = 126; - SYS_EKEYEXPIRED = 127; - SYS_EKEYREVOKED = 128; - SYS_EKEYREJECTED = 129; - SYS_EOWNERDEAD = 130; - SYS_ENOTRECOVERABLE = 131; - SYS_ERFKILL = 132; - SYS_EHWPOISON = 133; -} - -message DnsQueryEvent { - optional android.stats.dnsresolver.NsRcode rcode = 1; - - optional android.stats.dnsresolver.NsType type = 2; - - optional android.stats.dnsresolver.CacheStatus cache_hit = 3; - - optional android.stats.dnsresolver.IpVersion ip_version = 4; - - optional android.stats.dnsresolver.Protocol protocol = 5; - - // Number of DNS query retry times - optional int32 retry_times = 6; - - // Ordinal number of name server. - optional int32 dns_server_index = 7; - - // Used only by TCP and DOT. True for new connections. - optional bool connected = 8; - - optional int32 latency_micros = 9; - - optional android.stats.dnsresolver.LinuxErrno linux_errno = 10; -} - -message DnsQueryEvents { - repeated DnsQueryEvent dns_query_event = 1; -} diff --git a/core/proto/android/stats/docsui/docsui_enums.proto b/core/proto/android/stats/docsui/docsui_enums.proto deleted file mode 100644 index 5963f6a7f938..000000000000 --- a/core/proto/android/stats/docsui/docsui_enums.proto +++ /dev/null @@ -1,197 +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. - */ - -syntax = "proto2"; -package android.stats.docsui; -option java_multiple_files = true; - -enum LaunchAction { - UNKNOWN = 0; - OPEN = 1; - CREATE = 2; - GET_CONTENT = 3; - OPEN_TREE = 4; - PICK_COPY_DEST = 5; - BROWSE = 6; - OTHER = 7; -} - -enum MimeType { - MIME_UNKNOWN = 0; - MIME_NONE = 1; - MIME_ANY = 2; - MIME_APPLICATION = 3; - MIME_AUDIO = 4; - MIME_IMAGE = 5; - MIME_MESSAGE = 6; - MIME_MULTIPART = 7; - MIME_TEXT = 8; - MIME_VIDEO = 9; - MIME_OTHER = 10; -} - -enum Root { - ROOT_UNKNOWN = 0; - ROOT_NONE = 1; - ROOT_OTHER_DOCS_PROVIDER = 2; - ROOT_AUDIO = 3; - ROOT_DEVICE_STORAGE = 4; - ROOT_DOWNLOADS = 5; - ROOT_HOME = 6; - ROOT_IMAGES = 7; - ROOT_RECENTS = 8; - ROOT_VIDEOS = 9; - ROOT_MTP = 10; - ROOT_THIRD_PARTY_APP = 11; - ROOT_DOCUMENTS = 12; -} - -enum ContextScope { - SCOPE_UNKNOWN = 0; - SCOPE_FILES = 1; - SCOPE_PICKER = 2; -} - -enum Provider { - PROVIDER_UNKNOWN = 0; - PROVIDER_SYSTEM = 1; - PROVIDER_EXTERNAL = 2; -} - -enum FileOperation { - OP_UNKNOWN = 0; - OP_OTHER = 1; - OP_COPY = 2; - OP_COPY_INTRA_PROVIDER = 3; - OP_COPY_SYSTEM_PROVIDER = 4; - OP_COPY_EXTERNAL_PROVIDER = 5; - OP_MOVE = 6; - OP_MOVE_INTRA_PROVIDER = 7; - OP_MOVE_SYSTEM_PROVIDER = 8; - OP_MOVE_EXTERNAL_PROVIDER = 9; - OP_DELETE = 10; - OP_RENAME = 11; - OP_CREATE_DIR = 12; - OP_OTHER_ERROR = 13; - OP_DELETE_ERROR = 14; - OP_MOVE_ERROR = 15; - OP_COPY_ERROR = 16; - OP_RENAME_ERROR = 17; - OP_CREATE_DIR_ERROR = 18; - OP_COMPRESS_INTRA_PROVIDER = 19; - OP_COMPRESS_SYSTEM_PROVIDER = 20; - OP_COMPRESS_EXTERNAL_PROVIDER = 21; - OP_EXTRACT_INTRA_PROVIDER = 22; - OP_EXTRACT_SYSTEM_PROVIDER = 23; - OP_EXTRACT_EXTERNAL_PROVIDER = 24; - OP_COMPRESS_ERROR = 25; - OP_EXTRACT_ERROR = 26; -} - -enum SubFileOperation { - SUB_OP_UNKNOWN = 0; - SUB_OP_QUERY_DOC = 1; - SUB_OP_QUERY_CHILD = 2; - SUB_OP_OPEN_FILE = 3; - SUB_OP_READ_FILE = 4; - SUB_OP_CREATE_DOC = 5; - SUB_OP_WRITE_FILE = 6; - SUB_OP_DELETE_DOC = 7; - SUB_OP_OBTAIN_STREAM_TYPE = 8; - SUB_OP_QUICK_MOVE = 9; - SUB_OP_QUICK_COPY = 10; -} - -enum CopyMoveOpMode { - MODE_UNKNOWN = 0; - MODE_PROVIDER = 1; - MODE_CONVERTED = 2; - MODE_CONVENTIONAL = 3; -} - -enum Authority { - AUTH_UNKNOWN = 0; - AUTH_OTHER = 1; - AUTH_MEDIA = 2; - AUTH_STORAGE_INTERNAL = 3; - AUTH_STORAGE_EXTERNAL = 4; - AUTH_DOWNLOADS = 5; - AUTH_MTP = 6; -} - -enum UserAction { - ACTION_UNKNOWN = 0; - ACTION_OTHER = 1; - ACTION_GRID = 2; - ACTION_LIST = 3; - ACTION_SORT_NAME = 4; - ACTION_SORT_DATE = 5; - ACTION_SORT_SIZE = 6; - ACTION_SORT_TYPE = 7; - ACTION_SEARCH = 8; - ACTION_SHOW_SIZE = 9; - ACTION_HIDE_SIZE = 10; - ACTION_SETTINGS = 11; - ACTION_COPY_TO = 12; - ACTION_MOVE_TO = 13; - ACTION_DELETE = 14; - ACTION_RENAME = 15; - ACTION_CREATE_DIR = 16; - ACTION_SELECT_ALL = 17; - ACTION_SHARE = 18; - ACTION_OPEN = 19; - ACTION_SHOW_ADVANCED = 20; - ACTION_HIDE_ADVANCED = 21; - ACTION_NEW_WINDOW = 22; - ACTION_PASTE_CLIPBOARD = 23; - ACTION_COPY_CLIPBOARD = 24; - ACTION_DRAG_N_DROP = 25; - ACTION_DRAG_N_DROP_MULTI_WINDOW = 26; - ACTION_CUT_CLIPBOARD = 27; - ACTION_COMPRESS = 28; - ACTION_EXTRACT_TO = 29; - ACTION_VIEW_IN_APPLICATION = 30; - ACTION_INSPECTOR = 31; - ACTION_SEARCH_CHIP = 32; - ACTION_SEARCH_HISTORY = 33; -} - -enum InvalidScopedAccess { - SCOPED_DIR_ACCESS_UNKNOWN = 0; - SCOPED_DIR_ACCESS_INVALID_ARGUMENTS = 1; - SCOPED_DIR_ACCESS_INVALID_DIRECTORY = 2; - SCOPED_DIR_ACCESS_ERROR = 3; - SCOPED_DIR_ACCESS_DEPRECATED = 4; -} - -enum SearchType { - TYPE_UNKNOWN = 0; - TYPE_CHIP_IMAGES = 1; - TYPE_CHIP_AUDIOS = 2; - TYPE_CHIP_VIDEOS = 3; - TYPE_CHIP_DOCS = 4; - TYPE_SEARCH_HISTORY = 5; - TYPE_SEARCH_STRING = 6; - TYPE_CHIP_LARGE_FILES = 7; - TYPE_CHIP_FROM_THIS_WEEK = 8; -} - -enum SearchMode { - SEARCH_UNKNOWN = 0; - SEARCH_KEYWORD = 1; - SEARCH_CHIPS = 2; - SEARCH_KEYWORD_N_CHIPS = 3; -} diff --git a/core/proto/android/stats/hdmi/enums.proto b/core/proto/android/stats/hdmi/enums.proto deleted file mode 100644 index acb8899fbdd9..000000000000 --- a/core/proto/android/stats/hdmi/enums.proto +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -syntax = "proto2"; - -package android.stats.hdmi; -option java_multiple_files = true; -option java_outer_classname = "HdmiStatsEnums"; - -// HDMI CEC logical addresses. -// Values correspond to "CEC Table 5 Logical Addresses" in the HDMI CEC 1.4b spec. -enum LogicalAddress { - LOGICAL_ADDRESS_UNKNOWN = -1; - TV = 0; - RECORDING_DEVICE_1 = 1; - RECORDING_DEVICE_2 = 2; - TUNER_1 = 3; - PLAYBACK_DEVICE_1 = 4; - AUDIO_SYSTEM = 5; - TUNER_2 = 6; - TUNER_3 = 7; - PLAYBACK_DEVICE_2 = 8; - RECORDING_DEVICE_3 = 9; - TUNER_4 = 10; - PLAYBACK_DEVICE_3 = 11; - RESERVED_1 = 12; - RESERVED_2 = 13; - SPECIFIC_USE = 14; - UNREGISTERED_OR_BROADCAST = 15; -} - -// The relationship between two paths. -// Values correspond exactly to PathRelationship in com.android.server.hdmi.Constants. -enum PathRelationship { - RELATIONSHIP_TO_ACTIVE_SOURCE_UNKNOWN = 0; - DIFFERENT_BRANCH = 1; - ANCESTOR = 2; - DESCENDANT = 3; - SIBLING = 4; - SAME = 5; -} - -// The result of attempting to send a HDMI CEC message. -// Values correspond to the constants in android.hardware.tv.cec.V1_0.SendMessageResult, -// offset by 10. -enum SendMessageResult { - SEND_MESSAGE_RESULT_UNKNOWN = 0; - SUCCESS = 10; - NACK = 11; - BUSY = 12; - FAIL = 13; -} - -// Whether a HDMI CEC message is sent from this device, to this device, or neither. -enum MessageDirection { - MESSAGE_DIRECTION_UNKNOWN = 0; - MESSAGE_DIRECTION_OTHER = 1; // None of the other options. - OUTGOING = 2; // Sent from this device. - INCOMING = 3; // Sent to this device. - TO_SELF = 4; // Sent from this device, to this device. Indicates a bug. -} - -// User control commands. Each value can represent an individual command, or a set of commands. -// Values correspond to "CEC Table 30 UI Command Codes" in the HDMI CEC 1.4b spec, offset by 0x100. -enum UserControlPressedCommand { - USER_CONTROL_PRESSED_COMMAND_UNKNOWN = 0; - - // Represents all codes that are not represented by another value. - USER_CONTROL_PRESSED_COMMAND_OTHER = 1; - - // Represents all number codes (codes 0x1E through 0x29). - NUMBER = 2; - - // Navigation - SELECT = 0x100; - UP = 0x101; - DOWN = 0x102; - LEFT = 0x103; - RIGHT = 0x104; - RIGHT_UP = 0x105; - RIGHT_DOWN = 0x106; - LEFT_UP = 0x107; - LEFT_DOWN = 0x108; - EXIT = 0x10D; - - // Volume - VOLUME_UP = 0x141; - VOLUME_DOWN = 0x142; - VOLUME_MUTE = 0x143; - - // Power - POWER = 0x140; - POWER_TOGGLE = 0x16B; - POWER_OFF = 0x16C; - POWER_ON = 0x16D; -} - -// Reason parameter of the <Feature Abort> message. -// Values correspond to "CEC Table 29 Operand Descriptions" in the HDMI CEC 1.4b spec, -// offset by 10. -enum FeatureAbortReason { - FEATURE_ABORT_REASON_UNKNOWN = 0; - UNRECOGNIZED_OPCODE = 10; - NOT_IN_CORRECT_MODE_TO_RESPOND = 11; - CANNOT_PROVIDE_SOURCE = 12; - INVALID_OPERAND = 13; - REFUSED = 14; - UNABLE_TO_DETERMINE = 15; -}
\ No newline at end of file diff --git a/core/proto/android/stats/intelligence/enums.proto b/core/proto/android/stats/intelligence/enums.proto deleted file mode 100644 index 0c210e3fd08f..000000000000 --- a/core/proto/android/stats/intelligence/enums.proto +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -syntax = "proto2"; - -package android.stats.intelligence; -option java_outer_classname = "IntelligenceStatsEnums"; - -enum Status { - // The value wasn't set. - // protoc requires enum values to be unique by package rather than enum type. - // This forces us to prefix the enum values. - STATUS_UNKNOWN = 0; - // The event succeeded. - STATUS_SUCCEEDED = 1; - // The event had an error. - STATUS_FAILED = 2; -} - -enum EventType { - // The value wasn't set. - EVENT_UNKNOWN = 0; - // ContentSuggestionsService classifyContentSelections call. - EVENT_CONTENT_SUGGESTIONS_CLASSIFY_CONTENT_CALL = 1; - // ContentSuggestionsService suggestContentSelections call. - EVENT_CONTENT_SUGGESTIONS_SUGGEST_CONTENT_CALL = 2; -} diff --git a/core/proto/android/stats/launcher/Android.bp b/core/proto/android/stats/launcher/Android.bp deleted file mode 100644 index 976a0b8634a3..000000000000 --- a/core/proto/android/stats/launcher/Android.bp +++ /dev/null @@ -1,40 +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. - -java_library { - name: "launcherprotosnano", - proto: { - type: "nano", - output_params: ["store_unknown_fields=true"], - include_dirs: ["external/protobuf/src"], - }, - - sdk_version: "current", - srcs: [ - "*.proto", - ], -} - -java_library { - name: "launcherprotoslite", - proto: { - type: "lite", - include_dirs: ["external/protobuf/src"], - }, - - sdk_version: "current", - srcs: [ - "*.proto", - ], -} diff --git a/core/proto/android/stats/launcher/launcher.proto b/core/proto/android/stats/launcher/launcher.proto deleted file mode 100644 index fc177d57b193..000000000000 --- a/core/proto/android/stats/launcher/launcher.proto +++ /dev/null @@ -1,88 +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. - */ - -syntax = "proto2"; -package android.stats.launcher; -option java_multiple_files = true; - -enum LauncherAction { - DEFAULT_ACTION = 0; - LAUNCH_APP = 1; - LAUNCH_TASK = 2; - DISMISS_TASK = 3; - LONGPRESS = 4; - DRAGDROP = 5; - SWIPE_UP = 6; - SWIPE_DOWN = 7; - SWIPE_LEFT = 8; - SWIPE_RIGHT = 9; -} - -enum LauncherState { - LAUNCHER_STATE_UNSPECIFIED = 0; - BACKGROUND = 1; - HOME = 2; - OVERVIEW = 3; - ALLAPPS = 4; - UNCHANGED = 5; -} - -message LauncherTarget { - enum Type { - NONE = 0; - ITEM_TYPE = 1; - CONTROL_TYPE = 2; - CONTAINER_TYPE = 3; - } - enum Item { - DEFAULT_ITEM = 0; - APP_ICON = 1; - SHORTCUT = 2; - WIDGET = 3; - FOLDER_ICON = 4; - DEEPSHORTCUT = 5; - SEARCHBOX = 6; - EDITTEXT = 7; - NOTIFICATION = 8; - TASK = 9; - } - enum Container { - DEFAULT_CONTAINER = 0; - HOTSEAT = 1; - FOLDER = 2; - PREDICTION = 3; - SEARCHRESULT = 4; - } - enum Control { - DEFAULT_CONTROL = 0; - MENU = 1; - UNINSTALL = 2; - REMOVE = 3; - } - optional Type type = 1; - optional Item item = 2; - optional Container container = 3; - optional Control control = 4; - optional string launch_component = 5; - optional int32 page_id = 6; - optional int32 grid_x = 7; - optional int32 grid_y = 8; -} - -message LauncherExtension { - repeated LauncherTarget src_target = 1; - repeated LauncherTarget dst_target = 2; -} diff --git a/core/proto/android/stats/location/location_enums.proto b/core/proto/android/stats/location/location_enums.proto deleted file mode 100644 index 553c01c5d0dd..000000000000 --- a/core/proto/android/stats/location/location_enums.proto +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -syntax = "proto2"; - -package android.stats.location; -option java_outer_classname = "LocationStatsEnums"; - - -// APIs from LocationManagerService -enum LocationManagerServiceApi { - API_UNKNOWN = 0; - API_REQUEST_LOCATION_UPDATES = 1; - API_ADD_GNSS_MEASUREMENTS_LISTENER = 2; - API_REGISTER_GNSS_STATUS_CALLBACK = 3; - API_REQUEST_GEOFENCE = 4; - API_SEND_EXTRA_COMMAND = 5; -} - -enum UsageState { - USAGE_STARTED = 0; - USAGE_ENDED = 1; -} - -// Type of location providers -enum ProviderType { - PROVIDER_UNKNOWN = 0; - PROVIDER_NETWORK = 1; - PROVIDER_GPS = 2; - PROVIDER_PASSIVE = 3; - PROVIDER_FUSED = 4; -} - -// Type of Callback passed in for this API -enum CallbackType { - CALLBACK_UNKNOWN = 0; - // Current API does not need a callback, e.g. sendExtraCommand - CALLBACK_NOT_APPLICABLE = 1; - CALLBACK_LISTENER = 2; - CALLBACK_PENDING_INTENT = 3; -} - -// Possible values for mQuality field in -// frameworks/base/location/java/android/location/LocationRequest.java -enum LocationRequestQuality { - QUALITY_UNKNOWN = 0; - ACCURACY_FINE = 100; - ACCURACY_BLOCK = 102; - ACCURACY_CITY = 104; - POWER_NONE = 200; - POWER_LOW = 201; - POWER_HIGH = 203; -} - -// Bucketized values for interval field in -// frameworks/base/location/java/android/location/LocationRequest.java -enum LocationRequestIntervalBucket { - INTERVAL_UNKNOWN = 0; - INTERVAL_BETWEEN_0_SEC_AND_1_SEC = 1; - INTERVAL_BETWEEN_1_SEC_AND_5_SEC = 2; - INTERVAL_BETWEEN_5_SEC_AND_1_MIN = 3; - INTERVAL_BETWEEN_1_MIN_AND_10_MIN = 4; - INTERVAL_BETWEEN_10_MIN_AND_1_HOUR = 5; - INTERVAL_LARGER_THAN_1_HOUR = 6; -} - -// Bucketized values for small displacement field in -// frameworks/base/location/java/android/location/LocationRequest.java -// Value in meters. -enum SmallestDisplacementBucket { - DISTANCE_UNKNOWN = 0; - DISTANCE_ZERO = 1; - DISTANCE_BETWEEN_0_AND_100 = 2; - DISTANCE_LARGER_THAN_100 = 3; -} - -// Bucketized values for expire_in field in -// frameworks/base/location/java/android/location/LocationRequest.java -enum ExpirationBucket { - EXPIRATION_UNKNOWN = 0; - EXPIRATION_BETWEEN_0_AND_20_SEC = 1; - EXPIRATION_BETWEEN_20_SEC_AND_1_MIN = 2; - EXPIRATION_BETWEEN_1_MIN_AND_10_MIN = 3; - EXPIRATION_BETWEEN_10_MIN_AND_1_HOUR = 4; - EXPIRATION_LARGER_THAN_1_HOUR = 5; - EXPIRATION_NO_EXPIRY = 6; -} - -// Bucketized values for radius field in -// frameworks/base/location/java/android/location/Geofence.java -// Value in meters. -enum GeofenceRadiusBucket { - RADIUS_UNKNOWN = 0; - RADIUS_BETWEEN_0_AND_100 = 1; - RADIUS_BETWEEN_100_AND_200 = 2; - RADIUS_BETWEEN_200_AND_300 = 3; - RADIUS_BETWEEN_300_AND_1000 = 4; - RADIUS_BETWEEN_1000_AND_10000 = 5; - RADIUS_LARGER_THAN_100000 = 6; - RADIUS_NEGATIVE = 7; -} - -// Caller Activity Importance. -enum ActivityImportance { - IMPORTANCE_UNKNOWN = 0; - IMPORTANCE_TOP = 1; - IMPORTANCE_FORGROUND_SERVICE = 2; - IMPORTANCE_BACKGROUND = 3; -} diff --git a/core/proto/android/stats/mediametrics/mediametrics.proto b/core/proto/android/stats/mediametrics/mediametrics.proto deleted file mode 100644 index 9d491263f8e0..000000000000 --- a/core/proto/android/stats/mediametrics/mediametrics.proto +++ /dev/null @@ -1,343 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -syntax = "proto2"; - -package android.stats.mediametrics; - -/** - * Track how we arbitrate between microphone/input requests. - * Logged from - * frameworks/av/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp - * frameworks/av/services/mediaanalytics/statsd_audiopolicy.cpp - * Next Tag: 10 - */ -message AudioPolicyData { - optional int32 status = 1; - optional string request_source = 2; - optional string request_package = 3; - optional int32 request_session = 4; - optional string request_device = 5; - optional string active_source = 6; - optional string active_package = 7; - optional int32 active_session = 8; - optional string active_device = 9; -} - -/** - * Track properties of audio recording - * Logged from - * frameworks/av/media/libaudioclient/AudioRecord.cpp - * frameworks/av/services/mediaanalytics/statsd_audiorecord.cpp - * Next Tag: 16 - */ -message AudioRecordData { - optional string encoding = 1; - optional string source = 2; - optional int32 latency = 3; - optional int32 samplerate = 4; - optional int32 channels = 5; - optional int64 created_millis = 6; - optional int64 duration_millis = 7; - optional int32 count = 8; - optional int32 error_code = 9; - optional string error_function = 10; - optional int32 port_id = 11; - optional int32 frame_count = 12; - optional string attributes = 13; - optional int64 channel_mask = 14; - optional int64 start_count = 15; - -} - -/** - * Track audio thread performance data - * Logged from - * frameworks/av/media/libnblog/ReportPerformance.cpp - * frameworks/av/services/mediaanalytics/statsd_audiothread.cpp - * Next Tag: 28 - */ -message AudioThreadData { - optional string type = 1; - optional int32 framecount = 2; - optional int32 samplerate = 3; - optional string work_millis_hist = 4; - optional string latency_millis_hist = 5; - optional string warmup_millis_hist = 6; - optional int64 underruns = 7; - optional int64 overruns = 8; - optional int64 active_millis = 9; - optional int64 duration_millis = 10; - - optional int32 id = 11; - optional int32 port_id = 12; - optional int32 sample_rate = 13; - optional int64 channel_mask = 14; - optional string encoding = 15; - optional int32 frame_count = 16; - optional string output_device = 17; - optional string input_device = 18; - optional double io_jitter_mean_millis = 19; - optional double io_jitter_stddev_millis = 20; - optional double process_time_mean_millis = 21; - optional double process_time_stddev_millis = 22; - optional double timestamp_jitter_mean_millis = 23; - optional double timestamp_jitter_stddev_millis = 24; - optional double latency_mean_millis = 25; - optional double latency_stddev_millis = 26; - -} - -/** - * Track audio track playback data - * Logged from - * frameworks/av/media/libaudioclient/AudioTrack.cpp - * frameworks/av/services/mediaanalytics/statsd_audiotrack.cpp - * Next Tag: 12 - */ -message AudioTrackData { - optional string stream_type = 1; - optional string content_type = 2; - optional string track_usage = 3; - optional int32 sample_rate = 4; - optional int64 channel_mask = 5; - - optional int32 underrun_frames = 6; - optional int32 startup_glitch = 7; - - optional int32 port_id = 8; - optional string encoding = 9; - optional int32 frame_count = 10; - optional string attributes = 11; - - -} - -/** - * Track Media Codec usage - * Logged from: - * frameworks/av/media/libstagefright/MediaCodec.cpp - * frameworks/av/services/mediaanalytics/statsd_codec.cpp - * Next Tag: 26 - */ -message CodecData { - optional string codec = 1; - optional string mime = 2; - optional string mode = 3; - optional int32 encoder = 4; - optional int32 secure = 5; - optional int32 width = 6; - optional int32 height = 7; - optional int32 rotation = 8; - optional int32 crypto = 9; - optional int32 profile = 10; - optional int32 level = 11; - optional int32 max_width = 12; - optional int32 max_height = 13; - optional int32 error_code = 14; - optional string error_state = 15; - optional int64 latency_max = 16; - optional int64 latency_min = 17; - optional int64 latency_avg = 18; - optional int64 latency_count = 19; - optional int64 latency_unknown = 20; - optional int32 queue_input_buffer_error = 21; - optional int32 queue_secure_input_buffer_error = 22; - optional string bitrate_mode = 23; - optional int32 bitrate = 24; - optional int64 lifetime_millis = 25; -} - -/** - * Track Media Extractor (pulling video/audio streams out of containers) usage - * Logged from: - * frameworks/av/media/libstagefright/RemoteMediaExtractor.cpp - * frameworks/av/services/mediaanalytics/statsd_extractor.cpp - * Next Tag: 4 - */ -message ExtractorData { - optional string format = 1; - optional string mime = 2; - optional int32 tracks = 3; -} - -/** - * Track Media Player usage - * this handles both nuplayer and nuplayer2 - * Logged from: - * frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp - * frameworks/av/media/libmediaplayer2/nuplayer2/NuPlayer2Driver.cpp - * frameworks/av/services/mediaanalytics/statsd_nuplayer.cpp - * Next Tag: 21 - */ -message NuPlayerData { - optional string whichPlayer = 1; - - optional string video_mime = 2; - optional string video_codec = 3; - optional int32 width = 4; - optional int32 height = 5; - optional int64 frames = 6; - optional int64 frames_dropped = 7; - optional double framerate = 8; - optional string audio_mime = 9; - optional string audio_codec = 10; - optional int64 duration_millis = 11; - optional int64 playing_millis = 12; - optional int32 error = 13; - optional int32 error_code = 14; - optional string error_state = 15; - optional string data_source_type = 16; - optional int64 rebuffering_millis = 17; - optional int32 rebuffers = 18; - optional int32 rebuffer_at_exit = 19; - optional int64 frames_dropped_startup = 20; -} - -/** - * Track information about recordings (e.g. camcorder) - * Logged from - * frameworks/av/media/libmediaplayerservice/StagefrightRecorder.cpp - * frameworks/av/services/mediaanalytics/if_statsd.cpp - * Next Tag: 22 - */ -message RecorderData { - optional string audio_mime = 1; - optional string video_mime = 2; - optional int32 video_profile = 3; - optional int32 video_level = 4; - optional int32 width = 5; - optional int32 height = 6; - optional int32 rotation = 7; - optional int32 framerate = 8; - optional int32 capture_fps = 9; - optional double capture_fps_enable = 10; - optional int64 duration_millis = 11; - optional int64 paused_millis = 12; - optional int32 paused_count = 13; - optional int32 audio_bitrate = 14; - optional int32 audio_channels = 15; - optional int32 audio_samplerate = 16; - optional int32 movie_timescale = 17; - optional int32 audio_timescale = 18; - optional int32 video_timescale = 19; - optional int32 video_bitrate = 20; - optional int32 iframe_interval = 21; -} - -enum StreamType { - STREAM_TYPE_UNKNOWN = 0; - STREAM_TYPE_OTHER = 1; - STREAM_TYPE_PROGRESSIVE = 2; - STREAM_TYPE_DASH = 3; - STREAM_TYPE_HLS = 4; - STREAM_TYPE_SS = 5; -} - -enum DrmType { - DRM_TYPE_NONE = 0; - DRM_TYPE_OTHER = 1; - DRM_TYPE_PLAY_READY = 2; - DRM_TYPE_WV_L1 = 3; - DRM_TYPE_WV_L3 = 4; -} - -enum PlaybackType { - PLAYBACK_TYPE_VOD = 0; - PLAYBACK_TYPE_LIVE = 1; - PLAYBACK_TYPE_OTHER = 2; -} - -enum ContentType { - CONTENT_TYPE_MAIN = 0; - CONTENT_TYPE_AD = 1; - CONTENT_TYPE_OTHER = 2; -} - -enum StreamSourceType { - STREAM_SOURCE_UNKNOWN = 0; - STREAM_SOURCE_NETWORK = 1; - STREAM_SOURCE_DEVICE = 2; - STREAM_SOURCE_MIXED = 3; -} -enum NetworkType { - NETWORK_TYPE_NONE = 0; - NETWORK_TYPE_OTHER = 1; - NETWORK_TYPE_WIFI = 2; - NETWORK_TYPE_ETHERNET = 3; - NETWORK_TYPE_2G = 4; - NETWORK_TYPE_3G = 5; - NETWORK_TYPE_4G = 6; - NETWORK_TYPE_5G_NSA = 7; - NETWORK_TYPE_5G_SA = 8; -} - -enum PlaybackState { - // Playback has not started (initial state) - NOT_STARTED = 0; - // Playback is buffering in the background for initial playback start - JOINING_BACKGROUND = 1; - // Playback is buffering in the foreground for initial playback start - JOINING_FOREGROUND = 2; - // Playback is actively playing - PLAYING = 3; - // Playback is paused but ready to play - PAUSED = 4; - // Playback is handling a seek - SEEKING = 5; - // Playback is buffering to resume active playback - BUFFERING = 6; - // Playback is buffering while paused - PAUSED_BUFFERING = 7; - // Playback is suppressed (e.g. due to audio focus loss) - SUPPRESSED = 8; - // Playback is suppressed (e.g. due to audio focus loss) while buffering to resume a - // playback - SUPPRESSED_BUFFERING = 9; - // Playback has reached the end of the media - ENDED = 10; - // Playback is stopped and can be restarted - STOPPED = 11; - // Playback is stopped due a fatal error and can be retried - FAILED = 12; - // Playback is interrupted by an ad - INTERRUPTED_BY_AD = 13; - // Playback is abandoned before reaching the end of the media - ABANDONED = 14; -} - -enum PlaybackErrorCode { - ERROR_CODE_UNKNOWN = 0; - ERROR_CODE_OTHER = 1; - ERROR_CODE_RUNTIME = 2; -} - -enum TrackType { - AUDIO = 0; - VIDEO = 1; - TEXT = 2; -} -enum TrackState { - OFF = 0; - ON = 1; -} -enum TrackChangeReason { - REASON_UNKNOWN = 0; - REASON_OTHER = 1; - REASON_INITIAL = 2; - REASON_MANUAL = 3; - REASON_ADAPTIVE = 4; -} diff --git a/core/proto/android/stats/mediaprovider/mediaprovider_enums.proto b/core/proto/android/stats/mediaprovider/mediaprovider_enums.proto deleted file mode 100644 index 138782bf5d19..000000000000 --- a/core/proto/android/stats/mediaprovider/mediaprovider_enums.proto +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -syntax = "proto2"; -package android.stats.mediaprovider; -option java_multiple_files = true; - -enum VolumeType { - // Volume is unknown - UNKNOWN = 0; - // Volume is MediaStore.VOLUME_INTERNAL - INTERNAL = 1; - // Volume is MediaStore.VOLUME_EXTERNAL_PRIMARY - EXTERNAL_PRIMARY = 2; - // Volume is non-primary external storage - EXTERNAL_OTHER = 3; -} diff --git a/core/proto/android/stats/otaupdate/updateengine_enums.proto b/core/proto/android/stats/otaupdate/updateengine_enums.proto deleted file mode 100644 index a6e9919ba606..000000000000 --- a/core/proto/android/stats/otaupdate/updateengine_enums.proto +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -syntax = "proto2"; -package android.stats.otaupdate; - -// The payload type of an OTA update attempt on A/B devices. -enum PayloadType { - FULL = 10000; - DELTA = 10001; -} - -// The attempt result reported by the update engine for an OTA update. -enum AttemptResult { - UPDATE_SUCCEEDED = 10000; - INTERNAL_ERROR = 10001; - PAYLOAD_DOWNLOAD_ERROR = 10002; - METADATA_MALFORMED = 10003; - OPERATION_MALFORMED = 10004; - OPERATION_EXECUTION_ERROR = 10005; - METADATA_VERIFICATION_FAILED = 10006; - PAYLOAD_VERIFICATION_FAILED = 10007; - VERIFICATION_FAILED = 10008; - POSTINSTALL_FAILED = 10009; - ABNORMAL_TERMINATION = 10010; - UPDATE_CANCELED = 10011; - UPDATE_SUCCEEDED_NOT_ACTIVE = 10012; -} - -// The error code reported by the update engine after an OTA update attempt -// on A/B devices. More details in system/update_engine/common/error_code.h -enum ErrorCode { - SUCCESS = 10000; - ERROR = 10001; - FILESYSTEM_COPIER_ERROR = 10004; - POST_INSTALL_RUNNER_ERROR = 10005; - PAYLOAD_MISMATCHED_TYPE_ERROR = 10006; - INSTALL_DEVICE_OPEN_ERROR = 10007; - KERNEL_DEVICE_OPEN_ERROR = 10008; - DOWNLOAD_TRANSFER_ERROR = 10009; - PAYLOAD_HASH_MISMATCH_ERROR = 10010; - PAYLOAD_SIZE_MISMATCH_ERROR = 10011; - DOWNLOAD_PAYLOAD_VERIFICATION_ERROR = 10012; - DOWNLOAD_NEW_PARTITION_INFO_ERROR = 10013; - DOWNLOAD_WRITE_ERROR = 10014; - NEW_ROOTFS_VERIFICATION_ERROR = 10015; - SIGNED_DELTA_PAYLOAD_EXPECTED_ERROR = 10017; - DOWNLOAD_PAYLOAD_PUB_KEY_VERIFICATION_ERROR = 10018; - DOWNLOAD_STATE_INITIALIZATION_ERROR = 10020; - DOWNLOAD_INVALID_METADATA_MAGIC_STRING = 10021; - DOWNLOAD_SIGNATURE_MISSING_IN_MANIFEST = 10022; - DOWNLOAD_MANIFEST_PARSE_ERROR = 10023; - DOWNLOAD_METADATA_SIGNATURE_ERROR = 10024; - DOWNLOAD_METADATA_SIGNATURE_VERIFICATION_ERROR = 10025; - DOWNLOAD_METADATA_SIGNATURE_MISMATCH = 10026; - DOWNLOAD_OPERATION_HASH_VERIFICATION_ERROR = 10027; - DOWNLOAD_OPERATION_EXECUTION_ERROR = 10028; - DOWNLOAD_OPERATION_HASH_MISMATCH = 10029; - DOWNLOAD_INVALID_METADATA_SIZE = 10032; - DOWNLOAD_INVALID_METADATA_SIGNATURE = 10033; - DOWNLOAD_OPERATION_HASH_MISSING_ERROR = 10038; - DOWNLOAD_METADATA_SIGNATURE_MISSING_ERROR = 10039; - UNSUPPORTED_MAJOR_PAYLOAD_VERSION = 10044; - UNSUPPORTED_MINOR_PAYLOAD_VERSION = 10045; - FILESYSTEM_VERIFIER_ERROR = 10047; - USER_CANCELED = 10048; - PAYLOAD_TIMESTAMP_ERROR = 10051; - UPDATED_BUT_NOT_ACTIVE = 10052; -} diff --git a/core/proto/android/stats/style/Android.bp b/core/proto/android/stats/style/Android.bp deleted file mode 100644 index f085a52f8cdb..000000000000 --- a/core/proto/android/stats/style/Android.bp +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (C) 2019 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -java_library { - name: "styleprotosnano", - proto: { - type: "nano", - output_params: ["store_unknown_fields=true"], - include_dirs: ["external/protobuf/src"], - }, - - sdk_version: "current", - srcs: [ - "*.proto", - ], -} diff --git a/core/proto/android/stats/style/style_enums.proto b/core/proto/android/stats/style/style_enums.proto deleted file mode 100644 index 2876882e78ba..000000000000 --- a/core/proto/android/stats/style/style_enums.proto +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -syntax = "proto2"; -package android.stats.style; -option java_multiple_files = true; - -enum Action { - DEFAULT_ACTION = 0; - ONRESUME = 1; - ONSTOP = 2; - PICKER_SELECT = 3; - PICKER_APPLIED = 4; - WALLPAPER_OPEN_CATEGORY = 5; - WALLPAPER_SELECT = 6; - WALLPAPER_APPLIED = 7; - WALLPAPER_EXPLORE = 8; - WALLPAPER_DOWNLOAD = 9; - WALLPAPER_REMOVE = 10; - LIVE_WALLPAPER_DOWNLOAD_SUCCESS = 11; - LIVE_WALLPAPER_DOWNLOAD_FAILED = 12; - LIVE_WALLPAPER_DOWNLOAD_CANCELLED = 13; - LIVE_WALLPAPER_DELETE_SUCCESS = 14; - LIVE_WALLPAPER_DELETE_FAILED = 15; - LIVE_WALLPAPER_APPLIED = 16; - LIVE_WALLPAPER_INFO_SELECT = 17; - LIVE_WALLPAPER_CUSTOMIZE_SELECT = 18; - LIVE_WALLPAPER_QUESTIONNAIRE_SELECT = 19; - LIVE_WALLPAPER_QUESTIONNAIRE_APPLIED = 20; - LIVE_WALLPAPER_EFFECT_SHOW = 21; - APP_LAUNCHED = 22; -} - -enum LocationPreference { - LOCATION_PREFERENCE_UNSPECIFIED = 0; - LOCATION_UNAVAILABLE = 1; - LOCATION_CURRENT = 2; - LOCATION_MANUAL = 3; -} - -enum DatePreference { - DATE_PREFERENCE_UNSPECIFIED = 0; - DATE_UNAVAILABLE = 1; - DATE_MANUAL = 2; -} - -enum LaunchedPreference { - LAUNCHED_PREFERENCE_UNSPECIFIED = 0; - LAUNCHED_LAUNCHER = 1; - LAUNCHED_SETTINGS = 2; - LAUNCHED_SUW = 3; - LAUNCHED_TIPS = 4; - LAUNCHED_LAUNCH_ICON = 5; - LAUNCHED_CROP_AND_SET_ACTION = 6; - LAUNCHED_DEEP_LINK = 7; -} diff --git a/core/proto/android/stats/sysui/notification_enums.proto b/core/proto/android/stats/sysui/notification_enums.proto deleted file mode 100644 index 30bdecae07d1..000000000000 --- a/core/proto/android/stats/sysui/notification_enums.proto +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -syntax = "proto2"; - -package android.stats.sysui; - -// Enum used in NotificationReported and NotificationChannelModified atoms -enum NotificationImportance { // Constants from NotificationManager.java - IMPORTANCE_UNSPECIFIED = -1000; // Should not occur for real notifications. - IMPORTANCE_NONE = 0; // No importance: does not show in the shade. - IMPORTANCE_MIN = 1; // Minimum to show in the shade. - IMPORTANCE_LOW = 2; // Shows in shade, maybe status bar, no buzz/beep. - IMPORTANCE_DEFAULT = 3; // Shows everywhere, makes noise, no heads-up. - IMPORTANCE_HIGH = 4; // Shows everywhere, makes noise, heads-up, may full-screen. - IMPORTANCE_IMPORTANT_CONVERSATION = 5; // High + isImportantConversation(). -} diff --git a/core/proto/android/stats/textclassifier/Android.bp b/core/proto/android/stats/textclassifier/Android.bp deleted file mode 100644 index bf9022711206..000000000000 --- a/core/proto/android/stats/textclassifier/Android.bp +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (C) 2019 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -java_library_static { - name: "textclassifierprotoslite", - proto: { - type: "lite", - }, - srcs: [ - "*.proto", - ], -}
\ No newline at end of file diff --git a/core/proto/android/stats/textclassifier/textclassifier_enums.proto b/core/proto/android/stats/textclassifier/textclassifier_enums.proto deleted file mode 100644 index 4be7b7c2df7c..000000000000 --- a/core/proto/android/stats/textclassifier/textclassifier_enums.proto +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -syntax = "proto2"; -package android.stats.textclassifier; -option java_multiple_files = true; - -enum EventType { - TYPE_UNKNOWN = 0; - // User started a new selection. - SELECTION_STARTED = 1; - // User modified an existing selection. - SELECTION_MODIFIED = 2; - // Smart selection triggered for a single token (word). - SMART_SELECTION_SINGLE = 3; - // Smart selection triggered spanning multiple tokens (words). - SMART_SELECTION_MULTI = 4; - // Something else other than user or the default TextClassifier triggered a selection. - AUTO_SELECTION = 5; - // Smart actions shown to the user. - ACTIONS_SHOWN = 6; - // User clicked a link. - LINK_CLICKED = 7; - // User typed over the selection. - OVERTYPE = 8; - // User clicked on Copy action. - COPY_ACTION = 9; - // User clicked on Paste action. - PASTE_ACTION = 10; - // User clicked on Cut action. - CUT_ACTION = 11; - // User clicked on Share action. - SHARE_ACTION = 12; - // User clicked on a Smart action. - SMART_ACTION = 13; - // User dragged+dropped the selection. - SELECTION_DRAG = 14; - // Selection is destroyed. - SELECTION_DESTROYED = 15; - // User clicked on a custom action. - OTHER_ACTION = 16; - // User clicked on Select All action - SELECT_ALL = 17; - // User reset the smart selection. - SELECTION_RESET = 18; - // User composed a reply. - MANUAL_REPLY = 19; - // TextClassifier generated some actions - ACTIONS_GENERATED = 20; - // Some text links were generated - LINKS_GENERATED = 21; -} - -enum WidgetType { - WIDGET_TYPE_UNKNOWN = 0; - // Standard TextView - WIDGET_TYPE_TEXTVIEW = 1; - // EditText - WIDGET_TYPE_EDITTEXT = 2; - // Not selectable textview - WIDGET_TYPE_UNSELECTABLE_TEXTVIEW = 3; - // Standard Webview - WIDGET_TYPE_WEBVIEW = 4; - // Editable TextView - WIDGET_TYPE_EDIT_WEBVIEW = 5; - // Custom text widget - WIDGET_TYPE_CUSTOM_TEXTVIEW = 6; - // Custom editable text widget. - WIDGET_TYPE_CUSTOM_EDITTEXT = 7; - // Non-selectable text widget. - WIDGET_TYPE_CUSTOM_UNSELECTABLE_TEXTVIEW = 8; - // Notification - WIDGET_TYPE_NOTIFICATION = 9; -} diff --git a/core/proto/android/stats/tls/enums.proto b/core/proto/android/stats/tls/enums.proto deleted file mode 100644 index a64137d7caa9..000000000000 --- a/core/proto/android/stats/tls/enums.proto +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -syntax = "proto2"; -package android.stats.tls; - -// Keep in sync with -// external/conscrypt/{android,platform}/src/main/java/org/conscrypt/Platform.java -enum Protocol { - UNKNOWN_PROTO = 0; - SSL_V3 = 1; - TLS_V1 = 2; - TLS_V1_1 = 3; - TLS_V1_2 = 4; - TLS_V1_3 = 5; -} - -// Cipher suites' ids are based on IANA's database: -// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-4 -// -// If you add new cipher suite, make sure id is the same as in IANA's database (see link above) -// -// Keep in sync with -// external/conscrypt/{android,platform}/src/main/java/org/conscrypt/Platform.java -enum CipherSuite { - UNKNOWN_CIPHER_SUITE = 0x0000; - - TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA = 0xC00A; - TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = 0xC014; - TLS_RSA_WITH_AES_256_CBC_SHA = 0x0035; - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA = 0xC009; - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = 0xC013; - TLS_RSA_WITH_AES_128_CBC_SHA = 0x002F; - TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0x000A; - - // TLSv1.2 cipher suites - TLS_RSA_WITH_AES_128_GCM_SHA256 = 0x009C; - TLS_RSA_WITH_AES_256_GCM_SHA384 = 0x009D; - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02F; - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 = 0xC030; - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02B; - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02C; - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCA9; - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCA8; - - // Pre-Shared Key (PSK) cipher suites - TLS_PSK_WITH_AES_128_CBC_SHA = 0x008C; - TLS_PSK_WITH_AES_256_CBC_SHA = 0x008D; - TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA = 0xC035; - TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA = 0xC036; - TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 = 0xCCAC; - - // TLS 1.3 cipher suites - TLS_AES_128_GCM_SHA256 = 0x1301; - TLS_AES_256_GCM_SHA384 = 0x1302; - TLS_CHACHA20_POLY1305_SHA256 = 0x1303; -} - diff --git a/core/proto/android/telecomm/enums.proto b/core/proto/android/telecomm/enums.proto deleted file mode 100644 index 5ca4a85f7c6a..000000000000 --- a/core/proto/android/telecomm/enums.proto +++ /dev/null @@ -1,203 +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. - */ - -syntax = "proto2"; -package android.telecom; - -option java_outer_classname = "TelecomProtoEnums"; -option java_multiple_files = true; - -/** - * Call states, primarily used in CallState.java, - * Call.java, and CallsManager.java in packages/services. - */ -enum CallStateEnum { - /** - * Indicates that a call is new and not connected. This is used as the default state internally - * within Telecom and should not be used between Telecom and call services. Call services are - * not expected to ever interact with NEW calls, but {@link android.telecom.InCallService}s will - * see calls in this state. - */ - NEW = 0; - - /** - * The initial state of an outgoing {@code Call}. - * Common transitions are to {@link #DIALING} state for a successful call or - * {@link #DISCONNECTED} if it failed. - */ - CONNECTING = 1; - - /** - * The state of an outgoing {@code Call} when waiting on user to select a - * {@link android.telecom.PhoneAccount} through which to place the call. - */ - SELECT_PHONE_ACCOUNT = 2; - - /** - * Indicates that a call is outgoing and in the dialing state. A call transitions to this state - * once an outgoing call has begun (e.g., user presses the dial button in Dialer). Calls in this - * state usually transition to {@link #ACTIVE} if the call was answered or {@link #DISCONNECTED} - * if the call was disconnected somehow (e.g., failure or cancellation of the call by the user). - */ - DIALING = 3; - - /** - * Indicates that a call is incoming and the user still has the option of answering, rejecting, - * or doing nothing with the call. This state is usually associated with some type of audible - * ringtone. Normal transitions are to {@link #ACTIVE} if answered or {@link #DISCONNECTED} - * otherwise. - */ - RINGING = 4; - - /** - * Indicates that a call is currently connected to another party and a communication channel is - * open between them. The normal transition to this state is by the user answering a - * {@link #DIALING} call or a {@link #RINGING} call being answered by the other party. - */ - ACTIVE = 5; - - /** - * Indicates that the call is currently on hold. In this state, the call is not terminated - * but no communication is allowed until the call is no longer on hold. The typical transition - * to this state is by the user putting an {@link #ACTIVE} call on hold by explicitly performing - * an action, such as clicking the hold button. - */ - ON_HOLD = 6; - - /** - * Indicates that a call is currently disconnected. All states can transition to this state - * by the call service giving notice that the connection has been severed. When the user - * explicitly ends a call, it will not transition to this state until the call service confirms - * the disconnection or communication was lost to the call service currently responsible for - * this call (e.g., call service crashes). - */ - DISCONNECTED = 7; - - /** - * Indicates that the call was attempted (mostly in the context of outgoing, at least at the - * time of writing) but cancelled before it was successfully connected. - */ - ABORTED = 8; - - /** - * Indicates that the call is in the process of being disconnected and will transition next - * to a {@link #DISCONNECTED} state. - * <p> - * This state is not expected to be communicated from the Telephony layer, but will be reported - * to the InCall UI for calls where disconnection has been initiated by the user but the - * ConnectionService has confirmed the call as disconnected. - */ - DISCONNECTING = 9; - - /** - * Indicates that the call is in the process of being pulled to the local device. - * <p> - * This state should only be set on a call with - * {@link android.telecom.Connection#PROPERTY_IS_EXTERNAL_CALL} and - * {@link android.telecom.Connection#CAPABILITY_CAN_PULL_CALL}. - */ - PULLING = 10; - - /** - * Indicates that an incoming call has been answered by the in-call UI, but Telephony hasn't yet - * set the call to active. - */ - ANSWERED = 11; - - /** - * Indicates that the call is undergoing audio processing by a different app in the background. - * @see android.telecom.Call#STATE_AUDIO_PROCESSING - */ - AUDIO_PROCESSING = 12; - - /** - * Indicates that the call is in a fake ringing state. - * @see android.telecom.Call#STATE_SIMULATED_RINGING - */ - SIMULATED_RINGING = 13; -} - -// Disconnect causes for a call. Primarily used by android/telecom/DisconnectCause.java -enum DisconnectCauseEnum { - /** - * Disconnected because of an unknown or unspecified reason. - */ - UNKNOWN = 0; - - /** - * Disconnected because there was an error, such as a problem with the network. - */ - ERROR = 1; - - /** - * Disconnected because of a local user-initiated action, such as hanging up. - */ - LOCAL = 2; - - /** - * Disconnected because of a remote user-initiated action, such as the other party hanging up - * up. - */ - REMOTE = 3; - - /** - * Disconnected because it has been canceled. - */ - CANCELED = 4; - - /** - * Disconnected because there was no response to an incoming call. - */ - MISSED = 5; - - /** - * Disconnected because the user rejected an incoming call. - */ - REJECTED = 6; - - /** - * Disconnected because the other party was busy. - */ - BUSY = 7; - - /** - * Disconnected because of a restriction on placing the call, such as dialing in airplane - * mode. - */ - RESTRICTED = 8; - - /** - * Disconnected for reason not described by other disconnect codes. - */ - OTHER = 9; - - /** - * Disconnected because the connection manager did not support the call. The call will be tried - * again without a connection manager. See {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER}. - */ - CONNECTION_MANAGER_NOT_SUPPORTED = 10; - - /** - * Disconnected because the user did not locally answer the incoming call, but it was answered - * on another device where the call was ringing. - */ - ANSWERED_ELSEWHERE = 11; - - /** - * Disconnected because the call was pulled from the current device to another device. - */ - CALL_PULLED = 12; -} diff --git a/core/proto/android/telephony/enums.proto b/core/proto/android/telephony/enums.proto deleted file mode 100644 index b435fe7d9784..000000000000 --- a/core/proto/android/telephony/enums.proto +++ /dev/null @@ -1,288 +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. - */ - -syntax = "proto2"; -package android.telephony; - -option java_outer_classname = "TelephonyProtoEnums"; -option java_multiple_files = true; - -enum CallBearerEnum { - /** Call bearer is unknown or invalid */ - CALL_BEARER_UNKNOWN = 0; - - /** Call bearer is legacy CS */ - CALL_BEARER_CS = 1; - - /** Call bearer is IMS */ - CALL_BEARER_IMS = 2; -} - -enum CallDirectionEnum { - /** Call direction: unknown or invalid */ - CALL_DIRECTION_UNKNOWN = 0; - - /** Call direction: mobile originated (outgoing for this device) */ - CALL_DIRECTION_MO = 1; - - /** Call direction: mobile terminated (incoming for this device) */ - CALL_DIRECTION_MT = 2; -} - -// Call setup duration buckets. -// See com.android.internal.telephony.metrics.VoiceCallSessionStats for definition. -enum CallSetupDurationEnum { - CALL_SETUP_DURATION_UNKNOWN = 0; - CALL_SETUP_DURATION_EXTREMELY_FAST = 1; - CALL_SETUP_DURATION_ULTRA_FAST = 2; - CALL_SETUP_DURATION_VERY_FAST = 3; - CALL_SETUP_DURATION_FAST = 4; - CALL_SETUP_DURATION_NORMAL = 5; - CALL_SETUP_DURATION_SLOW = 6; - CALL_SETUP_DURATION_VERY_SLOW = 7; - CALL_SETUP_DURATION_ULTRA_SLOW = 8; - CALL_SETUP_DURATION_EXTREMELY_SLOW = 9; -} - -// Data conn. power states, primarily used by android/telephony/DataConnectionRealTimeInfo.java. -enum DataConnectionPowerStateEnum { - DATA_CONNECTION_POWER_STATE_LOW = 1; - DATA_CONNECTION_POWER_STATE_MEDIUM = 2; - DATA_CONNECTION_POWER_STATE_HIGH = 3; - DATA_CONNECTION_POWER_STATE_UNKNOWN = 2147483647; // Java Integer.MAX_VALUE; -} - -// Network type enums, primarily used by android/telephony/TelephonyManager.java. -// Do not add negative types. -enum NetworkTypeEnum { - NETWORK_TYPE_UNKNOWN = 0; - NETWORK_TYPE_GPRS = 1; - NETWORK_TYPE_EDGE = 2; - NETWORK_TYPE_UMTS = 3; - NETWORK_TYPE_CDMA = 4; - NETWORK_TYPE_EVDO_0 = 5; - NETWORK_TYPE_EVDO_A = 6; - NETWORK_TYPE_1XRTT = 7; - NETWORK_TYPE_HSDPA = 8; - NETWORK_TYPE_HSUPA = 9; - NETWORK_TYPE_HSPA = 10; - NETWORK_TYPE_IDEN = 11; - NETWORK_TYPE_EVDO_B = 12; - NETWORK_TYPE_LTE = 13; - NETWORK_TYPE_EHRPD = 14; - NETWORK_TYPE_HSPAP = 15; - NETWORK_TYPE_GSM = 16; - NETWORK_TYPE_TD_SCDMA = 17; - NETWORK_TYPE_IWLAN = 18; - NETWORK_TYPE_LTE_CA = 19; - NETWORK_TYPE_NR = 20; -} - -// Roaming type enums, see android.telephony.ServiceState.RoamingType for definitions. -enum RoamingTypeEnum { - ROAMING_TYPE_NOT_ROAMING = 0; - ROAMING_TYPE_ROAMING = 1; - ROAMING_TYPE_ROAMING_DOMESTIC = 2; - ROAMING_TYPE_ROAMING_INTERNATIONAL = 3; -} - -// Signal strength levels, as defined in android/telephony/SignalStrength.java. -enum SignalStrengthEnum { - SIGNAL_STRENGTH_NONE_OR_UNKNOWN = 0; - SIGNAL_STRENGTH_POOR = 1; - SIGNAL_STRENGTH_MODERATE = 2; - SIGNAL_STRENGTH_GOOD = 3; - SIGNAL_STRENGTH_GREAT = 4; -} - -enum ServiceStateEnum { - /** - * Normal operation condition, the phone is registered - * with an operator either in home network or in roaming. - */ - SERVICE_STATE_IN_SERVICE = 0; - - /** - * Phone is not registered with any operator, the phone - * can be currently searching a new operator to register to, or not - * searching to registration at all, or registration is denied, or radio - * signal is not available. - */ - SERVICE_STATE_OUT_OF_SERVICE = 1; - - /** - * The phone is registered and locked. Only emergency numbers are allowed. {@more} - */ - SERVICE_STATE_EMERGENCY_ONLY = 2; - - /** - * Radio of telephony is explicitly powered off. - */ - SERVICE_STATE_POWER_OFF = 3; -} - -enum SimStateEnum { - SIM_STATE_UNKNOWN = 0; - /** SIM card state: no SIM card is available in the device */ - SIM_STATE_ABSENT = 1; - /** SIM card state: Locked: requires the user's SIM PIN to unlock */ - SIM_STATE_PIN_REQUIRED = 2; - /** SIM card state: Locked: requires the user's SIM PUK to unlock */ - SIM_STATE_PUK_REQUIRED = 3; - /** SIM card state: Locked: requires a network PIN to unlock */ - SIM_STATE_NETWORK_LOCKED = 4; - /** SIM card state: Ready */ - SIM_STATE_READY = 5; - /** SIM card state: SIM Card is NOT READY */ - SIM_STATE_NOT_READY = 6; - /** SIM card state: SIM Card Error, permanently disabled */ - SIM_STATE_PERM_DISABLED = 7; - /** SIM card state: SIM Card Error, present but faulty */ - SIM_STATE_CARD_IO_ERROR = 8; - /** SIM card state: SIM Card restricted, present but not usable due to - * carrier restrictions. - */ - SIM_STATE_CARD_RESTRICTED = 9; - /** - * SIM card state: Loaded: SIM card applications have been loaded - * @hide - */ - SIM_STATE_LOADED = 10; - /** - * SIM card state: SIM Card is present - * @hide - */ - SIM_STATE_PRESENT = 11; -} - -// Format of SMS message -enum SmsFormatEnum { - /** Unknown format */ - SMS_FORMAT_UNKNOWN = 0; - /** Format compliant with 3GPP TS 23.040 */ - SMS_FORMAT_3GPP = 1; - /** Format compliant with 3GPP2 TS C.S0015-B */ - SMS_FORMAT_3GPP2 = 2; -} - -// Technology used to carry an SMS message -enum SmsTechEnum { - /** - * Unknown SMS technology used to carry the SMS. - * This value is also used for injected SMS. - */ - SMS_TECH_UNKNOWN = 0; - /** The SMS was carried over CS bearer in 3GPP network */ - SMS_TECH_CS_3GPP = 1; - /** The SMS was carried over CS bearer in 3GPP2 network */ - SMS_TECH_CS_3GPP2 = 2; - /** The SMS was carried over IMS */ - SMS_TECH_IMS = 3; -} - -// Types of SMS message -enum SmsTypeEnum { - /** Normal type. */ - SMS_TYPE_NORMAL = 0; - /** SMS-PP (point-to-point). */ - SMS_TYPE_SMS_PP = 1; - /** Voicemail indication. */ - SMS_TYPE_VOICEMAIL_INDICATION = 2; - /** Type 0 message (3GPP TS 23.040 9.2.3.9). */ - SMS_TYPE_ZERO = 3; - /** WAP-PUSH message. */ - SMS_TYPE_WAP_PUSH = 4; -} - -// Incoming SMS errors -enum SmsIncomingErrorEnum { - SMS_SUCCESS = 0; - SMS_ERROR_GENERIC = 1; - SMS_ERROR_NO_MEMORY = 2; - SMS_ERROR_NOT_SUPPORTED = 3; -} - -// Outgoing SMS results -enum SmsSendResultEnum { - // Unknown error - SMS_SEND_RESULT_UNKNOWN = 0; - // Success - SMS_SEND_RESULT_SUCCESS = 1; - // Permanent error - SMS_SEND_RESULT_ERROR = 2; - // Temporary error, retry - SMS_SEND_RESULT_ERROR_RETRY = 3; - // Error over IMS, retry on CS - SMS_SEND_RESULT_ERROR_FALLBACK = 4; -} - -// Data profile of the data call. From -// frameworks/base/telephony/java/com/android/internal/telephony/RILConstants.java -enum DataProfileEnum { - DATA_PROFILE_DEFAULT = 0; - DATA_PROFILE_TETHERED = 1; - DATA_PROFILE_IMS = 2; - DATA_PROFILE_FOTA = 3; - DATA_PROFILE_CBS = 4; - DATA_PROFILE_OEM_BASE = 1000; - DATA_PROFILE_INVALID = -1; -} - -// Reason of data call deactivation. From -// frameworks/base/telephony/java/android/telephony/data/DataService.java#DeactivateDataReason -enum DataDeactivateReasonEnum { - DEACTIVATE_REASON_UNKNOWN = 0; - DEACTIVATE_REASON_NORMAL = 1; - DEACTIVATE_REASON_RADIO_OFF = 2; - DEACTIVATE_REASON_HANDOVER = 3; -} - -// IP type of the data call -// see frameworks/base/telephony/java/android/telephony/data/ApnSetting.java#ProtocolType -enum ApnProtocolEnum { - APN_PROTOCOL_IPV4 = 0; - APN_PROTOCOL_IPV6 = 1; - APN_PROTOCOL_IPV4V6 = 2; - APN_PROTOCOL_PPP = 3; -} - -// Action taken to recover a data call that is stalled. From -// frameworks/opt/telephony/src/java/com/android/internal/telephony/dataconnection/DcTracker.java -// #RecoveryAction -enum DataStallRecoveryActionEnum { - RECOVERY_ACTION_GET_DATA_CALL_LIST = 0; - RECOVERY_ACTION_CLEANUP = 1; - RECOVERY_ACTION_REREGISTER = 2; - RECOVERY_ACTION_RADIO_RESTART = 3; -} - -// Codec quality -enum CodecQuality { - /** Codec quality: unknown */ - CODEC_QUALITY_UNKNOWN = 0; - - /** Codec quality: narrowband */ - CODEC_QUALITY_NARROWBAND = 1; - - /** Codec quality: wideband */ - CODEC_QUALITY_WIDEBAND = 2; - - /** Codec quality: super-wideband */ - CODEC_QUALITY_SUPER_WIDEBAND = 3; - - /** Codec quality: fullband */ - CODEC_QUALITY_FULLBAND = 4; -} diff --git a/core/proto/android/view/enums.proto b/core/proto/android/view/enums.proto deleted file mode 100644 index a601abee17f6..000000000000 --- a/core/proto/android/view/enums.proto +++ /dev/null @@ -1,71 +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. - */ - -syntax = "proto2"; -package android.view; - -option java_outer_classname = "ViewProtoEnums"; -option java_multiple_files = true; - -// Screen states, primarily used by android/view/Display.java. -enum DisplayStateEnum { - // The display state is unknown. - DISPLAY_STATE_UNKNOWN = 0; - // The display state is off. - DISPLAY_STATE_OFF = 1; - // The display state is on. - DISPLAY_STATE_ON = 2; - // The display is dozing in a low power state; it is still on but is - // optimized for showing system-provided content while the device is - // non-interactive. - DISPLAY_STATE_DOZE = 3; - // The display is dozing in a suspended low power state; it is still on - // but is optimized for showing static system-provided content while the - // device is non-interactive. - DISPLAY_STATE_DOZE_SUSPEND = 4; - // The display is on and optimized for VR mode. - DISPLAY_STATE_VR = 5; - // The display is in a suspended full power state; it is still on but the - // CPU is not updating it. - DISPLAY_STATE_ON_SUSPEND = 6; -} - -// Constants found in android.view.WindowManager. -enum TransitionTypeEnum { - TRANSIT_NONE = 0; - TRANSIT_UNSET = -1; - TRANSIT_ACTIVITY_OPEN = 6; - TRANSIT_ACTIVITY_CLOSE = 7; - TRANSIT_TASK_OPEN = 8; - TRANSIT_TASK_CLOSE = 9; - TRANSIT_TASK_TO_FRONT = 10; - TRANSIT_TASK_TO_BACK = 11; - TRANSIT_WALLPAPER_CLOSE = 12; - TRANSIT_WALLPAPER_OPEN = 13; - TRANSIT_WALLPAPER_INTRA_OPEN = 14; - TRANSIT_WALLPAPER_INTRA_CLOSE = 15; - TRANSIT_TASK_OPEN_BEHIND = 16; - TRANSIT_TASK_IN_PLACE = 17; - TRANSIT_ACTIVITY_RELAUNCH = 18; - TRANSIT_DOCK_TASK_FROM_RECENTS = 19 [deprecated=true]; - TRANSIT_KEYGUARD_GOING_AWAY = 20; - TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER = 21; - TRANSIT_KEYGUARD_OCCLUDE = 22; - TRANSIT_KEYGUARD_UNOCCLUDE = 23; - TRANSIT_TRANSLUCENT_ACTIVITY_OPEN = 24; - TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE = 25; - TRANSIT_CRASHING_ACTIVITY_CLOSE = 26; -} diff --git a/core/proto/android/hardware/sensor/assist/enums.proto b/core/proto/android/view/inputmethod/inputconnection.proto index 012dcb2e937e..ad9a95aa95e6 100644 --- a/core/proto/android/hardware/sensor/assist/enums.proto +++ b/core/proto/android/view/inputmethod/inputconnection.proto @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,20 +15,20 @@ */ syntax = "proto2"; -package android.hardware.sensor.assist; -option java_outer_classname = "AssistGestureProtoEnums"; -option java_multiple_files = true; +import "frameworks/base/core/proto/android/privacy.proto"; + +package android.view.inputmethod; -enum AssistGestureStageEnum { - ASSIST_GESTURE_STAGE_UNKNOWN = 0; - ASSIST_GESTURE_STAGE_PROGRESS = 1; - ASSIST_GESTURE_STAGE_PRIMED = 2; - ASSIST_GESTURE_STAGE_DETECTED = 3; -} +option java_multiple_files = true; -enum AssistGestureFeedbackEnum { - ASSIST_GESTURE_FEEDBACK_UNKNOWN = 0; - ASSIST_GESTURE_FEEDBACK_NOT_USED = 1; - ASSIST_GESTURE_FEEDBACK_USED = 2; +/** + * Represents a {@link android.view.inputmethod.InputConnection} object. + */ +message InputConnectionProto { + optional string editable_text = 1 [(.android.privacy).dest = DEST_LOCAL]; + optional string selected_text = 2 [(.android.privacy).dest = DEST_LOCAL]; + optional int32 selected_text_start = 3; + optional int32 selected_text_end = 4; + optional int32 cursor_caps_mode = 5; }
\ No newline at end of file diff --git a/core/proto/android/view/inputmethod/inputmethodeditortrace.proto b/core/proto/android/view/inputmethod/inputmethodeditortrace.proto index 5c0f341cb9e4..c1dce6f2d093 100644 --- a/core/proto/android/view/inputmethod/inputmethodeditortrace.proto +++ b/core/proto/android/view/inputmethod/inputmethodeditortrace.proto @@ -24,6 +24,7 @@ import "frameworks/base/core/proto/android/view/viewrootimpl.proto"; import "frameworks/base/core/proto/android/view/insetscontroller.proto"; import "frameworks/base/core/proto/android/view/imeinsetssourceconsumer.proto"; import "frameworks/base/core/proto/android/view/inputmethod/editorinfo.proto"; +import "frameworks/base/core/proto/android/view/inputmethod/inputconnection.proto"; import "frameworks/base/core/proto/android/view/imefocuscontroller.proto"; import "frameworks/base/core/proto/android/server/inputmethod/inputmethodmanagerservice.proto"; @@ -70,6 +71,7 @@ message InputMethodClientsTraceProto { optional ImeInsetsSourceConsumerProto ime_insets_source_consumer = 5; optional EditorInfoProto editor_info = 6; optional ImeFocusControllerProto ime_focus_controller = 7; + optional InputConnectionProto input_connection = 8; } } diff --git a/core/proto/android/wifi/enums.proto b/core/proto/android/wifi/enums.proto deleted file mode 100644 index e676fef8c2e0..000000000000 --- a/core/proto/android/wifi/enums.proto +++ /dev/null @@ -1,112 +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. - */ - -syntax = "proto2"; -package android.net.wifi; - -option java_outer_classname = "WifiProtoEnums"; -option java_multiple_files = true; - -/** - * Wifi Lock modes, primarily used in - * frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiLockManager.java. - */ -enum WifiModeEnum { - /** - * Deprecated. - * Wi-Fi will be kept active, and will behave normally. - */ - WIFI_MODE_FULL = 1 [deprecated=true]; - - /** - * Deprecated. - * Wi-Fi will be kept active, but the only operation that will be supported is initiation of - * scans, and the subsequent reporting of scan results. - */ - WIFI_MODE_SCAN_ONLY = 2 [deprecated=true]; - - /** - * Wi-Fi will not go to power save. - */ - WIFI_MODE_FULL_HIGH_PERF = 3; - - /** - * Wi-Fi will operate with a priority to achieve low latency. - */ - WIFI_MODE_FULL_LOW_LATENCY = 4; -} - -/** - * Wifi authentication type. - */ -enum WifiAuthType { - AUTH_TYPE_NONE = 0; - - // WPA pre-shared key. - AUTH_TYPE_WPA_PSK = 1; - // WPA using EAP authentication. Generally used with an external authentication server. - AUTH_TYPE_WPA_EAP = 2; - // IEEE 802.1X using EAP authentication and (optionally) dynamically generated WEP keys. - AUTH_TYPE_IEEE8021X = 3; - // WPA2 pre-shared key for use with soft access point. - AUTH_TYPE_WPA2_PSK = 4; - // Hotspot 2.0 r2 OSEN. - AUTH_TYPE_OSEN = 5; - // IEEE 802.11r Fast BSS Transition with PSK authentication. - AUTH_TYPE_FT_PSK = 6; - // IEEE 802.11r Fast BSS Transition with EAP authentication. - AUTH_TYPE_FT_EAP = 7; - // Simultaneous Authentication of Equals. - AUTH_TYPE_SAE = 8; - // Opportunistic Wireless Encryption. - AUTH_TYPE_OWE = 9; - // SUITE_B_192 192 bit level - AUTH_TYPE_SUITE_B_192 = 10; - // WPA pre-shared key with stronger SHA256-based algorithms. - AUTH_TYPE_WPA_PSK_SHA256 = 11; - // WPA using EAP authentication with stronger SHA256-based algorithms. - AUTH_TYPE_WPA_EAP_SHA256 = 12; - // WAPI pre-shared key. - AUTH_TYPE_WAPI_PSK = 13; - // WAPI certificate to be specified. - AUTH_TYPE_WAPI_CERT = 14; - // IEEE 802.11ai FILS SK with SHA256. - AUTH_TYPE_FILS_SHA256 = 15; - // IEEE 802.11ai FILS SK with SHA384. - AUTH_TYPE_FILS_SHA384 = 16; -} - -/** - * Bucketed wifi band. - */ -enum WifiBandBucket { - BAND_UNKNOWN = 0; - - // All of 2.4GHz band - BAND_2G = 1; - // Frequencies in the range of [5150, 5250) GHz - BAND_5G_LOW = 2; - // Frequencies in the range of [5250, 5725) GHz - BAND_5G_MIDDLE = 3; - // Frequencies in the range of [5725, 5850) GHz - BAND_5G_HIGH = 4; - // Frequencies in the range of [5925, 6425) GHz - BAND_6G_LOW = 5; - // Frequencies in the range of [6425, 6875) GHz - BAND_6G_MIDDLE = 6; - // Frequencies in the range of [6875, 7125) GHz - BAND_6G_HIGH = 7; -}
\ No newline at end of file diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index ea667277efee..db802879cd0b 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2557,6 +2557,10 @@ <permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" android:protectionLevel="signature|privileged|vendorPrivileged|oem|verifier" /> + <!-- @SystemApi @hide Allows an application to start foreground services from background --> + <permission android:name="android.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND" + android:protectionLevel="signature|privileged|vendorPrivileged|oem|verifier" /> + <!-- @SystemApi Must be required by activities that handle the intent action {@link Intent#ACTION_SEND_SHOW_SUSPENDED_APP_DETAILS}. This is for use by apps that hold {@link Manifest.permission#SUSPEND_APPS} to interact with the system. @@ -4186,6 +4190,14 @@ <permission android:name="android.permission.FACTORY_TEST" android:protectionLevel="signature" /> + <!-- @hide @TestApi Allows an application to broadcast the intent {@link + android.content.Intent#ACTION_CLOSE_SYSTEM_DIALOGS}. + <p>Not for use by third-party applications. + --> + <permission android:name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS" + android:protectionLevel="signature|recents" /> + <uses-permission android:name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS" /> + <!-- Allows an application to broadcast a notification that an application package has been removed. <p>Not for use by third-party applications. @@ -5219,6 +5231,9 @@ android:label="@string/sensor_notification_service"/> <!-- Attribution for Twilight service. --> <attribution android:tag="TwilightService" android:label="@string/twilight_service"/> + <!-- Attribution for the Offline LocationTimeZoneProvider, used to detect time zone using + on-device data --> + <attribution android:tag="OfflineLocationTimeZoneProvider" android:label="@string/offline_location_time_zone_detection_service"/> <application android:process="system" android:persistent="true" @@ -5685,6 +5700,18 @@ </intent-filter> </service> + <!-- AOSP configures a default secondary LocationTimeZoneProvider that uses an on-device + data set from the com.android.geotz APEX. --> + <uses-library android:name="com.android.location.provider" /> + <service android:name="com.android.timezone.geotz.provider.OfflineLocationTimeZoneService" + android:exported="false"> + <intent-filter> + <action android:name="com.android.location.timezone.service.v1.SecondaryLocationTimeZoneProvider" /> + </intent-filter> + <meta-data android:name="serviceVersion" android:value="1" /> + <meta-data android:name="serviceIsMultiuser" android:value="true" /> + </service> + <provider android:name="com.android.server.textclassifier.IconsContentProvider" android:authorities="com.android.textclassifier.icons" diff --git a/core/res/res/layout/notification_material_action_list.xml b/core/res/res/layout/notification_material_action_list.xml index 552a1bd6aa2e..9662b8e35bf6 100644 --- a/core/res/res/layout/notification_material_action_list.xml +++ b/core/res/res/layout/notification_material_action_list.xml @@ -28,7 +28,6 @@ android:layout_height="wrap_content" android:gravity="end" android:orientation="horizontal" - android:paddingEnd="@dimen/bubble_gone_padding_end" android:background="@color/notification_action_list_background_color" > @@ -45,22 +44,34 @@ <!-- actions will be added here --> </com.android.internal.widget.NotificationActionListLayout> - <ImageView - android:id="@+id/snooze_button" - android:layout_width="@dimen/notification_actions_icon_size" - android:layout_height="@dimen/notification_actions_icon_size" - android:layout_gravity="center_vertical|end" - android:visibility="gone" - android:scaleType="centerInside" - /> + <!-- + This nested linear layout exists to ensure that if the neither of the contained + actions is visible we have some minimum padding at the end of the actions is present, + then there will be 12dp of padding at the end of the actions list. + --> + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:orientation="horizontal" + android:minWidth="@dimen/snooze_and_bubble_gone_padding_end" + > + <ImageView + android:id="@+id/snooze_button" + android:layout_width="@dimen/notification_actions_icon_size" + android:layout_height="@dimen/notification_actions_icon_size" + android:layout_gravity="center_vertical|end" + android:visibility="gone" + android:scaleType="centerInside" + /> - <ImageView - android:id="@+id/bubble_button" - android:layout_width="@dimen/notification_actions_icon_size" - android:layout_height="@dimen/notification_actions_icon_size" - android:layout_gravity="center_vertical|end" - android:visibility="gone" - android:scaleType="centerInside" - /> + <ImageView + android:id="@+id/bubble_button" + android:layout_width="@dimen/notification_actions_icon_size" + android:layout_height="@dimen/notification_actions_icon_size" + android:layout_gravity="center_vertical|end" + android:visibility="gone" + android:scaleType="centerInside" + /> + </LinearLayout> </LinearLayout> </FrameLayout> diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml index a26473ad6010..b0ee12a520d9 100644 --- a/core/res/res/layout/notification_template_header.xml +++ b/core/res/res/layout/notification_template_header.xml @@ -50,20 +50,18 @@ android:theme="@style/Theme.DeviceDefault.Notification" > - <TextView - android:id="@+id/app_name_text" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginEnd="@dimen/notification_header_separating_margin" - android:singleLine="true" - android:textAppearance="?attr/notificationHeaderTextAppearance" - android:visibility="?attr/notificationHeaderAppNameVisibility" - /> - <include layout="@layout/notification_top_line_views" /> </NotificationTopLineView> + <FrameLayout + android:id="@+id/alternate_expand_target" + android:layout_width="@dimen/notification_content_margin_start" + android:layout_height="match_parent" + android:layout_gravity="start" + android:importantForAccessibility="no" + /> + <com.android.internal.widget.NotificationExpandButton android:id="@+id/expand_button" android:layout_width="@dimen/notification_header_expand_icon_size" diff --git a/core/res/res/layout/notification_template_material_base.xml b/core/res/res/layout/notification_template_material_base.xml index ded16b7edf87..69d4a12f4d69 100644 --- a/core/res/res/layout/notification_template_material_base.xml +++ b/core/res/res/layout/notification_template_material_base.xml @@ -68,6 +68,12 @@ android:theme="@style/Theme.DeviceDefault.Notification" > + <!-- + NOTE: The notification_top_line_views layout contains the app_name_text. + In order to include the title view at the beginning, the Notification.Builder + has logic to hide that view whenever this title view is to be visible. + --> + <TextView android:id="@+id/title" android:layout_width="wrap_content" @@ -138,6 +144,14 @@ /> <FrameLayout + android:id="@+id/alternate_expand_target" + android:layout_width="@dimen/notification_content_margin_start" + android:layout_height="match_parent" + android:layout_gravity="start" + android:importantForAccessibility="no" + /> + + <FrameLayout android:id="@+id/expand_button_touch_container" android:layout_width="wrap_content" android:layout_height="match_parent" diff --git a/core/res/res/layout/notification_template_material_big_media.xml b/core/res/res/layout/notification_template_material_big_media.xml index 4cf323b24f0a..696cb6572e29 100644 --- a/core/res/res/layout/notification_template_material_big_media.xml +++ b/core/res/res/layout/notification_template_material_big_media.xml @@ -50,16 +50,56 @@ android:id="@+id/notification_main_column" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginTop="@dimen/notification_content_margin_top" + android:layout_marginTop="46dp" android:layout_marginStart="@dimen/notification_content_margin_start" android:layout_marginBottom="@dimen/notification_content_margin" android:layout_marginEnd="@dimen/notification_content_margin_end" android:orientation="vertical" > + <!-- TODO(b/172652345): fix the media style --> + <!--<include layout="@layout/notification_template_part_line1"/>--> + <!--<include layout="@layout/notification_template_text"/>--> - <include layout="@layout/notification_template_part_line1" /> + <LinearLayout + android:id="@+id/line1" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + > + <TextView android:id="@+id/title" + android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:singleLine="true" + android:ellipsize="marquee" + android:fadingEdge="horizontal" + android:textAlignment="viewStart" + /> + <TextView android:id="@+id/text_line_1" + style="@style/Widget.DeviceDefault.Notification.Text" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="end|bottom" + android:layout_marginStart="16dp" + android:singleLine="true" + android:ellipsize="marquee" + android:fadingEdge="horizontal" + /> + </LinearLayout> - <include layout="@layout/notification_template_text" /> + <com.android.internal.widget.ImageFloatingTextView + style="@style/Widget.DeviceDefault.Notification.Text" + android:id="@+id/text" + android:layout_width="match_parent" + android:layout_height="@dimen/notification_text_height" + android:layout_gravity="top" + android:layout_marginTop="0.5dp" + android:ellipsize="marquee" + android:fadingEdge="horizontal" + android:gravity="top" + android:singleLine="true" + android:textAlignment="viewStart" + /> </LinearLayout> <LinearLayout diff --git a/core/res/res/layout/notification_template_material_conversation.xml b/core/res/res/layout/notification_template_material_conversation.xml index 520ae282b942..f9364d565f3b 100644 --- a/core/res/res/layout/notification_template_material_conversation.xml +++ b/core/res/res/layout/notification_template_material_conversation.xml @@ -327,10 +327,10 @@ android:id="@+id/expand_button_touch_container" android:layout_width="wrap_content" android:layout_height="@dimen/conversation_expand_button_size" - android:paddingStart="16dp" + android:paddingStart="@dimen/conversation_expand_button_side_margin" android:orientation="horizontal" android:layout_gravity="end|top" - android:paddingEnd="@dimen/notification_content_margin_end" + android:paddingEnd="@dimen/conversation_expand_button_side_margin" android:clipToPadding="false" android:clipChildren="false" > diff --git a/core/res/res/layout/notification_template_material_media.xml b/core/res/res/layout/notification_template_material_media.xml index 52053dcdf800..7daccd2b8544 100644 --- a/core/res/res/layout/notification_template_material_media.xml +++ b/core/res/res/layout/notification_template_material_media.xml @@ -45,7 +45,7 @@ android:layout_height="wrap_content" android:orientation="horizontal" android:layout_marginStart="@dimen/notification_content_margin_start" - android:layout_marginTop="@dimen/notification_content_margin_top" + android:layout_marginTop="46dp" android:layout_alignParentTop="true" android:tag="media" > @@ -58,8 +58,50 @@ android:paddingBottom="@dimen/notification_content_margin" android:orientation="vertical" > - <include layout="@layout/notification_template_part_line1"/> - <include layout="@layout/notification_template_text"/> + <!-- TODO(b/172652345): fix the media style --> + <!--<include layout="@layout/notification_template_part_line1"/>--> + <!--<include layout="@layout/notification_template_text"/>--> + + <LinearLayout + android:id="@+id/line1" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + > + <TextView android:id="@+id/title" + android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:singleLine="true" + android:ellipsize="marquee" + android:fadingEdge="horizontal" + android:textAlignment="viewStart" + /> + <TextView android:id="@+id/text_line_1" + style="@style/Widget.DeviceDefault.Notification.Text" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="end|bottom" + android:layout_marginStart="16dp" + android:singleLine="true" + android:ellipsize="marquee" + android:fadingEdge="horizontal" + /> + </LinearLayout> + + <com.android.internal.widget.ImageFloatingTextView + style="@style/Widget.DeviceDefault.Notification.Text" + android:id="@+id/text" + android:layout_width="match_parent" + android:layout_height="@dimen/notification_text_height" + android:layout_gravity="top" + android:layout_marginTop="0.5dp" + android:ellipsize="marquee" + android:fadingEdge="horizontal" + android:gravity="top" + android:singleLine="true" + android:textAlignment="viewStart" + /> </LinearLayout> <LinearLayout android:id="@+id/media_actions" diff --git a/core/res/res/layout/notification_top_line_views.xml b/core/res/res/layout/notification_top_line_views.xml index 51974ac7dcf3..c71e8863502c 100644 --- a/core/res/res/layout/notification_top_line_views.xml +++ b/core/res/res/layout/notification_top_line_views.xml @@ -14,13 +14,23 @@ ~ limitations under the License --> <!-- - This layout file should be included inside a NotificationTopLineView, usually after either a - <TextView android:id="@+id/app_name_text"/> or <TextView android:id="@+id/title"/> + This layout file should be included inside a NotificationTopLineView, sometimes after a + <TextView android:id="@+id/title"/> --> <merge xmlns:android="http://schemas.android.com/apk/res/android"> <TextView + android:id="@+id/app_name_text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginEnd="@dimen/notification_header_separating_margin" + android:singleLine="true" + android:textAppearance="?attr/notificationHeaderTextAppearance" + android:visibility="?attr/notificationHeaderAppNameVisibility" + /> + + <TextView android:id="@+id/header_text_secondary_divider" android:layout_width="wrap_content" android:layout_height="wrap_content" diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml index 31993fb5860d..171d89628518 100644 --- a/core/res/res/values-af/strings.xml +++ b/core/res/res/values-af/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB-verbinding"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Program loop tans"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Programme wat batterykrag gebruik"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> gebruik tans batterykrag"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> programme gebruik tans batterykrag"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Tik vir besonderhede oor battery- en datagebruik"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml index dee3521a1b2e..5871fe24920a 100644 --- a/core/res/res/values-am/strings.xml +++ b/core/res/res/values-am/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"የዩኤስቢ ግንኙነት"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"APP እየሠራ ነው"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"ባትሪ በመፍጀት ላይ ያሉ መተግበሪያዎች"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> ባትሪ እየተጠቀመ ነው"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> መተግበሪያዎች ባትሪ እየተጠቀሙ ነው"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"በባትሪ እና ውሂብ አጠቃቀም ላይ ዝርዝሮችን ለማግኘት መታ ያድርጉ"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml index 905d2ddad1db..b57876906a94 100644 --- a/core/res/res/values-ar/strings.xml +++ b/core/res/res/values-ar/strings.xml @@ -299,6 +299,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"اتصال USB"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"التطبيق قيد التشغيل"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"التطبيقات التي تستهلك البطارية"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"يستخدم تطبيق <xliff:g id="APP_NAME">%1$s</xliff:g> البطارية"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"تستخدم <xliff:g id="NUMBER">%1$d</xliff:g> من التطبيقات البطارية"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"انقر للحصول على تفاصيل حول البطارية واستخدام البيانات"</string> @@ -2324,4 +2326,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml index e58274cedf89..f1b0d84883a0 100644 --- a/core/res/res/values-as/strings.xml +++ b/core/res/res/values-as/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"ইউএছবি সংযোগ"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"এপ্ চলি আছে"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"বেটাৰি খৰচ কৰা এপসমূহ"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g>এ বেটাৰি ব্যৱহাৰ কৰি আছে"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g>টা এপে বেটাৰি ব্যৱহাৰ কৰি আছে"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"বেটাৰি আৰু ডেটাৰ ব্যৱহাৰৰ বিষয়ে বিশদভাৱে জানিবলৈ টিপক"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml index 499cd202b8ea..01fb48e18462 100644 --- a/core/res/res/values-az/strings.xml +++ b/core/res/res/values-az/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB əlaqə"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Tətbiq işləyir"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Batareyadan istifadə edən tətbiqlər"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> batareyadan istifadə edir"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> tətbiq batareyadan istifadə edir"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Batareya və data istifadəsi haqqında ətraflı məlumat üçün klikləyin"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml index 5e264537c2b0..bfbd3e4586f2 100644 --- a/core/res/res/values-b+sr+Latn/strings.xml +++ b/core/res/res/values-b+sr+Latn/strings.xml @@ -123,28 +123,28 @@ <string name="roamingText11" msgid="5245687407203281407">"Baner rominga je uključen"</string> <string name="roamingText12" msgid="673537506362152640">"Baner rominga je isključen"</string> <string name="roamingTextSearching" msgid="5323235489657753486">"Pretraživanje usluge"</string> - <string name="wfcRegErrorTitle" msgid="3193072971584858020">"Podešavanje pozivanja preko WiFi-ja nije uspelo"</string> + <string name="wfcRegErrorTitle" msgid="3193072971584858020">"Podešavanje pozivanja preko WiFi-a nije uspelo"</string> <string-array name="wfcOperatorErrorAlertMessages"> - <item msgid="468830943567116703">"Da biste upućivali pozive i slali poruke preko WiFi-ja, prvo zatražite od mobilnog operatera da vam omogući ovu uslugu. Zatim u Podešavanjima ponovo uključite Pozivanje preko WiFi-ja. (kôd greške: <xliff:g id="CODE">%1$s</xliff:g>)"</item> + <item msgid="468830943567116703">"Da biste upućivali pozive i slali poruke preko WiFi-a, prvo zatražite od mobilnog operatera da vam omogući ovu uslugu. Zatim u Podešavanjima ponovo uključite Pozivanje preko WiFi-a. (kôd greške: <xliff:g id="CODE">%1$s</xliff:g>)"</item> </string-array> <string-array name="wfcOperatorErrorNotificationMessages"> <item msgid="4795145070505729156">"Problem u vezi sa registrovanjem pozivanja preko Wi‑Fi-ja kod mobilnog operatera: <xliff:g id="CODE">%1$s</xliff:g>"</item> </string-array> <!-- no translation found for wfcSpnFormat_spn (2982505428519096311) --> <skip /> - <string name="wfcSpnFormat_spn_wifi_calling" msgid="3165949348000906194">"<xliff:g id="SPN">%s</xliff:g> pozivanje preko WiFi-ja"</string> - <string name="wfcSpnFormat_spn_wifi_calling_vo_hyphen" msgid="3836827895369365298">"<xliff:g id="SPN">%s</xliff:g> – pozivanje preko WiFi-ja"</string> + <string name="wfcSpnFormat_spn_wifi_calling" msgid="3165949348000906194">"<xliff:g id="SPN">%s</xliff:g> pozivanje preko WiFi-a"</string> + <string name="wfcSpnFormat_spn_wifi_calling_vo_hyphen" msgid="3836827895369365298">"<xliff:g id="SPN">%s</xliff:g> – pozivanje preko WiFi-a"</string> <string name="wfcSpnFormat_wlan_call" msgid="4895315549916165700">"WLAN poziv"</string> <string name="wfcSpnFormat_spn_wlan_call" msgid="255919245825481510">"<xliff:g id="SPN">%s</xliff:g> WLAN poziv"</string> <string name="wfcSpnFormat_spn_wifi" msgid="7232899594327126970">"<xliff:g id="SPN">%s</xliff:g> WiFi"</string> - <string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="8383917598312067365">"Pozivanje preko WiFi-ja | <xliff:g id="SPN">%s</xliff:g>"</string> + <string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="8383917598312067365">"Pozivanje preko WiFi-a | <xliff:g id="SPN">%s</xliff:g>"</string> <string name="wfcSpnFormat_spn_vowifi" msgid="6865214948822061486">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string> - <string name="wfcSpnFormat_wifi_calling" msgid="6178935388378661755">"Pozivanje preko WiFi-ja"</string> + <string name="wfcSpnFormat_wifi_calling" msgid="6178935388378661755">"Pozivanje preko WiFi-a"</string> <string name="wfcSpnFormat_wifi" msgid="1376356951297043426">"WiFi"</string> - <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="7178561009225028264">"Pozivanje preko WiFi-ja"</string> + <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="7178561009225028264">"Pozivanje preko WiFi-a"</string> <string name="wfcSpnFormat_vowifi" msgid="8371335230890725606">"VoWifi"</string> <string name="wifi_calling_off_summary" msgid="5626710010766902560">"Isključeno"</string> - <string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Pozivanje preko WiFi-ja"</string> + <string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Pozivanje preko WiFi-a"</string> <string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Poziv preko mobilne mreže"</string> <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Samo WiFi"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Nije prosleđeno"</string> @@ -290,6 +290,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB veza"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Aktivna aplikacija"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Aplikacije koje troše bateriju"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> koristi bateriju"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"Aplikacije (<xliff:g id="NUMBER">%1$d</xliff:g>) koriste bateriju"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Dodirnite za detalje o bateriji i potrošnji podataka"</string> @@ -2222,4 +2224,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml index d6a725706433..c4a3a7e980c4 100644 --- a/core/res/res/values-be/strings.xml +++ b/core/res/res/values-be/strings.xml @@ -293,6 +293,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"Падключэнне USB"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Праграма працуе"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Праграмы, якія выкарыстоўваюць акумулятар"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> выкарыстоўвае акумулятар"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"Наступная колькасць праграм выкарыстоўваюць акумулятар: <xliff:g id="NUMBER">%1$d</xliff:g>"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Дакраніцеся, каб даведацца пра выкарыстанне трафіка і акумулятара"</string> @@ -2256,4 +2258,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml index 376ef26412ff..07f1ce7b9bf9 100644 --- a/core/res/res/values-bg/strings.xml +++ b/core/res/res/values-bg/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB връзка"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Приложението работи"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Приложения, използващи батерията"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> използва батерията"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> приложения използват батерията"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Докоснете за информация относно използването на батерията и преноса на данни"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml index 3b92f87cfc1c..9b85fc7a4d7d 100644 --- a/core/res/res/values-bn/strings.xml +++ b/core/res/res/values-bn/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB সংযোগ"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"অ্যাপ চলছে"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"কিছু অ্যাপ ব্যাটারি ব্যবহার করছে"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> অ্যাপটি ব্যাটারি ব্যবহার করছে"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g>টি অ্যাপ ব্যাটারি ব্যবহার করছে"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"ব্যাটারি এবং ডেটার ব্যবহারের বিশদ বিবরণের জন্য ট্যাপ করুন"</string> @@ -802,7 +804,7 @@ <string name="orgTypeOther" msgid="5450675258408005553">"অন্যান্য"</string> <string name="orgTypeCustom" msgid="1126322047677329218">"কাস্টম"</string> <string name="relationTypeCustom" msgid="282938315217441351">"কাস্টম"</string> - <string name="relationTypeAssistant" msgid="4057605157116589315">"Assistant"</string> + <string name="relationTypeAssistant" msgid="4057605157116589315">"অ্যাসিস্ট্যান্ট"</string> <string name="relationTypeBrother" msgid="7141662427379247820">"ভাই"</string> <string name="relationTypeChild" msgid="9076258911292693601">"সন্তান"</string> <string name="relationTypeDomesticPartner" msgid="7825306887697559238">"জীবনসাথি"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml index f180df6dbe71..3d2249b17122 100644 --- a/core/res/res/values-bs/strings.xml +++ b/core/res/res/values-bs/strings.xml @@ -290,6 +290,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB veza"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Pokrenuta aplikacija"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Aplikacije koje troše bateriju"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> troši bateriju"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"Broj aplikacija koje troše bateriju: <xliff:g id="NUMBER">%1$d</xliff:g>"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Dodirnite za detalje o potrošnji baterije i prijenosa podataka"</string> @@ -2222,4 +2224,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml index 4d45791c9bdc..f1845e340afa 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"Connexió USB"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Aplicació en execució"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Aplicacions que consumeixen bateria"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> està consumint bateria"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> aplicacions estan consumint bateria"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Toca per obtenir informació sobre l\'ús de dades i de bateria"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index e9f1fad5e95d..42d56369c3af 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -293,6 +293,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"Připojení USB"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Aplikace je spuštěna"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Aplikace spotřebovávají baterii"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> využívá baterii"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"Aplikace (<xliff:g id="NUMBER">%1$d</xliff:g>) využívají baterii"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Klepnutím zobrazíte podrobnosti o využití baterie a dat"</string> @@ -2256,4 +2258,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index 75bdda0b590c..0487406d4e7c 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB-forbindelse"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Appen kører"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Apps, der bruger batteri"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> bruger batteri"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> apps bruger batteri"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Tryk for at se info om batteri- og dataforbrug"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index 6d82b62daf2e..ebbde3c9afef 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB-Verbindung"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"App wird ausgeführt"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Strom verbrauchende Apps"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> verbraucht Strom"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> Apps verbrauchen Strom"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Für Details zur Akku- und Datennutzung tippen"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml index 42513aa77300..306d36e0ae9f 100644 --- a/core/res/res/values-el/strings.xml +++ b/core/res/res/values-el/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"Σύνδεση USB"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Η εφαρμογή εκτελείται"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Εφαρμογές που καταναλώνουν μπαταρία"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> χρησιμοποιεί μπαταρία"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> εφαρμογές χρησιμοποιούν μπαταρία"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Πατήστε για λεπτομέρειες σχετικά με τη χρήση μπαταρίας και δεδομένων"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml index ef5a92a6351c..a8706cbe32c2 100644 --- a/core/res/res/values-en-rAU/strings.xml +++ b/core/res/res/values-en-rAU/strings.xml @@ -287,6 +287,7 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB connection"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"App running"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Apps consuming battery"</string> + <string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Magnification"</string> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> is using battery"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> apps are using battery"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Tap for details on battery and data usage"</string> @@ -2188,4 +2189,8 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <string name="window_magnification_prompt_title" msgid="8197528399699536320">"New: Window magnifier"</string> + <string name="window_magnification_prompt_content" msgid="4166711383253283838">"You can now magnify some or all of your screen"</string> + <string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Turn on in settings"</string> + <string name="dismiss_action" msgid="1728820550388704784">"Dismiss"</string> </resources> diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml index 6dae39bbc638..57e2eae62f9c 100644 --- a/core/res/res/values-en-rCA/strings.xml +++ b/core/res/res/values-en-rCA/strings.xml @@ -287,6 +287,7 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB connection"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"App running"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Apps consuming battery"</string> + <string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Magnification"</string> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> is using battery"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> apps are using battery"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Tap for details on battery and data usage"</string> @@ -2188,4 +2189,8 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <string name="window_magnification_prompt_title" msgid="8197528399699536320">"New: Window magnifier"</string> + <string name="window_magnification_prompt_content" msgid="4166711383253283838">"You can now magnify some or all of your screen"</string> + <string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Turn on in settings"</string> + <string name="dismiss_action" msgid="1728820550388704784">"Dismiss"</string> </resources> diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml index 62e42b38abf5..33ffbe386fc4 100644 --- a/core/res/res/values-en-rGB/strings.xml +++ b/core/res/res/values-en-rGB/strings.xml @@ -287,6 +287,7 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB connection"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"App running"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Apps consuming battery"</string> + <string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Magnification"</string> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> is using battery"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> apps are using battery"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Tap for details on battery and data usage"</string> @@ -2188,4 +2189,8 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <string name="window_magnification_prompt_title" msgid="8197528399699536320">"New: Window magnifier"</string> + <string name="window_magnification_prompt_content" msgid="4166711383253283838">"You can now magnify some or all of your screen"</string> + <string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Turn on in settings"</string> + <string name="dismiss_action" msgid="1728820550388704784">"Dismiss"</string> </resources> diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml index 85796287543d..f5be6fd8c337 100644 --- a/core/res/res/values-en-rIN/strings.xml +++ b/core/res/res/values-en-rIN/strings.xml @@ -287,6 +287,7 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB connection"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"App running"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Apps consuming battery"</string> + <string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Magnification"</string> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> is using battery"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> apps are using battery"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Tap for details on battery and data usage"</string> @@ -2188,4 +2189,8 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <string name="window_magnification_prompt_title" msgid="8197528399699536320">"New: Window magnifier"</string> + <string name="window_magnification_prompt_content" msgid="4166711383253283838">"You can now magnify some or all of your screen"</string> + <string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Turn on in settings"</string> + <string name="dismiss_action" msgid="1728820550388704784">"Dismiss"</string> </resources> diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml index 6674ecd63906..cd3ff368c289 100644 --- a/core/res/res/values-en-rXC/strings.xml +++ b/core/res/res/values-en-rXC/strings.xml @@ -287,6 +287,7 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB connection"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"App running"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Apps consuming battery"</string> + <string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Magnification"</string> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> is using battery"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> apps are using battery"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Tap for details on battery and data usage"</string> @@ -2188,4 +2189,8 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <string name="window_magnification_prompt_title" msgid="8197528399699536320">"New: Window Magnifier"</string> + <string name="window_magnification_prompt_content" msgid="4166711383253283838">"You can now magnify some or all of your screen"</string> + <string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Turn on in Settings"</string> + <string name="dismiss_action" msgid="1728820550388704784">"Dismiss"</string> </resources> diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml index a7e299456e95..2d521b3290c2 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"Conexión USB"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"App en ejecución"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Apps que consumen batería"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> está consumiendo batería"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> apps están consumiendo batería"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Presiona para obtener información sobre el uso de datos y de la batería"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index 0d8fc2b6ca69..abc694686bee 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"Conexión USB"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Aplicación en ejecución"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Aplicaciones que consumen batería"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> está usando la batería"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> aplicaciones están usando la batería"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Toca para ver información detallada sobre el uso de datos y de la batería"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml index 7a0a0ed005aa..130ef6e47b9f 100644 --- a/core/res/res/values-et/strings.xml +++ b/core/res/res/values-et/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB-ühendus"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Rakendus töötab"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Rakendused kasutavad akutoidet"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> kasutab akutoidet"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> rakendust kasutab akutoidet"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Aku ja andmekasutuse üksikasjade nägemiseks puudutage"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml index a00282ff4292..5273a48fa3b5 100644 --- a/core/res/res/values-eu/strings.xml +++ b/core/res/res/values-eu/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB konexioa"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Aplikazio bat abian da"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Bateria kontsumitzen ari diren aplikazioak"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> ari da bateria erabiltzen"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> aplikazio ari dira bateria erabiltzen"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Sakatu bateria eta datuen erabilerari buruzko xehetasunak ikusteko"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index fb0ec661b2cb..7596bab30f16 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"اتصال USB"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"برنامه درحال اجرا"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"برنامههای مصرفکننده باتری"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> درحال استفاده کردن از باتری است"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> برنامه درحال استفاده کردن از باتری هستند"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"برای جزئیات مربوط به مصرف باتری و داده، ضربه بزنید"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml index 27abeea5dd86..d3373154196a 100644 --- a/core/res/res/values-fi/strings.xml +++ b/core/res/res/values-fi/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB-yhteys"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Sovellus käynnissä"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Akkua kuluttavat sovellukset"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> käyttää akkua."</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> sovellusta käyttää akkua."</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Katso lisätietoja akun ja datan käytöstä napauttamalla."</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml index c09c71e9244c..d2c89bc8d842 100644 --- a/core/res/res/values-fr-rCA/strings.xml +++ b/core/res/res/values-fr-rCA/strings.xml @@ -290,6 +290,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"Connexion USB"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Application en cours d\'exécution"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Applications qui sollicitent la pile"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> sollicite la pile"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> applications sollicitent la pile"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Touchez pour afficher des détails sur l\'utilisation de la pile et des données"</string> @@ -2222,4 +2224,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index 9c9cdd112bc6..1992068a4a85 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -290,6 +290,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"Connexion USB"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Application en cours d\'exécution"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Applications utilisant la batterie"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> utilise la batterie"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> applications utilisent la batterie"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Appuyer pour obtenir des informations sur l\'utilisation de la batterie et des données"</string> @@ -2222,4 +2224,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml index 7dd89aa93702..7583502fb988 100644 --- a/core/res/res/values-gl/strings.xml +++ b/core/res/res/values-gl/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"conexión USB"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Estase executando a aplicación"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Aplicacións que consomen batería"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"A aplicación <xliff:g id="APP_NAME">%1$s</xliff:g> está consumindo batería"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> aplicacións están consumindo batería"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Toca para obter información sobre o uso de datos e a batería"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml index 6ace38db11ff..6f140c4b5d85 100644 --- a/core/res/res/values-gu/strings.xml +++ b/core/res/res/values-gu/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB કનેક્શન"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"ઍપ ચાલી રહ્યું છે"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"ઍપ બૅટરીનો વપરાશ કરી રહ્યાં છે"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> બૅટરીનો ઉપયોગ કરી રહ્યું છે"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> ઍપ બૅટરીનો ઉપયોગ કરી રહ્યાં છે"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"બૅટરી અને ડેટા વપરાશ વિશેની વિગતો માટે ટૅપ કરો"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index f1e57801d3ec..5892dab23628 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB कनेक्शन"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"ऐप अभी इस्तेमाल हो रहा है"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"बैटरी की खपत करने वाले ऐप"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> बैटरी का इस्तेमाल कर रहा है"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> ऐप बैटरी का इस्तेमाल कर रहे हैं"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"बैटरी और डेटा खर्च की जानकारी के लिए छूएं"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml index 4ff37c575a0f..f4d984275576 100644 --- a/core/res/res/values-hr/strings.xml +++ b/core/res/res/values-hr/strings.xml @@ -290,6 +290,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB veza"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Izvodi se aplikacija"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Aplikacije troše bateriju"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> koristi bateriju"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"Broj aplikacija koje koriste bateriju: <xliff:g id="NUMBER">%1$d</xliff:g>"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Dodirnite da biste vidjeli pojedinosti o potrošnji baterije i podatkovnom prometu"</string> @@ -2222,4 +2224,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml index d01373f749f3..bff17731ac84 100644 --- a/core/res/res/values-hu/strings.xml +++ b/core/res/res/values-hu/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB-kapcsolat"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Jelenleg futó alkalmazás"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Akkumulátort használó alkalmazások"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> alkalmazás használja az akkumulátort"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> alkalmazás használja az akkumulátort"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Koppintson az akkumulátor- és adathasználat részleteinek megtekintéséhez"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml index a85ed708faba..1f2e642c74c6 100644 --- a/core/res/res/values-hy/strings.xml +++ b/core/res/res/values-hy/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB կապակցում"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Հավելվածն աշխատում է"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Մարտկոցի լիցքը ծախսող հավելվածներ"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"«<xliff:g id="APP_NAME">%1$s</xliff:g>» հավելվածը ծախսում է մարտկոցի լիցքը"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> հավելված ծախսում է մարտկոցի լիցքը"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Հպեք՝ մարտկոցի և թրաֆիկի մանրամասները տեսնելու համար"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index cbcb131a64b0..4f26eb687082 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"Sambungan USB"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Aplikasi berjalan"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Aplikasi yang menggunakan baterai"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> sedang menggunakan baterai"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> aplikasi sedang meggunakan baterai"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Ketuk untuk melihat detail penggunaan baterai dan data"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml index 5d1912236a99..d5539a395206 100644 --- a/core/res/res/values-is/strings.xml +++ b/core/res/res/values-is/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB-tenging"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Forrit er í gangi"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Forrit sem nota rafhlöðuorku"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> notar rafhlöðuorku"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> forrit nota rafhlöðuorku"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Ýttu til að fá upplýsingar um rafhlöðu- og gagnanotkun"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index bf26d99b7e19..152cfee90863 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -287,6 +287,7 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"Connessione USB"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"App in esecuzione"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"App che consumano la batteria"</string> + <string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Ingrandimento"</string> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"L\'app <xliff:g id="APP_NAME">%1$s</xliff:g> sta consumando la batteria"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> app stanno consumando la batteria"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Tocca per conoscere i dettagli sull\'utilizzo dei dati e della batteria"</string> @@ -2188,4 +2189,8 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <string name="window_magnification_prompt_title" msgid="8197528399699536320">"Novità: Ingrandimento finestra"</string> + <string name="window_magnification_prompt_content" msgid="4166711383253283838">"Puoi ingrandire lo schermo in parte o per intero"</string> + <string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Attiva nelle Impostazioni"</string> + <string name="dismiss_action" msgid="1728820550388704784">"Ignora"</string> </resources> diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml index 422d810d3c09..5ae9436dd1b3 100644 --- a/core/res/res/values-iw/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -293,6 +293,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"חיבור USB"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"אפליקציה פועלת"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"אפליקציות שמרוקנות את הסוללה"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> משתמשת בסוללה"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> אפליקציות משתמשות בסוללה"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"הקש לקבלת פרטים על צריכה של נתונים וסוללה"</string> @@ -2256,4 +2258,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index f5adb5962783..7a54a1594501 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB 接続"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"実行中のアプリ"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"電池を消費しているアプリ"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」が電池を使用しています"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> 個のアプリが電池を使用しています"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"タップして電池やデータの使用量を確認"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml index 0c3e7b9f207c..1653cb91c3fb 100644 --- a/core/res/res/values-ka/strings.xml +++ b/core/res/res/values-ka/strings.xml @@ -287,6 +287,7 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB კავშირი"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"აპი გაშვებულია"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"ბატარეის მხარჯავი აპები"</string> + <string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"გადიდება"</string> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> იყენებს ბატარეას"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"ბატარეას <xliff:g id="NUMBER">%1$d</xliff:g> აპი იყენებს"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"შეეხეთ ბატარეისა და მონაცემების მოხმარების შესახებ დეტალური ინფორმაციისთვის"</string> @@ -2188,4 +2189,8 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <string name="window_magnification_prompt_title" msgid="8197528399699536320">"სიახლე: ფანჯრის გამადიდებელი"</string> + <string name="window_magnification_prompt_content" msgid="4166711383253283838">"ახლა შეგიძლიათ, გაადიდოთ ეკრანი ან მისი ნაწილი"</string> + <string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"ჩართვა პარამეტრებში"</string> + <string name="dismiss_action" msgid="1728820550388704784">"უარყოფა"</string> </resources> diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml index 3139dcee7482..635fed7f6bf1 100644 --- a/core/res/res/values-kk/strings.xml +++ b/core/res/res/values-kk/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB байланысы"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Қолданба қосулы"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Батареяны пайдаланып жатқан қолданбалар"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> батареяны пайдалануда"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> қолданба батареяны пайдалануда"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Батарея мен деректер трафигі туралы білу үшін түртіңіз"</string> @@ -319,7 +321,7 @@ <string name="permgroupdesc_sensors" msgid="2610631290633747752">"ағза күйінің көрсеткіштері туралы сенсор деректеріне қатынасу"</string> <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Терезе мазмұнын оқып отыру"</string> <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Ашық тұрған терезе мазмұнын тексеру."</string> - <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Explore by Touch функциясын қосу"</string> + <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Түртілген элементтерді дыбыстау функциясын қосу"</string> <string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Түртілген элементтер дауыстап айтылады және экранды қимылдар арқылы зерттеуге болады."</string> <string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"Терілген мәтінді тексеру"</string> <string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"Несиелік карта нөмірі және құпия сөздер сияқты жеке деректі қоса."</string> @@ -994,9 +996,9 @@ <string name="searchview_description_clear" msgid="1989371719192982900">"Сұрақты өшіру"</string> <string name="searchview_description_submit" msgid="6771060386117334686">"Сұрақ жіберу"</string> <string name="searchview_description_voice" msgid="42360159504884679">"Дауыс арқылы іздеу"</string> - <string name="enable_explore_by_touch_warning_title" msgid="5095399706284943314">"Explore by Touch функциясы қосылсын ба?"</string> - <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="1037295476738940824">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> қызметі Explore by Touch мүмкіндігін қосқысы келеді. Explore by Touch мүмкіндігі қосылған кезде, саусағыңыздың астындағы нәрсенің сипаттамаларын естисіз не көресіз немесе планшетпен өзара байланысу үшін қимылдайсыз."</string> - <string name="enable_explore_by_touch_warning_message" product="default" msgid="4312979647356179250">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> қызметі Explore by Touch мүмкіндігін қосқысы келеді. Explore by Touch мүмкіндігі қосылған кезде, саусағыңыздың астындағы нәрсенің сипаттамаларын естисіз не көресіз немесе телефонмен өзара байланысу үшін қимылдайсыз."</string> + <string name="enable_explore_by_touch_warning_title" msgid="5095399706284943314">"Түртілген элементтерді дыбыстау функциясы қосылсын ба?"</string> + <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="1037295476738940824">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> қызметі Түртілген элементтерді дыбыстау функциясын қосуға рұқсат сұрап тұр. Ол қосылған кезде, саусағыңыздың астындағы элементтің сипаттамасын естіп не көріп тұрасыз немесе планшетті қимылмен басқарасыз."</string> + <string name="enable_explore_by_touch_warning_message" product="default" msgid="4312979647356179250">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> қызметі Түртілген элементтерді дыбыстау функциясын қосуға рұқсат сұрап тұр. Ол қосылған кезде, саусағыңыздың астындағы элементтің сипаттамасын естіп не көріп тұрасыз немесе телефонды қимылмен басқарасыз."</string> <string name="oneMonthDurationPast" msgid="4538030857114635777">"1 ай бұрын"</string> <string name="beforeOneMonthDurationPast" msgid="8315149541372065392">"Осыған дейін 1 ай бұрын"</string> <plurals name="last_num_days" formatted="false" msgid="687443109145393632"> @@ -1316,7 +1318,7 @@ <string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"Аналогтық аудиожабдық анықталды"</string> <string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"Жалғанған құрылғы бұл телефонмен үйлесімсіз. Қосымша ақпарат алу үшін түртіңіз."</string> <string name="adb_active_notification_title" msgid="408390247354560331">"USB арқылы түзету қосылған"</string> - <string name="adb_active_notification_message" msgid="5617264033476778211">"USB арқылы түзетуді өшіру үшін түртіңіз"</string> + <string name="adb_active_notification_message" msgid="5617264033476778211">"USB арқылы түзетуді өшіру үшін түртіңіз."</string> <string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"USB арқылы түзетуді өшіру үшін таңдаңыз."</string> <string name="adbwifi_active_notification_title" msgid="6147343659168302473">"Сымсыз түзету байланыстырылды"</string> <string name="adbwifi_active_notification_message" msgid="930987922852867972">"Сымсыз түзетуді өшіру үшін түртіңіз."</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml index cc7c72abc180..1160a40bb899 100644 --- a/core/res/res/values-km/strings.xml +++ b/core/res/res/values-km/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"ការតភ្ជាប់ USB"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"កម្មវិធីដែលកំពុងដំណើរការ"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"កម្មវិធីដែលកំពុងប្រើថ្ម"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> កំពុងប្រើថ្ម"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"កម្មវិធីចំនួន <xliff:g id="NUMBER">%1$d</xliff:g> កំពុងប្រើថ្ម"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"ចុចដើម្បីមើលព័ត៌មានលម្អិតអំពីការប្រើប្រាស់ទិន្នន័យ និងថ្ម"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml index d3a314859a88..8eb3467dd32e 100644 --- a/core/res/res/values-kn/strings.xml +++ b/core/res/res/values-kn/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB ಸಂಪರ್ಕ"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"App ರನ್ ಆಗುತ್ತಿದೆ"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"ಅಪ್ಲಿಕೇಶನ್ಗಳು ಬ್ಯಾಟರಿಯನ್ನು ಉಪಯೋಗಿಸುತ್ತಿವೆ"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಆ್ಯಪ್, ಬ್ಯಾಟರಿಯನ್ನು ಬಳಸುತ್ತಿದೆ"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> ಅಪ್ಲಿಕೇಶನ್ಗಳು ಬ್ಯಾಟರಿ ಬಳಸುತ್ತಿವೆ"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"ಬ್ಯಾಟರಿ,ಡೇಟಾ ಬಳಕೆಯ ವಿವರಗಳಿಗಾಗಿ ಟ್ಯಾಪ್ ಮಾಡಿ"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index f08ed6c253f1..46b4a1b3ed1c 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB 연결"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"실행 중인 앱"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"배터리를 소모하는 앱"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g>에서 배터리 사용 중"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"앱 <xliff:g id="NUMBER">%1$d</xliff:g>개에서 배터리 사용 중"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"탭하여 배터리 및 데이터 사용량 확인"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml index af980235650a..bb005f3281fc 100644 --- a/core/res/res/values-ky/strings.xml +++ b/core/res/res/values-ky/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB аркылуу туташуу"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Колдонмо иштеп жатат"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Колдонмолор батареяңызды коротууда"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосу батареяны пайдаланып жатат"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> колдонмо батареяны пайдаланып жатат"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Батареянын кубаты жана трафиктин көлөмү жөнүндө билүү үчүн таптап коюңуз"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml index 35d2e2d9857b..8e4e467fdd50 100644 --- a/core/res/res/values-lo/strings.xml +++ b/core/res/res/values-lo/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"ການເຊື່ອມຕໍ່ USB"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"ແອັບກຳລັງເຮັດວຽກ"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"ແອັບທີ່ກຳລັງໃຊ້ແບັດເຕີຣີ"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> ກຳລັງໃຊ້ແບັດເຕີຣີຢູ່"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> ແອັບກຳລັງໃຊ້ແບັດເຕີຣີຢູ່"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"ແຕະເພື່ອເບິ່ງລາຍລະອຽດການນຳໃຊ້ແບັດເຕີຣີ ແລະ ອິນເຕີເນັດ"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml index 99e8b1f7277f..a9227163bf6b 100644 --- a/core/res/res/values-lt/strings.xml +++ b/core/res/res/values-lt/strings.xml @@ -293,6 +293,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB jungtis"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Programa paleista"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Programos, naudojančios akumuliatoriaus energiją"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ naudoja akumuliatoriaus energiją"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"Programų, naudojančių akumuliatoriaus energiją: <xliff:g id="NUMBER">%1$d</xliff:g>"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Palieskite ir sužinokite išsamios informacijos apie akumuliatoriaus bei duomenų naudojimą"</string> @@ -2256,4 +2258,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml index 95752c336749..2fec712d0cd0 100644 --- a/core/res/res/values-lv/strings.xml +++ b/core/res/res/values-lv/strings.xml @@ -290,6 +290,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB savienojums"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Lietotne darbojas"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Lietotnes, kas patērē akumulatora jaudu"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"Lietotne <xliff:g id="APP_NAME">%1$s</xliff:g> izmanto akumulatoru"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> lietotne(-es) izmanto akumulatoru"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Pieskarieties, lai skatītu detalizētu informāciju par akumulatora un datu lietojumu"</string> @@ -2222,4 +2224,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml index 5e62762dc787..3d02757d8ed9 100644 --- a/core/res/res/values-mk/strings.xml +++ b/core/res/res/values-mk/strings.xml @@ -155,19 +155,19 @@ <string name="fcError" msgid="5325116502080221346">"Проблем со поврзувањето или неважечки код за карактеристиката."</string> <string name="httpErrorOk" msgid="6206751415788256357">"Во ред"</string> <string name="httpError" msgid="3406003584150566720">"Настана грешка на мрежа."</string> - <string name="httpErrorLookup" msgid="3099834738227549349">"Не можеше да се најде URL."</string> + <string name="httpErrorLookup" msgid="3099834738227549349">"Не може да се најде URL."</string> <string name="httpErrorUnsupportedAuthScheme" msgid="3976195595501606787">"Шемата за автентикација на локацијата не е поддржана."</string> - <string name="httpErrorAuth" msgid="469553140922938968">"Не можеше да се автентицира."</string> + <string name="httpErrorAuth" msgid="469553140922938968">"Не може да се автентицира."</string> <string name="httpErrorProxyAuth" msgid="7229662162030113406">"Автентикацијата преку прокси серверот беше неуспешна."</string> - <string name="httpErrorConnect" msgid="3295081579893205617">"Не можеше да се поврзе со серверот."</string> - <string name="httpErrorIO" msgid="3860318696166314490">"Не можеше да се комуницира со серверот. Обидете се повторно подоцна."</string> + <string name="httpErrorConnect" msgid="3295081579893205617">"Не може да се поврзе со серверот."</string> + <string name="httpErrorIO" msgid="3860318696166314490">"Не може да се комуницира со серверот. Обидете се повторно подоцна."</string> <string name="httpErrorTimeout" msgid="7446272815190334204">"Времето за поврзување до серверот истече."</string> <string name="httpErrorRedirectLoop" msgid="8455757777509512098">"Страницата содржи премногу пренасочувања од серверот."</string> <string name="httpErrorUnsupportedScheme" msgid="2664108769858966374">"Протоколот не е поддржан."</string> - <string name="httpErrorFailedSslHandshake" msgid="546319061228876290">"Не можеше да се воспостави безбедна врска."</string> - <string name="httpErrorBadUrl" msgid="754447723314832538">"Страницата не можеше да се отвори, бидејќи URL е неважечки."</string> - <string name="httpErrorFile" msgid="3400658466057744084">"Не можеше да се пристапи до датотеката."</string> - <string name="httpErrorFileNotFound" msgid="5191433324871147386">"Не можеше да се најде бараната датотека."</string> + <string name="httpErrorFailedSslHandshake" msgid="546319061228876290">"Не може да се воспостави безбедна врска."</string> + <string name="httpErrorBadUrl" msgid="754447723314832538">"Страницата не може да се отвори, бидејќи URL е неважечки."</string> + <string name="httpErrorFile" msgid="3400658466057744084">"Не може да се пристапи до датотеката."</string> + <string name="httpErrorFileNotFound" msgid="5191433324871147386">"Не може да се најде бараната датотека."</string> <string name="httpErrorTooManyRequests" msgid="2149677715552037198">"Се обработуваат премногу барања. Обидете се повторно подоцна."</string> <string name="notification_title" msgid="5783748077084481121">"Грешка при пријавување за <xliff:g id="ACCOUNT">%1$s</xliff:g>"</string> <string name="contentServiceSync" msgid="2341041749565687871">"Синхронизирај"</string> @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB-врска"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Апликацијата работи"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Апликации што ја трошат батеријата"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> користи батерија"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> апликации користат батерија"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Допрете за детали за батеријата и потрошениот сообраќај"</string> @@ -550,7 +552,7 @@ <string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Не е поставен PIN, шема или лозинка"</string> <string name="biometric_error_generic" msgid="6784371929985434439">"Грешка при проверката"</string> <string name="fingerprint_acquired_partial" msgid="8532380671091299342">"Откриен е делумен отпечаток. Обидете се повторно."</string> - <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Отпечатокот не можеше да се обработи. Обидете се повторно."</string> + <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Отпечатокот не може да се обработи. Обидете се повторно."</string> <string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"Сензорот за отпечатоци е валкан. Исчистете го и обидете се повторно."</string> <string name="fingerprint_acquired_too_fast" msgid="5151661932298844352">"Прстот се движеше пребрзо. Обидете се повторно."</string> <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Прстот се движеше премногу бавно. Обидете се повторно."</string> @@ -1405,7 +1407,7 @@ <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"прашај дали да се игнорираат оптимизациите на батеријата"</string> <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Овозможува апликацијата да побара дозвола за игнорирање на оптимизациите на батеријата за таа апликација."</string> <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Допрете двапати за контрола на зумот"</string> - <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Не можеше да се додаде виџет."</string> + <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Не може да се додаде виџет."</string> <string name="ime_action_go" msgid="5536744546326495436">"Оди"</string> <string name="ime_action_search" msgid="4501435960587287668">"Пребарај"</string> <string name="ime_action_send" msgid="8456843745664334138">"Испрати"</string> @@ -1440,7 +1442,7 @@ <string name="vpn_lockdown_connecting" msgid="6096725311950342607">"Поврзување со секогаш вклучена VPN..."</string> <string name="vpn_lockdown_connected" msgid="2853127976590658469">"Поврзани со секогаш вклучена VPN"</string> <string name="vpn_lockdown_disconnected" msgid="5573611651300764955">"Исклучено од секогаш вклучената VPN"</string> - <string name="vpn_lockdown_error" msgid="4453048646854247947">"Не можеше да се поврзе на секогаш вклучената VPN"</string> + <string name="vpn_lockdown_error" msgid="4453048646854247947">"Не може да се поврзе на секогаш вклучената VPN"</string> <string name="vpn_lockdown_config" msgid="8331697329868252169">"Променете ја мрежата или поставките за VPN"</string> <string name="upload_file" msgid="8651942222301634271">"Избери датотека"</string> <string name="no_file_chosen" msgid="4146295695162318057">"Не е избрана датотека"</string> @@ -1982,9 +1984,9 @@ <string name="popup_window_default_title" msgid="6907717596694826919">"Појавен прозорец"</string> <string name="slice_more_content" msgid="3377367737876888459">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string> <string name="shortcut_restored_on_lower_version" msgid="9206301954024286063">"Верзијата на апликацијата е постара или не е компатибилна со кратенкава"</string> - <string name="shortcut_restore_not_supported" msgid="4763198938588468400">"Не можеше да се врати кратенката бидејќи апликацијата не поддржува бекап и враќање"</string> - <string name="shortcut_restore_signature_mismatch" msgid="579345304221605479">"Не можеше да се врати кратенката бидејќи потписот на апликацијата не се совпаѓа"</string> - <string name="shortcut_restore_unknown_issue" msgid="2478146134395982154">"Не можеше да се врати кратенката"</string> + <string name="shortcut_restore_not_supported" msgid="4763198938588468400">"Не може да се врати кратенката бидејќи апликацијата не поддржува бекап и враќање"</string> + <string name="shortcut_restore_signature_mismatch" msgid="579345304221605479">"Не може да се врати кратенката бидејќи потписот на апликацијата не се совпаѓа"</string> + <string name="shortcut_restore_unknown_issue" msgid="2478146134395982154">"Не може да се врати кратенката"</string> <string name="shortcut_disabled_reason_unknown" msgid="753074793553599166">"Кратенката е оневозможена"</string> <string name="harmful_app_warning_uninstall" msgid="6472912975664191772">"ДЕИНСТАЛИРАЈ"</string> <string name="harmful_app_warning_open_anyway" msgid="5963657791740211807">"СЕПАК ОТВОРИ"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml index 85a797547475..583c46e325a3 100644 --- a/core/res/res/values-ml/strings.xml +++ b/core/res/res/values-ml/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB കണക്ഷൻ"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"ആപ്പ് പ്രവർത്തിക്കുന്നു"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"ആപ്പുകൾ ബാറ്ററി ഉപയോഗിക്കുന്നു"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> ബാറ്ററി ഉപയോഗിക്കുന്നു"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> ആപ്പുകൾ ബാറ്ററി ഉപയോഗിക്കുന്നു"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"ബാറ്ററി, ഡാറ്റ ഉപയോഗം എന്നിവയുടെ വിശദാംശങ്ങളറിയാൻ ടാപ്പുചെയ്യുക"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml index 6523784a42e4..081e17065b56 100644 --- a/core/res/res/values-mn/strings.xml +++ b/core/res/res/values-mn/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB холболт"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Апп ажиллаж байна"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Апп батарей ашиглаж байна"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> батерей ашиглаж байна"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> апп батерей ашиглаж байна"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Батерей, дата ашиглалтын талаар дэлгэрэнгүйг харахын тулд товшино уу"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml index e39d46dfb6f7..058863389c75 100644 --- a/core/res/res/values-mr/strings.xml +++ b/core/res/res/values-mr/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB कनेक्शन"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"APP चालत आहे"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"बॅटरी लवकर संपवणारी अॅप्स"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> बॅटरी वापरत आहे"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> अॅप्स बॅटरी वापरत आहेत"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"बॅटरी आणि डेटा वापराच्या तपशीलांसाठी टॅप करा"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml index b38dec2c25ba..1d70f76f3cd7 100644 --- a/core/res/res/values-ms/strings.xml +++ b/core/res/res/values-ms/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"Sambungan USB"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Apl berjalan"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Apl yang menggunakan bateri"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> sedang menggunakan bateri"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> apl sedang menggunakan bateri"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Ketik untuk mendapatkan butiran tentang penggunaan kuasa bateri dan data"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml index e1f3dcf31a49..097c6c599e2b 100644 --- a/core/res/res/values-my/strings.xml +++ b/core/res/res/values-my/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB ချိတ်ဆက်မှု"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"APP လုပ်ဆောင်နေသည်"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"အက်ပ်များက ဘက်ထရီကုန်စေသည်"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> က ဘက်ထရီကို အသုံးပြုနေသည်"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"အက်ပ် <xliff:g id="NUMBER">%1$d</xliff:g> ခုက ဘက်ထရီကို အသုံးပြုနေသည်"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"ဘက်ထရီနှင့် ဒေတာအသုံးပြုမှု အသေးစိတ်ကို ကြည့်ရန် တို့ပါ"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml index 1c2e5c03d658..6fa80dd10722 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB-tilkobling"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"App kjører"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Apper bruker batteri"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> bruker batteri"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> apper bruker batteri"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Trykk for detaljer om batteri- og databruk"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml index 915e158f3267..28c35254c5db 100644 --- a/core/res/res/values-ne/strings.xml +++ b/core/res/res/values-ne/strings.xml @@ -142,7 +142,7 @@ <string name="wfcSpnFormat_wifi" msgid="1376356951297043426">"Wi-Fi"</string> <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="7178561009225028264">"WiFi कलिङ"</string> <string name="wfcSpnFormat_vowifi" msgid="8371335230890725606">"VoWifi"</string> - <string name="wifi_calling_off_summary" msgid="5626710010766902560">"निष्क्रिय"</string> + <string name="wifi_calling_off_summary" msgid="5626710010766902560">"अफ"</string> <string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Wi-Fi मार्फत कल गर्नुहोस्"</string> <string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"मोबाइल नेटवर्कमार्फत कल गर्नुहोस्"</string> <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi मात्र"</string> @@ -203,7 +203,7 @@ <string name="factory_reset_message" msgid="2657049595153992213">"प्रशासकको एप प्रयोग गर्न मिल्दैन। तपाईंको यन्त्रको डेटा अब मेटाइने छ।\n\nतपाईंसँग प्रश्नहरू भएका खण्डमा आफ्नो संगठनका प्रशासकसँग सम्पर्क गर्नुहोस्।"</string> <string name="printing_disabled_by" msgid="3517499806528864633">"<xliff:g id="OWNER_APP">%s</xliff:g> ले छाप्ने कार्यलाई असक्षम पार्यो।"</string> <string name="personal_apps_suspension_title" msgid="7561416677884286600">"आफ्नो कार्य प्रोफाइल सक्रिय गर्नुहोस्"</string> - <string name="personal_apps_suspension_text" msgid="6115455688932935597">"तपाईंले आफ्नो कार्य प्रोफाइल सक्रिय नगरुन्जेल तपाईंका व्यक्तिगत अनुप्रयोगहरूलाई रोक लगाइन्छ"</string> + <string name="personal_apps_suspension_text" msgid="6115455688932935597">"तपाईंले आफ्नो कार्य प्रोफाइल सक्रिय नगरुन्जेल तपाईंका व्यक्तिगत एपहरूलाई रोक लगाइन्छ"</string> <string name="personal_apps_suspension_soon_text" msgid="8123898693479590">"मिति <xliff:g id="DATE">%1$s</xliff:g> <xliff:g id="TIME">%2$s</xliff:g> बजे व्यक्तिगत एपहरूलाई रोक लगाइने छ। तपाईंका IT एडमिन तपाईंलाई आफ्नो कार्य प्रोफाइल <xliff:g id="NUMBER">%3$d</xliff:g> भन्दा धेरै दिन निष्क्रिय राख्ने अनुमति दिनुहुन्न।"</string> <string name="personal_apps_suspended_turn_profile_on" msgid="2758012869627513689">"सक्रिय गर्नुहोस्"</string> <string name="me" msgid="6207584824693813140">"मलाई"</string> @@ -231,7 +231,7 @@ <string name="shutdown_confirm" product="default" msgid="136816458966692315">"तपाईँको फोन बन्द हुने छ।"</string> <string name="shutdown_confirm_question" msgid="796151167261608447">"के तपाईं बन्द गर्न चाहनुहुन्छ?"</string> <string name="reboot_safemode_title" msgid="5853949122655346734">"सुरक्षित मोडमा पुनःबुट गर्नुहोस्"</string> - <string name="reboot_safemode_confirm" msgid="1658357874737219624">"सुरक्षित मोडमा तपाईं पुनःबुट गर्न चाहनु हुन्छ? तपाईंले स्थापना गरेका सबै तेस्रो पक्षका अनुप्रयोगहरूलाई असक्षम गराउने छ।"</string> + <string name="reboot_safemode_confirm" msgid="1658357874737219624">"सुरक्षित मोडमा तपाईं पुनःबुट गर्न चाहनु हुन्छ? तपाईंले स्थापना गरेका सबै तेस्रो पक्षका एपहरूलाई असक्षम गराउने छ।"</string> <string name="recent_tasks_title" msgid="8183172372995396653">"नयाँ"</string> <string name="no_recent_tasks" msgid="9063946524312275906">"कुनै नयाँ एपहरू छैनन्।"</string> <string name="global_actions" product="tablet" msgid="4412132498517933867">"ट्याब्लेट विकल्पहरू"</string> @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB जडान"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"एप चलिरहेको छ"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"एपहरूले ब्याट्री खपत गर्दै छन्"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> ले ब्याट्री प्रयोग गर्दै छ"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> एपहरूले ब्याट्री प्रयोग गर्दै छन्"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"ब्याट्री र डेटाका प्रयोग सम्बन्धी विवरणहरूका लागि ट्याप गर्नुहोस्"</string> @@ -366,13 +368,13 @@ <string name="permlab_getTasks" msgid="7460048811831750262">"चलिरहेका एपहरू पुनःबहाली गर्नुहोस्"</string> <string name="permdesc_getTasks" msgid="7388138607018233726">"वर्तमानमा र भरखरै चलिरहेका कार्यहरू बारेको सूचना पुनःबहाली गर्न एपलाई अनुमित दिन्छ। यसले उपकरणमा प्रयोग भएका अनुप्रयोगहरूको बारेमा सूचना पत्ता लगाउन एपलाई अनुमति दिन सक्छ।"</string> <string name="permlab_manageProfileAndDeviceOwners" msgid="639849495253987493">"प्रोफाइल र यन्त्र मालिकहरूको व्यवस्थापन गराउनुहोस्"</string> - <string name="permdesc_manageProfileAndDeviceOwners" msgid="7304240671781989283">"अनुप्रयोगहरूलाई प्रोफाइल र यन्त्र मालिकहरू सेट गर्न अनुमति दिनुहोस्।"</string> - <string name="permlab_reorderTasks" msgid="7598562301992923804">"चलिरहेका अनुप्रयोगहरूलाई पुनःक्रम गराउनुहोस्"</string> + <string name="permdesc_manageProfileAndDeviceOwners" msgid="7304240671781989283">"एपहरूलाई प्रोफाइल र यन्त्र मालिकहरू सेट गर्न अनुमति दिनुहोस्।"</string> + <string name="permlab_reorderTasks" msgid="7598562301992923804">"चलिरहेका एपहरूलाई पुनःक्रम गराउनुहोस्"</string> <string name="permdesc_reorderTasks" msgid="8796089937352344183">"कामहरूलाई अग्रभाग र पृष्ठभूमिमा सार्न एपलाई अनुमति दिन्छ। अनुप्रयोगले यो तपाईँको इनपुट बिना नै गर्न सक्छ।"</string> <string name="permlab_enableCarMode" msgid="893019409519325311">"कार मोड सक्षम गर्नुहोस्"</string> <string name="permdesc_enableCarMode" msgid="56419168820473508">"कार मोडलाई सक्षम पार्न एपलाई अनुमति दिन्छ।"</string> <string name="permlab_killBackgroundProcesses" msgid="6559320515561928348">"एपहरू बन्द गर्नुहोस्"</string> - <string name="permdesc_killBackgroundProcesses" msgid="2357013583055434685">"एपलाई अन्य अनुप्रयोगहरूको पृष्ठभूमि प्रक्रियाहरू बन्द गर्न अनुमति दिन्छ। यसले अन्य अनुप्रयोगहरूलाई चल्नबाट रोक्न सक्दछ।"</string> + <string name="permdesc_killBackgroundProcesses" msgid="2357013583055434685">"एपलाई अन्य अनुप्रयोगहरूको पृष्ठभूमि प्रक्रियाहरू बन्द गर्न अनुमति दिन्छ। यसले अन्य एपहरूलाई चल्नबाट रोक्न सक्दछ।"</string> <string name="permlab_systemAlertWindow" msgid="5757218350944719065">"यो एप अन्य एपहरूमाथि देखा पर्न सक्छ"</string> <string name="permdesc_systemAlertWindow" msgid="1145660714855738308">"यो एप अन्य एपहरूमाथि वा स्क्रिनका अन्य भागहरूमा देखा पर्न सक्छ। यसले एपको सामान्य प्रयोगमा अवरोध पुर्याउन सक्छ र अन्य एपहरू देखा पर्ने तरिकालाई परिवर्तन गर्न सक्छ।"</string> <string name="permlab_runInBackground" msgid="541863968571682785">"पृष्ठभूमिमा चलाउनुहोस्"</string> @@ -380,7 +382,7 @@ <string name="permlab_useDataInBackground" msgid="783415807623038947">"पृष्ठभूमिमा डेटा प्रयोग गर्नुहोस्"</string> <string name="permdesc_useDataInBackground" msgid="1230753883865891987">"यो अनुप्रयोगले पृष्ठभूमिमा डेटा प्रयोग गर्नसक्छ। यसले गर्दा धेरै डेटा प्रयोग हुनसक्छ।"</string> <string name="permlab_persistentActivity" msgid="464970041740567970">"एपहरू जहिले पनि चल्ने बनाउनुहोस्"</string> - <string name="permdesc_persistentActivity" product="tablet" msgid="6055271149187369916">"यसको आफ्नै मेमोरीमा दृढ भएकोको अंश बनाउनको लागि एपलाई अनुमति दिन्छ। ट्याब्लेटलाई ढिलो गराउँदै गरेका अन्य अनुप्रयोगहरूलाई सीमित मात्रामा यसले मेमोरी उपलब्ध गराउन सक्छ।"</string> + <string name="permdesc_persistentActivity" product="tablet" msgid="6055271149187369916">"यसको आफ्नै मेमोरीमा दृढ भएकोको अंश बनाउनको लागि एपलाई अनुमति दिन्छ। ट्याब्लेटलाई ढिलो गराउँदै गरेका अन्य एपहरूलाई सीमित मात्रामा यसले मेमोरी उपलब्ध गराउन सक्छ।"</string> <string name="permdesc_persistentActivity" product="tv" msgid="6800526387664131321">"एपलाई आफ्ना केही अंशहरू मेमोरीमा स्थायी रूपमा राख्ने अनुमति दिन्छ। यसले गर्दा अन्य अनुप्रयोगहरूका लागि मेमोरीको अभाव हुन सक्ने भएकाले तपाईंको Android टिभी यन्त्र सुस्त हुन सक्छ।"</string> <string name="permdesc_persistentActivity" product="default" msgid="1914841924366562051">"एपलाई मेमोरीमा आफैंको निरन्तरको अंश बनाउन अनुमति दिन्छ। यसले फोनलाई ढिला बनाएर अन्य एपहरूमा मेमोरी SIMित गर्न सक्दछन्।"</string> <string name="permlab_foregroundService" msgid="1768855976818467491">"अग्रभूमिको सेवा सञ्चालन गर्नुहोस्"</string> @@ -398,9 +400,9 @@ <string name="permdesc_broadcastSticky" product="tv" msgid="2338185920171000650">"एपलाई प्रसारण समाप्त भइसकेपछि पनि रहिरहने स्टिकी प्रसारणहरू पठाउने अनुमति दिन्छ। यो सुविधाको अत्यधिक प्रयोगले धेरै मेमोरी प्रयोग हुने भएकाले तपाईंको Android टिभी यन्त्र सुस्त वा अस्थिर हुन सक्छ।"</string> <string name="permdesc_broadcastSticky" product="default" msgid="134529339678913453">"औपचारिक प्रसारणलाई पठाउनको लागि एक एपलाई अनुमति दिन्छ, जुन प्रसारण समाप्त भएपछि बाँकी रहन्छ। अत्यधिक प्रयोगले धेरै मेमोरी प्रयोग गरेको कारणले फोनलाई ढिलो र अस्थिर बनाउन सक्छ।"</string> <string name="permlab_readContacts" msgid="8776395111787429099">"तपाईँका सम्पर्कहरू पढ्नुहोस्"</string> - <string name="permdesc_readContacts" product="tablet" msgid="6430093481659992692">"एपलाई तपाईंको ट्याब्लेटमा भण्डार गरिएका सम्पर्क ठेगानाहरूसँग सम्बन्धित डेटा पढ्ने अनुमति दिन्छ। एपहरूले सम्पर्क ठेगानाहरू बनाउने तपाईंको ट्याब्लेटमा भण्डार गरिएका खाताहरूमाथि पनि पहुँच प्राप्त गर्ने छन्। यसमा तपाईंले स्थापना गरेका एपहरूले बनाएका खाताहरू पर्न सक्छन्। यस अनुमतिले अनुप्रयोगहरूलाई तपाईंको सम्पर्क ठेगानासम्बन्धी डेटा सुरक्षित गर्न दिने भएकाले हानिकारक एपहरूले तपाईंलाई थाहै नदिइकन सम्पर्क ठेगानासम्बन्धी डेटा आदान प्रदान गर्न सक्छन्।"</string> - <string name="permdesc_readContacts" product="tv" msgid="8400138591135554789">"एपलाई तपाईंको Android टिभी यन्त्रमा भण्डारण गरिएका सम्पर्क ठेगानासम्बन्धी डेटा पढ्न अनुमति दिन्छ। एपहरूले सम्पर्क ठेगानाहरू बनाउने तपाईंको Android टिभी यन्त्रमा भण्डार गरिएका खाताहरूमाथि पनि पहुँच प्राप्त गर्ने छन्। यसमा तपाईंले स्थापना गरेका एपहरूले बनाएका खाताहरू पर्न सक्छन्। यस अनुमतिले अनुप्रयोगहरूलाई तपाईंको सम्पर्क ठेगानासम्बन्धी डेटा सुरक्षित गर्न दिने भएकाले हानिकारक एपहरूले तपाईंलाई थाहै नदिइकन सम्पर्क ठेगानासम्बन्धी डेटा आदान प्रदान गर्न सक्छन्।"</string> - <string name="permdesc_readContacts" product="default" msgid="4911989776203207644">"एपलाई तपाईंको फोनमा भण्डार गरिएका सम्पर्क ठेगानाहरूसँग सम्बन्धित डेटा पढ्ने अनुमति दिन्छ। एपहरूले सम्पर्क ठेगानाहरू बनाउने तपाईंको फोनमा भण्डार गरिएका खाताहरूमाथि पनि पहुँच प्राप्त गर्ने छन्। यसमा तपाईंले स्थापना गरेका एपहरूले बनाएका खाताहरू पर्न सक्छन्। यस अनुमतिले अनुप्रयोगहरूलाई तपाईंको सम्पर्क ठेगानासम्बन्धी डेटा सुरक्षित गर्न दिने भएकाले हानिकारक एपहरूले तपाईंलाई थाहै नदिइकन सम्पर्क ठेगानासम्बन्धी डेटा आदान प्रदान गर्न सक्छन्।"</string> + <string name="permdesc_readContacts" product="tablet" msgid="6430093481659992692">"एपलाई तपाईंको ट्याब्लेटमा भण्डार गरिएका सम्पर्क ठेगानाहरूसँग सम्बन्धित डेटा पढ्ने अनुमति दिन्छ। एपहरूले सम्पर्क ठेगानाहरू बनाउने तपाईंको ट्याब्लेटमा भण्डार गरिएका खाताहरूमाथि पनि पहुँच प्राप्त गर्ने छन्। यसमा तपाईंले स्थापना गरेका एपहरूले बनाएका खाताहरू पर्न सक्छन्। यस अनुमतिले एपहरूलाई तपाईंको सम्पर्क ठेगानासम्बन्धी डेटा सुरक्षित गर्न दिने भएकाले हानिकारक एपहरूले तपाईंलाई थाहै नदिइकन सम्पर्क ठेगानासम्बन्धी डेटा आदान प्रदान गर्न सक्छन्।"</string> + <string name="permdesc_readContacts" product="tv" msgid="8400138591135554789">"एपलाई तपाईंको Android टिभी यन्त्रमा भण्डारण गरिएका सम्पर्क ठेगानासम्बन्धी डेटा पढ्न अनुमति दिन्छ। एपहरूले सम्पर्क ठेगानाहरू बनाउने तपाईंको Android टिभी यन्त्रमा भण्डार गरिएका खाताहरूमाथि पनि पहुँच प्राप्त गर्ने छन्। यसमा तपाईंले स्थापना गरेका एपहरूले बनाएका खाताहरू पर्न सक्छन्। यस अनुमतिले एपहरूलाई तपाईंको सम्पर्क ठेगानासम्बन्धी डेटा सुरक्षित गर्न दिने भएकाले हानिकारक एपहरूले तपाईंलाई थाहै नदिइकन सम्पर्क ठेगानासम्बन्धी डेटा आदान प्रदान गर्न सक्छन्।"</string> + <string name="permdesc_readContacts" product="default" msgid="4911989776203207644">"एपलाई तपाईंको फोनमा भण्डार गरिएका सम्पर्क ठेगानाहरूसँग सम्बन्धित डेटा पढ्ने अनुमति दिन्छ। एपहरूले सम्पर्क ठेगानाहरू बनाउने तपाईंको फोनमा भण्डार गरिएका खाताहरूमाथि पनि पहुँच प्राप्त गर्ने छन्। यसमा तपाईंले स्थापना गरेका एपहरूले बनाएका खाताहरू पर्न सक्छन्। यस अनुमतिले एपहरूलाई तपाईंको सम्पर्क ठेगानासम्बन्धी डेटा सुरक्षित गर्न दिने भएकाले हानिकारक एपहरूले तपाईंलाई थाहै नदिइकन सम्पर्क ठेगानासम्बन्धी डेटा आदान प्रदान गर्न सक्छन्।"</string> <string name="permlab_writeContacts" msgid="8919430536404830430">"तपाईँका सम्पर्कहरू परिवर्तन गर्नुहोस्"</string> <string name="permdesc_writeContacts" product="tablet" msgid="6422419281427826181">"एपलाई तपाईंको ट्याब्लेटमा भण्डारण गरिएका सम्पर्क ठेगानासम्बन्धी डेटा परिमार्जन गर्न अनुमति दिन्छ। यो अनुमतिले एपलाई सम्पर्क ठेगानासम्बन्धी डेटा मेटाउन अनुमति दिन्छ।"</string> <string name="permdesc_writeContacts" product="tv" msgid="6488872735379978935">"एपलाई तपाईंको Android टिभी यन्त्रमा भण्डारण गरिएका सम्पर्क ठेगानासम्बन्धी डेटा परिमार्जन गर्न अनुमति दिन्छ। यो अनुमतिले एपलाई सम्पर्क ठेगानासम्बन्धी डेटा मेटाउन अनुमति दिन्छ।"</string> @@ -621,7 +623,7 @@ <string name="permlab_readSyncSettings" msgid="6250532864893156277">"समीकरण सेटिङहरू पढ्नुहोस्"</string> <string name="permdesc_readSyncSettings" msgid="1325658466358779298">"एपलाई खाताको लागि सिंक सेटिङहरू पढ्न अनुमति दिन्छ। उदाहरणको लागि यसले व्यक्तिहरको एप खातासँग सिंक भएको नभएको निर्धारण गर्न सक्दछ।"</string> <string name="permlab_writeSyncSettings" msgid="6583154300780427399">"टगल सिंक खुला र बन्द"</string> - <string name="permdesc_writeSyncSettings" msgid="6029151549667182687">"अनुप्रयोगहरूलाई खाताको लागि सिंक सेटिङहरू परिमार्जन गर्न अनुमति दिन्छ। उदाहरणको लागि, यो खातासँग व्यक्ति एपको सिंक सक्षम गर्न प्रयोग गर्न सकिन्छ।"</string> + <string name="permdesc_writeSyncSettings" msgid="6029151549667182687">"एपहरूलाई खाताको लागि सिंक सेटिङहरू परिमार्जन गर्न अनुमति दिन्छ। उदाहरणको लागि, यो खातासँग व्यक्ति एपको सिंक सक्षम गर्न प्रयोग गर्न सकिन्छ।"</string> <string name="permlab_readSyncStats" msgid="3747407238320105332">"सिंक तथ्याङ्कहरू पढ्नुहोस्"</string> <string name="permdesc_readSyncStats" msgid="3867809926567379434">"एपलाई खाताको लागि समीकरणको आँकडा समीकरण घटनाहरूको इतिहास र समीकरण गरिएको डेटाको मापन समेत, पढ्न अनुमति दिन्छ।"</string> <string name="permlab_sdcardRead" msgid="5791467020950064920">"आफ्नो आदान प्रदान गरिएको भण्डारणको सामग्रीहरूहरू पढ्नुहोस्"</string> @@ -649,9 +651,9 @@ <string name="permlab_modifyNetworkAccounting" msgid="7448790834938749041">"नेटवर्क उपयोग लेखालाई परिमार्जन गर्नुहोस्"</string> <string name="permdesc_modifyNetworkAccounting" msgid="5076042642247205390">"एपलाई कसरी अनुप्रयोगहरूको विरूद्धमा कसरी नेटवर्क उपयोगी अकाउन्टेड छ भनेर परिमार्जन गर्न अनुमति दिन्छ। साधारण अनुप्रयोगहरूद्वारा प्रयोगको लागि होइन।"</string> <string name="permlab_accessNotifications" msgid="7130360248191984741">"सूचनाहरू पहुँच गर्नुहोस्"</string> - <string name="permdesc_accessNotifications" msgid="761730149268789668">"अन्य अनुप्रयोगहरूबाट पोस्ट गरिएकासहित पुनःप्राप्त गर्न, परीक्षण गर्न र सूचनाहरू हटाउन अनुप्रयोगहरूलाई अनुमति दिन्छ।"</string> + <string name="permdesc_accessNotifications" msgid="761730149268789668">"अन्य अनुप्रयोगहरूबाट पोस्ट गरिएकासहित पुनःप्राप्त गर्न, परीक्षण गर्न र सूचनाहरू हटाउन एपहरूलाई अनुमति दिन्छ।"</string> <string name="permlab_bindNotificationListenerService" msgid="5848096702733262458">"जानकारी श्रोता सेवामा बाँध्नुहोस्"</string> - <string name="permdesc_bindNotificationListenerService" msgid="4970553694467137126">"होल्डरलाई सूचना श्रोता सेवाको शीर्ष-स्तरको इन्टरफेस बाँध्न अनुमति दिन्छ। सामान्य अनुप्रयोगहरूलाई कहिले पनि आवश्यक नपर्न सक्दछ।"</string> + <string name="permdesc_bindNotificationListenerService" msgid="4970553694467137126">"होल्डरलाई सूचना श्रोता सेवाको शीर्ष-स्तरको इन्टरफेस बाँध्न अनुमति दिन्छ। सामान्य एपहरूलाई कहिले पनि आवश्यक नपर्न सक्दछ।"</string> <string name="permlab_bindConditionProviderService" msgid="5245421224814878483">"सर्त प्रदायक सेवामा जोड्न"</string> <string name="permdesc_bindConditionProviderService" msgid="6106018791256120258">"सर्त प्रदायक सेवाको माथिल्लो स्तरको इन्टरफेसमा जोड्न बाहकलाई अनुमति दिन्छ। साधारण अनुप्रयोगहरूको लागि कहिल्यै पनि आवश्यक पर्दैन।"</string> <string name="permlab_bindDreamService" msgid="4776175992848982706">"सपना सेवामा बाँध्नुहोस्"</string> @@ -675,7 +677,7 @@ <string name="permlab_access_notification_policy" msgid="5524112842876975537">"बाधा नपुर्याउँनुहोस् पहुँच गर्नुहोस्"</string> <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"बाधा नपुर्याउँनुहोस् कन्फिगरेसन पढ्न र लेख्नको लागि एपलाई अनुमति दिनुहोस्।"</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"हेर्ने अनुमतिको प्रयोग सुरु गर्नुहोस्"</string> - <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"वाहकलाई कुनै अनुप्रयोगसम्बन्धी अनुमतिको प्रयोग सुरु गर्न दिन्छ। साधारण अनुप्रयोगहरूलाई कहिल्यै आवश्यक नपर्नु पर्ने हो।"</string> + <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"वाहकलाई कुनै एपसम्बन्धी अनुमतिको प्रयोग सुरु गर्न दिन्छ। साधारण एपहरूलाई कहिल्यै आवश्यक नपर्नु पर्ने हो।"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"पासवर्ड नियमहरू मिलाउनुहोस्"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"स्क्रिन लक पासवर्ड र PIN हरूमा अनुमति दिइएको लम्बाइ र वर्णहरूको नियन्त्रण गर्नुहोस्।"</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"मनिटरको स्क्रिन अनलक गर्ने प्रयासहरू"</string> @@ -1126,7 +1128,7 @@ <string name="dialog_alert_title" msgid="651856561974090712">"सावधानी"</string> <string name="loading" msgid="3138021523725055037">"लोड हुँदै..."</string> <string name="capital_on" msgid="2770685323900821829">"चालु"</string> - <string name="capital_off" msgid="7443704171014626777">"बन्द"</string> + <string name="capital_off" msgid="7443704171014626777">"अफ"</string> <string name="checked" msgid="9179896827054513119">"जाँच गरिएको"</string> <string name="not_checked" msgid="7972320087569023342">"जाँच गरिएको छैन"</string> <string name="selected" msgid="6614607926197755875">"चयन गरियो"</string> @@ -1879,7 +1881,7 @@ <string name="default_notification_channel_label" msgid="3697928973567217330">"वर्गीकरण नगरिएको"</string> <string name="importance_from_user" msgid="2782756722448800447">"तपाईंले यी सूचनाहरूको महत्त्व सेट गर्नुहोस् ।"</string> <string name="importance_from_person" msgid="4235804979664465383">"यसमा सङ्लग्न भएका मानिसहरूको कारणले गर्दा यो महत्वपूर्ण छ।"</string> - <string name="notification_history_title_placeholder" msgid="7748630986182249599">"अनुप्रयोगसम्बन्धी आफ्नो रोजाइअनुसारको सूचना"</string> + <string name="notification_history_title_placeholder" msgid="7748630986182249599">"एपसम्बन्धी आफ्नो रोजाइअनुसारको सूचना"</string> <string name="user_creation_account_exists" msgid="2239146360099708035">"<xliff:g id="ACCOUNT">%2$s</xliff:g> (यस खाताको प्रयोगकर्ता पहिले नै अवस्थित छ) मा नयाँ प्रयोगकर्ता सिर्जना गर्न <xliff:g id="APP">%1$s</xliff:g> लाई अनुमति दिने हो?"</string> <string name="user_creation_adding" msgid="7305185499667958364">"<xliff:g id="ACCOUNT">%2$s</xliff:g> मा नयाँ प्रयोगकर्ता सिर्जना गर्न <xliff:g id="APP">%1$s</xliff:g> लाई अनुमति दिने हो?"</string> <string name="language_selection_title" msgid="52674936078683285">"भाषा थप्नुहोस्"</string> @@ -1911,7 +1913,7 @@ <string name="pin_specific_target" msgid="7824671240625957415">"<xliff:g id="LABEL">%1$s</xliff:g> लाई पिन गर्नुहोस्"</string> <string name="unpin_target" msgid="3963318576590204447">"अनपिन गर्नुहोस्"</string> <string name="unpin_specific_target" msgid="3859828252160908146">"<xliff:g id="LABEL">%1$s</xliff:g> लाई अनपिन गर्नुहोस्"</string> - <string name="app_info" msgid="6113278084877079851">"अनुप्रयोगका बारे जानकारी"</string> + <string name="app_info" msgid="6113278084877079851">"एपका बारे जानकारी"</string> <string name="negative_duration" msgid="1938335096972945232">"−<xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="demo_starting_message" msgid="6577581216125805905">"डेमो सुरु गर्दै…"</string> <string name="demo_restarting_message" msgid="1160053183701746766">"यन्त्रलाई रिसेट गर्दै…"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index f768c833da66..8b571e82dea8 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB-verbinding"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"App actief"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Apps die de batterij gebruiken"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> gebruikt de batterij"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> apps gebruiken de batterij"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Tik voor batterij- en datagebruik"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml index 520673279ce7..4b5c9b0be420 100644 --- a/core/res/res/values-or/strings.xml +++ b/core/res/res/values-or/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB ସଂଯୋଗ"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"ଆପ୍ ଚାଲୁଛି"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"ଆପ୍ଗୁଡ଼ିକ ବ୍ୟାଟେରୀ ଖର୍ଚ୍ଚ କରିଥା\'ନ୍ତି"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> ବ୍ୟାଟେରୀ ବ୍ୟବହାର କରୁଛି"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g>ଟି ଆପ୍ ବ୍ୟାଟେରୀ ବ୍ୟବହାର କରୁଛନ୍ତି"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"ବ୍ୟାଟେରୀ ଏବଂ ଡାଟା ବ୍ୟବହାର ଉପରେ ବିବରଣୀ ପାଇଁ ଟାପ୍ କରନ୍ତୁ"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml index 11559f39565f..5de08850a3ee 100644 --- a/core/res/res/values-pa/strings.xml +++ b/core/res/res/values-pa/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB ਕਨੈਕਸ਼ਨ"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"ਚੱਲ ਰਹੀ ਐਪ"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"ਬੈਟਰੀ ਦੀ ਖਪਤ ਕਰਨ ਵਾਲੀਆਂ ਐਪਾਂ"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਵੱਲੋਂ ਬੈਟਰੀ ਦੀ ਵਰਤੋਂ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> ਐਪਾਂ ਬੈਟਰੀ ਦੀ ਵਰਤੋਂ ਕਰ ਰਹੀਆਂ ਹਨ"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"ਬੈਟਰੀ ਅਤੇ ਡਾਟਾ ਵਰਤੋਂ ਸਬੰਧੀ ਵੇਰਵਿਆਂ ਲਈ ਟੈਪ ਕਰੋ"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index 07773da7f933..427beb508366 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -186,10 +186,10 @@ <item quantity="one">Urząd certyfikacji został zainstalowany</item> </plurals> <string name="ssl_ca_cert_noti_by_unknown" msgid="4961102218216815242">"Przez nieznany podmiot zewnętrzny"</string> - <string name="ssl_ca_cert_noti_by_administrator" msgid="4564941950768783879">"Przez administratora Twojego profilu do pracy"</string> + <string name="ssl_ca_cert_noti_by_administrator" msgid="4564941950768783879">"Przez administratora Twojego profilu służbowego"</string> <string name="ssl_ca_cert_noti_managed" msgid="217337232273211674">"Przez <xliff:g id="MANAGING_DOMAIN">%s</xliff:g>"</string> <string name="work_profile_deleted" msgid="5891181538182009328">"Usunięto profil służbowy"</string> - <string name="work_profile_deleted_details" msgid="3773706828364418016">"Brakuje aplikacji administratora profilu do pracy lub jest ona uszkodzona. Dlatego Twój profil służbowy i związane z nim dane zostały usunięte. Skontaktuj się ze swoim administratorem, by uzyskać pomoc."</string> + <string name="work_profile_deleted_details" msgid="3773706828364418016">"Brakuje aplikacji administratora profilu służbowego lub jest ona uszkodzona. Dlatego Twój profil służbowy i związane z nim dane zostały usunięte. Skontaktuj się ze swoim administratorem, by uzyskać pomoc."</string> <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Twój profil służbowy nie jest już dostępny na tym urządzeniu"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Zbyt wiele prób podania hasła"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"Administrator odstąpił urządzenie do użytku osobistego"</string> @@ -206,8 +206,8 @@ <string name="factory_reset_warning" msgid="6858705527798047809">"Twoje urządzenie zostanie wyczyszczone"</string> <string name="factory_reset_message" msgid="2657049595153992213">"Nie można użyć aplikacji administratora. Dane z urządzenia zostaną wykasowane.\n\nJeśli masz pytania, skontaktuj się z administratorem organizacji."</string> <string name="printing_disabled_by" msgid="3517499806528864633">"Drukowanie wyłączone przez: <xliff:g id="OWNER_APP">%s</xliff:g>."</string> - <string name="personal_apps_suspension_title" msgid="7561416677884286600">"Włącz profil do pracy"</string> - <string name="personal_apps_suspension_text" msgid="6115455688932935597">"Zablokowano aplikacje osobiste do czasu włączenia profilu do pracy"</string> + <string name="personal_apps_suspension_title" msgid="7561416677884286600">"Włącz profil służbowy"</string> + <string name="personal_apps_suspension_text" msgid="6115455688932935597">"Zablokowano aplikacje osobiste do czasu włączenia profilu służbowego"</string> <string name="personal_apps_suspension_soon_text" msgid="8123898693479590">"Aplikacje osobiste zostaną zablokowane <xliff:g id="DATE">%1$s</xliff:g> o <xliff:g id="TIME">%2$s</xliff:g>. Administrator IT nie pozwala na wyłączenie profilu służbowego na dłużej niż <xliff:g id="NUMBER">%3$d</xliff:g> dni."</string> <string name="personal_apps_suspended_turn_profile_on" msgid="2758012869627513689">"Włącz"</string> <string name="me" msgid="6207584824693813140">"Ja"</string> @@ -293,6 +293,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"Połączenie USB"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Działa aplikacja"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Aplikacje zużywające baterię"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> zużywa baterię"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"Liczba aplikacji zużywających baterię: <xliff:g id="NUMBER">%1$d</xliff:g>"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Kliknij, by wyświetlić szczegóły wykorzystania baterii i użycia danych"</string> @@ -716,7 +718,7 @@ <string-array name="phoneTypes"> <item msgid="8996339953292723951">"Dom"</item> <item msgid="7740243458912727194">"Komórka"</item> - <item msgid="8526146065496663766">"Praca"</item> + <item msgid="8526146065496663766">"Służbowy"</item> <item msgid="8150904584178569699">"Faks w pracy"</item> <item msgid="4537253139152229577">"Faks domowy"</item> <item msgid="6751245029698664340">"Pager"</item> @@ -725,24 +727,24 @@ </string-array> <string-array name="emailAddressTypes"> <item msgid="7786349763648997741">"Dom"</item> - <item msgid="435564470865989199">"Praca"</item> + <item msgid="435564470865989199">"Służbowy"</item> <item msgid="4199433197875490373">"Inne"</item> <item msgid="3233938986670468328">"Niestandardowy"</item> </string-array> <string-array name="postalAddressTypes"> <item msgid="3861463339764243038">"Dom"</item> - <item msgid="5472578890164979109">"Praca"</item> + <item msgid="5472578890164979109">"Służbowy"</item> <item msgid="5718921296646594739">"Inny"</item> <item msgid="5523122236731783179">"Niestandardowy"</item> </string-array> <string-array name="imAddressTypes"> <item msgid="588088543406993772">"Dom"</item> - <item msgid="5503060422020476757">"Praca"</item> + <item msgid="5503060422020476757">"Służbowy"</item> <item msgid="2530391194653760297">"Inne"</item> <item msgid="7640927178025203330">"Niestandardowy"</item> </string-array> <string-array name="organizationTypes"> - <item msgid="6144047813304847762">"Praca"</item> + <item msgid="6144047813304847762">"Służbowy"</item> <item msgid="7402720230065674193">"Inne"</item> <item msgid="808230403067569648">"Niestandardowy"</item> </string-array> @@ -1462,8 +1464,8 @@ <string name="deny" msgid="6632259981847676572">"Odmów"</string> <string name="permission_request_notification_title" msgid="1810025922441048273">"Prośba o pozwolenie"</string> <string name="permission_request_notification_with_subtitle" msgid="3743417870360129298">"Prośba o pozwolenie\ndotyczące konta <xliff:g id="ACCOUNT">%s</xliff:g>"</string> - <string name="forward_intent_to_owner" msgid="4620359037192871015">"Używasz tej aplikacji poza profilem do pracy"</string> - <string name="forward_intent_to_work" msgid="3620262405636021151">"Używasz tej aplikacji w swoim profilu do pracy"</string> + <string name="forward_intent_to_owner" msgid="4620359037192871015">"Używasz tej aplikacji poza profilem służbowym"</string> + <string name="forward_intent_to_work" msgid="3620262405636021151">"Używasz tej aplikacji w swoim profilu służbowym"</string> <string name="input_method_binding_label" msgid="1166731601721983656">"Sposób wprowadzania tekstu"</string> <string name="sync_binding_label" msgid="469249309424662147">"Synchronizacja"</string> <string name="accessibility_binding_label" msgid="1974602776545801715">"Ułatwienia dostępu"</string> @@ -1597,7 +1599,7 @@ <string name="SetupCallDefault" msgid="5581740063237175247">"Odebrać połączenie?"</string> <string name="activity_resolver_use_always" msgid="5575222334666843269">"Zawsze"</string> <string name="activity_resolver_use_once" msgid="948462794469672658">"Tylko raz"</string> - <string name="activity_resolver_work_profiles_support" msgid="4071345609235361269">"%1$s nie obsługuje profilu do pracy"</string> + <string name="activity_resolver_work_profiles_support" msgid="4071345609235361269">"%1$s nie obsługuje profilu służbowego"</string> <string name="default_audio_route_name" product="tablet" msgid="367936735632195517">"Tablet"</string> <string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"Telewizor"</string> <string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Telefon"</string> @@ -1834,7 +1836,7 @@ <string name="select_day" msgid="2060371240117403147">"Wybierz miesiąc i dzień"</string> <string name="select_year" msgid="1868350712095595393">"Wybierz rok"</string> <string name="deleted_key" msgid="9130083334943364001">"<xliff:g id="KEY">%1$s</xliff:g> usunięte"</string> - <string name="managed_profile_label_badge" msgid="6762559569999499495">"<xliff:g id="LABEL">%1$s</xliff:g> (praca)"</string> + <string name="managed_profile_label_badge" msgid="6762559569999499495">"<xliff:g id="LABEL">%1$s</xliff:g> (służbowy)"</string> <string name="managed_profile_label_badge_2" msgid="5673187309555352550">"<xliff:g id="LABEL">%1$s</xliff:g> – praca 2"</string> <string name="managed_profile_label_badge_3" msgid="6882151970556391957">"<xliff:g id="LABEL">%1$s</xliff:g> – praca 3"</string> <string name="lock_to_app_unlock_pin" msgid="3890940811866290782">"Podaj PIN, aby odpiąć"</string> @@ -1957,8 +1959,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"Aplikacja <xliff:g id="APP_NAME_0">%1$s</xliff:g> nie jest teraz dostępna. Zarządza tym aplikacja <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"Więcej informacji"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Wznów działanie aplikacji"</string> - <string name="work_mode_off_title" msgid="5503291976647976560">"Włączyć profil do pracy?"</string> - <string name="work_mode_off_message" msgid="8417484421098563803">"Aplikacje do pracy, powiadomienia, dane i inne funkcje profilu do pracy zostaną włączone"</string> + <string name="work_mode_off_title" msgid="5503291976647976560">"Włączyć profil służbowy?"</string> + <string name="work_mode_off_message" msgid="8417484421098563803">"Aplikacje służbowe, powiadomienia, dane i inne funkcje profilu służbowego zostaną włączone"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"Włącz"</string> <string name="app_blocked_title" msgid="7353262160455028160">"Aplikacja jest niedostępna"</string> <string name="app_blocked_message" msgid="542972921087873023">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> jest obecnie niedostępna."</string> @@ -2256,4 +2258,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml index bfd32f35c993..3fed0287bc1e 100644 --- a/core/res/res/values-pt-rBR/strings.xml +++ b/core/res/res/values-pt-rBR/strings.xml @@ -287,6 +287,7 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"Conexão USB"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"App em execução"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Apps que estão consumindo a bateria"</string> + <string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Ampliação"</string> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> está consumindo a bateria"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> apps estão consumindo a bateria"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Tocar para ver detalhes sobre a bateria e o uso de dados"</string> @@ -2188,4 +2189,8 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <string name="window_magnification_prompt_title" msgid="8197528399699536320">"Novo: Lupa de janela"</string> + <string name="window_magnification_prompt_content" msgid="4166711383253283838">"Agora você pode ampliar uma ou todas as suas telas"</string> + <string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Ativar nas Configurações"</string> + <string name="dismiss_action" msgid="1728820550388704784">"Dispensar"</string> </resources> diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml index e83d5578cb32..1c890dd4b651 100644 --- a/core/res/res/values-pt-rPT/strings.xml +++ b/core/res/res/values-pt-rPT/strings.xml @@ -287,6 +287,7 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"Ligação USB"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Aplicação em execução"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Apps que estão a consumir bateria"</string> + <string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Ampliação"</string> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> está a consumir bateria."</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> aplicações estão a consumir bateria."</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Toque para obter detalhes acerca da utilização da bateria e dos dados"</string> @@ -2188,4 +2189,8 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <string name="window_magnification_prompt_title" msgid="8197528399699536320">"Novidade: ampliador da janela"</string> + <string name="window_magnification_prompt_content" msgid="4166711383253283838">"Já pode ampliar o ecrã parcial ou totalmente."</string> + <string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Ativar nas Definições"</string> + <string name="dismiss_action" msgid="1728820550388704784">"Ignorar"</string> </resources> diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index bfd32f35c993..3fed0287bc1e 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -287,6 +287,7 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"Conexão USB"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"App em execução"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Apps que estão consumindo a bateria"</string> + <string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Ampliação"</string> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> está consumindo a bateria"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> apps estão consumindo a bateria"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Tocar para ver detalhes sobre a bateria e o uso de dados"</string> @@ -2188,4 +2189,8 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <string name="window_magnification_prompt_title" msgid="8197528399699536320">"Novo: Lupa de janela"</string> + <string name="window_magnification_prompt_content" msgid="4166711383253283838">"Agora você pode ampliar uma ou todas as suas telas"</string> + <string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Ativar nas Configurações"</string> + <string name="dismiss_action" msgid="1728820550388704784">"Dispensar"</string> </resources> diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml index fcd70eb61a25..5cf30763d8f0 100644 --- a/core/res/res/values-ro/strings.xml +++ b/core/res/res/values-ro/strings.xml @@ -290,6 +290,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"Conexiune USB"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Aplicația rulează"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Aplicațiile consumă bateria"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> folosește bateria"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> aplicații folosesc bateria"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Atingeți pentru mai multe detalii privind bateria și utilizarea datelor"</string> @@ -2222,4 +2224,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index 35bf873dc5a6..efd3c6c5ad96 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -293,6 +293,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB-подключение"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Приложение активно"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Приложения, расходующие заряд"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" расходует заряд"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"Несколько приложений (<xliff:g id="NUMBER">%1$d</xliff:g>) расходуют заряд"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Нажмите, чтобы проверить энергопотребление и трафик"</string> @@ -2256,4 +2258,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml index 70cfaa7a741d..100aed1c3c66 100644 --- a/core/res/res/values-si/strings.xml +++ b/core/res/res/values-si/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB සම්බන්ධතාවය"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"යෙදුම ධාවනය කරමින්"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"බැටරිය භාවිත කරන යෙදුම්"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> බැටරිය භාවිත කරයි"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"යෙදුම් <xliff:g id="NUMBER">%1$d</xliff:g>ක් බැටරිය භාවිත කරයි"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"බැටරි හා දත්ත භාවිතය පිළිබඳව විස්තර සඳහා තට්ටු කරන්න"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml index cef059d8cfde..f7756e5959ce 100644 --- a/core/res/res/values-sk/strings.xml +++ b/core/res/res/values-sk/strings.xml @@ -293,6 +293,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"Pripojenie USB"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Aplikácia je spustená"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Aplikácie spotrebúvajúce batériu"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> používa batériu"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"Aplikácie (<xliff:g id="NUMBER">%1$d</xliff:g>) používajú batériu"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Klepnutím zobrazíte podrobnosti o batérii a spotrebe dát"</string> @@ -2256,4 +2258,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml index b29748c1360f..df7e0ee1a856 100644 --- a/core/res/res/values-sl/strings.xml +++ b/core/res/res/values-sl/strings.xml @@ -293,6 +293,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"Povezava USB"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Aplikacija se izvaja"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Aplikacije, ki porabljajo energijo baterije"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> porablja energijo baterije"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"Toliko aplikacij porablja energijo baterije: <xliff:g id="NUMBER">%1$d</xliff:g>"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Dotaknite se za prikaz podrobnosti porabe baterije in prenosa podatkov"</string> @@ -2256,4 +2258,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml index c852d277a92f..8de3f8ca4f80 100644 --- a/core/res/res/values-sq/strings.xml +++ b/core/res/res/values-sq/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"Lidhja USB"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Aplikacioni është në ekzekutim"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Aplikacionet që konsumojnë baterinë"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> po përdor baterinë"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> aplikacione po përdorin baterinë"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Trokit për detaje mbi baterinë dhe përdorimin e të dhënave"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml index 6a66c1be3d07..e4031189f26e 100644 --- a/core/res/res/values-sr/strings.xml +++ b/core/res/res/values-sr/strings.xml @@ -123,28 +123,28 @@ <string name="roamingText11" msgid="5245687407203281407">"Банер роминга је укључен"</string> <string name="roamingText12" msgid="673537506362152640">"Банер роминга је искључен"</string> <string name="roamingTextSearching" msgid="5323235489657753486">"Претраживање услуге"</string> - <string name="wfcRegErrorTitle" msgid="3193072971584858020">"Подешавање позивања преко WiFi-ја није успело"</string> + <string name="wfcRegErrorTitle" msgid="3193072971584858020">"Подешавање позивања преко WiFi-а није успело"</string> <string-array name="wfcOperatorErrorAlertMessages"> - <item msgid="468830943567116703">"Да бисте упућивали позиве и слали поруке преко WiFi-ја, прво затражите од мобилног оператера да вам омогући ову услугу. Затим у Подешавањима поново укључите Позивање преко WiFi-ја. (кôд грешке: <xliff:g id="CODE">%1$s</xliff:g>)"</item> + <item msgid="468830943567116703">"Да бисте упућивали позиве и слали поруке преко WiFi-а, прво затражите од мобилног оператера да вам омогући ову услугу. Затим у Подешавањима поново укључите Позивање преко WiFi-а. (кôд грешке: <xliff:g id="CODE">%1$s</xliff:g>)"</item> </string-array> <string-array name="wfcOperatorErrorNotificationMessages"> <item msgid="4795145070505729156">"Проблем у вези са регистровањем позивања преко Wi‑Fi-ја код мобилног оператера: <xliff:g id="CODE">%1$s</xliff:g>"</item> </string-array> <!-- no translation found for wfcSpnFormat_spn (2982505428519096311) --> <skip /> - <string name="wfcSpnFormat_spn_wifi_calling" msgid="3165949348000906194">"<xliff:g id="SPN">%s</xliff:g> позивање преко WiFi-ја"</string> - <string name="wfcSpnFormat_spn_wifi_calling_vo_hyphen" msgid="3836827895369365298">"<xliff:g id="SPN">%s</xliff:g> – позивање преко WiFi-ја"</string> + <string name="wfcSpnFormat_spn_wifi_calling" msgid="3165949348000906194">"<xliff:g id="SPN">%s</xliff:g> позивање преко WiFi-а"</string> + <string name="wfcSpnFormat_spn_wifi_calling_vo_hyphen" msgid="3836827895369365298">"<xliff:g id="SPN">%s</xliff:g> – позивање преко WiFi-а"</string> <string name="wfcSpnFormat_wlan_call" msgid="4895315549916165700">"WLAN позив"</string> <string name="wfcSpnFormat_spn_wlan_call" msgid="255919245825481510">"<xliff:g id="SPN">%s</xliff:g> WLAN позив"</string> <string name="wfcSpnFormat_spn_wifi" msgid="7232899594327126970">"<xliff:g id="SPN">%s</xliff:g> WiFi"</string> - <string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="8383917598312067365">"Позивање преко WiFi-ја | <xliff:g id="SPN">%s</xliff:g>"</string> + <string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="8383917598312067365">"Позивање преко WiFi-а | <xliff:g id="SPN">%s</xliff:g>"</string> <string name="wfcSpnFormat_spn_vowifi" msgid="6865214948822061486">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string> - <string name="wfcSpnFormat_wifi_calling" msgid="6178935388378661755">"Позивање преко WiFi-ја"</string> + <string name="wfcSpnFormat_wifi_calling" msgid="6178935388378661755">"Позивање преко WiFi-а"</string> <string name="wfcSpnFormat_wifi" msgid="1376356951297043426">"WiFi"</string> - <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="7178561009225028264">"Позивање преко WiFi-ја"</string> + <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="7178561009225028264">"Позивање преко WiFi-а"</string> <string name="wfcSpnFormat_vowifi" msgid="8371335230890725606">"VoWifi"</string> <string name="wifi_calling_off_summary" msgid="5626710010766902560">"Искључено"</string> - <string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Позивање преко WiFi-ја"</string> + <string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Позивање преко WiFi-а"</string> <string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Позив преко мобилне мреже"</string> <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Само WiFi"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Није прослеђено"</string> @@ -290,6 +290,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB веза"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Активна апликација"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Апликације које троше батерију"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> користи батерију"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"Апликације (<xliff:g id="NUMBER">%1$d</xliff:g>) користе батерију"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Додирните за детаље о батерији и потрошњи података"</string> @@ -2222,4 +2224,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml index 8c28252bdfd3..cc21f3929356 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB-anslutning"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"App körs"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Appar som drar batteri"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> drar batteri"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> appar drar batteri"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Tryck för information om batteri- och dataanvändning"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml index 1ca8fb8bc482..9478b0b50832 100644 --- a/core/res/res/values-sw/strings.xml +++ b/core/res/res/values-sw/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"Muunganisho wa USB"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Programu inaendelea kutekelezwa"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Programu zinazotumia betri"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> inatumia betri"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"Programu <xliff:g id="NUMBER">%1$d</xliff:g> zinatumia betri"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Gusa ili upate maelezo kuhusu betri na matumizi ya data"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml index c2972f13e372..ebaeaddfd86f 100644 --- a/core/res/res/values-ta/strings.xml +++ b/core/res/res/values-ta/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB இணைப்பு"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"ஆப்ஸ் இயங்குகிறது"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"பேட்டரியைப் பயன்படுத்தும் ஆப்ஸ்"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸ் பேட்டரியைப் பயன்படுத்துகிறது"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> ஆப்ஸ் பேட்டரியைப் பயன்படுத்துகின்றன"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"பேட்டரி மற்றும் டேட்டா உபயோக விவரங்களைக் காண, தட்டவும்"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml index d959a37d8f3d..c63d175002ea 100644 --- a/core/res/res/values-te/strings.xml +++ b/core/res/res/values-te/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB కనెక్షన్"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"యాప్ అమలవుతోంది"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"బ్యాటరీని ఉపయోగిస్తున్న యాప్లు"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> బ్యాటరీని ఉపయోగిస్తోంది"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> యాప్లు బ్యాటరీని ఉపయోగిస్తున్నాయి"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"బ్యాటరీ మరియు డేటా వినియోగ వివరాల కోసం నొక్కండి"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml index 5102f148bb7c..b0acb1217b8b 100644 --- a/core/res/res/values-th/strings.xml +++ b/core/res/res/values-th/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"การเชื่อมต่อ USB"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"แอปที่ทำงานอยู่"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"แอปหลายแอปกำลังใช้แบตเตอรี่"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> กำลังใช้แบตเตอรี่"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"แอป <xliff:g id="NUMBER">%1$d</xliff:g> แอปกำลังใช้แบตเตอรี่"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"แตะเพื่อดูรายละเอียดเกี่ยวกับแบตเตอรี่และปริมาณการใช้อินเทอร์เน็ต"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml index 4112fa5c4ff1..f04e7e1f3d69 100644 --- a/core/res/res/values-tl/strings.xml +++ b/core/res/res/values-tl/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"Koneksyon ng USB"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Tumatakbo ang app"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Mga app na kumokonsumo ng baterya"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"Gumagamit ng baterya ang <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"Gumagamit ng baterya ang <xliff:g id="NUMBER">%1$d</xliff:g> (na) app"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"I-tap para sa mga detalye tungkol sa paggamit ng baterya at data"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml index 8ee1d30a11bc..79cfe191d35f 100644 --- a/core/res/res/values-tr/strings.xml +++ b/core/res/res/values-tr/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB bağlantısı"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Uygulama çalışıyor"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Pil kullanan uygulamalar"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> pil kullanıyor"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> uygulama pil kullanıyor"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Pil ve veri kullanımı ile ilgili ayrıntılar için dokunun"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml index 778e4a5789fb..a752010655a9 100644 --- a/core/res/res/values-uk/strings.xml +++ b/core/res/res/values-uk/strings.xml @@ -293,6 +293,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"З’єднання USB"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Працює додаток"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Додатки, що використовують заряд акумулятора"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"Додаток <xliff:g id="APP_NAME">%1$s</xliff:g> використовує заряд акумулятора"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"Додатків, що використовують заряд акумулятора: <xliff:g id="NUMBER">%1$d</xliff:g>"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Торкніться, щоб перевірити використання акумулятора й трафік"</string> @@ -2256,4 +2258,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml index 78c9330a85e9..4324e36c62b9 100644 --- a/core/res/res/values-ur/strings.xml +++ b/core/res/res/values-ur/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB کنکشن"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"ایپ چل رہی ہے"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"ایپس بیٹری خرچ کر رہی ہیں"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> بیٹری کا استعمال کر رہی ہے"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> ایپس بیٹری کا استعمال کر رہی ہیں"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"بیٹری اور ڈیٹا استعمال کے بارے میں تفصیلات کے لیے تھپتھپائیں"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml index 683a9115ce25..923831c00b88 100644 --- a/core/res/res/values-uz/strings.xml +++ b/core/res/res/values-uz/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB orqali ulanish"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Ilova faol"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Batareya quvvatini sarflayotgan ilovalar"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi batareya quvvatini sarflamoqda"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> ta ilova batareya quvvatini sarflamoqda"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Batareya va trafik sarfi tafsilotlari uchun ustiga bosing"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml index 050fa0b24cc4..296261f850e8 100644 --- a/core/res/res/values-vi/strings.xml +++ b/core/res/res/values-vi/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"Kết nối USB"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Ứng dụng đang chạy"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Các ứng dụng tiêu thụ pin"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> đang sử dụng pin"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> ứng dụng đang sử dụng pin"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Nhấn để biết chi tiết về mức sử dụng dữ liệu và pin"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index ec01ae3acbd7..a27e7b1ba94c 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB 连接"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"应用正在运行中"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"消耗电量的应用"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g>正在消耗电量"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> 个应用正在消耗电量"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"点按即可详细了解电量和流量消耗情况"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml index 58232b912b7f..5099ebfd463f 100644 --- a/core/res/res/values-zh-rHK/strings.xml +++ b/core/res/res/values-zh-rHK/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB 連線"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"應用程式正在執行"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"耗用電量的應用程式"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在使用電量"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> 個應用程式正在使用電量"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"輕按即可查看電池和數據用量詳情"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index 617a671500ec..31bd99be57b1 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"USB 連線"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"應用程式執行中"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"正在耗用電量的應用程式"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在耗用電量"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> 個應用程式正在耗用電量"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"輕觸即可查看電池和數據用量詳情"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml index fab89962ef69..28eed9b98efd 100644 --- a/core/res/res/values-zu/strings.xml +++ b/core/res/res/values-zu/strings.xml @@ -287,6 +287,8 @@ <string name="notification_channel_usb" msgid="1528280969406244896">"Ukuxhumeka kwe-USB"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Uhlelo loksuebenza olusebenzayo"</string> <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Izinhlelo zokusebenza ezidla ibhethri"</string> + <!-- no translation found for notification_channel_accessibility_magnification (1707913872219798098) --> + <skip /> <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> isebenzisa ibhethri"</string> <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> izinhlelo zokusebenza zisebenzisa ibhethri"</string> <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Thepha ngemininingwane ekusetshenzisweni kwebhethri nedatha"</string> @@ -2188,4 +2190,12 @@ <string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string> <string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string> + <!-- no translation found for window_magnification_prompt_title (8197528399699536320) --> + <skip /> + <!-- no translation found for window_magnification_prompt_content (4166711383253283838) --> + <skip /> + <!-- no translation found for turn_on_magnification_settings_action (8521433346684847700) --> + <skip /> + <!-- no translation found for dismiss_action (1728820550388704784) --> + <skip /> </resources> diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index fc9f670f31a7..85b19e7de04b 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -303,6 +303,9 @@ <!-- Additional flag from base permission type: this permission will be granted to the retail demo app, as defined by the OEM. --> <flag name="retailDemo" value="0x1000000" /> + <!-- Additional flag from base permission type: this permission will be granted to the + recents app. --> + <flag name="recents" value="0x2000000" /> </attr> <!-- Flags indicating more context for a permission group. --> @@ -2834,6 +2837,38 @@ <attr name="resource" format="reference" /> </declare-styleable> + <!-- The <code>property</code> tag is used to attach additional data that can + be supplied to the parent component. A component element can contain any + number of <code>property</code> subelements. Valid names are any of the + <code>PROPERTY_</code> constants defined in the + {@link android.content.pm.PackageManager PackageManager} class. Values + are obtained using the appropriate method on the + {@link android.content.pm.PackageManager.Property PackageManager.Property} class. + <p>Ordinary values are specified through the value attribute. Resource IDs are + specified through the resource attribute. + <p>It is invalid to specify both a value and resource attributes. --> + <declare-styleable name="AndroidManifestProperty" + parent="AndroidManifestApplication + AndroidManifestActivity + AndroidManifestReceiver + AndroidManifestProvider + AndroidManifestService"> + <attr name="name" /> + <!-- Concrete value to assign to this property. + The data can later be retrieved from the property object + through + {@link android.content.pm.PackageManager.Property#getString Property.getString}, + {@link android.content.pm.PackageManager.Property#getInteger Property.getInteger}, + {@link android.content.pm.PackageManager.Property#getBoolean Property.getBoolean}, + or {@link android.content.pm.PackageManager.Property#getFloat Property.getFloat} + depending on the type used here. --> + <attr name="value" /> + <!-- The resource identifier to assign to this property. + The resource identifier can later be retrieved from the property object through + {@link android.content.pm.PackageManager.Property#getResourceId Property.getResourceId}. --> + <attr name="resource" /> + </declare-styleable> + <!-- The <code>intent-filter</code> tag is used to construct an {@link android.content.IntentFilter} object that will be used to determine which component can handle a particular diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index eda1c7a7950f..c9f140df68cb 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1603,7 +1603,7 @@ <integer name="config_timeZoneRulesCheckRetryCount">5</integer> <!-- Whether the geolocation time zone detection feature is enabled. --> - <bool name="config_enableGeolocationTimeZoneDetection" translatable="false">false</bool> + <bool name="config_enableGeolocationTimeZoneDetection" translatable="false">true</bool> <!-- Whether to enable primary location time zone provider overlay which allows the primary location time zone provider to be replaced by an app at run-time. When disabled, only the @@ -1622,8 +1622,13 @@ wants to disable the overlay mechanism can set it to false. --> <bool name="config_enableSecondaryLocationTimeZoneOverlay" translatable="false">false</bool> <!-- Package name providing the secondary location time zone provider. Used only when - config_enableSecondaryLocationTimeZoneOverlay is false. --> - <string name="config_secondaryLocationTimeZoneProviderPackageName" translatable="false">@null</string> + config_enableSecondaryLocationTimeZoneOverlay is false. + + By default, set to "android" to pick up the default LocationTimeZoneProvider configured in + the system server's AndroidManifest.xml. See the + com.android.location.timezone.service.v1.SecondaryLocationTimeZoneProvider intent-filter + definition there for more information. --> + <string name="config_secondaryLocationTimeZoneProviderPackageName" translatable="false">android</string> <!-- Whether to enable network location overlay which allows network location provider to be replaced by an app at run-time. When disabled, only the diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 79eae67e5fba..19591f6a666f 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -259,6 +259,9 @@ <!-- The height of the background for a notification header on a group --> <dimen name="notification_header_background_height">49.5dp</dimen> + <!-- The height of the full-width touch rectangle for the notification header --> + <dimen name="notification_header_touchable_height">36dp</dimen> + <!-- The top padding for the notification header --> <dimen name="notification_header_padding_top">16dp</dimen> @@ -340,11 +343,8 @@ <!-- The margin of the content to an image--> <dimen name="notification_content_image_margin_end">8dp</dimen> - <!-- The padding at the end of actions when the bubble button is visible--> - <dimen name="bubble_visible_padding_end">3dp</dimen> - - <!-- The padding at the end of actions when the bubble button is gone--> - <dimen name="bubble_gone_padding_end">12dp</dimen> + <!-- The padding at the end of actions when the snooze and bubble buttons are gone--> + <dimen name="snooze_and_bubble_gone_padding_end">12dp</dimen> <!-- The spacing between messages in Notification.MessagingStyle --> <dimen name="notification_messaging_spacing">6dp</dimen> @@ -744,6 +744,9 @@ <dimen name="conversation_expand_button_size">80dp</dimen> <!-- Top margin of the expand button for conversations when expanded --> <dimen name="conversation_expand_button_top_margin_expanded">18dp</dimen> + <!-- Side margin of the expand button for conversations. + width of expand asset (22) + 2 * this (13) == notification_header_expand_icon_size (48) --> + <dimen name="conversation_expand_button_side_margin">13dp</dimen> <!-- Side margins of the conversation badge in relation to the conversation icon --> <dimen name="conversation_badge_side_margin">36dp</dimen> <!-- size of the notification badge when applied to the conversation icon --> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 8eb0853fda24..8e3a0cbdd10c 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -433,6 +433,9 @@ <string name="sensor_notification_service">Sensor Notification Service</string> <!-- Attribution for Twilight service. [CHAR LIMIT=NONE]--> <string name="twilight_service">Twilight Service</string> + <!-- Attribution for Offline LocationTimeZoneDetector service, i.e. one capable of performing + time zone lookup using geo-spacial information held on the device. [CHAR LIMIT=NONE]--> + <string name="offline_location_time_zone_detection_service">Offline Time Zone Detection Service</string> <!-- Factory reset warning dialog strings--> <skip /> <!-- Shows up in the dialog's title to warn about an impeding factory reset. [CHAR LIMIT=NONE] --> @@ -717,6 +720,10 @@ [CHAR LIMIT=NONE BACKUP_MESSAGE_ID=6665375982962336520] --> <string name="notification_channel_foreground_service">Apps consuming battery</string> + <!-- Text shown when viewing channel settings for notifications related to accessibility + magnification. [CHAR_LIMIT=NONE]--> + <string name="notification_channel_accessibility_magnification">Magnification</string> + <!-- Label for foreground service notification when one app is running. [CHAR LIMIT=NONE BACKUP_MESSAGE_ID=6826789589341671842] --> <string name="foreground_service_app_in_background"><xliff:g id="app_name">%1$s</xliff:g> is @@ -5767,4 +5774,16 @@ ul.</string> <string name="config_pdp_reject_service_not_subscribed"></string> <!-- pdp data reject dialog string for cause 55 (MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED) [CHAR LIMIT=100] --> <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed"></string> + + <!-- Window magnification prompt related string. --> + + <!-- Notification title to prompt the user that new magnification feature is available. [CHAR LIMIT=50] --> + <string name="window_magnification_prompt_title">New: Window Magnifier</string> + <!-- Notification content to prompt the user that new magnification feature is available. [CHAR LIMIT=50] --> + <string name="window_magnification_prompt_content">You can now magnify some or all of your screen</string> + <!-- Notification action to bring the user to magnification settings page. [CHAR LIMIT=50] --> + <string name="turn_on_magnification_settings_action">Turn on in Settings</string> + <!-- Notification action to dismiss. [CHAR LIMIT=50] --> + <string name="dismiss_action">Dismiss</string> + </resources> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 23733af4455e..40aae9e4e085 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2861,6 +2861,7 @@ <java-symbol type="id" name="header_text" /> <java-symbol type="id" name="header_text_secondary" /> <java-symbol type="id" name="expand_button" /> + <java-symbol type="id" name="alternate_expand_target" /> <java-symbol type="id" name="notification_header" /> <java-symbol type="id" name="notification_top_line" /> <java-symbol type="id" name="time_divider" /> @@ -2878,6 +2879,7 @@ <java-symbol type="dimen" name="notification_content_margin_top" /> <java-symbol type="dimen" name="notification_content_margin" /> <java-symbol type="dimen" name="notification_header_background_height" /> + <java-symbol type="dimen" name="notification_header_touchable_height" /> <java-symbol type="dimen" name="notification_header_expand_icon_size" /> <java-symbol type="dimen" name="notification_expand_button_padding_top" /> <java-symbol type="dimen" name="notification_header_icon_size" /> @@ -3458,6 +3460,7 @@ <java-symbol type="string" name="notification_channel_heavy_weight_app" /> <java-symbol type="string" name="notification_channel_system_changes" /> <java-symbol type="string" name="notification_channel_do_not_disturb" /> + <java-symbol type="string" name="notification_channel_accessibility_magnification" /> <java-symbol type="string" name="config_defaultAutofillService" /> <java-symbol type="string" name="config_defaultTextClassifierPackage" /> <java-symbol type="string" name="config_defaultWellbeingPackage" /> @@ -3559,8 +3562,6 @@ <java-symbol type="id" name="clip_children_tag" /> <java-symbol type="id" name="bubble_button" /> <java-symbol type="id" name="snooze_button" /> - <java-symbol type="dimen" name="bubble_visible_padding_end" /> - <java-symbol type="dimen" name="bubble_gone_padding_end" /> <java-symbol type="dimen" name="text_size_body_2_material" /> <java-symbol type="dimen" name="messaging_avatar_size" /> <java-symbol type="dimen" name="messaging_group_sending_progress_size" /> @@ -4096,4 +4097,10 @@ <java-symbol type="dimen" name="config_taskLetterboxAspectRatio" /> <java-symbol type="bool" name="config_hideDisplayCutoutWithDisplayArea" /> + + <!-- Window magnification prompt --> + <java-symbol type="string" name="window_magnification_prompt_title" /> + <java-symbol type="string" name="window_magnification_prompt_content" /> + <java-symbol type="string" name="turn_on_magnification_settings_action" /> + <java-symbol type="string" name="dismiss_action" /> </resources> diff --git a/core/res/res/xml/config_user_types.xml b/core/res/res/xml/config_user_types.xml index 5fd8a95af64d..71dfc551ccc1 100644 --- a/core/res/res/xml/config_user_types.xml +++ b/core/res/res/xml/config_user_types.xml @@ -40,7 +40,7 @@ The following example modifies two AOSP user types (the FULL user android.os.use and the PROFILE user android.os.usertype.profile.MANAGED) and creates a new PROFILE user type (com.example.profilename): -<user-types> +<user-types version="0"> <full-type name="android.os.usertype.full.SECONDARY" > <default-restrictions no_sms="true" /> </full-type> @@ -65,6 +65,11 @@ and the PROFILE user android.os.usertype.profile.MANAGED) and creates a new PROF <profile-type name="com.example.profilename" max-allowed-per-parent="2" /> + + <change-user-type + from="android.os.usertype.profile.MANAGED" + to="com.example.profilename" + whenVersionLeq="1" /> </user-types> Mandatory attributes: @@ -93,6 +98,10 @@ If this file is updated, the properties of any pre-existing user types will be u Note, however, that default-restrictions refers to the restrictions applied at the time of user creation; therefore, the active restrictions of any pre-existing users will not be updated. +The 'change-user-type' tag should be used in conjunction with the 'version' property of +'user-types'. It defines a type change for all pre-existing users of 'from' type to the new 'to' +type, if the former 'user-type's version of device is less than or equal to 'whenVersionLeq'. + --> <user-types> </user-types> diff --git a/core/tests/coretests/src/android/app/ApplicationLoadersTest.java b/core/tests/coretests/src/android/app/ApplicationLoadersTest.java index 4b9910c79770..19e7f80dfa5b 100644 --- a/core/tests/coretests/src/android/app/ApplicationLoadersTest.java +++ b/core/tests/coretests/src/android/app/ApplicationLoadersTest.java @@ -42,7 +42,7 @@ public class ApplicationLoadersTest { return new SharedLibraryInfo( zip, null /*packageName*/, null /*codePaths*/, null /*name*/, 0 /*version*/, SharedLibraryInfo.TYPE_BUILTIN, null /*declaringPackage*/, - null /*dependentPackages*/, null /*dependencies*/); + null /*dependentPackages*/, null /*dependencies*/, false /*isNative*/); } @Test diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java index 4fe68cd5a27a..e5da41c7c113 100644 --- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java +++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java @@ -51,6 +51,8 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Rect; import android.hardware.display.DisplayManager; +import android.hardware.display.VirtualDisplay; +import android.os.Bundle; import android.os.IBinder; import android.platform.test.annotations.Presubmit; import android.util.DisplayMetrics; @@ -66,6 +68,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.content.ReferrerIntent; +import org.junit.After; import org.junit.Test; import org.junit.runner.RunWith; @@ -95,6 +98,16 @@ public class ActivityThreadTest { new ActivityTestRule<>(TestActivity.class, true /* initialTouchMode */, false /* launchActivity */); + private ArrayList<VirtualDisplay> mCreatedVirtualDisplays; + + @After + public void tearDown() { + if (mCreatedVirtualDisplays != null) { + mCreatedVirtualDisplays.forEach(VirtualDisplay::release); + mCreatedVirtualDisplays = null; + } + } + @Test public void testDoubleRelaunch() throws Exception { final Activity activity = mActivityTestRule.launchActivity(new Intent()); @@ -410,7 +423,6 @@ public class ActivityThreadTest { Context appContext = activity.getApplication(); Configuration originalAppConfig = new Configuration(appContext.getResources().getConfiguration()); - DisplayManager dm = appContext.getSystemService(DisplayManager.class); int virtualDisplayWidth; int virtualDisplayHeight; @@ -421,8 +433,8 @@ public class ActivityThreadTest { virtualDisplayWidth = 200; virtualDisplayHeight = 100; } - Display virtualDisplay = dm.createVirtualDisplay("virtual-display", - virtualDisplayWidth, virtualDisplayHeight, 200, null, 0).getDisplay(); + final Display virtualDisplay = createVirtualDisplay(appContext, + virtualDisplayWidth, virtualDisplayHeight); Context virtualDisplayContext = appContext.createDisplayContext(virtualDisplay); int originalVirtualDisplayOrientation = virtualDisplayContext.getResources() .getConfiguration().orientation; @@ -467,7 +479,6 @@ public class ActivityThreadTest { InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { Configuration originalActivityConfig = new Configuration(activity.getResources().getConfiguration()); - DisplayManager dm = activity.getSystemService(DisplayManager.class); int virtualDisplayWidth; int virtualDisplayHeight; @@ -478,8 +489,8 @@ public class ActivityThreadTest { virtualDisplayWidth = 200; virtualDisplayHeight = 100; } - Display virtualDisplay = dm.createVirtualDisplay("virtual-display", - virtualDisplayWidth, virtualDisplayHeight, 200, null, 0).getDisplay(); + final Display virtualDisplay = createVirtualDisplay(activity, + virtualDisplayWidth, virtualDisplayHeight); Context virtualDisplayContext = activity.createDisplayContext(virtualDisplay); int originalVirtualDisplayOrientation = virtualDisplayContext.getResources() .getConfiguration().orientation; @@ -704,6 +715,17 @@ public class ActivityThreadTest { return config.seq; } + private Display createVirtualDisplay(Context context, int w, int h) { + final DisplayManager dm = context.getSystemService(DisplayManager.class); + final VirtualDisplay virtualDisplay = dm.createVirtualDisplay("virtual-display", w, h, + 200 /* densityDpi */, null /* surface */, 0 /* flags */); + if (mCreatedVirtualDisplays == null) { + mCreatedVirtualDisplays = new ArrayList<>(); + } + mCreatedVirtualDisplays.add(virtualDisplay); + return virtualDisplay.getDisplay(); + } + private static ActivityClientRecord getActivityClientRecord(Activity activity) { final ActivityThread thread = activity.getActivityThread(); final IBinder token = activity.getActivityToken(); @@ -796,6 +818,14 @@ public class ActivityThreadTest { volatile CountDownLatch mConfigLatch; @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().getDecorView().setKeepScreenOn(true); + setShowWhenLocked(true); + setTurnScreenOn(true); + } + + @Override public void onConfigurationChanged(Configuration config) { super.onConfigurationChanged(config); mConfig.setTo(config); diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchEmailTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchEmailTest.java index 2f2bef8bc5cc..119b70ab0439 100644 --- a/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchEmailTest.java +++ b/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchEmailTest.java @@ -24,16 +24,18 @@ public class AppSearchEmailTest { @Test public void testBuildEmailAndGetValue() { - AppSearchEmail email = new AppSearchEmail.Builder("uri") - .setFrom("FakeFromAddress") - .setCc("CC1", "CC2") - // Score and Property are mixed into the middle to make sure DocumentBuilder's - // methods can be interleaved with EmailBuilder's methods. - .setScore(1) - .setPropertyString("propertyKey", "propertyValue1", "propertyValue2") - .setSubject("subject") - .setBody("EmailBody") - .build(); + AppSearchEmail email = + new AppSearchEmail.Builder("uri") + .setFrom("FakeFromAddress") + .setCc("CC1", "CC2") + // Score and Property are mixed into the middle to make sure + // DocumentBuilder's + // methods can be interleaved with EmailBuilder's methods. + .setScore(1) + .setPropertyString("propertyKey", "propertyValue1", "propertyValue2") + .setSubject("subject") + .setBody("EmailBody") + .build(); assertThat(email.getUri()).isEqualTo("uri"); assertThat(email.getFrom()).isEqualTo("FakeFromAddress"); @@ -42,8 +44,9 @@ public class AppSearchEmailTest { assertThat(email.getBcc()).isNull(); assertThat(email.getScore()).isEqualTo(1); assertThat(email.getPropertyString("propertyKey")).isEqualTo("propertyValue1"); - assertThat(email.getPropertyStringArray("propertyKey")).asList().containsExactly( - "propertyValue1", "propertyValue2"); + assertThat(email.getPropertyStringArray("propertyKey")) + .asList() + .containsExactly("propertyValue1", "propertyValue2"); assertThat(email.getSubject()).isEqualTo("subject"); assertThat(email.getBody()).isEqualTo("EmailBody"); } diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java deleted file mode 100644 index 9c29943dbef4..000000000000 --- a/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java +++ /dev/null @@ -1,252 +0,0 @@ -/* - * Copyright 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.app.appsearch; - -import static com.google.common.truth.Truth.assertThat; - -import static org.testng.Assert.expectThrows; - -import org.junit.Test; - -public class GenericDocumentTest { - private static final byte[] sByteArray1 = new byte[]{(byte) 1, (byte) 2, (byte) 3}; - private static final byte[] sByteArray2 = new byte[]{(byte) 4, (byte) 5, (byte) 6, (byte) 7}; - private static final GenericDocument sDocumentProperties1 = new GenericDocument - .Builder("sDocumentProperties1", "sDocumentPropertiesSchemaType1") - .setCreationTimestampMillis(12345L) - .build(); - private static final GenericDocument sDocumentProperties2 = new GenericDocument - .Builder("sDocumentProperties2", "sDocumentPropertiesSchemaType2") - .setCreationTimestampMillis(6789L) - .build(); - - @Test - public void testDocumentEquals_Identical() { - GenericDocument document1 = new GenericDocument.Builder("uri1", "schemaType1") - .setCreationTimestampMillis(5L) - .setTtlMillis(1L) - .setPropertyLong("longKey1", 1L, 2L, 3L) - .setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0) - .setPropertyBoolean("booleanKey1", true, false, true) - .setPropertyString("stringKey1", "test-value1", "test-value2", "test-value3") - .setPropertyBytes("byteKey1", sByteArray1, sByteArray2) - .setPropertyDocument("documentKey1", sDocumentProperties1, sDocumentProperties2) - .build(); - GenericDocument document2 = new GenericDocument.Builder("uri1", "schemaType1") - .setCreationTimestampMillis(5L) - .setTtlMillis(1L) - .setPropertyLong("longKey1", 1L, 2L, 3L) - .setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0) - .setPropertyBoolean("booleanKey1", true, false, true) - .setPropertyString("stringKey1", "test-value1", "test-value2", "test-value3") - .setPropertyBytes("byteKey1", sByteArray1, sByteArray2) - .setPropertyDocument("documentKey1", sDocumentProperties1, sDocumentProperties2) - .build(); - assertThat(document1).isEqualTo(document2); - assertThat(document1.hashCode()).isEqualTo(document2.hashCode()); - } - - @Test - public void testDocumentEquals_DifferentOrder() { - GenericDocument document1 = new GenericDocument.Builder("uri1", "schemaType1") - .setCreationTimestampMillis(5L) - .setPropertyLong("longKey1", 1L, 2L, 3L) - .setPropertyBytes("byteKey1", sByteArray1, sByteArray2) - .setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0) - .setPropertyBoolean("booleanKey1", true, false, true) - .setPropertyDocument("documentKey1", sDocumentProperties1, sDocumentProperties2) - .setPropertyString("stringKey1", "test-value1", "test-value2", "test-value3") - .build(); - - // Create second document with same parameter but different order. - GenericDocument document2 = new GenericDocument.Builder("uri1", "schemaType1") - .setCreationTimestampMillis(5L) - .setPropertyBoolean("booleanKey1", true, false, true) - .setPropertyDocument("documentKey1", sDocumentProperties1, sDocumentProperties2) - .setPropertyString("stringKey1", "test-value1", "test-value2", "test-value3") - .setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0) - .setPropertyBytes("byteKey1", sByteArray1, sByteArray2) - .setPropertyLong("longKey1", 1L, 2L, 3L) - .build(); - assertThat(document1).isEqualTo(document2); - assertThat(document1.hashCode()).isEqualTo(document2.hashCode()); - } - - @Test - public void testDocumentEquals_Failure() { - GenericDocument document1 = new GenericDocument.Builder("uri1", "schemaType1") - .setCreationTimestampMillis(5L) - .setPropertyLong("longKey1", 1L, 2L, 3L) - .build(); - - // Create second document with same order but different value. - GenericDocument document2 = new GenericDocument.Builder("uri1", "schemaType1") - .setCreationTimestampMillis(5L) - .setPropertyLong("longKey1", 1L, 2L, 4L) // Different - .build(); - assertThat(document1).isNotEqualTo(document2); - assertThat(document1.hashCode()).isNotEqualTo(document2.hashCode()); - } - - @Test - public void testDocumentEquals_Failure_RepeatedFieldOrder() { - GenericDocument document1 = new GenericDocument.Builder("uri1", "schemaType1") - .setCreationTimestampMillis(5L) - .setPropertyBoolean("booleanKey1", true, false, true) - .build(); - - // Create second document with same order but different value. - GenericDocument document2 = new GenericDocument.Builder("uri1", "schemaType1") - .setCreationTimestampMillis(5L) - .setPropertyBoolean("booleanKey1", true, true, false) // Different - .build(); - assertThat(document1).isNotEqualTo(document2); - assertThat(document1.hashCode()).isNotEqualTo(document2.hashCode()); - } - - @Test - public void testDocumentGetSingleValue() { - GenericDocument document = new GenericDocument.Builder("uri1", "schemaType1") - .setCreationTimestampMillis(5L) - .setScore(1) - .setTtlMillis(1L) - .setPropertyLong("longKey1", 1L) - .setPropertyDouble("doubleKey1", 1.0) - .setPropertyBoolean("booleanKey1", true) - .setPropertyString("stringKey1", "test-value1") - .setPropertyBytes("byteKey1", sByteArray1) - .setPropertyDocument("documentKey1", sDocumentProperties1) - .build(); - assertThat(document.getUri()).isEqualTo("uri1"); - assertThat(document.getTtlMillis()).isEqualTo(1L); - assertThat(document.getSchemaType()).isEqualTo("schemaType1"); - assertThat(document.getCreationTimestampMillis()).isEqualTo(5); - assertThat(document.getScore()).isEqualTo(1); - assertThat(document.getPropertyLong("longKey1")).isEqualTo(1L); - assertThat(document.getPropertyDouble("doubleKey1")).isEqualTo(1.0); - assertThat(document.getPropertyBoolean("booleanKey1")).isTrue(); - assertThat(document.getPropertyString("stringKey1")).isEqualTo("test-value1"); - assertThat(document.getPropertyBytes("byteKey1")) - .asList().containsExactly((byte) 1, (byte) 2, (byte) 3); - assertThat(document.getPropertyDocument("documentKey1")).isEqualTo(sDocumentProperties1); - } - - @Test - public void testDocumentGetArrayValues() { - GenericDocument document = new GenericDocument.Builder("uri1", "schemaType1") - .setCreationTimestampMillis(5L) - .setPropertyLong("longKey1", 1L, 2L, 3L) - .setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0) - .setPropertyBoolean("booleanKey1", true, false, true) - .setPropertyString("stringKey1", "test-value1", "test-value2", "test-value3") - .setPropertyBytes("byteKey1", sByteArray1, sByteArray2) - .setPropertyDocument("documentKey1", sDocumentProperties1, sDocumentProperties2) - .build(); - - assertThat(document.getUri()).isEqualTo("uri1"); - assertThat(document.getSchemaType()).isEqualTo("schemaType1"); - assertThat(document.getPropertyLongArray("longKey1")).asList().containsExactly(1L, 2L, 3L); - assertThat(document.getPropertyDoubleArray("doubleKey1")).usingExactEquality() - .containsExactly(1.0, 2.0, 3.0); - assertThat(document.getPropertyBooleanArray("booleanKey1")).asList() - .containsExactly(true, false, true); - assertThat(document.getPropertyStringArray("stringKey1")).asList() - .containsExactly("test-value1", "test-value2", "test-value3"); - assertThat(document.getPropertyBytesArray("byteKey1")).asList() - .containsExactly(sByteArray1, sByteArray2); - assertThat(document.getPropertyDocumentArray("documentKey1")).asList() - .containsExactly(sDocumentProperties1, sDocumentProperties2); - } - - @Test - public void testDocument_ToString() throws Exception { - GenericDocument document = new GenericDocument.Builder("uri1", "schemaType1") - .setCreationTimestampMillis(5L) - .setPropertyLong("longKey1", 1L, 2L, 3L) - .setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0) - .setPropertyBoolean("booleanKey1", true, false, true) - .setPropertyString("stringKey1", "String1", "String2", "String3") - .setPropertyBytes("byteKey1", sByteArray1, sByteArray2) - .setPropertyDocument("documentKey1", sDocumentProperties1, sDocumentProperties2) - .build(); - String exceptedString = "{ key: 'creationTimestampMillis' value: 5 } " - + "{ key: 'namespace' value: } " - + "{ key: 'properties' value: " - + "{ key: 'booleanKey1' value: [ 'true' 'false' 'true' ] } " - + "{ key: 'byteKey1' value: " - + "{ key: 'byteArray' value: [ '1' '2' '3' ] } " - + "{ key: 'byteArray' value: [ '4' '5' '6' '7' ] } } " - + "{ key: 'documentKey1' value: [ '" - + "{ key: 'creationTimestampMillis' value: 12345 } " - + "{ key: 'namespace' value: } " - + "{ key: 'properties' value: } " - + "{ key: 'schemaType' value: sDocumentPropertiesSchemaType1 } " - + "{ key: 'score' value: 0 } " - + "{ key: 'ttlMillis' value: 0 } " - + "{ key: 'uri' value: sDocumentProperties1 } ' '" - + "{ key: 'creationTimestampMillis' value: 6789 } " - + "{ key: 'namespace' value: } " - + "{ key: 'properties' value: } " - + "{ key: 'schemaType' value: sDocumentPropertiesSchemaType2 } " - + "{ key: 'score' value: 0 } " - + "{ key: 'ttlMillis' value: 0 } " - + "{ key: 'uri' value: sDocumentProperties2 } ' ] } " - + "{ key: 'doubleKey1' value: [ '1.0' '2.0' '3.0' ] } " - + "{ key: 'longKey1' value: [ '1' '2' '3' ] } " - + "{ key: 'stringKey1' value: [ 'String1' 'String2' 'String3' ] } } " - + "{ key: 'schemaType' value: schemaType1 } " - + "{ key: 'score' value: 0 } " - + "{ key: 'ttlMillis' value: 0 } " - + "{ key: 'uri' value: uri1 } "; - assertThat(document.toString()).isEqualTo(exceptedString); - } - - @Test - public void testDocumentGetValues_DifferentTypes() { - GenericDocument document = new GenericDocument.Builder("uri1", "schemaType1") - .setScore(1) - .setPropertyLong("longKey1", 1L) - .setPropertyBoolean("booleanKey1", true, false, true) - .setPropertyString("stringKey1", "test-value1", "test-value2", "test-value3") - .build(); - - // Get a value for a key that doesn't exist - assertThat(document.getPropertyDouble("doubleKey1")).isEqualTo(0.0); - assertThat(document.getPropertyDoubleArray("doubleKey1")).isNull(); - - // Get a value with a single element as an array and as a single value - assertThat(document.getPropertyLong("longKey1")).isEqualTo(1L); - assertThat(document.getPropertyLongArray("longKey1")).asList().containsExactly(1L); - - // Get a value with multiple elements as an array and as a single value - assertThat(document.getPropertyString("stringKey1")).isEqualTo("test-value1"); - assertThat(document.getPropertyStringArray("stringKey1")).asList() - .containsExactly("test-value1", "test-value2", "test-value3"); - - // Get a value of the wrong type - assertThat(document.getPropertyDouble("longKey1")).isEqualTo(0.0); - assertThat(document.getPropertyDoubleArray("longKey1")).isNull(); - } - - @Test - public void testDocumentInvalid() { - GenericDocument.Builder builder = new GenericDocument.Builder("uri1", "schemaType1"); - expectThrows( - IllegalArgumentException.class, - () -> builder.setPropertyBoolean("test", new boolean[]{})); - } -} diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java index d4635fdda052..9fd480d9e22a 100644 --- a/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java +++ b/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java @@ -18,69 +18,33 @@ package android.app.appsearch; import static com.google.common.truth.Truth.assertThat; -import static org.testng.Assert.expectThrows; - import android.os.Bundle; import org.junit.Test; public class SearchSpecTest { @Test - public void buildSearchSpecWithoutTermMatchType() { - expectThrows(RuntimeException.class, () -> new SearchSpec.Builder() - .addSchema("testSchemaType") - .build()); - } - - @Test - public void testBuildSearchSpec() { - SearchSpec searchSpec = new SearchSpec.Builder() - .setTermMatch(SearchSpec.TERM_MATCH_PREFIX) - .addNamespace("namespace1", "namespace2") - .addSchema("schemaTypes1", "schemaTypes2") - .setSnippetCount(5) - .setSnippetCountPerProperty(10) - .setMaxSnippetSize(15) - .setNumPerPage(42) - .setOrder(SearchSpec.ORDER_ASCENDING) - .setRankingStrategy(SearchSpec.RANKING_STRATEGY_DOCUMENT_SCORE) - .build(); - - assertThat(searchSpec.getTermMatch()).isEqualTo(SearchSpec.TERM_MATCH_PREFIX); - assertThat(searchSpec.getNamespaces()) - .containsExactly("namespace1", "namespace2").inOrder(); - assertThat(searchSpec.getSchemas()) - .containsExactly("schemaTypes1", "schemaTypes2").inOrder(); - assertThat(searchSpec.getSnippetCount()).isEqualTo(5); - assertThat(searchSpec.getSnippetCountPerProperty()).isEqualTo(10); - assertThat(searchSpec.getMaxSnippetSize()).isEqualTo(15); - assertThat(searchSpec.getNumPerPage()).isEqualTo(42); - assertThat(searchSpec.getOrder()).isEqualTo(SearchSpec.ORDER_ASCENDING); - assertThat(searchSpec.getRankingStrategy()) - .isEqualTo(SearchSpec.RANKING_STRATEGY_DOCUMENT_SCORE); - } - - @Test public void testGetBundle() { - SearchSpec searchSpec = new SearchSpec.Builder() - .setTermMatch(SearchSpec.TERM_MATCH_PREFIX) - .addNamespace("namespace1", "namespace2") - .addSchema("schemaTypes1", "schemaTypes2") - .setSnippetCount(5) - .setSnippetCountPerProperty(10) - .setMaxSnippetSize(15) - .setNumPerPage(42) - .setOrder(SearchSpec.ORDER_ASCENDING) - .setRankingStrategy(SearchSpec.RANKING_STRATEGY_DOCUMENT_SCORE) - .build(); + SearchSpec searchSpec = + new SearchSpec.Builder() + .setTermMatch(SearchSpec.TERM_MATCH_PREFIX) + .addNamespace("namespace1", "namespace2") + .addSchemaType("schemaTypes1", "schemaTypes2") + .setSnippetCount(5) + .setSnippetCountPerProperty(10) + .setMaxSnippetSize(15) + .setResultCountPerPage(42) + .setOrder(SearchSpec.ORDER_ASCENDING) + .setRankingStrategy(SearchSpec.RANKING_STRATEGY_DOCUMENT_SCORE) + .build(); Bundle bundle = searchSpec.getBundle(); assertThat(bundle.getInt(SearchSpec.TERM_MATCH_TYPE_FIELD)) .isEqualTo(SearchSpec.TERM_MATCH_PREFIX); - assertThat(bundle.getStringArrayList(SearchSpec.NAMESPACE_FIELD)).containsExactly( - "namespace1", "namespace2"); - assertThat(bundle.getStringArrayList(SearchSpec.SCHEMA_TYPE_FIELD)).containsExactly( - "schemaTypes1", "schemaTypes2"); + assertThat(bundle.getStringArrayList(SearchSpec.NAMESPACE_FIELD)) + .containsExactly("namespace1", "namespace2"); + assertThat(bundle.getStringArrayList(SearchSpec.SCHEMA_TYPE_FIELD)) + .containsExactly("schemaTypes1", "schemaTypes2"); assertThat(bundle.getInt(SearchSpec.SNIPPET_COUNT_FIELD)).isEqualTo(5); assertThat(bundle.getInt(SearchSpec.SNIPPET_COUNT_PER_PROPERTY_FIELD)).isEqualTo(10); assertThat(bundle.getInt(SearchSpec.MAX_SNIPPET_FIELD)).isEqualTo(15); diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/cts/AppSearchResultCtsTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/cts/AppSearchResultCtsTest.java new file mode 100644 index 000000000000..9c34b172e1ff --- /dev/null +++ b/core/tests/coretests/src/android/app/appsearch/external/app/cts/AppSearchResultCtsTest.java @@ -0,0 +1,79 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.appsearch.cts; + +import static com.google.common.truth.Truth.assertThat; + +import android.app.appsearch.AppSearchResult; + +import org.junit.Test; + +public class AppSearchResultCtsTest { + + @Test + public void testResultEquals_identical() { + AppSearchResult<String> result1 = AppSearchResult.newSuccessfulResult("String"); + AppSearchResult<String> result2 = AppSearchResult.newSuccessfulResult("String"); + + assertThat(result1).isEqualTo(result2); + assertThat(result1.hashCode()).isEqualTo(result2.hashCode()); + + AppSearchResult<String> result3 = + AppSearchResult.newFailedResult( + AppSearchResult.RESULT_INTERNAL_ERROR, "errorMessage"); + AppSearchResult<String> result4 = + AppSearchResult.newFailedResult( + AppSearchResult.RESULT_INTERNAL_ERROR, "errorMessage"); + + assertThat(result3).isEqualTo(result4); + assertThat(result3.hashCode()).isEqualTo(result4.hashCode()); + } + + @Test + public void testResultEquals_failure() { + AppSearchResult<String> result1 = AppSearchResult.newSuccessfulResult("String"); + AppSearchResult<String> result2 = AppSearchResult.newSuccessfulResult("Wrong"); + AppSearchResult<String> resultNull = AppSearchResult.newSuccessfulResult(/*value=*/ null); + + assertThat(result1).isNotEqualTo(result2); + assertThat(result1.hashCode()).isNotEqualTo(result2.hashCode()); + assertThat(result1).isNotEqualTo(resultNull); + assertThat(result1.hashCode()).isNotEqualTo(resultNull.hashCode()); + + AppSearchResult<String> result3 = + AppSearchResult.newFailedResult( + AppSearchResult.RESULT_INTERNAL_ERROR, "errorMessage"); + AppSearchResult<String> result4 = + AppSearchResult.newFailedResult(AppSearchResult.RESULT_IO_ERROR, "errorMessage"); + + assertThat(result3).isNotEqualTo(result4); + assertThat(result3.hashCode()).isNotEqualTo(result4.hashCode()); + + AppSearchResult<String> result5 = + AppSearchResult.newFailedResult(AppSearchResult.RESULT_INTERNAL_ERROR, "Wrong"); + + assertThat(result3).isNotEqualTo(result5); + assertThat(result3.hashCode()).isNotEqualTo(result5.hashCode()); + + AppSearchResult<String> result6 = + AppSearchResult.newFailedResult( + AppSearchResult.RESULT_INTERNAL_ERROR, /*errorMessage=*/ null); + + assertThat(result3).isNotEqualTo(result6); + assertThat(result3.hashCode()).isNotEqualTo(result6.hashCode()); + } +} diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchSchemaTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/cts/AppSearchSchemaCtsTest.java index c171270e23ea..2eaebd6e7b93 100644 --- a/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchSchemaTest.java +++ b/core/tests/coretests/src/android/app/appsearch/external/app/cts/AppSearchSchemaCtsTest.java @@ -14,19 +14,19 @@ * limitations under the License. */ -package android.app.appsearch; - +package android.app.appsearch.cts; import static com.google.common.truth.Truth.assertThat; import static org.testng.Assert.expectThrows; +import android.app.appsearch.AppSearchSchema; import android.app.appsearch.AppSearchSchema.PropertyConfig; import android.app.appsearch.exceptions.IllegalSchemaException; import org.junit.Test; -public class AppSearchSchemaTest { +public class AppSearchSchemaCtsTest { @Test public void testInvalidEnums() { PropertyConfig.Builder builder = new PropertyConfig.Builder("test"); @@ -54,21 +54,28 @@ public class AppSearchSchemaTest { @Test public void testDuplicateProperties() { - AppSearchSchema.Builder builder = new AppSearchSchema.Builder("Email") - .addProperty(new PropertyConfig.Builder("subject") - .setDataType(PropertyConfig.DATA_TYPE_STRING) - .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) - .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES) - .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) - .build() - ); - IllegalSchemaException e = expectThrows(IllegalSchemaException.class, - () -> builder.addProperty(new PropertyConfig.Builder("subject") - .setDataType(PropertyConfig.DATA_TYPE_STRING) - .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) - .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES) - .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) - .build())); + AppSearchSchema.Builder builder = + new AppSearchSchema.Builder("Email") + .addProperty( + new PropertyConfig.Builder("subject") + .setDataType(PropertyConfig.DATA_TYPE_STRING) + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES) + .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) + .build()); + IllegalSchemaException e = + expectThrows( + IllegalSchemaException.class, + () -> + builder.addProperty( + new PropertyConfig.Builder("subject") + .setDataType(PropertyConfig.DATA_TYPE_STRING) + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType( + PropertyConfig.INDEXING_TYPE_PREFIXES) + .setTokenizerType( + PropertyConfig.TOKENIZER_TYPE_PLAIN) + .build())); assertThat(e).hasMessageThat().contains("Property defined more than once: subject"); } } diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/cts/GenericDocumentCtsTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/cts/GenericDocumentCtsTest.java new file mode 100644 index 000000000000..657d55696f1f --- /dev/null +++ b/core/tests/coretests/src/android/app/appsearch/external/app/cts/GenericDocumentCtsTest.java @@ -0,0 +1,286 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.appsearch.cts; + +import static com.google.common.truth.Truth.assertThat; + +import static org.testng.Assert.expectThrows; + +import android.app.appsearch.GenericDocument; + +import org.junit.Test; + +public class GenericDocumentCtsTest { + private static final byte[] sByteArray1 = new byte[] {(byte) 1, (byte) 2, (byte) 3}; + private static final byte[] sByteArray2 = new byte[] {(byte) 4, (byte) 5, (byte) 6, (byte) 7}; + private static final GenericDocument sDocumentProperties1 = + new GenericDocument.Builder<>("sDocumentProperties1", "sDocumentPropertiesSchemaType1") + .setCreationTimestampMillis(12345L) + .build(); + private static final GenericDocument sDocumentProperties2 = + new GenericDocument.Builder<>("sDocumentProperties2", "sDocumentPropertiesSchemaType2") + .setCreationTimestampMillis(6789L) + .build(); + + @Test + public void testDocumentEquals_identical() { + GenericDocument document1 = + new GenericDocument.Builder<>("uri1", "schemaType1") + .setCreationTimestampMillis(5L) + .setTtlMillis(1L) + .setPropertyLong("longKey1", 1L, 2L, 3L) + .setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0) + .setPropertyBoolean("booleanKey1", true, false, true) + .setPropertyString( + "stringKey1", "test-value1", "test-value2", "test-value3") + .setPropertyBytes("byteKey1", sByteArray1, sByteArray2) + .setPropertyDocument( + "documentKey1", sDocumentProperties1, sDocumentProperties2) + .build(); + GenericDocument document2 = + new GenericDocument.Builder<>("uri1", "schemaType1") + .setCreationTimestampMillis(5L) + .setTtlMillis(1L) + .setPropertyLong("longKey1", 1L, 2L, 3L) + .setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0) + .setPropertyBoolean("booleanKey1", true, false, true) + .setPropertyString( + "stringKey1", "test-value1", "test-value2", "test-value3") + .setPropertyBytes("byteKey1", sByteArray1, sByteArray2) + .setPropertyDocument( + "documentKey1", sDocumentProperties1, sDocumentProperties2) + .build(); + assertThat(document1).isEqualTo(document2); + assertThat(document1.hashCode()).isEqualTo(document2.hashCode()); + } + + @Test + public void testDocumentEquals_differentOrder() { + GenericDocument document1 = + new GenericDocument.Builder<>("uri1", "schemaType1") + .setCreationTimestampMillis(5L) + .setPropertyLong("longKey1", 1L, 2L, 3L) + .setPropertyBytes("byteKey1", sByteArray1, sByteArray2) + .setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0) + .setPropertyBoolean("booleanKey1", true, false, true) + .setPropertyDocument( + "documentKey1", sDocumentProperties1, sDocumentProperties2) + .setPropertyString( + "stringKey1", "test-value1", "test-value2", "test-value3") + .build(); + + // Create second document with same parameter but different order. + GenericDocument document2 = + new GenericDocument.Builder<>("uri1", "schemaType1") + .setCreationTimestampMillis(5L) + .setPropertyBoolean("booleanKey1", true, false, true) + .setPropertyDocument( + "documentKey1", sDocumentProperties1, sDocumentProperties2) + .setPropertyString( + "stringKey1", "test-value1", "test-value2", "test-value3") + .setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0) + .setPropertyBytes("byteKey1", sByteArray1, sByteArray2) + .setPropertyLong("longKey1", 1L, 2L, 3L) + .build(); + assertThat(document1).isEqualTo(document2); + assertThat(document1.hashCode()).isEqualTo(document2.hashCode()); + } + + @Test + public void testDocumentEquals_failure() { + GenericDocument document1 = + new GenericDocument.Builder<>("uri1", "schemaType1") + .setCreationTimestampMillis(5L) + .setPropertyLong("longKey1", 1L, 2L, 3L) + .build(); + + // Create second document with same order but different value. + GenericDocument document2 = + new GenericDocument.Builder<>("uri1", "schemaType1") + .setCreationTimestampMillis(5L) + .setPropertyLong("longKey1", 1L, 2L, 4L) // Different + .build(); + assertThat(document1).isNotEqualTo(document2); + assertThat(document1.hashCode()).isNotEqualTo(document2.hashCode()); + } + + @Test + public void testDocumentEquals_repeatedFieldOrder_failure() { + GenericDocument document1 = + new GenericDocument.Builder<>("uri1", "schemaType1") + .setCreationTimestampMillis(5L) + .setPropertyBoolean("booleanKey1", true, false, true) + .build(); + + // Create second document with same order but different value. + GenericDocument document2 = + new GenericDocument.Builder<>("uri1", "schemaType1") + .setCreationTimestampMillis(5L) + .setPropertyBoolean("booleanKey1", true, true, false) // Different + .build(); + assertThat(document1).isNotEqualTo(document2); + assertThat(document1.hashCode()).isNotEqualTo(document2.hashCode()); + } + + @Test + public void testDocumentGetSingleValue() { + GenericDocument document = + new GenericDocument.Builder<>("uri1", "schemaType1") + .setCreationTimestampMillis(5L) + .setScore(1) + .setTtlMillis(1L) + .setPropertyLong("longKey1", 1L) + .setPropertyDouble("doubleKey1", 1.0) + .setPropertyBoolean("booleanKey1", true) + .setPropertyString("stringKey1", "test-value1") + .setPropertyBytes("byteKey1", sByteArray1) + .setPropertyDocument("documentKey1", sDocumentProperties1) + .build(); + assertThat(document.getUri()).isEqualTo("uri1"); + assertThat(document.getTtlMillis()).isEqualTo(1L); + assertThat(document.getSchemaType()).isEqualTo("schemaType1"); + assertThat(document.getCreationTimestampMillis()).isEqualTo(5); + assertThat(document.getScore()).isEqualTo(1); + assertThat(document.getPropertyLong("longKey1")).isEqualTo(1L); + assertThat(document.getPropertyDouble("doubleKey1")).isEqualTo(1.0); + assertThat(document.getPropertyBoolean("booleanKey1")).isTrue(); + assertThat(document.getPropertyString("stringKey1")).isEqualTo("test-value1"); + assertThat(document.getPropertyBytes("byteKey1")) + .asList() + .containsExactly((byte) 1, (byte) 2, (byte) 3); + assertThat(document.getPropertyDocument("documentKey1")).isEqualTo(sDocumentProperties1); + } + + @Test + public void testDocumentGetArrayValues() { + GenericDocument document = + new GenericDocument.Builder<>("uri1", "schemaType1") + .setCreationTimestampMillis(5L) + .setPropertyLong("longKey1", 1L, 2L, 3L) + .setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0) + .setPropertyBoolean("booleanKey1", true, false, true) + .setPropertyString( + "stringKey1", "test-value1", "test-value2", "test-value3") + .setPropertyBytes("byteKey1", sByteArray1, sByteArray2) + .setPropertyDocument( + "documentKey1", sDocumentProperties1, sDocumentProperties2) + .build(); + + assertThat(document.getUri()).isEqualTo("uri1"); + assertThat(document.getSchemaType()).isEqualTo("schemaType1"); + assertThat(document.getPropertyLongArray("longKey1")).asList().containsExactly(1L, 2L, 3L); + assertThat(document.getPropertyDoubleArray("doubleKey1")) + .usingExactEquality() + .containsExactly(1.0, 2.0, 3.0); + assertThat(document.getPropertyBooleanArray("booleanKey1")) + .asList() + .containsExactly(true, false, true); + assertThat(document.getPropertyStringArray("stringKey1")) + .asList() + .containsExactly("test-value1", "test-value2", "test-value3"); + assertThat(document.getPropertyBytesArray("byteKey1")) + .asList() + .containsExactly(sByteArray1, sByteArray2); + assertThat(document.getPropertyDocumentArray("documentKey1")) + .asList() + .containsExactly(sDocumentProperties1, sDocumentProperties2); + } + + @Test + public void testDocument_toString() { + GenericDocument document = + new GenericDocument.Builder<>("uri1", "schemaType1") + .setCreationTimestampMillis(5L) + .setPropertyLong("longKey1", 1L, 2L, 3L) + .setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0) + .setPropertyBoolean("booleanKey1", true, false, true) + .setPropertyString("stringKey1", "String1", "String2", "String3") + .setPropertyBytes("byteKey1", sByteArray1, sByteArray2) + .setPropertyDocument( + "documentKey1", sDocumentProperties1, sDocumentProperties2) + .build(); + String exceptedString = + "{ key: 'creationTimestampMillis' value: 5 } " + + "{ key: 'namespace' value: } " + + "{ key: 'properties' value: " + + "{ key: 'booleanKey1' value: [ 'true' 'false' 'true' ] } " + + "{ key: 'byteKey1' value: " + + "{ key: 'byteArray' value: [ '1' '2' '3' ] } " + + "{ key: 'byteArray' value: [ '4' '5' '6' '7' ] } } " + + "{ key: 'documentKey1' value: [ '" + + "{ key: 'creationTimestampMillis' value: 12345 } " + + "{ key: 'namespace' value: } " + + "{ key: 'properties' value: } " + + "{ key: 'schemaType' value: sDocumentPropertiesSchemaType1 } " + + "{ key: 'score' value: 0 } " + + "{ key: 'ttlMillis' value: 0 } " + + "{ key: 'uri' value: sDocumentProperties1 } ' '" + + "{ key: 'creationTimestampMillis' value: 6789 } " + + "{ key: 'namespace' value: } " + + "{ key: 'properties' value: } " + + "{ key: 'schemaType' value: sDocumentPropertiesSchemaType2 } " + + "{ key: 'score' value: 0 } " + + "{ key: 'ttlMillis' value: 0 } " + + "{ key: 'uri' value: sDocumentProperties2 } ' ] } " + + "{ key: 'doubleKey1' value: [ '1.0' '2.0' '3.0' ] } " + + "{ key: 'longKey1' value: [ '1' '2' '3' ] } " + + "{ key: 'stringKey1' value: [ 'String1' 'String2' 'String3' ] } } " + + "{ key: 'schemaType' value: schemaType1 } " + + "{ key: 'score' value: 0 } " + + "{ key: 'ttlMillis' value: 0 } " + + "{ key: 'uri' value: uri1 } "; + assertThat(document.toString()).isEqualTo(exceptedString); + } + + @Test + public void testDocumentGetValues_differentTypes() { + GenericDocument document = + new GenericDocument.Builder<>("uri1", "schemaType1") + .setScore(1) + .setPropertyLong("longKey1", 1L) + .setPropertyBoolean("booleanKey1", true, false, true) + .setPropertyString( + "stringKey1", "test-value1", "test-value2", "test-value3") + .build(); + + // Get a value for a key that doesn't exist + assertThat(document.getPropertyDouble("doubleKey1")).isEqualTo(0.0); + assertThat(document.getPropertyDoubleArray("doubleKey1")).isNull(); + + // Get a value with a single element as an array and as a single value + assertThat(document.getPropertyLong("longKey1")).isEqualTo(1L); + assertThat(document.getPropertyLongArray("longKey1")).asList().containsExactly(1L); + + // Get a value with multiple elements as an array and as a single value + assertThat(document.getPropertyString("stringKey1")).isEqualTo("test-value1"); + assertThat(document.getPropertyStringArray("stringKey1")) + .asList() + .containsExactly("test-value1", "test-value2", "test-value3"); + + // Get a value of the wrong type + assertThat(document.getPropertyDouble("longKey1")).isEqualTo(0.0); + assertThat(document.getPropertyDoubleArray("longKey1")).isNull(); + } + + @Test + public void testDocumentInvalid() { + GenericDocument.Builder<?> builder = new GenericDocument.Builder<>("uri1", "schemaType1"); + expectThrows( + IllegalArgumentException.class, + () -> builder.setPropertyBoolean("test", new boolean[] {})); + } +} diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/cts/SearchSpecCtsTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/cts/SearchSpecCtsTest.java new file mode 100644 index 000000000000..50bca278534e --- /dev/null +++ b/core/tests/coretests/src/android/app/appsearch/external/app/cts/SearchSpecCtsTest.java @@ -0,0 +1,67 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.appsearch.cts; + +import static com.google.common.truth.Truth.assertThat; + +import static org.testng.Assert.expectThrows; + +import android.app.appsearch.SearchSpec; + +import org.junit.Test; + +public class SearchSpecCtsTest { + @Test + public void buildSearchSpecWithoutTermMatchType() { + RuntimeException e = + expectThrows( + RuntimeException.class, + () -> new SearchSpec.Builder().addSchemaType("testSchemaType").build()); + assertThat(e).hasMessageThat().contains("Missing termMatchType field"); + } + + @Test + public void testBuildSearchSpec() { + SearchSpec searchSpec = + new SearchSpec.Builder() + .setTermMatch(SearchSpec.TERM_MATCH_PREFIX) + .addNamespace("namespace1", "namespace2") + .addSchemaType("schemaTypes1", "schemaTypes2") + .setSnippetCount(5) + .setSnippetCountPerProperty(10) + .setMaxSnippetSize(15) + .setResultCountPerPage(42) + .setOrder(SearchSpec.ORDER_ASCENDING) + .setRankingStrategy(SearchSpec.RANKING_STRATEGY_DOCUMENT_SCORE) + .build(); + + assertThat(searchSpec.getTermMatch()).isEqualTo(SearchSpec.TERM_MATCH_PREFIX); + assertThat(searchSpec.getNamespaces()) + .containsExactly("namespace1", "namespace2") + .inOrder(); + assertThat(searchSpec.getSchemaTypes()) + .containsExactly("schemaTypes1", "schemaTypes2") + .inOrder(); + assertThat(searchSpec.getSnippetCount()).isEqualTo(5); + assertThat(searchSpec.getSnippetCountPerProperty()).isEqualTo(10); + assertThat(searchSpec.getMaxSnippetSize()).isEqualTo(15); + assertThat(searchSpec.getResultCountPerPage()).isEqualTo(42); + assertThat(searchSpec.getOrder()).isEqualTo(SearchSpec.ORDER_ASCENDING); + assertThat(searchSpec.getRankingStrategy()) + .isEqualTo(SearchSpec.RANKING_STRATEGY_DOCUMENT_SCORE); + } +} diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/customer/CustomerDocumentTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/cts/customer/CustomerDocumentTest.java index d56d0c3c5c8e..29b5754da520 100644 --- a/core/tests/coretests/src/android/app/appsearch/external/app/customer/CustomerDocumentTest.java +++ b/core/tests/coretests/src/android/app/appsearch/external/app/cts/customer/CustomerDocumentTest.java @@ -14,13 +14,13 @@ * limitations under the License. */ -package android.app.appsearch.customer; +package android.app.appsearch.cts.customer; + +import static com.google.common.truth.Truth.assertThat; import android.annotation.NonNull; import android.app.appsearch.GenericDocument; -import static com.google.common.truth.Truth.assertThat; - import org.junit.Test; /** @@ -32,50 +32,58 @@ import org.junit.Test; */ public class CustomerDocumentTest { - private static byte[] sByteArray1 = new byte[]{(byte) 1, (byte) 2, (byte) 3}; - private static byte[] sByteArray2 = new byte[]{(byte) 4, (byte) 5, (byte) 6}; - private static GenericDocument sDocumentProperties1 = new GenericDocument - .Builder("sDocumentProperties1", "sDocumentPropertiesSchemaType1") - .build(); - private static GenericDocument sDocumentProperties2 = new GenericDocument - .Builder("sDocumentProperties2", "sDocumentPropertiesSchemaType2") - .build(); + private static final byte[] BYTE_ARRAY1 = new byte[] {(byte) 1, (byte) 2, (byte) 3}; + private static final byte[] BYTE_ARRAY2 = new byte[] {(byte) 4, (byte) 5, (byte) 6}; + private static final GenericDocument DOCUMENT_PROPERTIES1 = + new GenericDocument.Builder<>("sDocumentProperties1", "sDocumentPropertiesSchemaType1") + .build(); + private static final GenericDocument DOCUMENT_PROPERTIES2 = + new GenericDocument.Builder<>("sDocumentProperties2", "sDocumentPropertiesSchemaType2") + .build(); @Test public void testBuildCustomerDocument() { - CustomerDocument customerDocument = new CustomerDocument.Builder("uri1") - .setScore(1) - .setCreationTimestampMillis(0) - .setPropertyLong("longKey1", 1L, 2L, 3L) - .setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0) - .setPropertyBoolean("booleanKey1", true, false, true) - .setPropertyString("stringKey1", "test-value1", "test-value2", "test-value3") - .setPropertyBytes("byteKey1", sByteArray1, sByteArray2) - .setPropertyDocument("documentKey1", sDocumentProperties1, sDocumentProperties2) - .build(); + CustomerDocument customerDocument = + new CustomerDocument.Builder("uri1") + .setScore(1) + .setCreationTimestampMillis(0) + .setPropertyLong("longKey1", 1L, 2L, 3L) + .setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0) + .setPropertyBoolean("booleanKey1", true, false, true) + .setPropertyString( + "stringKey1", "test-value1", "test-value2", "test-value3") + .setPropertyBytes("byteKey1", BYTE_ARRAY1, BYTE_ARRAY2) + .setPropertyDocument( + "documentKey1", DOCUMENT_PROPERTIES1, DOCUMENT_PROPERTIES2) + .build(); assertThat(customerDocument.getUri()).isEqualTo("uri1"); assertThat(customerDocument.getSchemaType()).isEqualTo("customerDocument"); assertThat(customerDocument.getScore()).isEqualTo(1); assertThat(customerDocument.getCreationTimestampMillis()).isEqualTo(0L); - assertThat(customerDocument.getPropertyLongArray("longKey1")).asList() + assertThat(customerDocument.getPropertyLongArray("longKey1")) + .asList() .containsExactly(1L, 2L, 3L); - assertThat(customerDocument.getPropertyDoubleArray("doubleKey1")).usingExactEquality() + assertThat(customerDocument.getPropertyDoubleArray("doubleKey1")) + .usingExactEquality() .containsExactly(1.0, 2.0, 3.0); - assertThat(customerDocument.getPropertyBooleanArray("booleanKey1")).asList() + assertThat(customerDocument.getPropertyBooleanArray("booleanKey1")) + .asList() .containsExactly(true, false, true); - assertThat(customerDocument.getPropertyStringArray("stringKey1")).asList() + assertThat(customerDocument.getPropertyStringArray("stringKey1")) + .asList() .containsExactly("test-value1", "test-value2", "test-value3"); - assertThat(customerDocument.getPropertyBytesArray("byteKey1")).asList() - .containsExactly(sByteArray1, sByteArray2); - assertThat(customerDocument.getPropertyDocumentArray("documentKey1")).asList() - .containsExactly(sDocumentProperties1, sDocumentProperties2); + assertThat(customerDocument.getPropertyBytesArray("byteKey1")) + .asList() + .containsExactly(BYTE_ARRAY1, BYTE_ARRAY2); + assertThat(customerDocument.getPropertyDocumentArray("documentKey1")) + .asList() + .containsExactly(DOCUMENT_PROPERTIES1, DOCUMENT_PROPERTIES2); } /** - * An example document type for test purposes, defined outside of - * {@link GenericDocument} (the way an external developer would define - * it). + * An example document type for test purposes, defined outside of {@link GenericDocument} (the + * way an external developer would define it). */ private static class CustomerDocument extends GenericDocument { private CustomerDocument(GenericDocument document) { diff --git a/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java b/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java new file mode 100644 index 000000000000..27584a597cc8 --- /dev/null +++ b/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.people; + +import static com.google.common.truth.Truth.assertThat; + +import static junit.framework.Assert.assertFalse; + +import static org.junit.Assert.assertTrue; + +import android.app.Notification; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ShortcutInfo; +import android.graphics.drawable.Icon; +import android.net.Uri; +import android.os.UserHandle; +import android.service.notification.StatusBarNotification; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class PeopleSpaceTileTest { + + private Context mContext; + + @Before + public void setUp() { + mContext = InstrumentationRegistry.getContext(); + } + + @Test + public void testId() { + PeopleSpaceTile tile = new PeopleSpaceTile.Builder( + new ShortcutInfo.Builder(mContext, "123").build()).build(); + assertThat(tile.getId()).isEqualTo("123"); + + tile = new PeopleSpaceTile.Builder(new ShortcutInfo.Builder(mContext, "123").build()).setId( + "5").build(); + assertThat(tile.getId()).isEqualTo("5"); + + tile = new PeopleSpaceTile.Builder("12", null, null, null).build(); + assertThat(tile.getId()).isEqualTo("12"); + } + + @Test + public void testUserName() { + PeopleSpaceTile tile = new PeopleSpaceTile.Builder( + new ShortcutInfo.Builder(mContext, "123").build()).build(); + assertThat(tile.getUserName()).isNull(); + + tile = new PeopleSpaceTile.Builder( + new ShortcutInfo.Builder(mContext, "123").build()).setUserName("Name 1").build(); + assertThat(tile.getUserName()).isEqualTo("Name 1"); + + tile = new PeopleSpaceTile.Builder(null, "Name 2", null, null).build(); + assertThat(tile.getUserName()).isEqualTo("Name 2"); + } + + @Test + public void testUserIcon() { + PeopleSpaceTile tile = new PeopleSpaceTile.Builder( + new ShortcutInfo.Builder(mContext, "123").build()).setUserIcon( + Icon.createWithResource(mContext, 1)).build(); + assertThat(tile.getUserIcon().toString()).isEqualTo( + Icon.createWithResource(mContext, 1).toString()); + + tile = new PeopleSpaceTile.Builder("12", null, Icon.createWithResource(mContext, 2), + null).build(); + assertThat(tile.getUserIcon().toString()).isEqualTo( + Icon.createWithResource(mContext, 2).toString()); + } + + @Test + public void testContactUri() { + PeopleSpaceTile tile = new PeopleSpaceTile.Builder( + new ShortcutInfo.Builder(mContext, "123").build()).setContactUri( + Uri.parse("test")).build(); + + assertThat(tile.getContactUri()).isEqualTo(Uri.parse("test")); + } + + @Test + public void testUid() { + PeopleSpaceTile tile = new PeopleSpaceTile.Builder( + new ShortcutInfo.Builder(mContext, "123").build()).setUid(42).build(); + + assertThat(tile.getUid()).isEqualTo(42); + } + + @Test + public void testPackageName() { + PeopleSpaceTile tile = new PeopleSpaceTile.Builder( + new ShortcutInfo.Builder(mContext, "123").build()).build(); + // Automatically added by creating a ShortcutInfo. + assertThat(tile.getPackageName()).isEqualTo("com.android.frameworks.coretests"); + + tile = new PeopleSpaceTile.Builder( + new ShortcutInfo.Builder(mContext, "123").build()).setPackageName( + "package.name").build(); + assertThat(tile.getPackageName()).isEqualTo("package.name"); + + tile = new PeopleSpaceTile.Builder("12", null, null, + new Intent().setPackage("intent.package")).build(); + assertThat(tile.getPackageName()).isEqualTo("intent.package"); + } + + @Test + public void testLastInteractionTimestamp() { + PeopleSpaceTile tile = new PeopleSpaceTile.Builder( + new ShortcutInfo.Builder(mContext, "123").build()).build(); + assertThat(tile.getLastInteractionTimestamp()).isEqualTo(0L); + + tile = new PeopleSpaceTile.Builder( + new ShortcutInfo.Builder(mContext, "123").build()).setLastInteractionTimestamp( + 7L).build(); + assertThat(tile.getLastInteractionTimestamp()).isEqualTo(7L); + } + + @Test + public void testImportantConversation() { + PeopleSpaceTile tile = new PeopleSpaceTile.Builder( + new ShortcutInfo.Builder(mContext, "123").build()).build(); + assertFalse(tile.isImportantConversation()); + + tile = new PeopleSpaceTile.Builder( + new ShortcutInfo.Builder(mContext, "123").build()).setIsImportantConversation( + true).build(); + assertTrue(tile.isImportantConversation()); + } + + @Test + public void testHiddenConversation() { + PeopleSpaceTile tile = new PeopleSpaceTile.Builder( + new ShortcutInfo.Builder(mContext, "123").build()).build(); + assertFalse(tile.isHiddenConversation()); + + tile = new PeopleSpaceTile.Builder( + new ShortcutInfo.Builder(mContext, "123").build()).setIsHiddenConversation( + true).build(); + assertTrue(tile.isHiddenConversation()); + } + + @Test + public void testNotification() { + Notification notification = new Notification.Builder(mContext, "test").build(); + StatusBarNotification sbn = new StatusBarNotification("pkg" /* pkg */, "pkg" /* opPkg */, + 1 /* id */, "" /* tag */, 0 /* uid */, 0 /* initialPid */, 0 /* score */, + notification, UserHandle.CURRENT, 0 /* postTime */); + PeopleSpaceTile tile = new PeopleSpaceTile.Builder( + new ShortcutInfo.Builder(mContext, "123").build()).setNotification(sbn).build(); + + assertThat(tile.getNotification()).isEqualTo(sbn); + } + + @Test + public void testIntent() { + PeopleSpaceTile tile = new PeopleSpaceTile.Builder( + new ShortcutInfo.Builder(mContext, "123").build()).build(); + assertThat(tile.getIntent()).isNull(); + + tile = new PeopleSpaceTile.Builder( + new ShortcutInfo.Builder(mContext, "123").build()).setIntent(new Intent()).build(); + assertThat(tile.getIntent().toString()).isEqualTo(new Intent().toString()); + + tile = new PeopleSpaceTile.Builder("12", null, null, new Intent()).build(); + assertThat(tile.getIntent().toString()).isEqualTo(new Intent().toString()); + } + +} diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerPropertyTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerPropertyTests.java new file mode 100644 index 000000000000..3757712010a2 --- /dev/null +++ b/core/tests/coretests/src/android/content/pm/PackageManagerPropertyTests.java @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.pm; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import android.content.pm.PackageManager.Property; +import android.os.Bundle; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class PackageManagerPropertyTests { + + @Test + public void testBooleanProperty() throws Exception { + final Property p = + new Property("booleanProperty", true, "android", null); + assertTrue(p.isBoolean()); + assertFalse(p.isFloat()); + assertFalse(p.isInteger()); + assertFalse(p.isResourceId()); + assertFalse(p.isString()); + assertTrue(p.getBoolean()); + assertEquals(0.0f, p.getFloat(), 0.0f); + assertEquals(0, p.getInteger()); + assertEquals(0, p.getResourceId()); + assertEquals(null, p.getString()); + assertEquals("android", p.getPackageName()); + assertNull(p.getClassName()); + } + + @Test + public void testBooleanPropertyToBundle() throws Exception { + final Bundle b = + new Property("booleanProperty", true, "android", null).toBundle(null); + assertTrue(b.getBoolean("booleanProperty")); + } + + @Test + public void testFloatProperty() throws Exception { + final Property p = + new Property("floatProperty", 3.14f, "android", null); + assertFalse(p.isBoolean()); + assertTrue(p.isFloat()); + assertFalse(p.isInteger()); + assertFalse(p.isResourceId()); + assertFalse(p.isString()); + assertFalse(p.getBoolean()); + assertEquals(3.14f, p.getFloat(), 0.0f); + assertEquals(0, p.getInteger()); + assertEquals(0, p.getResourceId()); + assertEquals(null, p.getString()); + assertEquals("android", p.getPackageName()); + assertNull(p.getClassName()); + } + + @Test + public void testFloatPropertyToBundle() throws Exception { + final Bundle b = + new Property("floatProperty", 3.14f, "android", null).toBundle(null); + assertEquals(3.14f, b.getFloat("floatProperty"), 0.0f); + } + + @Test + public void testIntegerProperty() throws Exception { + final Property p = + new Property("integerProperty", 42, false, "android", null); + assertFalse(p.isBoolean()); + assertFalse(p.isFloat()); + assertTrue(p.isInteger()); + assertFalse(p.isResourceId()); + assertFalse(p.isString()); + assertFalse(p.getBoolean()); + assertEquals(0.0f, p.getFloat(), 0.0f); + assertEquals(42, p.getInteger()); + assertEquals(0, p.getResourceId()); + assertEquals(null, p.getString()); + assertEquals("android", p.getPackageName()); + assertNull(p.getClassName()); + } + + @Test + public void testIntegerPropertyToBundle() throws Exception { + final Bundle b = + new Property("integerProperty", 42, false, "android", null).toBundle(null); + assertEquals(42, b.getInt("integerProperty")); + } + + @Test + public void testResourceProperty() throws Exception { + final Property p = + new Property("resourceProperty", 0x7f010001, true, "android", null); + assertFalse(p.isBoolean()); + assertFalse(p.isFloat()); + assertFalse(p.isInteger()); + assertTrue(p.isResourceId()); + assertFalse(p.isString()); + assertFalse(p.getBoolean()); + assertEquals(0.0f, p.getFloat(), 0.0f); + assertEquals(0, p.getInteger()); + assertEquals(0x7f010001, p.getResourceId()); + assertEquals(null, p.getString()); + assertEquals("android", p.getPackageName()); + assertNull(p.getClassName()); + } + + @Test + public void testResourcePropertyToBundle() throws Exception { + final Bundle b = + new Property("resourceProperty", 0x7f010001, true, "android", null).toBundle(null); + assertEquals(0x7f010001, b.getInt("resourceProperty")); + } + + @Test + public void testStringProperty() throws Exception { + final Property p = + new Property("stringProperty", "koala", "android", null); + assertFalse(p.isBoolean()); + assertFalse(p.isFloat()); + assertFalse(p.isInteger()); + assertFalse(p.isResourceId()); + assertTrue(p.isString()); + assertFalse(p.getBoolean()); + assertEquals(0.0f, p.getFloat(), 0.0f); + assertEquals(0, p.getInteger()); + assertEquals(0, p.getResourceId()); + assertEquals("koala", p.getString()); + assertEquals("android", p.getPackageName()); + assertNull(p.getClassName()); + } + + @Test + public void testStringPropertyToBundle() throws Exception { + final Bundle b = + new Property("stringProperty", "koala", "android", null).toBundle(null); + assertEquals("koala", b.getString("stringProperty")); + } + + @Test + public void testProperty_invalidName() throws Exception { + try { + final Property p = new Property(null, 1, "android", null); + fail("expected assertion error"); + } catch (AssertionError expected) { + } + } + + @Test + public void testProperty_invalidType() throws Exception { + try { + final Property p = new Property("invalidTypeProperty", 0, "android", null); + fail("expected assertion error"); + } catch (AssertionError expected) { + } + + try { + final Property p = new Property("invalidTypeProperty", 6, "android", null); + fail("expected assertion error"); + } catch (AssertionError expected) { + } + + try { + final Property p = new Property("invalidTypeProperty", -1, "android", null); + fail("expected assertion error"); + } catch (AssertionError expected) { + } + } + + @Test + public void testProperty_noPackageName() throws Exception { + try { + final Property p = new Property(null, 1, null, null); + fail("expected assertion error"); + } catch (AssertionError expected) { + } + } +} diff --git a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java index c66bac6cc335..82d066f6ab16 100644 --- a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java +++ b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java @@ -23,7 +23,6 @@ import static org.junit.Assert.assertTrue; import android.content.Context; import android.content.res.AssetManager; -import android.graphics.fonts.Font; import android.graphics.fonts.FontCustomizationParser; import android.graphics.fonts.FontFamily; import android.graphics.fonts.SystemFonts; @@ -49,7 +48,6 @@ import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.StandardCopyOption; -import java.util.ArrayList; import java.util.Locale; @SmallTest @@ -133,14 +131,14 @@ public class TypefaceSystemFallbackTest { private static void buildSystemFallback(String xml, FontCustomizationParser.Result oemCustomization, ArrayMap<String, Typeface> fontMap, ArrayMap<String, FontFamily[]> fallbackMap) { - final ArrayList<Font> availableFonts = new ArrayList<>(); try (FileOutputStream fos = new FileOutputStream(TEST_FONTS_XML)) { fos.write(xml.getBytes(Charset.forName("UTF-8"))); } catch (IOException e) { throw new RuntimeException(e); } + final FontConfig.Alias[] aliases = SystemFonts.buildSystemFallback(TEST_FONTS_XML, - TEST_FONT_DIR, oemCustomization, fallbackMap, availableFonts); + TEST_FONT_DIR, oemCustomization, fallbackMap); Typeface.initSystemDefaultTypefaces(fontMap, fallbackMap, aliases); } @@ -156,12 +154,11 @@ public class TypefaceSystemFallbackTest { public void testBuildSystemFallback() { final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); - final ArrayList<Font> availableFonts = new ArrayList<>(); final FontCustomizationParser.Result oemCustomization = new FontCustomizationParser.Result(); final FontConfig.Alias[] aliases = SystemFonts.buildSystemFallback(SYSTEM_FONTS_XML, - SYSTEM_FONT_DIR, oemCustomization, fallbackMap, availableFonts); + SYSTEM_FONT_DIR, oemCustomization, fallbackMap); assertNotNull(aliases); assertFalse(fallbackMap.isEmpty()); diff --git a/core/tests/coretests/src/android/graphics/TypefaceTest.java b/core/tests/coretests/src/android/graphics/TypefaceTest.java index 5fa8c4fbaa56..392c6b7199a5 100644 --- a/core/tests/coretests/src/android/graphics/TypefaceTest.java +++ b/core/tests/coretests/src/android/graphics/TypefaceTest.java @@ -23,8 +23,11 @@ import static org.junit.Assert.assertTrue; import android.content.Context; import android.content.res.AssetManager; import android.content.res.Resources; +import android.graphics.fonts.FontFamily; import android.graphics.fonts.SystemFonts; import android.os.SharedMemory; +import android.text.FontConfig; +import android.util.Pair; import androidx.test.InstrumentationRegistry; import androidx.test.filters.LargeTest; @@ -195,8 +198,9 @@ public class TypefaceTest { @Test public void testSerialize() throws Exception { HashMap<String, Typeface> systemFontMap = new HashMap<>(); - Typeface.initSystemDefaultTypefaces(systemFontMap, SystemFonts.getRawSystemFallbackMap(), - SystemFonts.getAliases()); + Pair<FontConfig.Alias[], Map<String, FontFamily[]>> res = + SystemFonts.initializePreinstalledFonts(); + Typeface.initSystemDefaultTypefaces(systemFontMap, res.second, res.first); SharedMemory sharedMemory = Typeface.serializeFontMap(systemFontMap); Map<String, Typeface> copiedFontMap = Typeface.deserializeFontMap(sharedMemory.mapReadOnly().order(ByteOrder.BIG_ENDIAN)); diff --git a/core/tests/coretests/src/android/security/CredentialManagementAppTest.java b/core/tests/coretests/src/android/security/CredentialManagementAppTest.java new file mode 100644 index 000000000000..366aabd183e3 --- /dev/null +++ b/core/tests/coretests/src/android/security/CredentialManagementAppTest.java @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; + +import android.net.Uri; +import android.util.Xml; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Iterator; +import java.util.Map; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public final class CredentialManagementAppTest { + + private static final String TEST_PACKAGE_NAME_1 = "com.android.test"; + private static final String TEST_PACKAGE_NAME_2 = "com.android.test2"; + private static final Uri TEST_URI_1 = Uri.parse("test.com"); + private static final Uri TEST_URI_2 = Uri.parse("test2.com"); + private static final String TEST_ALIAS_1 = "testAlias"; + private static final String TEST_ALIAS_2 = "testAlias2"; + + private static final String PACKAGE_NAME = "com.android.cred.mng.pkg"; + private static final AppUriAuthenticationPolicy AUTHENTICATION_POLICY = + new AppUriAuthenticationPolicy.Builder() + .addAppAndUriMapping(TEST_PACKAGE_NAME_1, TEST_URI_1, TEST_ALIAS_1) + .build(); + private static final CredentialManagementApp CREDENTIAL_MANAGEMENT_APP = + new CredentialManagementApp(PACKAGE_NAME, AUTHENTICATION_POLICY); + + private static final String TAG_CREDENTIAL_MANAGEMENT_APP = "credential-management-app"; + + @Test + public void credentialManagementApp_getters() { + CredentialManagementApp credentialManagementApp = + new CredentialManagementApp(PACKAGE_NAME, AUTHENTICATION_POLICY); + + assertThat(credentialManagementApp.getPackageName(), is(PACKAGE_NAME)); + assertThat(credentialManagementApp.getAuthenticationPolicy(), is(AUTHENTICATION_POLICY)); + } + + @Test + public void setAuthenticationPolicy_updatesAuthenticationPolicy() { + CredentialManagementApp credentialManagementApp = + new CredentialManagementApp(PACKAGE_NAME, AUTHENTICATION_POLICY); + AppUriAuthenticationPolicy updatedAuthenticationPolicy = + new AppUriAuthenticationPolicy.Builder().addAppAndUriMapping( + TEST_PACKAGE_NAME_2, TEST_URI_2, TEST_ALIAS_2).build(); + + credentialManagementApp.setAuthenticationPolicy(updatedAuthenticationPolicy); + + assertThat(credentialManagementApp.getAuthenticationPolicy(), + is(updatedAuthenticationPolicy)); + } + + @Test + public void constructor_nullPackageName_throwException() { + try { + new CredentialManagementApp(/* packageName= */ null, AUTHENTICATION_POLICY); + fail("Shall not take null inputs"); + } catch (NullPointerException expected) { + // Expected behavior, nothing to do. + } + } + + @Test + public void constructor_nullAuthenticationPolicy_throwException() { + try { + new CredentialManagementApp(PACKAGE_NAME, /* authenticationPolicy= */ null); + fail("Shall not take null inputs"); + } catch (NullPointerException expected) { + // Expected behavior, nothing to do. + } + } + + @Test + public void writeToXmlAndReadFromXml() throws IOException, XmlPullParserException { + File xmlFile = writeToXml(CREDENTIAL_MANAGEMENT_APP); + + CredentialManagementApp loadedCredentialManagementApp = readFromXml(xmlFile); + + assertCredentialManagementAppsEqual(loadedCredentialManagementApp, + CREDENTIAL_MANAGEMENT_APP); + } + + private File writeToXml(CredentialManagementApp credentialManagementApp) throws IOException { + File file = File.createTempFile("temp", "credmng"); + final FileOutputStream out = new FileOutputStream(file); + XmlSerializer xml = Xml.newSerializer(); + xml.setOutput(out, StandardCharsets.UTF_8.name()); + xml.startDocument(null, true); + xml.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); + xml.startTag(null, TAG_CREDENTIAL_MANAGEMENT_APP); + credentialManagementApp.writeToXml(xml); + xml.endTag(null, TAG_CREDENTIAL_MANAGEMENT_APP); + xml.endDocument(); + out.close(); + return file; + } + + private CredentialManagementApp readFromXml(File file) + throws IOException, XmlPullParserException { + CredentialManagementApp credentialManagementApp = null; + final XmlPullParser parser = Xml.newPullParser(); + final FileInputStream in = new FileInputStream(file); + parser.setInput(in, StandardCharsets.UTF_8.name()); + int type; + while ((type = parser.next()) != XmlPullParser.START_TAG + && type != XmlPullParser.END_DOCUMENT) { + } + String tag = parser.getName(); + if (TAG_CREDENTIAL_MANAGEMENT_APP.equals(tag)) { + credentialManagementApp = CredentialManagementApp.readFromXml(parser); + } + return credentialManagementApp; + } + + private void assertCredentialManagementAppsEqual(CredentialManagementApp actual, + CredentialManagementApp expected) { + assertThat(actual.getPackageName(), is(expected.getPackageName())); + assertAuthenticationPoliciesEqual(actual.getAuthenticationPolicy(), + expected.getAuthenticationPolicy()); + } + + private void assertAuthenticationPoliciesEqual(AppUriAuthenticationPolicy actual, + AppUriAuthenticationPolicy expected) { + Iterator<Map.Entry<String, Map<Uri, String>>> actualIter = + actual.getAppAndUriMappings().entrySet().iterator(); + Iterator<Map.Entry<String, Map<Uri, String>>> expectedIter = + expected.getAppAndUriMappings().entrySet().iterator(); + + assertThat(actual.getAppAndUriMappings().size(), + is(expected.getAppAndUriMappings().size())); + while (actualIter.hasNext()) { + Map.Entry<String, Map<Uri, String>> actualAppToUri = actualIter.next(); + Map.Entry<String, Map<Uri, String>> expectedAppToUri = expectedIter.next(); + assertThat(actualAppToUri.getKey(), is(expectedAppToUri.getKey())); + assertUrisToAliasesEqual(actualAppToUri.getValue(), expectedAppToUri.getValue()); + } + } + + private void assertUrisToAliasesEqual(Map<Uri, String> actual, Map<Uri, String> expected) { + Iterator<Map.Entry<Uri, String>> actualIter = actual.entrySet().iterator(); + Iterator<Map.Entry<Uri, String>> expectedIter = expected.entrySet().iterator(); + + assertThat(actual.size(), is(expected.size())); + while (actualIter.hasNext()) { + Map.Entry<Uri, String> actualUriToAlias = actualIter.next(); + Map.Entry<Uri, String> expectedUriToAlias = expectedIter.next(); + assertThat(actualUriToAlias.getKey(), is(expectedUriToAlias.getKey())); + assertThat(actualUriToAlias.getValue(), is(expectedUriToAlias.getValue())); + } + } +} diff --git a/core/tests/coretests/src/android/text/FontFallbackSetup.java b/core/tests/coretests/src/android/text/FontFallbackSetup.java index cc51ec3da1fd..64e6f82d82af 100644 --- a/core/tests/coretests/src/android/text/FontFallbackSetup.java +++ b/core/tests/coretests/src/android/text/FontFallbackSetup.java @@ -20,7 +20,6 @@ import android.annotation.NonNull; import android.content.Context; import android.content.res.AssetManager; import android.graphics.Typeface; -import android.graphics.fonts.Font; import android.graphics.fonts.FontCustomizationParser; import android.graphics.fonts.FontFamily; import android.graphics.fonts.SystemFonts; @@ -35,7 +34,6 @@ import java.io.InputStream; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.StandardCopyOption; -import java.util.ArrayList; public class FontFallbackSetup implements AutoCloseable { private final String[] mTestFontFiles; @@ -78,11 +76,10 @@ public class FontFallbackSetup implements AutoCloseable { } final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); - final ArrayList<Font> availableFonts = new ArrayList<>(); final FontCustomizationParser.Result oemCustomization = new FontCustomizationParser.Result(); final FontConfig.Alias[] aliases = SystemFonts.buildSystemFallback(testFontsXml, - mTestFontsDir, oemCustomization, fallbackMap, availableFonts); + mTestFontsDir, oemCustomization, fallbackMap); Typeface.initSystemDefaultTypefaces(mFontMap, fallbackMap, aliases); } diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java index 7cde19c30dd4..40ef04a5e369 100644 --- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java +++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java @@ -376,6 +376,56 @@ public class TextViewActivityTest { } @Test + public void testToolbarMenuItemClickAfterSelectionChange() throws Throwable { + final MenuItem[] latestItem = new MenuItem[1]; + final MenuItem[] clickedItem = new MenuItem[1]; + final String text = "abcd efg hijk"; + mActivityRule.runOnUiThread(() -> { + final TextView textView = mActivity.findViewById(R.id.textview); + textView.setText(text); + textView.setCustomSelectionActionModeCallback( + new ActionMode.Callback() { + @Override + public boolean onPrepareActionMode(ActionMode mode, Menu menu) { + menu.clear(); + latestItem[0] = menu.add("Item"); + return true; + } + + @Override + public boolean onActionItemClicked(ActionMode mode, MenuItem item) { + clickedItem[0] = item; + return true; + } + + @Override + public boolean onCreateActionMode(ActionMode mode, Menu menu) { + return true; + } + + @Override + public void onDestroyActionMode(ActionMode mode) {} + }); + }); + mInstrumentation.waitForIdleSync(); + + onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf("f"))); + sleepForFloatingToolbarPopup(); + + // Change the selection so that the menu items are refreshed. + final TextView textView = mActivity.findViewById(R.id.textview); + onHandleView(com.android.internal.R.id.selection_start_handle) + .perform(dragHandle(textView, Handle.SELECTION_START, 0)); + sleepForFloatingToolbarPopup(); + assertFloatingToolbarIsDisplayed(); + + clickFloatingToolbarItem("Item"); + mInstrumentation.waitForIdleSync(); + + assertEquals(latestItem[0], clickedItem[0]); + } + + @Test public void testSelectionRemovedWhenNonselectableTextLosesFocus() throws Throwable { final TextLinks.TextLink textLink = addLinkifiedTextToTextView(R.id.nonselectable_textview); final int position = (textLink.getStart() + textLink.getEnd()) / 2; diff --git a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java index d45d4b04af33..477978630ec2 100644 --- a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java +++ b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java @@ -28,10 +28,11 @@ import static androidx.test.espresso.matcher.ViewMatchers.withId; import static androidx.test.espresso.matcher.ViewMatchers.withTagValue; import static androidx.test.espresso.matcher.ViewMatchers.withText; +import static com.android.internal.widget.FloatingToolbar.MenuItemRepr; + import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.is; -import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; @@ -158,8 +159,8 @@ public class FloatingToolbarEspressoUtils { public void describeTo(Description description) {} private void collectMenuItemIds(View view) { - if (view.getTag() instanceof MenuItem) { - menuItemIds.add(((MenuItem) view.getTag()).getItemId()); + if (view.getTag() instanceof MenuItemRepr) { + menuItemIds.add(((MenuItemRepr) view.getTag()).itemId); } else if (view instanceof ViewGroup) { ViewGroup viewGroup = (ViewGroup) view; for (int i = 0; i < viewGroup.getChildCount(); i++) { @@ -178,8 +179,8 @@ public class FloatingToolbarEspressoUtils { */ public static void assertFloatingToolbarDoesNotContainItem(String itemLabel) { final Predicate<View> hasMenuItemLabel = view -> - view.getTag() instanceof MenuItem - && itemLabel.equals(((MenuItem) view.getTag()).getTitle().toString()); + view.getTag() instanceof MenuItemRepr + && itemLabel.equals(((MenuItemRepr) view.getTag()).title); assertFloatingToolbarMenuItem(hasMenuItemLabel, false); } @@ -191,8 +192,8 @@ public class FloatingToolbarEspressoUtils { */ public static void assertFloatingToolbarDoesNotContainItem(final int menuItemId) { final Predicate<View> hasMenuItemId = view -> - view.getTag() instanceof MenuItem - && ((MenuItem) view.getTag()).getItemId() == menuItemId; + view.getTag() instanceof MenuItemRepr + && ((MenuItemRepr) view.getTag()).itemId == menuItemId; assertFloatingToolbarMenuItem(hasMenuItemId, false); } diff --git a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java index e17800f7b6fd..7f7bfa3c5e3d 100644 --- a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java +++ b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java @@ -77,7 +77,8 @@ public class FrameTrackerTest { Session session = new Session(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE); mTracker = Mockito.spy( - new FrameTracker(session, handler, mRenderer, mWrapper)); + new FrameTracker(session, handler, mRenderer, mWrapper, + /*traceThresholdMissedFrames=*/ 1, /*traceThresholdFrameTimeMillis=*/ -1)); doNothing().when(mTracker).triggerPerfetto(); } diff --git a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java index a9cfd286688b..0ef5643b0905 100644 --- a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java +++ b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java @@ -24,6 +24,7 @@ import static com.google.common.truth.Truth.assertWithMessage; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; @@ -32,6 +33,7 @@ import static org.mockito.Mockito.verify; import android.os.Handler; import android.os.HandlerThread; import android.os.Message; +import android.provider.DeviceConfig; import android.view.View; import android.view.ViewAttachTestActivity; @@ -50,6 +52,7 @@ import org.mockito.ArgumentCaptor; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.Arrays; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.stream.Collectors; @@ -71,8 +74,6 @@ public class InteractionJankMonitorTest { mView = mActivity.getWindow().getDecorView(); assertThat(mView.isAttachedToWindow()).isTrue(); - InteractionJankMonitor.abandon(); - Handler handler = spy(new Handler(mActivity.getMainLooper())); doReturn(true).when(handler).sendMessageAtTime(any(), anyLong()); mWorker = spy(new HandlerThread("Interaction-jank-monitor-test")); @@ -93,7 +94,8 @@ public class InteractionJankMonitorTest { Session session = new Session(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE); FrameTracker tracker = spy(new FrameTracker(session, mWorker.getThreadHandler(), new ThreadedRendererWrapper(mView.getThreadedRenderer()), - new FrameMetricsWrapper())); + new FrameMetricsWrapper(), /*traceThresholdMissedFrames=*/ 1, + /*traceThresholdFrameTimeMillis=*/ -1)); doReturn(tracker).when(monitor).createFrameTracker(any()); // Simulate a trace session and see if begin / end are invoked. @@ -104,6 +106,21 @@ public class InteractionJankMonitorTest { } @Test + public void testDisabledThroughDeviceConfig() { + InteractionJankMonitor monitor = new InteractionJankMonitor(mWorker); + monitor.init(mView); + + HashMap<String, String> propertiesValues = new HashMap<>(); + propertiesValues.put("enabled", "false"); + DeviceConfig.Properties properties = new DeviceConfig.Properties( + DeviceConfig.NAMESPACE_INTERACTION_JANK_MONITOR, propertiesValues); + monitor.getPropertiesChangedListener().onPropertiesChanged(properties); + + assertThat(monitor.begin(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse(); + assertThat(monitor.end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse(); + } + + @Test public void testCheckInitState() { InteractionJankMonitor monitor = new InteractionJankMonitor(mWorker); @@ -134,12 +151,14 @@ public class InteractionJankMonitorTest { Session session = new Session(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE); FrameTracker tracker = spy(new FrameTracker(session, mWorker.getThreadHandler(), new ThreadedRendererWrapper(mView.getThreadedRenderer()), - new FrameMetricsWrapper())); + new FrameMetricsWrapper(), /*traceThresholdMissedFrames=*/ 1, + /*traceThresholdFrameTimeMillis=*/ -1)); doReturn(tracker).when(monitor).createFrameTracker(any()); assertThat(monitor.begin(session.getCuj())).isTrue(); verify(tracker).begin(); - verify(mWorker.getThreadHandler()).sendMessageAtTime(captor.capture(), anyLong()); + verify(mWorker.getThreadHandler(), atLeastOnce()).sendMessageAtTime(captor.capture(), + anyLong()); Runnable runnable = captor.getValue().getCallback(); assertThat(runnable).isNotNull(); mWorker.getThreadHandler().removeCallbacks(runnable); diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java index 75dd7fb82f30..ff1d965c643f 100644 --- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java +++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java @@ -192,8 +192,8 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl { } @Override - public Future<?> scheduleCpuSyncDueToScreenStateChange( - boolean onBattery, boolean onBatteryScreenOff) { + public Future<?> scheduleSyncDueToScreenStateChange( + int flag, boolean onBattery, boolean onBatteryScreenOff, int screenState) { return null; } diff --git a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java new file mode 100644 index 000000000000..88295efa90b9 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java @@ -0,0 +1,278 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.power; + +import static com.android.internal.power.MeasuredEnergyArray.NUMBER_SUBSYSTEMS; +import static com.android.internal.power.MeasuredEnergyArray.SUBSYSTEM_DISPLAY; +import static com.android.internal.power.MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_DOZE; +import static com.android.internal.power.MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON; +import static com.android.internal.power.MeasuredEnergyStats.NUMBER_ENERGY_BUCKETS; + +import static org.junit.Assert.assertEquals; + +import android.os.Parcel; +import android.view.Display; + +import org.junit.Before; +import org.junit.Test; + +/** + * Test class for {@link MeasuredEnergyStats}. + * + * To run the tests, use + * atest FrameworksCoreTests:com.android.internal.power.MeasuredEnergyStatsTest + */ +public class MeasuredEnergyStatsTest { + private MeasuredEnergyStats mStats; + private int[] mAllSubsystems = new int[NUMBER_SUBSYSTEMS]; + private long[] mCurrentSubsystemEnergyUJ = new long[NUMBER_SUBSYSTEMS]; + + MeasuredEnergyArray mMeasuredEnergyArray = new MeasuredEnergyArray() { + @Override + public int getSubsystem(int index) { + return mAllSubsystems[index]; + } + + @Override + public long getEnergy(int index) { + return mCurrentSubsystemEnergyUJ[index]; + } + + @Override + public int size() { + return NUMBER_SUBSYSTEMS; + } + }; + + @Before + public void setUp() { + // Populate all supported subsystems indexes and arbitrary starting energy values. + mAllSubsystems[SUBSYSTEM_DISPLAY] = SUBSYSTEM_DISPLAY; + mCurrentSubsystemEnergyUJ[SUBSYSTEM_DISPLAY] = 111; + + mStats = new MeasuredEnergyStats(mMeasuredEnergyArray, Display.STATE_UNKNOWN); + } + + @Test + public void testReadWriteParcel() { + // update with some arbitrary data + mCurrentSubsystemEnergyUJ[SUBSYSTEM_DISPLAY] += 222; + mStats.update(mMeasuredEnergyArray, Display.STATE_ON, true); + mCurrentSubsystemEnergyUJ[SUBSYSTEM_DISPLAY] += 321; + mStats.update(mMeasuredEnergyArray, Display.STATE_DOZE, true); + mCurrentSubsystemEnergyUJ[SUBSYSTEM_DISPLAY] += 456; + mStats.update(mMeasuredEnergyArray, Display.STATE_OFF, true); + + final Parcel parcel = Parcel.obtain(); + mStats.writeToParcel(parcel); + + parcel.setDataPosition(0); + MeasuredEnergyStats stats = new MeasuredEnergyStats(parcel); + + for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) { + assertEquals(mStats.getAccumulatedBucketEnergy(i), stats.getAccumulatedBucketEnergy(i)); + } + parcel.recycle(); + } + + @Test + public void testReadWriteSummaryParcel() { + // update with some arbitrary data + mCurrentSubsystemEnergyUJ[SUBSYSTEM_DISPLAY] += 222; + mStats.update(mMeasuredEnergyArray, Display.STATE_ON, true); + mCurrentSubsystemEnergyUJ[SUBSYSTEM_DISPLAY] += 321; + mStats.update(mMeasuredEnergyArray, Display.STATE_DOZE, true); + mCurrentSubsystemEnergyUJ[SUBSYSTEM_DISPLAY] += 456; + mStats.update(mMeasuredEnergyArray, Display.STATE_OFF, true); + + final Parcel parcel = Parcel.obtain(); + MeasuredEnergyStats.writeSummaryToParcel(mStats, parcel); + + parcel.setDataPosition(0); + MeasuredEnergyStats stats = new MeasuredEnergyStats(mMeasuredEnergyArray, + Display.STATE_UNKNOWN); + MeasuredEnergyStats.readSummaryFromParcel(stats, parcel); + + for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) { + assertEquals(mStats.getAccumulatedBucketEnergy(i), stats.getAccumulatedBucketEnergy(i)); + } + parcel.recycle(); + } + + @Test + public void testDisplayStateEnergyAttribution() { + long expectedScreenOnEnergy = 0; + long expectedScreenDozeEnergy = 0; + + // Display energy should be attributed to the previous screen state. + mStats.update(mMeasuredEnergyArray, Display.STATE_UNKNOWN, true); + + incrementDisplayState(222, Display.STATE_ON, true, expectedScreenOnEnergy, + expectedScreenDozeEnergy); + + expectedScreenOnEnergy += 321; + incrementDisplayState(321, Display.STATE_DOZE, true, expectedScreenOnEnergy, + expectedScreenDozeEnergy); + + expectedScreenDozeEnergy += 456; + incrementDisplayState(456, Display.STATE_OFF, true, expectedScreenOnEnergy, + expectedScreenDozeEnergy); + + incrementDisplayState(1111, Display.STATE_DOZE_SUSPEND, true, expectedScreenOnEnergy, + expectedScreenDozeEnergy); + + expectedScreenDozeEnergy += 2345; + incrementDisplayState(2345, Display.STATE_ON_SUSPEND, true, expectedScreenOnEnergy, + expectedScreenDozeEnergy); + + expectedScreenOnEnergy += 767; + incrementDisplayState(767, Display.STATE_VR, true, expectedScreenOnEnergy, + expectedScreenDozeEnergy); + + expectedScreenOnEnergy += 999; + incrementDisplayState(999, Display.STATE_UNKNOWN, true, expectedScreenOnEnergy, + expectedScreenDozeEnergy); + } + + @Test + public void testDisplayStateEnergyAttribution_notRunning() { + long expectedScreenOnEnergy = 0; + long expectedScreenDozeEnergy = 0; + + // Display energy should be attributed to the previous screen state. + mStats.update(mMeasuredEnergyArray, Display.STATE_UNKNOWN, true); + + incrementDisplayState(222, Display.STATE_ON, true, expectedScreenOnEnergy, + expectedScreenDozeEnergy); + + expectedScreenOnEnergy += 321; + incrementDisplayState(321, Display.STATE_DOZE, true, expectedScreenOnEnergy, + expectedScreenDozeEnergy); + + // Updates after this point should not result in energy accumulation. + incrementDisplayState(456, Display.STATE_OFF, false, expectedScreenOnEnergy, + expectedScreenDozeEnergy); + + incrementDisplayState(1111, Display.STATE_DOZE_SUSPEND, false, expectedScreenOnEnergy, + expectedScreenDozeEnergy); + + incrementDisplayState(2345, Display.STATE_ON_SUSPEND, false, expectedScreenOnEnergy, + expectedScreenDozeEnergy); + + incrementDisplayState(767, Display.STATE_VR, false, expectedScreenOnEnergy, + expectedScreenDozeEnergy); + + // Resume energy accumulation. + expectedScreenOnEnergy += 999; + incrementDisplayState(999, Display.STATE_UNKNOWN, true, expectedScreenOnEnergy, + expectedScreenDozeEnergy); + } + + @Test + public void testReset() { + // update with some arbitrary data. + mCurrentSubsystemEnergyUJ[SUBSYSTEM_DISPLAY] += 222; + mStats.update(mMeasuredEnergyArray, Display.STATE_ON, true); + mCurrentSubsystemEnergyUJ[SUBSYSTEM_DISPLAY] += 321; + mStats.update(mMeasuredEnergyArray, Display.STATE_DOZE, true); + mCurrentSubsystemEnergyUJ[SUBSYSTEM_DISPLAY] += 456; + mStats.update(mMeasuredEnergyArray, Display.STATE_OFF, true); + + mStats.reset(); + // All energy should be reset to 0 + for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) { + assertEquals(mStats.getAccumulatedBucketEnergy(i), 0); + } + + // Increment all subsystem energy by some arbitrary amount and update + for (int i = 0; i < NUMBER_SUBSYSTEMS; i++) { + mCurrentSubsystemEnergyUJ[i] += 100 * i; + } + mStats.update(mMeasuredEnergyArray, Display.STATE_OFF, true); + + // All energy should still be 0 after the first post-reset update. + for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) { + assertEquals(mStats.getAccumulatedBucketEnergy(i), 0); + } + + // Energy accumulation should continue like normal. + long expectedScreenOnEnergy = 0; + long expectedScreenDozeEnergy = 0; + incrementDisplayState(222, Display.STATE_ON, true, expectedScreenOnEnergy, + expectedScreenDozeEnergy); + + expectedScreenOnEnergy += 321; + incrementDisplayState(321, Display.STATE_DOZE, true, expectedScreenOnEnergy, + expectedScreenDozeEnergy); + + expectedScreenDozeEnergy += 456; + incrementDisplayState(456, Display.STATE_OFF, true, expectedScreenOnEnergy, + expectedScreenDozeEnergy); + } + + @Test + public void testHasSubsystem() { + for (int i = 0; i < NUMBER_SUBSYSTEMS; i++) { + assertEquals(mStats.hasSubsystem(i), true); + } + } + + @Test + public void testHasSubsystem_unavailable() { + // Setup MeasuredEnergyStats with not available subsystems. + int[] subsystems = new int[0]; + long[] energies = new long[0]; + MeasuredEnergyArray measuredEnergyArray = new MeasuredEnergyArray() { + @Override + public int getSubsystem(int index) { + return subsystems[index]; + } + + @Override + public long getEnergy(int index) { + return energies[index]; + } + + @Override + public int size() { + return 0; + } + }; + MeasuredEnergyStats stats = new MeasuredEnergyStats(measuredEnergyArray, + Display.STATE_UNKNOWN); + + for (int i = 0; i < NUMBER_SUBSYSTEMS; i++) { + assertEquals(stats.hasSubsystem(i), false); + } + + stats.reset(); + // a reset should not change the state of an unavailable subsystem. + for (int i = 0; i < NUMBER_SUBSYSTEMS; i++) { + assertEquals(stats.hasSubsystem(i), false); + } + } + + private void incrementDisplayState(long deltaEnergy, int nextState, boolean accumulate, + long expectScreenEnergy, long expectedDozeEnergy) { + mCurrentSubsystemEnergyUJ[SUBSYSTEM_DISPLAY] += deltaEnergy; + mStats.update(mMeasuredEnergyArray, nextState, accumulate); + assertEquals(expectScreenEnergy, + mStats.getAccumulatedBucketEnergy(ENERGY_BUCKET_SCREEN_ON)); + assertEquals(expectedDozeEnergy, + mStats.getAccumulatedBucketEnergy(ENERGY_BUCKET_SCREEN_DOZE)); + } +} diff --git a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java index 4094f836ff5a..306388f30e20 100644 --- a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java +++ b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java @@ -191,6 +191,10 @@ public class HdmiAudioSystemClientTest { } @Override + public void toggleAndFollowTvPower() { + } + + @Override public void queryDisplayStatus(final IHdmiControlCallback callback) { } diff --git a/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsData.java b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsData.java index 099ee22b12e4..7aeb86ae0cb1 100644 --- a/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsData.java +++ b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsData.java @@ -17,6 +17,7 @@ package com.android.frameworks.core.powerstatsviewer; import android.content.Context; +import android.os.BatteryStats; import android.os.Process; import com.android.internal.os.BatterySipper; @@ -32,6 +33,15 @@ public class PowerStatsData { private static final String[] PACKAGES_SYSTEM = {PACKAGE_MEDIA_PROVIDER, PACKAGE_CALENDAR_PROVIDER, PACKAGE_SYSTEMUI}; + // Temporary placeholder voltage for converting energy to charge + // TODO: remove this when b/173765509 is resolved + private static final double MOCK_NOMINAL_VOLTAGE = 3.7; + + // Unit conversion: + // mAh = uWs * (1/1000)(milli/micro) * (1/Voltage) * (1/3600)(hours/second) + private static final double UJ_2_MAH = + (1.0 / 1000) * (1.0 / MOCK_NOMINAL_VOLTAGE) * (1.0 / 3600); + enum EntryType { POWER, DURATION, @@ -50,6 +60,7 @@ public class PowerStatsData { public PowerStatsData(Context context, BatteryStatsHelper batteryStatsHelper, String powerConsumerId) { List<BatterySipper> usageList = batteryStatsHelper.getUsageList(); + BatteryStats batteryStats = batteryStatsHelper.getStats(); double totalPowerMah = 0; double totalSmearedPowerMah = 0; @@ -125,6 +136,8 @@ public class PowerStatsData { totalVideoTimeMs += sipper.videoTimeMs; } + long totalScreenMeasuredEnergyUJ = batteryStats.getScreenOnEnergy(); + if (requestedPowerConsumer == null) { mPowerConsumerInfo = null; return; @@ -135,10 +148,18 @@ public class PowerStatsData { addEntry("Total power", EntryType.POWER, requestedPowerConsumer.totalSmearedPowerMah, totalSmearedPowerMah); + maybeAddMeasuredEnergyEntry(requestedPowerConsumer.drainType, batteryStats); + addEntry("... excluding system", EntryType.POWER, requestedPowerConsumer.totalSmearedPowerMah, totalPowerExcludeSystemMah); addEntry("Screen, smeared", EntryType.POWER, requestedPowerConsumer.screenPowerMah, totalScreenPower); + if (totalScreenMeasuredEnergyUJ != BatteryStats.ENERGY_DATA_UNAVAILABLE) { + final double measuredCharge = UJ_2_MAH * totalScreenMeasuredEnergyUJ; + final double ratio = measuredCharge / totalScreenPower; + addEntry("Screen, smeared (PowerStatsHal adjusted)", EntryType.POWER, + requestedPowerConsumer.screenPowerMah * ratio, measuredCharge); + } addEntry("Other, smeared", EntryType.POWER, requestedPowerConsumer.proportionalSmearMah, totalProportionalSmearMah); addEntry("Excluding smeared", EntryType.POWER, @@ -218,6 +239,28 @@ public class PowerStatsData { mEntries.add(entry); } + private void maybeAddMeasuredEnergyEntry(BatterySipper.DrainType drainType, + BatteryStats batteryStats) { + switch (drainType) { + case AMBIENT_DISPLAY: + final long totalDozeMeasuredEnergyUJ = batteryStats.getScreenDozeEnergy(); + if (totalDozeMeasuredEnergyUJ != BatteryStats.ENERGY_DATA_UNAVAILABLE) { + final double measuredCharge = UJ_2_MAH * totalDozeMeasuredEnergyUJ; + addEntry("Measured ambient display power", EntryType.POWER, measuredCharge, + measuredCharge); + } + break; + case SCREEN: + final long totalScreenMeasuredEnergyUJ = batteryStats.getScreenOnEnergy(); + if (totalScreenMeasuredEnergyUJ != BatteryStats.ENERGY_DATA_UNAVAILABLE) { + final double measuredCharge = UJ_2_MAH * totalScreenMeasuredEnergyUJ; + addEntry("Measured screen power", EntryType.POWER, measuredCharge, + measuredCharge); + } + break; + } + } + public PowerConsumerInfoHelper.PowerConsumerInfo getPowerConsumerInfo() { return mPowerConsumerInfo; } diff --git a/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsViewerActivity.java b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsViewerActivity.java index 78f2b9135d0c..05679101f86a 100644 --- a/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsViewerActivity.java +++ b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsViewerActivity.java @@ -29,6 +29,7 @@ import android.widget.ImageView; import android.widget.TextView; import androidx.activity.ComponentActivity; +import androidx.activity.result.ActivityResultLauncher; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.loader.app.LoaderManager; @@ -60,6 +61,8 @@ public class PowerStatsViewerActivity extends ComponentActivity { private RecyclerView mPowerStatsDataView; private View mLoadingView; private View mEmptyView; + private ActivityResultLauncher<Void> mStartAppPicker = registerForActivityResult( + PowerConsumerPickerActivity.CONTRACT, this::onApplicationSelected); @Override protected void onCreate(@Nullable Bundle savedInstanceState) { @@ -105,8 +108,7 @@ public class PowerStatsViewerActivity extends ComponentActivity { } private void startAppPicker() { - registerForActivityResult(PowerConsumerPickerActivity.CONTRACT, this::onApplicationSelected) - .launch(null); + mStartAppPicker.launch(null); } private void onApplicationSelected(String powerConsumerId) { diff --git a/core/tests/uwbtests/src/android/uwb/SessionHandleTest.java b/core/tests/uwbtests/src/android/uwb/SessionHandleTest.java new file mode 100644 index 000000000000..8b42ff7f62a7 --- /dev/null +++ b/core/tests/uwbtests/src/android/uwb/SessionHandleTest.java @@ -0,0 +1,52 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.uwb; + +import static org.junit.Assert.assertEquals; + +import android.os.Parcel; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Test of {@link SessionHandle}. + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class SessionHandleTest { + + @Test + public void testBasic() { + int handleId = 12; + SessionHandle handle = new SessionHandle(handleId); + assertEquals(handle.getId(), handleId); + } + + @Test + public void testParcel() { + Parcel parcel = Parcel.obtain(); + SessionHandle handle = new SessionHandle(10); + handle.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + SessionHandle fromParcel = SessionHandle.CREATOR.createFromParcel(parcel); + assertEquals(handle, fromParcel); + } +} diff --git a/data/etc/car/Android.bp b/data/etc/car/Android.bp index 745de84a3f04..3e3aefc6f51d 100644 --- a/data/etc/car/Android.bp +++ b/data/etc/car/Android.bp @@ -156,4 +156,11 @@ prebuilt_etc { sub_dir: "permissions", src: "com.android.car.provision.xml", filename_from_src: true, -}
\ No newline at end of file +} + +prebuilt_etc { + name: "allowed_privapp_com.android.carshell", + sub_dir: "permissions", + src: "com.android.car.shell.xml", + filename_from_src: true, +} diff --git a/data/etc/car/com.android.car.shell.xml b/data/etc/car/com.android.car.shell.xml new file mode 100644 index 000000000000..32666c8d9f68 --- /dev/null +++ b/data/etc/car/com.android.car.shell.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<permissions> + <privapp-permissions package="com.android.car.shell"> + <permission name="android.permission.INSTALL_PACKAGES" /> + <permission name="android.permission.MEDIA_CONTENT_CONTROL"/> + </privapp-permissions> +</permissions> diff --git a/data/etc/com.android.provision.xml b/data/etc/com.android.provision.xml index 05404ef73732..d2ea0ec085d3 100644 --- a/data/etc/com.android.provision.xml +++ b/data/etc/com.android.provision.xml @@ -17,5 +17,7 @@ <permissions> <privapp-permissions package="com.android.provision"> <permission name="android.permission.WRITE_SECURE_SETTINGS"/> + <permissionn ame="android.permission.DISPATCH_PROVISIONING_MESSAGE"/> + <permission name="android.permission.MASTER_CLEAR"/> </privapp-permissions> </permissions> diff --git a/data/etc/com.android.settings.xml b/data/etc/com.android.settings.xml index fe1182ecad4f..e473c55ce010 100644 --- a/data/etc/com.android.settings.xml +++ b/data/etc/com.android.settings.xml @@ -56,5 +56,7 @@ <permission name="android.permission.WRITE_SECURE_SETTINGS"/> <permission name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" /> <permission name="android.permission.INSTALL_DYNAMIC_SYSTEM"/> + <permission name="android.permission.READ_DREAM_STATE"/> + <permission name="android.permission.READ_DREAM_SUPPRESSION"/> </privapp-permissions> </permissions> diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 52da707565d7..a52eca7e0d73 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -961,6 +961,12 @@ "group": "WM_DEBUG_WINDOW_ORGANIZER", "at": "com\/android\/server\/wm\/TaskOrganizerController.java" }, + "-948446688": { + "message": "Create TaskDisplayArea uid=%d", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_ORGANIZER", + "at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java" + }, "-937498525": { "message": "Executing finish of failed to pause activity: %s", "level": "VERBOSE", @@ -1273,6 +1279,12 @@ "group": "WM_DEBUG_STATES", "at": "com\/android\/server\/wm\/Task.java" }, + "-597091183": { + "message": "Delete TaskDisplayArea uid=%d", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_ORGANIZER", + "at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java" + }, "-593535526": { "message": "Binding proc %s with config %s", "level": "VERBOSE", @@ -1495,12 +1507,6 @@ "group": "WM_DEBUG_APP_TRANSITIONS", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, - "-371630969": { - "message": "New wallpaper target=%s, oldWallpaper=%s, openingApps=%s, closingApps=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/AppTransitionController.java" - }, "-354571697": { "message": "Existence Changed in transition %d: %s", "level": "VERBOSE", @@ -1555,6 +1561,12 @@ "group": "WM_DEBUG_WINDOW_TRANSITIONS", "at": "com\/android\/server\/wm\/Transition.java" }, + "-292790591": { + "message": "Attempted to set IME policy to a display that does not exist: %d", + "level": "WARN", + "group": "WM_ERROR", + "at": "com\/android\/server\/wm\/WindowManagerService.java" + }, "-279436615": { "message": "Moving to PAUSING: %s", "level": "VERBOSE", @@ -1813,12 +1825,6 @@ "group": "WM_DEBUG_TASKS", "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" }, - "91350919": { - "message": "Attempted to set IME flag to a display that does not exist: %d", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, "94402792": { "message": "Moving to RESUMED: %s (in existing)", "level": "VERBOSE", @@ -1831,12 +1837,6 @@ "group": "WM_DEBUG_IME", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, - "95281111": { - "message": "Attempted to get IME flag of a display that does not exist: %d", - "level": "WARN", - "group": "WM_ERROR", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, "95902367": { "message": "Relayout of %s: focusMayChange=%b", "level": "VERBOSE", @@ -2101,12 +2101,6 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, - "355940361": { - "message": "Config is destroying non-running %s", - "level": "VERBOSE", - "group": "WM_DEBUG_CONFIGURATION", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, "371173718": { "message": "finishSync cancel=%b for %s", "level": "VERBOSE", @@ -2749,6 +2743,12 @@ "group": "WM_SHOW_SURFACE_ALLOC", "at": "com\/android\/server\/wm\/ScreenRotationAnimation.java" }, + "1100065297": { + "message": "Attempted to get IME policy of a display that does not exist: %d", + "level": "WARN", + "group": "WM_ERROR", + "at": "com\/android\/server\/wm\/WindowManagerService.java" + }, "1112047265": { "message": "finishDrawingWindow: %s mDrawState=%s", "level": "DEBUG", diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java index a7f2739153e1..cb4dd9e8cacd 100644 --- a/graphics/java/android/graphics/HardwareRenderer.java +++ b/graphics/java/android/graphics/HardwareRenderer.java @@ -1163,7 +1163,7 @@ public class HardwareRenderer { // heuristic we don't need to be always 100% correct. Mode activeMode = display.getMode(); nInitDisplayInfo(activeMode.getPhysicalWidth(), activeMode.getPhysicalHeight(), - activeMode.getRefreshRate(), maxRefreshRate, + display.getRefreshRate(), maxRefreshRate, wideColorDataspace.mNativeDataspace, display.getAppVsyncOffsetNanos(), display.getPresentationDeadlineNanos()); diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index 5743df5216be..36ef0a48fa20 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -44,6 +44,7 @@ import android.text.FontConfig; import android.util.Base64; import android.util.LongSparseArray; import android.util.LruCache; +import android.util.Pair; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; @@ -169,6 +170,8 @@ public class Typeface { @UnsupportedAppUsage public long native_instance; + private Runnable mCleaner; + /** @hide */ @IntDef(value = {NORMAL, BOLD, ITALIC, BOLD_ITALIC}) @Retention(RetentionPolicy.SOURCE) @@ -808,18 +811,16 @@ public class Typeface { */ public @NonNull Typeface build() { final int userFallbackSize = mFamilies.size(); - final FontFamily[] fallback = SystemFonts.getSystemFallback(mFallbackName); - final long[] ptrArray = new long[fallback.length + userFallbackSize]; + final Typeface fallbackTypeface = getSystemDefaultTypeface(mFallbackName); + final long[] ptrArray = new long[userFallbackSize]; for (int i = 0; i < userFallbackSize; ++i) { ptrArray[i] = mFamilies.get(i).getNativePtr(); } - for (int i = 0; i < fallback.length; ++i) { - ptrArray[i + userFallbackSize] = fallback[i].getNativePtr(); - } final int weight = mStyle == null ? 400 : mStyle.getWeight(); final int italic = (mStyle == null || mStyle.getSlant() == FontStyle.FONT_SLANT_UPRIGHT) ? 0 : 1; - return new Typeface(nativeCreateFromArray(ptrArray, weight, italic)); + return new Typeface(nativeCreateFromArray( + ptrArray, fallbackTypeface.native_instance, weight, italic)); } } @@ -1056,7 +1057,8 @@ public class Typeface { ptrArray[i] = families[i].mNativePtr; } return new Typeface(nativeCreateFromArray( - ptrArray, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)); + ptrArray, 0, RESOLVE_BY_FONT_TABLE, + RESOLVE_BY_FONT_TABLE)); } /** @@ -1069,7 +1071,7 @@ public class Typeface { for (int i = 0; i < families.length; ++i) { ptrArray[i] = families[i].getNativePtr(); } - return new Typeface(nativeCreateFromArray(ptrArray, + return new Typeface(nativeCreateFromArray(ptrArray, 0, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)); } @@ -1104,15 +1106,13 @@ public class Typeface { @Deprecated private static Typeface createFromFamiliesWithDefault(android.graphics.FontFamily[] families, String fallbackName, int weight, int italic) { - android.graphics.fonts.FontFamily[] fallback = SystemFonts.getSystemFallback(fallbackName); - long[] ptrArray = new long[families.length + fallback.length]; + final Typeface fallbackTypeface = getSystemDefaultTypeface(fallbackName); + long[] ptrArray = new long[families.length]; for (int i = 0; i < families.length; i++) { ptrArray[i] = families[i].mNativePtr; } - for (int i = 0; i < fallback.length; i++) { - ptrArray[i + families.length] = fallback[i].getNativePtr(); - } - return new Typeface(nativeCreateFromArray(ptrArray, weight, italic)); + return new Typeface(nativeCreateFromArray( + ptrArray, fallbackTypeface.native_instance, weight, italic)); } // don't allow clients to call this directly @@ -1123,7 +1123,7 @@ public class Typeface { } native_instance = ni; - sRegistry.registerNativeAllocation(this, native_instance); + mCleaner = sRegistry.registerNativeAllocation(this, native_instance); mStyle = nativeGetStyle(ni); mWeight = nativeGetWeight(ni); } @@ -1237,6 +1237,13 @@ public class Typeface { bos.write(value & 0xFF); } + /** @hide */ + public static Map<String, Typeface> getSystemFontMap() { + synchronized (SYSTEM_FONT_MAP_LOCK) { + return sSystemFontMap; + } + } + /** * Deserialize font map and set it as system font map. This method should be called at most once * per process. @@ -1297,13 +1304,34 @@ public class Typeface { } } - static { + /** @hide */ + @VisibleForTesting + public static void destroySystemFontMap() { + synchronized (SYSTEM_FONT_MAP_LOCK) { + for (Typeface typeface : sSystemFontMap.values()) { + typeface.mCleaner.run(); + } + sSystemFontMap.clear(); + if (sSystemFontMapBuffer != null) { + SharedMemory.unmap(sSystemFontMapBuffer); + } + sSystemFontMapBuffer = null; + } + } + + /** @hide */ + public static void loadPreinstalledSystemFontMap() { final HashMap<String, Typeface> systemFontMap = new HashMap<>(); - initSystemDefaultTypefaces(systemFontMap, SystemFonts.getRawSystemFallbackMap(), - SystemFonts.getAliases()); + Pair<FontConfig.Alias[], Map<String, FontFamily[]>> pair = + SystemFonts.initializePreinstalledFonts(); + initSystemDefaultTypefaces(systemFontMap, pair.second, pair.first); setSystemFontMap(systemFontMap); } + static { + loadPreinstalledSystemFontMap(); + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -1350,7 +1378,8 @@ public class Typeface { @UnsupportedAppUsage private static native long nativeCreateWeightAlias(long native_instance, int weight); @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - private static native long nativeCreateFromArray(long[] familyArray, int weight, int italic); + private static native long nativeCreateFromArray( + long[] familyArray, long fallbackTypeface, int weight, int italic); private static native int[] nativeGetSupportedAxes(long native_instance); @CriticalNative diff --git a/graphics/java/android/graphics/drawable/TEST_MAPPING b/graphics/java/android/graphics/drawable/TEST_MAPPING new file mode 100644 index 000000000000..1018702e01c5 --- /dev/null +++ b/graphics/java/android/graphics/drawable/TEST_MAPPING @@ -0,0 +1,24 @@ +{ + "presubmit": [ + { + + "name": "CtsGraphicsTestCases", + "file_patterns": ["(/|^)Icon\\.java"], + "options" : [ + { + "include-filter": "android.graphics.drawable.cts.IconTest" + } + ] + }, + { + + "name": "FrameworksCoreTests", + "file_patterns": ["(/|^)Icon\\.java"], + "options" : [ + { + "include-filter": "android.graphics.drawable.IconTest" + } + ] + } + ] +} diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java index 95a8417b6f7f..3635adc3ce39 100644 --- a/graphics/java/android/graphics/fonts/SystemFonts.java +++ b/graphics/java/android/graphics/fonts/SystemFonts.java @@ -22,7 +22,9 @@ import android.graphics.FontListParser; import android.text.FontConfig; import android.util.ArrayMap; import android.util.Log; +import android.util.Pair; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; @@ -51,9 +53,9 @@ public final class SystemFonts { private SystemFonts() {} // Do not instansiate. - private static final Map<String, FontFamily[]> sSystemFallbackMap; - private static final FontConfig.Alias[] sAliases; - private static final List<Font> sAvailableFonts; + private static final Object LOCK = new Object(); + private static @GuardedBy("sLock") Set<Font> sAvailableFonts; + private static @GuardedBy("sLock") Map<String, FontFamily[]> sFamilyMap; /** * Returns all available font files in the system. @@ -61,42 +63,24 @@ public final class SystemFonts { * @return a set of system fonts */ public static @NonNull Set<Font> getAvailableFonts() { - HashSet<Font> set = new HashSet<>(); - set.addAll(sAvailableFonts); - return set; - } + synchronized (LOCK) { + if (sAvailableFonts != null) { + return sAvailableFonts; + } - /** - * Returns fallback list for the given family name. - * - * If no fallback found for the given family name, returns fallback for the default family. - * - * @param familyName family name, e.g. "serif" - * @hide - */ - public static @NonNull FontFamily[] getSystemFallback(@Nullable String familyName) { - final FontFamily[] families = sSystemFallbackMap.get(familyName); - return families == null ? sSystemFallbackMap.get(DEFAULT_FAMILY) : families; - } + Set<Font> set = new HashSet<>(); - /** - * Returns raw system fallback map. - * - * This method is intended to be used only by Typeface static initializer. - * @hide - */ - public static @NonNull Map<String, FontFamily[]> getRawSystemFallbackMap() { - return sSystemFallbackMap; - } + for (FontFamily[] items : sFamilyMap.values()) { + for (FontFamily family : items) { + for (int i = 0; i < family.getSize(); ++i) { + set.add(family.getFont(i)); + } + } + } - /** - * Returns a list of aliases. - * - * This method is intended to be used only by Typeface static initializer. - * @hide - */ - public static @NonNull FontConfig.Alias[] getAliases() { - return sAliases; + sAvailableFonts = Collections.unmodifiableSet(set); + return sAvailableFonts; + } } private static @Nullable ByteBuffer mmap(@NonNull String fullPath) { @@ -111,8 +95,7 @@ public final class SystemFonts { private static void pushFamilyToFallback(@NonNull FontConfig.Family xmlFamily, @NonNull ArrayMap<String, ArrayList<FontFamily>> fallbackMap, - @NonNull Map<String, ByteBuffer> cache, - @NonNull ArrayList<Font> availableFonts) { + @NonNull Map<String, ByteBuffer> cache) { final String languageTags = xmlFamily.getLanguages(); final int variant = xmlFamily.getVariant(); @@ -136,7 +119,7 @@ public final class SystemFonts { } final FontFamily defaultFamily = defaultFonts.isEmpty() ? null : createFontFamily( - xmlFamily.getName(), defaultFonts, languageTags, variant, cache, availableFonts); + xmlFamily.getName(), defaultFonts, languageTags, variant, cache); // Insert family into fallback map. for (int i = 0; i < fallbackMap.size(); i++) { @@ -148,8 +131,7 @@ public final class SystemFonts { } } else { final FontFamily family = createFontFamily( - xmlFamily.getName(), fallback, languageTags, variant, cache, - availableFonts); + xmlFamily.getName(), fallback, languageTags, variant, cache); if (family != null) { fallbackMap.valueAt(i).add(family); } else if (defaultFamily != null) { @@ -165,8 +147,7 @@ public final class SystemFonts { @NonNull List<FontConfig.Font> fonts, @NonNull String languageTags, @FontConfig.Family.Variant int variant, - @NonNull Map<String, ByteBuffer> cache, - @NonNull ArrayList<Font> availableFonts) { + @NonNull Map<String, ByteBuffer> cache) { if (fonts.size() == 0) { return null; } @@ -200,7 +181,6 @@ public final class SystemFonts { throw new RuntimeException(e); // Never reaches here } - availableFonts.add(font); if (b == null) { b = new FontFamily.Builder(font); } else { @@ -212,12 +192,11 @@ public final class SystemFonts { private static void appendNamedFamily(@NonNull FontConfig.Family xmlFamily, @NonNull HashMap<String, ByteBuffer> bufferCache, - @NonNull ArrayMap<String, ArrayList<FontFamily>> fallbackListMap, - @NonNull ArrayList<Font> availableFonts) { + @NonNull ArrayMap<String, ArrayList<FontFamily>> fallbackListMap) { final String familyName = xmlFamily.getName(); final FontFamily family = createFontFamily( familyName, Arrays.asList(xmlFamily.getFonts()), - xmlFamily.getLanguages(), xmlFamily.getVariant(), bufferCache, availableFonts); + xmlFamily.getLanguages(), xmlFamily.getVariant(), bufferCache); if (family == null) { return; } @@ -240,8 +219,7 @@ public final class SystemFonts { public static FontConfig.Alias[] buildSystemFallback(@NonNull String xmlPath, @NonNull String fontDir, @NonNull FontCustomizationParser.Result oemCustomization, - @NonNull ArrayMap<String, FontFamily[]> fallbackMap, - @NonNull ArrayList<Font> availableFonts) { + @NonNull Map<String, FontFamily[]> fallbackMap) { try { final FileInputStream fontsIn = new FileInputStream(xmlPath); final FontConfig fontConfig = FontListParser.parse(fontsIn, fontDir); @@ -256,12 +234,12 @@ public final class SystemFonts { if (familyName == null) { continue; } - appendNamedFamily(xmlFamily, bufferCache, fallbackListMap, availableFonts); + appendNamedFamily(xmlFamily, bufferCache, fallbackListMap); } for (int i = 0; i < oemCustomization.mAdditionalNamedFamilies.size(); ++i) { appendNamedFamily(oemCustomization.mAdditionalNamedFamilies.get(i), - bufferCache, fallbackListMap, availableFonts); + bufferCache, fallbackListMap); } // Then, add fallback fonts to the each fallback map. @@ -270,7 +248,7 @@ public final class SystemFonts { // The first family (usually the sans-serif family) is always placed immediately // after the primary family in the fallback. if (i == 0 || xmlFamily.getName() == null) { - pushFamilyToFallback(xmlFamily, fallbackListMap, bufferCache, availableFonts); + pushFamilyToFallback(xmlFamily, fallbackListMap, bufferCache); } } @@ -305,14 +283,17 @@ public final class SystemFonts { } } - static { - final ArrayMap<String, FontFamily[]> systemFallbackMap = new ArrayMap<>(); - final ArrayList<Font> availableFonts = new ArrayList<>(); + /** @hide */ + public static @NonNull Pair<FontConfig.Alias[], Map<String, FontFamily[]>> + initializePreinstalledFonts() { final FontCustomizationParser.Result oemCustomization = readFontCustomization("/product/etc/fonts_customization.xml", "/product/fonts/"); - sAliases = buildSystemFallback("/system/etc/fonts.xml", "/system/fonts/", - oemCustomization, systemFallbackMap, availableFonts); - sSystemFallbackMap = Collections.unmodifiableMap(systemFallbackMap); - sAvailableFonts = Collections.unmodifiableList(availableFonts); + Map<String, FontFamily[]> map = new ArrayMap<>(); + FontConfig.Alias[] aliases = buildSystemFallback("/system/etc/fonts.xml", "/system/fonts/", + oemCustomization, map); + synchronized (LOCK) { + sFamilyMap = map; + } + return new Pair(aliases, map); } } diff --git a/keystore/java/android/security/AppUriAuthenticationPolicy.aidl b/keystore/java/android/security/AppUriAuthenticationPolicy.aidl new file mode 100644 index 000000000000..5c52c86f0426 --- /dev/null +++ b/keystore/java/android/security/AppUriAuthenticationPolicy.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security; + +parcelable AppUriAuthenticationPolicy; diff --git a/keystore/java/android/security/AppUriAuthenticationPolicy.java b/keystore/java/android/security/AppUriAuthenticationPolicy.java new file mode 100644 index 000000000000..30f5a94ca0c8 --- /dev/null +++ b/keystore/java/android/security/AppUriAuthenticationPolicy.java @@ -0,0 +1,226 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.Uri; +import android.os.Parcel; +import android.os.Parcelable; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * The app-URI authentication policy is set by the credential management app. This policy determines + * which alias for a private key and certificate pair should be used for authentication. + * <p> + * The authentication policy should be added as a parameter when calling + * {@link KeyChain#createManageCredentialsIntent}. + * <p> + * Example: + * <pre>{@code + * AppUriAuthenticationPolicy authenticationPolicy = new AppUriAuthenticationPolicy.Builder() + * .addAppAndUriMapping("com.test.pkg", testUri, "testAlias") + * .addAppAndUriMapping("com.test2.pkg", testUri1, "testAlias2") + * .addAppAndUriMapping("com.test2.pkg", testUri2, "testAlias2") + * .build(); + * Intent requestIntent = KeyChain.createManageCredentialsIntent(authenticationPolicy); + * }</pre> + * <p> + */ +public final class AppUriAuthenticationPolicy implements Parcelable { + + private static final String KEY_AUTHENTICATION_POLICY_APP_TO_URIS = + "authentication_policy_app_to_uris"; + private static final String KEY_AUTHENTICATION_POLICY_APP = "policy_app"; + + /** + * The mappings from an app and list of URIs to a list of aliases, which will be used for + * authentication. + * <p> + * appPackageName -> uri -> alias + */ + @NonNull + private final Map<String, UrisToAliases> mAppToUris; + + private AppUriAuthenticationPolicy(@NonNull Map<String, UrisToAliases> appToUris) { + Objects.requireNonNull(appToUris); + this.mAppToUris = appToUris; + } + + /** + * Builder class for {@link AppUriAuthenticationPolicy} objects. + */ + public static final class Builder { + private Map<String, UrisToAliases> mPackageNameToUris; + + /** + * Initialize a new Builder to construct an {@link AppUriAuthenticationPolicy}. + */ + public Builder() { + mPackageNameToUris = new HashMap<>(); + } + + /** + * Adds mappings from an app and URI to an alias, which will be used for authentication. + * <p> + * If this method is called with a package name and URI that was previously added, the + * previous alias will be overwritten. + * + * @param appPackageName The app's package name to authenticate the user to. + * @param uri The URI to authenticate the user to. + * @param alias The alias which will be used for authentication. + * + * @return the same Builder instance. + */ + @NonNull + public Builder addAppAndUriMapping(@NonNull String appPackageName, @NonNull Uri uri, + @NonNull String alias) { + Objects.requireNonNull(appPackageName); + Objects.requireNonNull(uri); + Objects.requireNonNull(alias); + UrisToAliases urisToAliases = + mPackageNameToUris.getOrDefault(appPackageName, new UrisToAliases()); + urisToAliases.addUriToAlias(uri, alias); + mPackageNameToUris.put(appPackageName, urisToAliases); + return this; + } + + /** + * Adds mappings from an app and list of URIs to a list of aliases, which will be used for + * authentication. + * <p> + * appPackageName -> uri -> alias + * + * @hide + */ + @NonNull + public Builder addAppAndUriMapping(@NonNull String appPackageName, + @NonNull UrisToAliases urisToAliases) { + Objects.requireNonNull(appPackageName); + Objects.requireNonNull(urisToAliases); + mPackageNameToUris.put(appPackageName, urisToAliases); + return this; + } + + /** + * Combines all of the attributes that have been set on the {@link Builder} + * + * @return a new {@link AppUriAuthenticationPolicy} object. + */ + @NonNull + public AppUriAuthenticationPolicy build() { + return new AppUriAuthenticationPolicy(mPackageNameToUris); + } + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeMap(mAppToUris); + } + + @NonNull + public static final Parcelable.Creator<AppUriAuthenticationPolicy> CREATOR = + new Parcelable.Creator<AppUriAuthenticationPolicy>() { + @Override + public AppUriAuthenticationPolicy createFromParcel(Parcel in) { + Map<String, UrisToAliases> appToUris = new HashMap<>(); + in.readMap(appToUris, UrisToAliases.class.getClassLoader()); + return new AppUriAuthenticationPolicy(appToUris); + } + + @Override + public AppUriAuthenticationPolicy[] newArray(int size) { + return new AppUriAuthenticationPolicy[size]; + } + }; + + @Override + public String toString() { + return "AppUriAuthenticationPolicy{" + + "mPackageNameToUris=" + mAppToUris + + '}'; + } + + /** + * Return the authentication policy mapping, which determines which alias for a private key + * and certificate pair should be used for authentication. + * <p> + * appPackageName -> uri -> alias + */ + @NonNull + public Map<String, Map<Uri, String>> getAppAndUriMappings() { + Map<String, Map<Uri, String>> appAndUris = new HashMap<>(); + for (Map.Entry<String, UrisToAliases> entry : mAppToUris.entrySet()) { + appAndUris.put(entry.getKey(), entry.getValue().getUrisToAliases()); + } + return appAndUris; + } + + /** + * Restore a previously saved {@link AppUriAuthenticationPolicy} from XML. + * + * @hide + */ + @Nullable + public static AppUriAuthenticationPolicy readFromXml(@NonNull XmlPullParser parser) + throws IOException, XmlPullParserException { + AppUriAuthenticationPolicy.Builder builder = new AppUriAuthenticationPolicy.Builder(); + int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + if (!parser.getName().equals(KEY_AUTHENTICATION_POLICY_APP_TO_URIS)) { + continue; + } + String app = parser.getAttributeValue(null, KEY_AUTHENTICATION_POLICY_APP); + UrisToAliases urisToAliases = UrisToAliases.readFromXml(parser); + builder.addAppAndUriMapping(app, urisToAliases); + } + return builder.build(); + } + + /** + * Save the {@link AppUriAuthenticationPolicy} to XML. + * + * @hide + */ + public void writeToXml(@NonNull XmlSerializer out) throws IOException { + for (Map.Entry<String, UrisToAliases> appsToUris : mAppToUris.entrySet()) { + out.startTag(null, KEY_AUTHENTICATION_POLICY_APP_TO_URIS); + out.attribute(null, KEY_AUTHENTICATION_POLICY_APP, appsToUris.getKey()); + appsToUris.getValue().writeToXml(out); + out.endTag(null, KEY_AUTHENTICATION_POLICY_APP_TO_URIS); + } + } + +} diff --git a/keystore/java/android/security/CredentialManagementApp.java b/keystore/java/android/security/CredentialManagementApp.java new file mode 100644 index 000000000000..cbb23015dbe8 --- /dev/null +++ b/keystore/java/android/security/CredentialManagementApp.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.util.Log; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import java.io.IOException; +import java.util.Objects; + +/** + * The credential management app has the ability to manage the user's KeyChain credentials on + * unmanaged devices. {@link KeyChain#createManageCredentialsIntent} should be used by an app to + * request to become the credential management app. The user must approve this request before the + * app can manage the user's credentials. + * <p> + * Note: there can only be one credential management on the device. If another app requests to + * become the credential management app and the user approves, then the existing credential + * management app will no longer be able to manage credentials. + * <p> + * The requesting credential management app should include its authentication policy in the + * requesting intent. The authentication policy declares which certificates should be used for a + * given list of apps and URIs. + * + * @hide + * @see AppUriAuthenticationPolicy + */ +public class CredentialManagementApp { + + private static final String TAG = "CredentialManagementApp"; + private static final String KEY_PACKAGE_NAME = "package_name"; + + /** + * The credential management app's package name + */ + @NonNull + private final String mPackageName; + + /** + * The mappings from an app and list of URIs to a list of aliases, which will be used for + * authentication. + * <p> + * appPackageName -> uri -> alias + */ + @NonNull + private AppUriAuthenticationPolicy mAuthenticationPolicy; + + public CredentialManagementApp(@NonNull String packageName, + @NonNull AppUriAuthenticationPolicy authenticationPolicy) { + Objects.requireNonNull(packageName); + Objects.requireNonNull(authenticationPolicy); + mPackageName = packageName; + mAuthenticationPolicy = authenticationPolicy; + } + + /** + * Returns the package name of the credential management app. + */ + @NonNull + public String getPackageName() { + return mPackageName; + } + + /** + * Returns the authentication policy of the credential management app. + */ + @NonNull + public AppUriAuthenticationPolicy getAuthenticationPolicy() { + return mAuthenticationPolicy; + } + + /** + * Sets the authentication policy of the credential management app. + */ + public void setAuthenticationPolicy(@Nullable AppUriAuthenticationPolicy authenticationPolicy) { + Objects.requireNonNull(authenticationPolicy); + mAuthenticationPolicy = authenticationPolicy; + } + + /** + * Restore a previously saved {@link CredentialManagementApp} from XML. + */ + @Nullable + public static CredentialManagementApp readFromXml(@NonNull XmlPullParser parser) { + try { + String packageName = parser.getAttributeValue(null, KEY_PACKAGE_NAME); + AppUriAuthenticationPolicy policy = AppUriAuthenticationPolicy.readFromXml(parser); + return new CredentialManagementApp(packageName, policy); + } catch (XmlPullParserException | IOException e) { + Log.w(TAG, "Reading from xml failed", e); + } + return null; + } + + /** + * Save the {@link CredentialManagementApp} to XML. + */ + public void writeToXml(@NonNull XmlSerializer out) throws IOException { + out.attribute(null, KEY_PACKAGE_NAME, mPackageName); + if (mAuthenticationPolicy != null) { + mAuthenticationPolicy.writeToXml(out); + } + } +} diff --git a/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java index 7abcfdc98bc6..f41b6081e38c 100644 --- a/keystore/java/android/security/Credentials.java +++ b/keystore/java/android/security/Credentials.java @@ -49,6 +49,8 @@ public class Credentials { public static final String INSTALL_AS_USER_ACTION = "android.credentials.INSTALL_AS_USER"; + public static final String ACTION_MANAGE_CREDENTIALS = "android.security.MANAGE_CREDENTIALS"; + /** * Key prefix for CA certificates. * diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java index a77aec2788af..c6e72b0e9f6e 100644 --- a/keystore/java/android/security/KeyChain.java +++ b/keystore/java/android/security/KeyChain.java @@ -15,6 +15,8 @@ */ package android.security; +import static android.security.Credentials.ACTION_MANAGE_CREDENTIALS; + import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SdkConstant; @@ -122,6 +124,11 @@ public final class KeyChain { private static final String CERT_INSTALLER_PACKAGE = "com.android.certinstaller"; /** + * Package name for Settings. + */ + private static final String SETTINGS_PACKAGE = "com.android.settings"; + + /** * Extra for use with {@link #ACTION_CHOOSER} * @hide Also used by KeyChainActivity implementation */ @@ -202,6 +209,20 @@ public final class KeyChain { public static final String EXTRA_PKCS12 = "PKCS12"; /** + * Extra used by {@link #createManageCredentialsIntent(AppUriAuthenticationPolicy)} to specify + * the authentication policy of the credential management app. + * + * <p>The authentication policy declares which alias for a private key and certificate pair + * should be used for authentication, given a list of apps and URIs. + * + * <p>The extra value should be a {@link AppUriAuthenticationPolicy}. + * + * @hide + */ + public static final String EXTRA_AUTHENTICATION_POLICY = + "android.security.extra.AUTHENTICATION_POLICY"; + + /** * Broadcast Action: Indicates the trusted storage has changed. Sent when * one of this happens: * @@ -386,6 +407,23 @@ public final class KeyChain { } /** + * Returns an {@code Intent} that should be used by an app to request to manage the user's + * credentials. This is limited to unmanaged devices. The authentication policy must be + * provided to be able to make this request successfully. + * + * @param policy The authentication policy determines which alias for a private key and + * certificate pair should be used for authentication. + */ + @NonNull + public static Intent createManageCredentialsIntent(@NonNull AppUriAuthenticationPolicy policy) { + Intent intent = new Intent(ACTION_MANAGE_CREDENTIALS); + intent.setComponent(ComponentName.createRelative(SETTINGS_PACKAGE, + ".security.RequestManageCredentials")); + intent.putExtra(EXTRA_AUTHENTICATION_POLICY, policy); + return intent; + } + + /** * Launches an {@code Activity} for the user to select the alias * for a private key and certificate pair for authentication. The * selected alias or null will be returned via the diff --git a/keystore/java/android/security/UrisToAliases.java b/keystore/java/android/security/UrisToAliases.java new file mode 100644 index 000000000000..65d433abe166 --- /dev/null +++ b/keystore/java/android/security/UrisToAliases.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.Uri; +import android.os.Parcel; +import android.os.Parcelable; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * The mapping from URI to alias, which determines the alias to use when the user visits a URI. + * This mapping is part of the {@link AppUriAuthenticationPolicy}, which specifies which app this + * mapping should be used for. + * + * @hide + * @see AppUriAuthenticationPolicy + */ +public final class UrisToAliases implements Parcelable { + + private static final String KEY_AUTHENTICATION_POLICY_URI_TO_ALIAS = + "authentication_policy_uri_to_alias"; + private static final String KEY_AUTHENTICATION_POLICY_URI = "policy_uri"; + private static final String KEY_AUTHENTICATION_POLICY_ALIAS = "policy_alias"; + + /** + * The mappings from URIs to aliases, which will be used for authentication. + */ + @NonNull + private final Map<Uri, String> mUrisToAliases; + + public UrisToAliases() { + this.mUrisToAliases = new HashMap<>(); + } + + private UrisToAliases(@NonNull Map<Uri, String> urisToAliases) { + this.mUrisToAliases = urisToAliases; + } + + @NonNull + public static final Creator<UrisToAliases> CREATOR = new Creator<UrisToAliases>() { + @Override + public UrisToAliases createFromParcel(Parcel in) { + Map<Uri, String> urisToAliases = new HashMap<>(); + in.readMap(urisToAliases, String.class.getClassLoader()); + return new UrisToAliases(urisToAliases); + } + + @Override + public UrisToAliases[] newArray(int size) { + return new UrisToAliases[size]; + } + }; + + /** + * Returns the mapping from URIs to aliases. + */ + @NonNull + public Map<Uri, String> getUrisToAliases() { + return Collections.unmodifiableMap(mUrisToAliases); + } + + /** + * Adds mapping from an URI to an alias. + */ + public void addUriToAlias(@NonNull Uri uri, @NonNull String alias) { + mUrisToAliases.put(uri, alias); + } + + /** + * Restore a previously saved {@link UrisToAliases} from XML. + */ + @Nullable + public static UrisToAliases readFromXml(@NonNull XmlPullParser parser) + throws IOException, XmlPullParserException { + Map<Uri, String> urisToAliases = new HashMap<>(); + int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + if (!parser.getName().equals(KEY_AUTHENTICATION_POLICY_URI_TO_ALIAS)) { + continue; + } + Uri uri = Uri.parse(parser.getAttributeValue(null, KEY_AUTHENTICATION_POLICY_URI)); + String alias = parser.getAttributeValue(null, KEY_AUTHENTICATION_POLICY_ALIAS); + urisToAliases.put(uri, alias); + } + return new UrisToAliases(urisToAliases); + } + + /** + * Save the {@link UrisToAliases} to XML. + */ + public void writeToXml(@NonNull XmlSerializer out) throws IOException { + for (Map.Entry<Uri, String> urisToAliases : mUrisToAliases.entrySet()) { + out.startTag(null, KEY_AUTHENTICATION_POLICY_URI_TO_ALIAS); + out.attribute(null, KEY_AUTHENTICATION_POLICY_URI, urisToAliases.getKey().toString()); + out.attribute(null, KEY_AUTHENTICATION_POLICY_ALIAS, urisToAliases.getValue()); + out.endTag(null, KEY_AUTHENTICATION_POLICY_URI_TO_ALIAS); + } + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeMap(mUrisToAliases); + } +} diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java index 3ac9d68d5a9f..b1b6306e0cf6 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java @@ -83,8 +83,7 @@ public class AndroidKeyStoreProvider extends Provider { boolean supports3DES = "true".equals(android.os.SystemProperties.get(DESEDE_SYSTEM_PROPERTY)); // java.security.KeyStore - put("KeyStore.AndroidKeyStore", PACKAGE_NAME + ".AndroidKeyStoreSpi"); - put("alg.alias.KeyStore.AndroidKeyStoreLegacy", "AndroidKeyStore"); + put("KeyStore." + providerName, PACKAGE_NAME + ".AndroidKeyStoreSpi"); // java.security.KeyPairGenerator put("KeyPairGenerator.EC", PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$EC"); diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml index 6c7faded7dad..7b5bda72ccd4 100644 --- a/libs/WindowManager/Shell/res/values-ar/strings.xml +++ b/libs/WindowManager/Shell/res/values-ar/strings.xml @@ -28,7 +28,7 @@ <string name="pip_skip_to_next" msgid="8403429188794867653">"التخطي إلى التالي"</string> <string name="pip_skip_to_prev" msgid="7172158111196394092">"التخطي إلى السابق"</string> <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"تغيير الحجم"</string> - <string name="dock_forced_resizable" msgid="1749750436092293116">"يمكن ألا يعمل التطبيق مع وضع تقسيم الشاشة."</string> + <string name="dock_forced_resizable" msgid="1749750436092293116">"قد لا يعمل التطبيق بشكل سليم في وضع \"تقسيم الشاشة\"."</string> <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"التطبيق لا يتيح تقسيم الشاشة."</string> <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"قد لا يعمل التطبيق على شاشة عرض ثانوية."</string> <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"لا يمكن تشغيل التطبيق على شاشات عرض ثانوية."</string> @@ -45,7 +45,7 @@ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"عرض النافذة السفلية بملء الشاشة"</string> <string name="one_handed_tutorial_title" msgid="4583241688067426350">"استخدام وضع \"التصفح بيد واحدة\""</string> <string name="one_handed_tutorial_description" msgid="3486582858591353067">"للخروج، مرِّر سريعًا من أسفل الشاشة إلى أعلاها أو انقر في أي مكان فوق التطبيق."</string> - <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"تفعيل وضع \"التصفح بيد واحدة\""</string> + <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"بدء وضع \"التصفح بيد واحدة\""</string> <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"الخروج من وضع \"التصفح بيد واحدة\""</string> <string name="bubbles_settings_button_description" msgid="1301286017420516912">"إعدادات فقاعات المحادثات على <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"القائمة الكاملة"</string> diff --git a/libs/WindowManager/Shell/res/values-be/strings_tv.xml b/libs/WindowManager/Shell/res/values-be/strings_tv.xml index 973ae8ede5b3..d33bf99e2ebd 100644 --- a/libs/WindowManager/Shell/res/values-be/strings_tv.xml +++ b/libs/WindowManager/Shell/res/values-be/strings_tv.xml @@ -20,5 +20,5 @@ <string name="notification_channel_tv_pip" msgid="2576686079160402435">"Відарыс у відарысе"</string> <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Праграма без назвы)"</string> <string name="pip_close" msgid="9135220303720555525">"Закрыць PIP"</string> - <string name="pip_fullscreen" msgid="7278047353591302554">"Ва ўвесь экран"</string> + <string name="pip_fullscreen" msgid="7278047353591302554">"Поўнаэкранны рэжым"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml index 4979a4015525..f04796aca753 100644 --- a/libs/WindowManager/Shell/res/values-de/strings.xml +++ b/libs/WindowManager/Shell/res/values-de/strings.xml @@ -28,8 +28,7 @@ <string name="pip_skip_to_next" msgid="8403429188794867653">"Vorwärts springen"</string> <string name="pip_skip_to_prev" msgid="7172158111196394092">"Rückwärts springen"</string> <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Größe anpassen"</string> - <!-- no translation found for dock_forced_resizable (1749750436092293116) --> - <skip /> + <string name="dock_forced_resizable" msgid="1749750436092293116">"Die App funktioniert unter Umständen bei geteiltem Bildschirmmodus nicht."</string> <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Das Teilen des Bildschirms wird in dieser App nicht unterstützt."</string> <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Die App funktioniert auf einem sekundären Display möglicherweise nicht."</string> <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Die App unterstützt den Start auf sekundären Displays nicht."</string> @@ -44,14 +43,10 @@ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"50 % oben"</string> <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"30 % oben"</string> <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Vollbild unten"</string> - <!-- no translation found for one_handed_tutorial_title (4583241688067426350) --> - <skip /> - <!-- no translation found for one_handed_tutorial_description (3486582858591353067) --> - <skip /> - <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) --> - <skip /> - <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) --> - <skip /> + <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Einhandmodus wird verwendet"</string> + <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Wenn du die App schließen möchtest, wische vom unteren Rand des Displays nach oben oder tippe auf eine beliebige Stelle oberhalb der App"</string> + <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Einhandmodus starten"</string> + <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Einhandmodus beenden"</string> <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Einstellungen für <xliff:g id="APP_NAME">%1$s</xliff:g>-Bubbles"</string> <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Mehr anzeigen"</string> <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Wieder dem Stapel hinzufügen"</string> @@ -68,8 +63,7 @@ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Neue Unterhaltungen erscheinen als unverankerte Symbole, \"Bubbles\" genannt. Wenn du die Bubble öffnen möchtest, tippe sie an. Wenn du sie verschieben möchtest, zieh an ihr."</string> <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Bubble-Einstellungen festlegen"</string> <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tippe auf \"Verwalten\", um Bubbles für diese App zu deaktivieren"</string> - <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) --> - <skip /> + <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Keine kürzlich geschlossenen Bubbles"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Hier werden aktuelle und geschlossene Bubbles angezeigt"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string> diff --git a/libs/WindowManager/Shell/res/values-de/strings_tv.xml b/libs/WindowManager/Shell/res/values-de/strings_tv.xml index 0432f1c353e4..02cce9d73647 100644 --- a/libs/WindowManager/Shell/res/values-de/strings_tv.xml +++ b/libs/WindowManager/Shell/res/values-de/strings_tv.xml @@ -17,8 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <!-- no translation found for notification_channel_tv_pip (2576686079160402435) --> - <skip /> + <string name="notification_channel_tv_pip" msgid="2576686079160402435">"Bild im Bild"</string> <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Kein Sendungsname gefunden)"</string> <string name="pip_close" msgid="9135220303720555525">"PIP schließen"</string> <string name="pip_fullscreen" msgid="7278047353591302554">"Vollbild"</string> diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml index dfa4b2287b10..e675861166a3 100644 --- a/libs/WindowManager/Shell/res/values-ml/strings.xml +++ b/libs/WindowManager/Shell/res/values-ml/strings.xml @@ -28,8 +28,7 @@ <string name="pip_skip_to_next" msgid="8403429188794867653">"അടുത്തതിലേക്ക് പോകുക"</string> <string name="pip_skip_to_prev" msgid="7172158111196394092">"മുമ്പത്തേതിലേക്ക് പോകുക"</string> <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"വലുപ്പം മാറ്റുക"</string> - <!-- no translation found for dock_forced_resizable (1749750436092293116) --> - <skip /> + <string name="dock_forced_resizable" msgid="1749750436092293116">"സ്ക്രീൻ വിഭജന മോഡിൽ ആപ്പ് പ്രവർത്തിച്ചേക്കില്ല."</string> <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"സ്പ്ലിറ്റ്-സ്ക്രീനിനെ ആപ്പ് പിന്തുണയ്ക്കുന്നില്ല."</string> <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"രണ്ടാം ഡിസ്പ്ലേയിൽ ആപ്പ് പ്രവർത്തിച്ചേക്കില്ല."</string> <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"രണ്ടാം ഡിസ്പ്ലേകളിൽ സമാരംഭിക്കുന്നതിനെ ആപ്പ് അനുവദിക്കുന്നില്ല."</string> @@ -44,14 +43,10 @@ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"മുകളിൽ 50%"</string> <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"മുകളിൽ 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"താഴെ പൂർണ്ണ സ്ക്രീൻ"</string> - <!-- no translation found for one_handed_tutorial_title (4583241688067426350) --> - <skip /> - <!-- no translation found for one_handed_tutorial_description (3486582858591353067) --> - <skip /> - <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) --> - <skip /> - <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) --> - <skip /> + <string name="one_handed_tutorial_title" msgid="4583241688067426350">"ഒറ്റക്കൈ മോഡ് എങ്ങനെ ഉപയോഗിക്കാം"</string> + <string name="one_handed_tutorial_description" msgid="3486582858591353067">"പുറത്ത് കടക്കാൻ, സ്ക്രീനിന്റെ ചുവടെ നിന്ന് മുകളിലേക്ക് സ്വൈപ്പ് ചെയ്യുക അല്ലെങ്കിൽ ആപ്പിന് മുകളിലായി എവിടെയെങ്കിലും ടാപ്പ് ചെയ്യുക"</string> + <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"ഒറ്റക്കൈ മോഡ് ആരംഭിച്ചു"</string> + <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"ഒറ്റക്കൈ മോഡിൽ നിന്ന് പുറത്തുകടക്കുക"</string> <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g> ബബിളുകളുടെ ക്രമീകരണം"</string> <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"ഓവർഫ്ലോ"</string> <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"അടുക്കുകളിലേക്ക് തിരിച്ച് ചേർക്കുക"</string> @@ -68,8 +63,7 @@ <string name="bubbles_user_education_description" msgid="4215862563054175407">"പുതിയ സംഭാഷണങ്ങൾ ഫ്ലോട്ടിംഗ് ഐക്കണുകളോ ബബിളുകളോ ആയി ദൃശ്യമാവുന്നു. ബബിൾ തുറക്കാൻ ടാപ്പ് ചെയ്യൂ. ഇത് നീക്കാൻ വലിച്ചിടുക."</string> <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"ബബിളുകൾ ഏതുസമയത്തും നിയന്ത്രിക്കുക"</string> <string name="bubbles_user_education_manage" msgid="3460756219946517198">"ഈ ആപ്പിൽ നിന്നുള്ള ബബിളുകൾ ഓഫാക്കാൻ മാനേജ് ചെയ്യുക ടാപ്പ് ചെയ്യുക"</string> - <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) --> - <skip /> + <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"മനസ്സിലായി"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"അടുത്തിടെയുള്ള ബബിളുകൾ ഒന്നുമില്ല"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"അടുത്തിടെയുള്ള ബബിളുകൾ, ഡിസ്മിസ് ചെയ്ത ബബിളുകൾ എന്നിവ ഇവിടെ ദൃശ്യമാവും"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"ബബ്ൾ"</string> diff --git a/libs/WindowManager/Shell/res/values-ml/strings_tv.xml b/libs/WindowManager/Shell/res/values-ml/strings_tv.xml index 7aaf79fb666e..c74e0bbfaa5b 100644 --- a/libs/WindowManager/Shell/res/values-ml/strings_tv.xml +++ b/libs/WindowManager/Shell/res/values-ml/strings_tv.xml @@ -17,8 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <!-- no translation found for notification_channel_tv_pip (2576686079160402435) --> - <skip /> + <string name="notification_channel_tv_pip" msgid="2576686079160402435">"ചിത്രത്തിനുള്ളിൽ ചിത്രം"</string> <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(പേരില്ലാത്ത പ്രോഗ്രാം)"</string> <string name="pip_close" msgid="9135220303720555525">"PIP അടയ്ക്കുക"</string> <string name="pip_fullscreen" msgid="7278047353591302554">"പൂര്ണ്ണ സ്ക്രീന്"</string> diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml index cd6529f0bf94..4b0adc640ddd 100644 --- a/libs/WindowManager/Shell/res/values-ur/strings.xml +++ b/libs/WindowManager/Shell/res/values-ur/strings.xml @@ -28,8 +28,7 @@ <string name="pip_skip_to_next" msgid="8403429188794867653">"نظرانداز کرکے اگلے پر جائیں"</string> <string name="pip_skip_to_prev" msgid="7172158111196394092">"نظرانداز کرکے پچھلے پر جائیں"</string> <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"سائز تبدیل کریں"</string> - <!-- no translation found for dock_forced_resizable (1749750436092293116) --> - <skip /> + <string name="dock_forced_resizable" msgid="1749750436092293116">"ممکن ہے کہ ایپ اسپلٹ اسکرین کے ساتھ کام نہ کرے۔"</string> <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ایپ سپلٹ اسکرین کو سپورٹ نہیں کرتی۔"</string> <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ممکن ہے ایپ ثانوی ڈسپلے پر کام نہ کرے۔"</string> <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"ایپ ثانوی ڈسپلیز پر شروعات کا تعاون نہیں کرتی۔"</string> @@ -44,14 +43,10 @@ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"اوپر %50"</string> <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"اوپر %30"</string> <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"نچلی فل اسکرین"</string> - <!-- no translation found for one_handed_tutorial_title (4583241688067426350) --> - <skip /> - <!-- no translation found for one_handed_tutorial_description (3486582858591353067) --> - <skip /> - <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) --> - <skip /> - <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) --> - <skip /> + <string name="one_handed_tutorial_title" msgid="4583241688067426350">"ایک ہاتھ کی وضع کا استعمال کرنا"</string> + <string name="one_handed_tutorial_description" msgid="3486582858591353067">"باہر نکلنے کیلئے، اسکرین کے نیچے سے اوپر کی طرف سوائپ کریں یا ایپ کے اوپر کہیں بھی تھپتھپائیں"</string> + <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"ایک ہاتھ کی وضع شروع کریں"</string> + <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"ایک ہاتھ کی وضع سے باہر نکلیں"</string> <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g> بلبلوں کے لیے ترتیبات"</string> <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"اوورفلو"</string> <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"انبار میں واپس شامل کریں"</string> @@ -68,8 +63,7 @@ <string name="bubbles_user_education_description" msgid="4215862563054175407">"نئی گفتگوئیں فلوٹنگ آئیکن یا بلبلے کے طور پر ظاہر ہوں گی۔ بلبلہ کھولنے کے لیے تھپتھپائیں۔ اسے منتقل کرنے کے لیے گھسیٹیں۔"</string> <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"کسی بھی وقت بلبلے کو کنٹرول کریں"</string> <string name="bubbles_user_education_manage" msgid="3460756219946517198">"اس ایپ سے بلبلوں کو آف کرنے کے لیے نظم کریں پر تھپتھپائیں"</string> - <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) --> - <skip /> + <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"سمجھ آ گئی"</string> <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"کوئی حالیہ بلبلہ نہیں"</string> <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"حالیہ بلبلے اور برخاست شدہ بلبلے یہاں ظاہر ہوں گے"</string> <string name="notification_bubble_title" msgid="6082910224488253378">"بلبلہ"</string> diff --git a/libs/WindowManager/Shell/res/values-ur/strings_tv.xml b/libs/WindowManager/Shell/res/values-ur/strings_tv.xml index 64e5db5ae10a..317953309947 100644 --- a/libs/WindowManager/Shell/res/values-ur/strings_tv.xml +++ b/libs/WindowManager/Shell/res/values-ur/strings_tv.xml @@ -17,8 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <!-- no translation found for notification_channel_tv_pip (2576686079160402435) --> - <skip /> + <string name="notification_channel_tv_pip" msgid="2576686079160402435">"تصویر میں تصویر"</string> <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(بلا عنوان پروگرام)"</string> <string name="pip_close" msgid="9135220303720555525">"PIP بند کریں"</string> <string name="pip_fullscreen" msgid="7278047353591302554">"فُل اسکرین"</string> diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml index c913e0c441e5..f0eae97b107e 100644 --- a/libs/WindowManager/Shell/res/values/config.xml +++ b/libs/WindowManager/Shell/res/values/config.xml @@ -30,6 +30,12 @@ <!-- Animation duration when using long press on recents to dock --> <integer name="long_press_dock_anim_duration">250</integer> + <!-- Animation duration for translating of one handed when trigger / dismiss. --> + <integer name="config_one_handed_translate_animation_duration">300</integer> + + <!-- One handed mode default offset % of display size --> + <fraction name="config_one_handed_offset">40%</fraction> + <!-- Allow one handed to enable round corner --> <bool name="config_one_handed_enable_round_corner">true</bool> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/Transitions.java index 388eb28223dc..120039de1240 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/Transitions.java @@ -116,7 +116,7 @@ public class Transitions extends ITransitionPlayer.Stub { } @Override - public void onTransitionReady(@NonNull IBinder transitionToken, TransitionInfo info, + public void onTransitionReady(@NonNull IBinder transitionToken, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "onTransitionReady %s: %s", transitionToken, info); @@ -131,22 +131,53 @@ public class Transitions extends ITransitionPlayer.Stub { + transitionToken); } mActiveTransitions.put(transitionToken, new ArrayList<>()); - for (int i = 0; i < info.getChanges().size(); ++i) { - final SurfaceControl leash = info.getChanges().get(i).getLeash(); + boolean isOpening = isOpeningType(info.getType()); + if (info.getRootLeash().isValid()) { + t.show(info.getRootLeash()); + } + // changes should be ordered top-to-bottom in z + for (int i = info.getChanges().size() - 1; i >= 0; --i) { + final TransitionInfo.Change change = info.getChanges().get(i); + final SurfaceControl leash = change.getLeash(); final int mode = info.getChanges().get(i).getMode(); + + // Don't animate anything with an animating parent + if (change.getParent() != null) { + if (mode == TRANSIT_OPEN || mode == TRANSIT_SHOW) { + t.show(leash); + t.setMatrix(leash, 1, 0, 0, 1); + } + continue; + } + + t.reparent(leash, info.getRootLeash()); + t.setPosition(leash, change.getEndAbsBounds().left - info.getRootOffset().x, + change.getEndAbsBounds().top - info.getRootOffset().y); + // Put all the OPEN/SHOW on top if (mode == TRANSIT_OPEN || mode == TRANSIT_SHOW) { t.show(leash); t.setMatrix(leash, 1, 0, 0, 1); - if (isOpeningType(info.getType())) { + if (isOpening) { + // put on top and fade in + t.setLayer(leash, info.getChanges().size() - i); t.setAlpha(leash, 0.f); startExampleAnimation(transitionToken, leash, true /* show */); } else { + // put on bottom and leave it visible without fade + t.setLayer(leash, -i); t.setAlpha(leash, 1.f); } } else if (mode == TRANSIT_CLOSE || mode == TRANSIT_HIDE) { - if (!isOpeningType(info.getType())) { + if (isOpening) { + // put on bottom and leave visible without fade + t.setLayer(leash, -i); + } else { + // put on top and fade out + t.setLayer(leash, info.getChanges().size() - i); startExampleAnimation(transitionToken, leash, false /* show */); } + } else { + t.setLayer(leash, info.getChanges().size() - i); } } t.apply(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index beac59b699fa..aa7355b61eda 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -89,6 +89,7 @@ public class BubbleController implements Bubbles { // TODO(b/173386799) keep in sync with Launcher3 and also don't do a broadcast public static final String TASKBAR_CHANGED_BROADCAST = "taskbarChanged"; + public static final String EXTRA_TASKBAR_CREATED = "taskbarCreated"; public static final String EXTRA_BUBBLE_OVERFLOW_OPENED = "bubbleOverflowOpened"; public static final String EXTRA_TASKBAR_VISIBLE = "taskbarVisible"; public static final String EXTRA_TASKBAR_POSITION = "taskbarPosition"; @@ -350,12 +351,15 @@ public class BubbleController implements Bubbles { + " itemPosition: " + itemPosition[0] + "," + itemPosition[1] + " iconSize: " + iconSize); PointF point = new PointF(itemPosition[0], itemPosition[1]); - mBubblePositioner.setPinnedLocation(point); + mBubblePositioner.setPinnedLocation(isVisible ? point : null); mBubblePositioner.updateForTaskbar(iconSize, taskbarPosition, isVisible, taskbarSize); if (mStackView != null) { - if (isVisible) { - mStackView.updateStackPosition(); + if (isVisible && b.getBoolean(EXTRA_TASKBAR_CREATED, false /* default */)) { + // If taskbar was created, add and remove the window so that bubbles display on top + removeFromWindowManagerMaybe(); + addToWindowManagerMaybe(); } + mStackView.updateStackPosition(); mBubbleIconFactory = new BubbleIconFactory(mContext); mStackView.onDisplaySizeChanged(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java index 090d2270817b..4e62ea6e7233 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java @@ -272,8 +272,9 @@ class HideDisplayCutoutOrganizer extends DisplayAreaOrganizer { @VisibleForTesting void applyBoundsAndOffsets(WindowContainerToken token, SurfaceControl leash, WindowContainerTransaction wct, SurfaceControl.Transaction t) { - wct.setBounds(token, mCurrentDisplayBounds.isEmpty() ? null : mCurrentDisplayBounds); + wct.setBounds(token, mCurrentDisplayBounds); t.setPosition(leash, mOffsetX, mOffsetY); + t.setWindowCrop(leash, mCurrentDisplayBounds.width(), mCurrentDisplayBounds.height()); } @VisibleForTesting diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java index f84936e5f386..e8c6cb74af0c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java @@ -37,6 +37,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; +import com.android.wm.shell.R; import com.android.wm.shell.common.DisplayChangeController; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.TaskStackListenerCallback; @@ -205,8 +206,11 @@ public class OneHandedController implements OneHanded { mGestureHandler = gestureHandler; mOverlayManager = overlayManager; - mOffSetFraction = SystemProperties.getInt(ONE_HANDED_MODE_OFFSET_PERCENTAGE, 50) - / 100.0f; + final float offsetPercentageConfig = context.getResources().getFraction( + R.fraction.config_one_handed_offset, 1, 1); + final int sysPropPercentageConfig = SystemProperties.getInt( + ONE_HANDED_MODE_OFFSET_PERCENTAGE, Math.round(offsetPercentageConfig * 100.0f)); + mOffSetFraction = sysPropPercentageConfig / 100.0f; mIsOneHandedEnabled = OneHandedSettingsUtil.getSettingsOneHandedModeEnabled( context.getContentResolver()); mIsSwipeToNotificationEnabled = diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java index bd6c1e096d01..0311030890c4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java @@ -40,6 +40,7 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import com.android.internal.os.SomeArgs; +import com.android.wm.shell.R; import com.android.wm.shell.common.DisplayController; import java.io.PrintWriter; @@ -156,8 +157,11 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { mDisplayController = displayController; mDefaultDisplayBounds.set(getDisplayBounds()); mLastVisualDisplayBounds.set(getDisplayBounds()); + final int animationDurationConfig = context.getResources().getInteger( + R.integer.config_one_handed_translate_animation_duration); mEnterExitAnimationDurationMs = - SystemProperties.getInt(ONE_HANDED_MODE_TRANSLATE_ANIMATION_DURATION, 300); + SystemProperties.getInt(ONE_HANDED_MODE_TRANSLATE_ANIMATION_DURATION, + animationDurationConfig); mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new; mTutorialHandler = tutorialHandler; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java index b6b518d69c55..d65ad62cdbbb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java @@ -86,8 +86,11 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback { context.getSystemService(Context.ACCESSIBILITY_SERVICE); mTargetViewContainer = new FrameLayout(context); mTargetViewContainer.setClipChildren(false); - mTutorialAreaHeight = Math.round(mDisplaySize.y - * (SystemProperties.getInt(ONE_HANDED_MODE_OFFSET_PERCENTAGE, 50) / 100.0f)); + final float offsetPercentageConfig = context.getResources().getFraction( + R.fraction.config_one_handed_offset, 1, 1); + final int sysPropPercentageConfig = SystemProperties.getInt( + ONE_HANDED_MODE_OFFSET_PERCENTAGE, Math.round(offsetPercentageConfig * 100.0f)); + mTutorialAreaHeight = Math.round(mDisplaySize.y * (sysPropPercentageConfig / 100.0f)); mTutorialView = LayoutInflater.from(context).inflate(R.layout.one_handed_tutorial, null); mTargetViewContainer.addView(mTutorialView); mCanShowTutorial = (Settings.Secure.getInt(mContentResolver, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java index df6683ebb80b..1bb5eda25058 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java @@ -22,6 +22,7 @@ import android.annotation.NonNull; import android.content.Context; import android.content.res.Resources; import android.graphics.Point; +import android.graphics.PointF; import android.graphics.Rect; import android.util.DisplayMetrics; import android.util.Size; @@ -42,6 +43,9 @@ public class PipBoundsAlgorithm { private final @NonNull PipBoundsState mPipBoundsState; private final PipSnapAlgorithm mSnapAlgorithm; + private float mDefaultSizePercent; + private float mMinAspectRatioForMinSize; + private float mMaxAspectRatioForMinSize; private float mDefaultAspectRatio; private float mMinAspectRatio; private float mMaxAspectRatio; @@ -51,7 +55,7 @@ public class PipBoundsAlgorithm { public PipBoundsAlgorithm(Context context, @NonNull PipBoundsState pipBoundsState) { mPipBoundsState = pipBoundsState; - mSnapAlgorithm = new PipSnapAlgorithm(context); + mSnapAlgorithm = new PipSnapAlgorithm(); reloadResources(context); // Initialize the aspect ratio to the default aspect ratio. Don't do this in reload // resources as it would clobber mAspectRatio when entering PiP from fullscreen which @@ -83,6 +87,11 @@ public class PipBoundsAlgorithm { com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio); mMaxAspectRatio = res.getFloat( com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio); + mDefaultSizePercent = res.getFloat( + com.android.internal.R.dimen.config_pictureInPictureDefaultSizePercent); + mMaxAspectRatioForMinSize = res.getFloat( + com.android.internal.R.dimen.config_pictureInPictureAspectRatioLimitForMinSize); + mMinAspectRatioForMinSize = 1f / mMaxAspectRatioForMinSize; } /** @@ -174,7 +183,7 @@ public class PipBoundsAlgorithm { final int minEdgeSize = useCurrentMinEdgeSize ? mPipBoundsState.getMinEdgeSize() : defaultMinEdgeSize; // Use the existing size but adjusted to the aspect ratio and min edge size. - size = mSnapAlgorithm.getSizeForAspectRatio( + size = getSizeForAspectRatio( new Size(stackBounds.width(), stackBounds.height()), aspectRatio, minEdgeSize); } else { if (overrideMinSize != null) { @@ -184,7 +193,7 @@ public class PipBoundsAlgorithm { } else { // Calculate the default size using the display size and default min edge size. final DisplayInfo displayInfo = mPipBoundsState.getDisplayInfo(); - size = mSnapAlgorithm.getSizeForAspectRatio(aspectRatio, mDefaultMinSize, + size = getSizeForAspectRatio(aspectRatio, mDefaultMinSize, displayInfo.logicalWidth, displayInfo.logicalHeight); } } @@ -229,7 +238,7 @@ public class PipBoundsAlgorithm { defaultSize = adjustSizeToAspectRatio(overrideMinSize, mDefaultAspectRatio); } else { // Calculate the default size using the display size and default min edge size. - defaultSize = mSnapAlgorithm.getSizeForAspectRatio(mDefaultAspectRatio, + defaultSize = getSizeForAspectRatio(mDefaultAspectRatio, mDefaultMinSize, displayInfo.logicalWidth, displayInfo.logicalHeight); } Gravity.apply(mDefaultStackGravity, defaultSize.getWidth(), defaultSize.getHeight(), @@ -270,13 +279,28 @@ public class PipBoundsAlgorithm { getInsetBounds(movementBounds); // Apply the movement bounds adjustments based on the current state. - mSnapAlgorithm.getMovementBounds(stackBounds, movementBounds, movementBounds, + getMovementBounds(stackBounds, movementBounds, movementBounds, (adjustForIme && mPipBoundsState.isImeShowing()) ? mPipBoundsState.getImeHeight() : 0); + return movementBounds; } /** + * Adjusts movementBoundsOut so that it is the movement bounds for the given stackBounds. + */ + public void getMovementBounds(Rect stackBounds, Rect insetBounds, Rect movementBoundsOut, + int bottomOffset) { + // Adjust the right/bottom to ensure the stack bounds never goes offscreen + movementBoundsOut.set(insetBounds); + movementBoundsOut.right = Math.max(insetBounds.left, insetBounds.right + - stackBounds.width()); + movementBoundsOut.bottom = Math.max(insetBounds.top, insetBounds.bottom + - stackBounds.height()); + movementBoundsOut.bottom -= bottomOffset; + } + + /** * @return the default snap fraction to apply instead of the default gravity when calculating * the default stack bounds when first entering PiP. */ @@ -304,6 +328,62 @@ public class PipBoundsAlgorithm { } /** + * @return the size of the PiP at the given aspectRatio, ensuring that the minimum edge + * is at least minEdgeSize. + */ + public Size getSizeForAspectRatio(float aspectRatio, float minEdgeSize, int displayWidth, + int displayHeight) { + final int smallestDisplaySize = Math.min(displayWidth, displayHeight); + final int minSize = (int) Math.max(minEdgeSize, smallestDisplaySize * mDefaultSizePercent); + + final int width; + final int height; + if (aspectRatio <= mMinAspectRatioForMinSize || aspectRatio > mMaxAspectRatioForMinSize) { + // Beyond these points, we can just use the min size as the shorter edge + if (aspectRatio <= 1) { + // Portrait, width is the minimum size + width = minSize; + height = Math.round(width / aspectRatio); + } else { + // Landscape, height is the minimum size + height = minSize; + width = Math.round(height * aspectRatio); + } + } else { + // Within these points, we ensure that the bounds fit within the radius of the limits + // at the points + final float widthAtMaxAspectRatioForMinSize = mMaxAspectRatioForMinSize * minSize; + final float radius = PointF.length(widthAtMaxAspectRatioForMinSize, minSize); + height = (int) Math.round(Math.sqrt((radius * radius) + / (aspectRatio * aspectRatio + 1))); + width = Math.round(height * aspectRatio); + } + return new Size(width, height); + } + + /** + * @return the adjusted size so that it conforms to the given aspectRatio, ensuring that the + * minimum edge is at least minEdgeSize. + */ + public Size getSizeForAspectRatio(Size size, float aspectRatio, float minEdgeSize) { + final int smallestSize = Math.min(size.getWidth(), size.getHeight()); + final int minSize = (int) Math.max(minEdgeSize, smallestSize); + + final int width; + final int height; + if (aspectRatio <= 1) { + // Portrait, width is the minimum size. + width = minSize; + height = Math.round(width / aspectRatio); + } else { + // Landscape, height is the minimum size + height = minSize; + width = Math.round(height * aspectRatio); + } + return new Size(width, height); + } + + /** * Dumps internal states. */ public void dump(PrintWriter pw, String prefix) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java index ce1139b4264d..53aa61477483 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java @@ -59,42 +59,39 @@ public final class PipBoundsState { private final @NonNull Rect mExpandedBounds = new Rect(); private final @NonNull Rect mNormalMovementBounds = new Rect(); private final @NonNull Rect mExpandedMovementBounds = new Rect(); - private final Context mContext; + private final @NonNull Context mContext; private float mAspectRatio; private int mStashedState = STASH_TYPE_NONE; private int mStashOffset; - private PipReentryState mPipReentryState; - private ComponentName mLastPipComponentName; - private final DisplayInfo mDisplayInfo = new DisplayInfo(); - private final DisplayLayout mDisplayLayout = new DisplayLayout(); + private @Nullable PipReentryState mPipReentryState; + private @Nullable ComponentName mLastPipComponentName; + private final @NonNull DisplayInfo mDisplayInfo = new DisplayInfo(); + private final @NonNull DisplayLayout mDisplayLayout = new DisplayLayout(); /** The current minimum edge size of PIP. */ private int mMinEdgeSize; /** The preferred minimum (and default) size specified by apps. */ - private Size mOverrideMinSize; - private final @NonNull AnimatingBoundsState mAnimatingBoundsState = new AnimatingBoundsState(); + private @Nullable Size mOverrideMinSize; + private final @NonNull MotionBoundsState mMotionBoundsState = new MotionBoundsState(); private boolean mIsImeShowing; private int mImeHeight; private boolean mIsShelfShowing; private int mShelfHeight; - private Runnable mOnMinimalSizeChangeCallback; - private BiConsumer<Boolean, Integer> mOnShelfVisibilityChangeCallback; + private @Nullable Runnable mOnMinimalSizeChangeCallback; + private @Nullable BiConsumer<Boolean, Integer> mOnShelfVisibilityChangeCallback; - public PipBoundsState(Context context) { + public PipBoundsState(@NonNull Context context) { mContext = context; reloadResources(); } - /** - * Reloads the resources. - */ + /** Reloads the resources. */ public void onConfigurationChanged() { reloadResources(); } private void reloadResources() { - mStashOffset = mContext.getResources() - .getDimensionPixelSize(R.dimen.pip_stash_offset); + mStashOffset = mContext.getResources().getDimensionPixelSize(R.dimen.pip_stash_offset); } /** Set the current PIP bounds. */ @@ -102,12 +99,14 @@ public final class PipBoundsState { mBounds.set(bounds); } + /** Get the current PIP bounds. */ @NonNull public Rect getBounds() { return new Rect(mBounds); } /** Returns the current movement bounds. */ + @NonNull public Rect getMovementBounds() { return mMovementBounds; } @@ -135,28 +134,28 @@ public final class PipBoundsState { } /** Set the normal movement bounds. */ - public void setNormalMovementBounds(Rect bounds) { + public void setNormalMovementBounds(@NonNull Rect bounds) { mNormalMovementBounds.set(bounds); } /** Returns the normal movement bounds. */ + @NonNull public Rect getNormalMovementBounds() { return mNormalMovementBounds; } /** Set the expanded movement bounds. */ - public void setExpandedMovementBounds(Rect bounds) { + public void setExpandedMovementBounds(@NonNull Rect bounds) { mExpandedMovementBounds.set(bounds); } /** Returns the expanded movement bounds. */ + @NonNull public Rect getExpandedMovementBounds() { return mExpandedMovementBounds; } - /** - * Dictate where PiP currently should be stashed, if at all. - */ + /** Dictate where PiP currently should be stashed, if at all. */ public void setStashed(@StashType int stashedState) { mStashedState = stashedState; } @@ -169,50 +168,39 @@ public final class PipBoundsState { return mStashedState; } - /** - * Whether PiP is stashed or not. - */ + /** Whether PiP is stashed or not. */ public boolean isStashed() { return mStashedState != STASH_TYPE_NONE; } - /** - * Returns the offset from the edge of the screen for PiP stash. - */ + /** Returns the offset from the edge of the screen for PiP stash. */ public int getStashOffset() { return mStashOffset; } + /** Set the PIP aspect ratio. */ public void setAspectRatio(float aspectRatio) { mAspectRatio = aspectRatio; } + /** Get the PIP aspect ratio. */ public float getAspectRatio() { return mAspectRatio; } - /** - * Save the reentry state to restore to when re-entering PIP mode. - * - * TODO(b/169373982): consider refactoring this so that this class alone can use mBounds and - * calculate the snap fraction to save for re-entry. - */ + /** Save the reentry state to restore to when re-entering PIP mode. */ public void saveReentryState(@NonNull Rect bounds, float fraction) { mPipReentryState = new PipReentryState(new Size(bounds.width(), bounds.height()), fraction); } - /** - * Returns the saved reentry state. - */ + /** Returns the saved reentry state. */ @Nullable public PipReentryState getReentryState() { return mPipReentryState; } - /** - * Set the last {@link ComponentName} to enter PIP mode. - */ - public void setLastPipComponentName(ComponentName lastPipComponentName) { + /** Set the last {@link ComponentName} to enter PIP mode. */ + public void setLastPipComponentName(@Nullable ComponentName lastPipComponentName) { final boolean changed = !Objects.equals(mLastPipComponentName, lastPipComponentName); mLastPipComponentName = lastPipComponentName; if (changed) { @@ -220,41 +208,40 @@ public final class PipBoundsState { } } + /** Get the last PIP component name, if any. */ + @Nullable public ComponentName getLastPipComponentName() { return mLastPipComponentName; } + /** Get the current display info. */ @NonNull public DisplayInfo getDisplayInfo() { return mDisplayInfo; } - /** - * Update the display info. - */ + /** Update the display info. */ public void setDisplayInfo(@NonNull DisplayInfo displayInfo) { mDisplayInfo.copyFrom(displayInfo); } + /** Set the rotation of the display. */ public void setDisplayRotation(int rotation) { mDisplayInfo.rotation = rotation; } - /** - * Returns the display's bound. - */ + /** Returns the display's bounds. */ @NonNull public Rect getDisplayBounds() { return new Rect(0, 0, mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight); } - /** - * Update the display layout. - */ + /** Update the display layout. */ public void setDisplayLayout(@NonNull DisplayLayout displayLayout) { mDisplayLayout.set(displayLayout); } + /** Get the display layout. */ @NonNull public DisplayLayout getDisplayLayout() { return mDisplayLayout; @@ -275,10 +262,8 @@ public final class PipBoundsState { return mMinEdgeSize; } - /** - * Sets the preferred size of PIP as specified by the activity in PIP mode. - */ - public void setOverrideMinSize(Size overrideMinSize) { + /** Sets the preferred size of PIP as specified by the activity in PIP mode. */ + public void setOverrideMinSize(@Nullable Size overrideMinSize) { final boolean changed = !Objects.equals(overrideMinSize, mOverrideMinSize); mOverrideMinSize = overrideMinSize; if (changed && mOnMinimalSizeChangeCallback != null) { @@ -287,6 +272,7 @@ public final class PipBoundsState { } /** Returns the preferred minimal size specified by the activity in PIP. */ + @Nullable public Size getOverrideMinSize() { return mOverrideMinSize; } @@ -297,8 +283,10 @@ public final class PipBoundsState { return Math.min(mOverrideMinSize.getWidth(), mOverrideMinSize.getHeight()); } - public AnimatingBoundsState getAnimatingBoundsState() { - return mAnimatingBoundsState; + /** Get the state of the bounds in motion. */ + @NonNull + public MotionBoundsState getMotionBoundsState() { + return mMotionBoundsState; } /** Set whether the IME is currently showing and its height. */ @@ -344,41 +332,41 @@ public final class PipBoundsState { /** * Registers a callback when the minimal size of PIP that is set by the app changes. */ - public void setOnMinimalSizeChangeCallback(Runnable onMinimalSizeChangeCallback) { + public void setOnMinimalSizeChangeCallback(@Nullable Runnable onMinimalSizeChangeCallback) { mOnMinimalSizeChangeCallback = onMinimalSizeChangeCallback; } /** Set a callback to be notified when the shelf visibility changes. */ public void setOnShelfVisibilityChangeCallback( - BiConsumer<Boolean, Integer> onShelfVisibilityChangeCallback) { + @Nullable BiConsumer<Boolean, Integer> onShelfVisibilityChangeCallback) { mOnShelfVisibilityChangeCallback = onShelfVisibilityChangeCallback; } - /** Source of truth for the current animation bounds of PIP. */ - public static class AnimatingBoundsState { - /** The bounds used when PIP is being dragged or animated. */ - private final Rect mTemporaryBounds = new Rect(); + /** Source of truth for the current bounds of PIP that may be in motion. */ + public static class MotionBoundsState { + /** The bounds used when PIP is in motion (e.g. during a drag or animation) */ + private final @NonNull Rect mBoundsInMotion = new Rect(); /** The destination bounds to which PIP is animating. */ - private final Rect mAnimatingToBounds = new Rect(); + private final @NonNull Rect mAnimatingToBounds = new Rect(); /** Whether PIP is being dragged or animated (e.g. resizing, in fling, etc). */ - public boolean isAnimating() { - return !mTemporaryBounds.isEmpty(); + public boolean isInMotion() { + return !mBoundsInMotion.isEmpty(); } /** Set the temporary bounds used to represent the drag or animation bounds of PIP. */ - public void setTemporaryBounds(Rect bounds) { - mTemporaryBounds.set(bounds); + public void setBoundsInMotion(@NonNull Rect bounds) { + mBoundsInMotion.set(bounds); } /** Set the bounds to which PIP is animating. */ - public void setAnimatingToBounds(Rect bounds) { + public void setAnimatingToBounds(@NonNull Rect bounds) { mAnimatingToBounds.set(bounds); } - /** Called when all ongoing dragging and animation operations have ended. */ + /** Called when all ongoing motion operations have ended. */ public void onAllAnimationsEnded() { - mTemporaryBounds.setEmpty(); + mBoundsInMotion.setEmpty(); } /** Called when an ongoing physics animation has ended. */ @@ -386,20 +374,22 @@ public final class PipBoundsState { mAnimatingToBounds.setEmpty(); } - /** Returns the temporary animation bounds. */ - public Rect getTemporaryBounds() { - return mTemporaryBounds; + /** Returns the motion bounds. */ + @NonNull + public Rect getBoundsInMotion() { + return mBoundsInMotion; } /** Returns the destination bounds to which PIP is currently animating. */ + @NonNull public Rect getAnimatingToBounds() { return mAnimatingToBounds; } void dump(PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; - pw.println(prefix + AnimatingBoundsState.class.getSimpleName()); - pw.println(innerPrefix + "mTemporaryBounds=" + mTemporaryBounds); + pw.println(prefix + MotionBoundsState.class.getSimpleName()); + pw.println(innerPrefix + "mBoundsInMotion=" + mBoundsInMotion); pw.println(innerPrefix + "mAnimatingToBounds=" + mAnimatingToBounds); } } @@ -432,9 +422,7 @@ public final class PipBoundsState { } } - /** - * Dumps internal state. - */ + /** Dumps internal state. */ public void dump(PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; pw.println(prefix + TAG); @@ -461,6 +449,6 @@ public final class PipBoundsState { } else { mPipReentryState.dump(pw, innerPrefix); } - mAnimatingBoundsState.dump(pw, innerPrefix); + mMotionBoundsState.dump(pw, innerPrefix); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSnapAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSnapAlgorithm.java index 71060752df09..0528e4d88374 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSnapAlgorithm.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSnapAlgorithm.java @@ -20,11 +20,9 @@ import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_LEFT; import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_NONE; import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_RIGHT; -import android.content.Context; -import android.content.res.Resources; -import android.graphics.PointF; import android.graphics.Rect; -import android.util.Size; + +import com.android.internal.annotations.VisibleForTesting; /** * Calculates the snap targets and the snap position for the PIP given a position and a velocity. @@ -32,19 +30,6 @@ import android.util.Size; */ public class PipSnapAlgorithm { - private final float mDefaultSizePercent; - private final float mMinAspectRatioForMinSize; - private final float mMaxAspectRatioForMinSize; - - public PipSnapAlgorithm(Context context) { - Resources res = context.getResources(); - mDefaultSizePercent = res.getFloat( - com.android.internal.R.dimen.config_pictureInPictureDefaultSizePercent); - mMaxAspectRatioForMinSize = res.getFloat( - com.android.internal.R.dimen.config_pictureInPictureAspectRatioLimitForMinSize); - mMinAspectRatioForMinSize = 1f / mMaxAspectRatioForMinSize; - } - /** * Returns a fraction that describes where the PiP bounds is. * See {@link #getSnapFraction(Rect, Rect, int)}. @@ -136,81 +121,11 @@ public class PipSnapAlgorithm { } /** - * Adjusts {@param movementBoundsOut} so that it is the movement bounds for the given - * {@param stackBounds}. - */ - public void getMovementBounds(Rect stackBounds, Rect insetBounds, Rect movementBoundsOut, - int bottomOffset) { - // Adjust the right/bottom to ensure the stack bounds never goes offscreen - movementBoundsOut.set(insetBounds); - movementBoundsOut.right = Math.max(insetBounds.left, insetBounds.right - - stackBounds.width()); - movementBoundsOut.bottom = Math.max(insetBounds.top, insetBounds.bottom - - stackBounds.height()); - movementBoundsOut.bottom -= bottomOffset; - } - - /** - * @return the size of the PiP at the given {@param aspectRatio}, ensuring that the minimum edge - * is at least {@param minEdgeSize}. - */ - public Size getSizeForAspectRatio(float aspectRatio, float minEdgeSize, int displayWidth, - int displayHeight) { - final int smallestDisplaySize = Math.min(displayWidth, displayHeight); - final int minSize = (int) Math.max(minEdgeSize, smallestDisplaySize * mDefaultSizePercent); - - final int width; - final int height; - if (aspectRatio <= mMinAspectRatioForMinSize || aspectRatio > mMaxAspectRatioForMinSize) { - // Beyond these points, we can just use the min size as the shorter edge - if (aspectRatio <= 1) { - // Portrait, width is the minimum size - width = minSize; - height = Math.round(width / aspectRatio); - } else { - // Landscape, height is the minimum size - height = minSize; - width = Math.round(height * aspectRatio); - } - } else { - // Within these points, we ensure that the bounds fit within the radius of the limits - // at the points - final float widthAtMaxAspectRatioForMinSize = mMaxAspectRatioForMinSize * minSize; - final float radius = PointF.length(widthAtMaxAspectRatioForMinSize, minSize); - height = (int) Math.round(Math.sqrt((radius * radius) / - (aspectRatio * aspectRatio + 1))); - width = Math.round(height * aspectRatio); - } - return new Size(width, height); - } - - /** - * @return the adjusted size so that it conforms to the given aspectRatio, ensuring that the - * minimum edge is at least minEdgeSize. - */ - public Size getSizeForAspectRatio(Size size, float aspectRatio, float minEdgeSize) { - final int smallestSize = Math.min(size.getWidth(), size.getHeight()); - final int minSize = (int) Math.max(minEdgeSize, smallestSize); - - final int width; - final int height; - if (aspectRatio <= 1) { - // Portrait, width is the minimum size. - width = minSize; - height = Math.round(width / aspectRatio); - } else { - // Landscape, height is the minimum size - height = minSize; - width = Math.round(height * aspectRatio); - } - return new Size(width, height); - } - - /** * Snaps the {@param stackBounds} to the closest edge of the {@param movementBounds} and writes * the new bounds out to {@param boundsOut}. */ - public void snapRectToClosestEdge(Rect stackBounds, Rect movementBounds, Rect boundsOut, + @VisibleForTesting + void snapRectToClosestEdge(Rect stackBounds, Rect movementBounds, Rect boundsOut, @PipBoundsState.StashType int stashType) { int leftEdge = stackBounds.left; if (stashType == STASH_TYPE_LEFT) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActivityController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActivityController.java index 1d5430008501..d4217553ef2d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActivityController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActivityController.java @@ -239,7 +239,9 @@ public class PipMenuActivityController { + " callers=\n" + Debug.getCallers(5, " ")); } - maybeCreateSyncApplier(); + if (!maybeCreateSyncApplier()) { + return; + } mPipMenuView.showMenu(menuState, stackBounds, allowMenuTimeout, willResizeMenu, withDelay, showResizeHandle); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java index 8fa944886905..903f7d773896 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java @@ -90,7 +90,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, }); /** - * PhysicsAnimator instance for animating {@link PipBoundsState#getAnimatingBoundsState()} + * PhysicsAnimator instance for animating {@link PipBoundsState#getMotionBoundsState()} * using physics animations. */ private final PhysicsAnimator<Rect> mTemporaryBoundsPhysicsAnimator; @@ -98,7 +98,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, private MagnetizedObject<Rect> mMagnetizedPip; /** - * Update listener that resizes the PIP to {@link PipBoundsState#getAnimatingBoundsState()}. + * Update listener that resizes the PIP to {@link PipBoundsState#getMotionBoundsState()}. */ private final PhysicsAnimator.UpdateListener<Rect> mResizePipUpdateListener; @@ -172,14 +172,14 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, mFloatingContentCoordinator = floatingContentCoordinator; mPipTaskOrganizer.registerPipTransitionCallback(mPipTransitionCallback); mTemporaryBoundsPhysicsAnimator = PhysicsAnimator.getInstance( - mPipBoundsState.getAnimatingBoundsState().getTemporaryBounds()); + mPipBoundsState.getMotionBoundsState().getBoundsInMotion()); mTemporaryBoundsPhysicsAnimator.setCustomAnimationHandler( mSfAnimationHandlerThreadLocal.get()); mResizePipUpdateListener = (target, values) -> { - if (mPipBoundsState.getAnimatingBoundsState().isAnimating()) { + if (mPipBoundsState.getMotionBoundsState().isInMotion()) { mPipTaskOrganizer.scheduleUserResizePip(getBounds(), - mPipBoundsState.getAnimatingBoundsState().getTemporaryBounds(), null); + mPipBoundsState.getMotionBoundsState().getBoundsInMotion(), null); } }; } @@ -187,8 +187,8 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, @NonNull @Override public Rect getFloatingBoundsOnScreen() { - return !mPipBoundsState.getAnimatingBoundsState().getAnimatingToBounds().isEmpty() - ? mPipBoundsState.getAnimatingBoundsState().getAnimatingToBounds() : getBounds(); + return !mPipBoundsState.getMotionBoundsState().getAnimatingToBounds().isEmpty() + ? mPipBoundsState.getMotionBoundsState().getAnimatingToBounds() : getBounds(); } @NonNull @@ -207,7 +207,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, */ void synchronizePinnedStackBounds() { cancelPhysicsAnimation(); - mPipBoundsState.getAnimatingBoundsState().onAllAnimationsEnded(); + mPipBoundsState.getMotionBoundsState().onAllAnimationsEnded(); if (mPipTaskOrganizer.isInPip()) { mFloatingContentCoordinator.onContentMoved(this); @@ -242,7 +242,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, resizePipUnchecked(toBounds); mPipBoundsState.setBounds(toBounds); } else { - mPipBoundsState.getAnimatingBoundsState().setTemporaryBounds(toBounds); + mPipBoundsState.getMotionBoundsState().setBoundsInMotion(toBounds); mPipTaskOrganizer.scheduleUserResizePip(getBounds(), toBounds, (Rect newBounds) -> { mMainHandler.post(() -> { @@ -278,8 +278,8 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, // If we're already in the dismiss target area, then there won't be a move to set the // temporary bounds, so just initialize it to the current bounds. - if (!mPipBoundsState.getAnimatingBoundsState().isAnimating()) { - mPipBoundsState.getAnimatingBoundsState().setTemporaryBounds(getBounds()); + if (!mPipBoundsState.getMotionBoundsState().isInMotion()) { + mPipBoundsState.getMotionBoundsState().setBoundsInMotion(getBounds()); } mTemporaryBoundsPhysicsAnimator .spring(FloatProperties.RECT_X, destinationX, velX, mSpringConfig) @@ -396,7 +396,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, final float xEndValue = velocityX < 0 ? leftEdge : rightEdge; - final int startValueY = mPipBoundsState.getAnimatingBoundsState().getTemporaryBounds().top; + final int startValueY = mPipBoundsState.getMotionBoundsState().getBoundsInMotion().top; final float estimatedFlingYEndValue = PhysicsAnimator.estimateFlingEndValue(startValueY, velocityY, mFlingConfigY); @@ -411,7 +411,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, void animateToBounds(Rect bounds, PhysicsAnimator.SpringConfig springConfig) { if (!mTemporaryBoundsPhysicsAnimator.isRunning()) { // Animate from the current bounds if we're not already animating. - mPipBoundsState.getAnimatingBoundsState().setTemporaryBounds(getBounds()); + mPipBoundsState.getMotionBoundsState().setBoundsInMotion(getBounds()); } mTemporaryBoundsPhysicsAnimator @@ -492,7 +492,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, */ private void cancelPhysicsAnimation() { mTemporaryBoundsPhysicsAnimator.cancel(); - mPipBoundsState.getAnimatingBoundsState().onPhysicsAnimationEnded(); + mPipBoundsState.getMotionBoundsState().onPhysicsAnimationEnded(); mSpringingToTouch = false; } @@ -565,17 +565,17 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, if (!mDismissalPending && !mSpringingToTouch && !mMagnetizedPip.getObjectStuckToTarget()) { - // All animations (including dragging) have actually finished. + // All motion operations have actually finished. mPipBoundsState.setBounds( - mPipBoundsState.getAnimatingBoundsState().getTemporaryBounds()); - mPipBoundsState.getAnimatingBoundsState().onAllAnimationsEnded(); + mPipBoundsState.getMotionBoundsState().getBoundsInMotion()); + mPipBoundsState.getMotionBoundsState().onAllAnimationsEnded(); if (!mDismissalPending) { // do not schedule resize if PiP is dismissing, which may cause app re-open to // mBounds instead of it's normal bounds. mPipTaskOrganizer.scheduleFinishResizePip(getBounds()); } } - mPipBoundsState.getAnimatingBoundsState().onPhysicsAnimationEnded(); + mPipBoundsState.getMotionBoundsState().onPhysicsAnimationEnded(); mSpringingToTouch = false; mDismissalPending = false; } @@ -586,7 +586,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, * {@link FloatingContentCoordinator.FloatingContent#getFloatingBoundsOnScreen()}. */ private void setAnimatingToBounds(Rect bounds) { - mPipBoundsState.getAnimatingBoundsState().setAnimatingToBounds(bounds); + mPipBoundsState.getMotionBoundsState().setAnimatingToBounds(bounds); mFloatingContentCoordinator.onContentMoved(this); } @@ -625,7 +625,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, MagnetizedObject<Rect> getMagnetizedPip() { if (mMagnetizedPip == null) { mMagnetizedPip = new MagnetizedObject<Rect>( - mContext, mPipBoundsState.getAnimatingBoundsState().getTemporaryBounds(), + mContext, mPipBoundsState.getMotionBoundsState().getBoundsInMotion(), FloatProperties.RECT_X, FloatProperties.RECT_Y) { @Override public float getWidth(@NonNull Rect animatedPipBounds) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java index 1c5d5b8a8262..a78c4ecdb39f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java @@ -307,8 +307,7 @@ public class PipTouchHandler { public void adjustBoundsForRotation(Rect outBounds, Rect curBounds, Rect insetBounds) { final Rect toMovementBounds = new Rect(); - mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds(outBounds, insetBounds, - toMovementBounds, 0); + mPipBoundsAlgorithm.getMovementBounds(outBounds, insetBounds, toMovementBounds, 0); final int prevBottom = mPipBoundsState.getMovementBounds().bottom - mMovementBoundsExtraOffsets; if ((prevBottom - mBottomOffsetBufferPx) <= curBounds.top) { @@ -339,13 +338,13 @@ public class PipTouchHandler { // Re-calculate the expanded bounds Rect normalMovementBounds = new Rect(); - mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds(normalBounds, insetBounds, + mPipBoundsAlgorithm.getMovementBounds(normalBounds, insetBounds, normalMovementBounds, bottomOffset); if (mPipBoundsState.getMovementBounds().isEmpty()) { // mMovementBounds is not initialized yet and a clean movement bounds without // bottom offset shall be used later in this function. - mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds(curBounds, insetBounds, + mPipBoundsAlgorithm.getMovementBounds(curBounds, insetBounds, mPipBoundsState.getMovementBounds(), 0 /* bottomOffset */); } @@ -353,12 +352,12 @@ public class PipTouchHandler { float aspectRatio = (float) normalBounds.width() / normalBounds.height(); Point displaySize = new Point(); mContext.getDisplay().getRealSize(displaySize); - Size expandedSize = mPipBoundsAlgorithm.getSnapAlgorithm().getSizeForAspectRatio( + Size expandedSize = mPipBoundsAlgorithm.getSizeForAspectRatio( aspectRatio, mExpandedShortestEdgeSize, displaySize.x, displaySize.y); mPipBoundsState.setExpandedBounds( new Rect(0, 0, expandedSize.getWidth(), expandedSize.getHeight())); Rect expandedMovementBounds = new Rect(); - mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds( + mPipBoundsAlgorithm.getMovementBounds( mPipBoundsState.getExpandedBounds(), insetBounds, expandedMovementBounds, bottomOffset); @@ -381,7 +380,7 @@ public class PipTouchHandler { } else { final boolean isExpanded = mMenuState == MENU_STATE_FULL && willResizeMenu(); final Rect toMovementBounds = new Rect(); - mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds(curBounds, insetBounds, + mPipBoundsAlgorithm.getMovementBounds(curBounds, insetBounds, toMovementBounds, mIsImeShowing ? mImeHeight : 0); final int prevBottom = mPipBoundsState.getMovementBounds().bottom - mMovementBoundsExtraOffsets; @@ -659,7 +658,7 @@ public class PipTouchHandler { private void animateToUnexpandedState(Rect restoreBounds) { Rect restoredMovementBounds = new Rect(); - mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds(restoreBounds, + mPipBoundsAlgorithm.getMovementBounds(restoreBounds, mInsetBounds, restoredMovementBounds, mIsImeShowing ? mImeHeight : 0); mMotionHelper.animateToUnexpandedState(restoreBounds, mSavedSnapFraction, restoredMovementBounds, mPipBoundsState.getMovementBounds(), false /* immediate */); @@ -707,7 +706,7 @@ public class PipTouchHandler { return; } - Rect bounds = getPossiblyAnimatingBounds(); + Rect bounds = getPossiblyMotionBounds(); mDelta.set(0f, 0f); mStartPosition.set(bounds.left, bounds.top); mMovementWithinDismiss = touchState.getDownTouchPosition().y @@ -746,7 +745,7 @@ public class PipTouchHandler { mDelta.x += left - lastX; mDelta.y += top - lastY; - mTmpBounds.set(getPossiblyAnimatingBounds()); + mTmpBounds.set(getPossiblyMotionBounds()); mTmpBounds.offsetTo((int) left, (int) top); mMotionHelper.movePip(mTmpBounds, true /* isDragging */); @@ -865,7 +864,7 @@ public class PipTouchHandler { * resized. */ private void updateMovementBounds() { - mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds(mPipBoundsState.getBounds(), + mPipBoundsAlgorithm.getMovementBounds(mPipBoundsState.getBounds(), mInsetBounds, mPipBoundsState.getMovementBounds(), mIsImeShowing ? mImeHeight : 0); mMotionHelper.onMovementBoundsChanged(); @@ -877,7 +876,7 @@ public class PipTouchHandler { private Rect getMovementBounds(Rect curBounds) { Rect movementBounds = new Rect(); - mPipBoundsAlgorithm.getSnapAlgorithm().getMovementBounds(curBounds, mInsetBounds, + mPipBoundsAlgorithm.getMovementBounds(curBounds, mInsetBounds, movementBounds, mIsImeShowing ? mImeHeight : 0); return movementBounds; } @@ -896,12 +895,12 @@ public class PipTouchHandler { } /** - * Returns the PIP bounds if we're not animating, or the current, temporary animating bounds - * otherwise. + * Returns the PIP bounds if we're not in the middle of a motion operation, or the current, + * temporary motion bounds otherwise. */ - Rect getPossiblyAnimatingBounds() { - return mPipBoundsState.getAnimatingBoundsState().isAnimating() - ? mPipBoundsState.getAnimatingBoundsState().getTemporaryBounds() + Rect getPossiblyMotionBounds() { + return mPipBoundsState.getMotionBoundsState().isInMotion() + ? mPipBoundsState.getMotionBoundsState().getBoundsInMotion() : mPipBoundsState.getBounds(); } diff --git a/libs/WindowManager/Shell/tests/OWNERS b/libs/WindowManager/Shell/tests/OWNERS new file mode 100644 index 000000000000..2c6c7b358e3b --- /dev/null +++ b/libs/WindowManager/Shell/tests/OWNERS @@ -0,0 +1,2 @@ +# includes OWNERS from parent directories +natanieljr@google.com diff --git a/libs/WindowManager/Shell/tests/flicker/Android.bp b/libs/WindowManager/Shell/tests/flicker/Android.bp index d7afa0e166b3..4a498d2ec581 100644 --- a/libs/WindowManager/Shell/tests/flicker/Android.bp +++ b/libs/WindowManager/Shell/tests/flicker/Android.bp @@ -29,7 +29,11 @@ android_test { "truth-prebuilt", "app-helpers-core", "launcher-helper-lib", - "launcher-aosp-tapl" + "launcher-aosp-tapl", + "wm-flicker-common-assertions", + "wm-flicker-common-app-helpers", + "platform-test-annotations", + "wmshell-flicker-test-components", ], } @@ -47,6 +51,10 @@ android_test { "truth-prebuilt", "app-helpers-core", "launcher-helper-lib", - "launcher-aosp-tapl" + "launcher-aosp-tapl", + "wm-flicker-common-assertions", + "wm-flicker-common-app-helpers", + "platform-test-annotations", + "wmshell-flicker-test-components", ], } diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/AndroidManifest.xml index 9e8330973b40..101b5bf27c77 100644 --- a/libs/WindowManager/Shell/tests/flicker/AndroidManifest.xml +++ b/libs/WindowManager/Shell/tests/flicker/AndroidManifest.xml @@ -36,6 +36,8 @@ <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES"/> <!-- Control test app's media session --> <uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL"/> + <!-- ATM.removeRootTasksWithActivityTypes() --> + <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS" /> <application> <uses-library android:name="android.test.runner"/> diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt index e1c9384f7081..0fb43e263d05 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt @@ -92,6 +92,26 @@ fun LayersAssertion.navBarLayerIsAlwaysVisible( } @JvmOverloads +fun LayersAssertion.appPairsDividerIsVisible( + bugId: Int = 0, + enabled: Boolean = bugId == 0 +) { + end("appPairsDividerIsVisible", bugId, enabled) { + this.showsLayer(FlickerTestBase.APP_PAIRS_DIVIDER) + } +} + +@JvmOverloads +fun LayersAssertion.appPairsDividerIsInvisible( + bugId: Int = 0, + enabled: Boolean = bugId == 0 +) { + end("appPairsDividerIsInVisible", bugId, enabled) { + this.hasNotLayer(FlickerTestBase.APP_PAIRS_DIVIDER) + } +} + +@JvmOverloads fun LayersAssertion.dockedStackDividerIsVisible( bugId: Int = 0, enabled: Boolean = bugId == 0 diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt index 3c222e7d8b56..96234fcc8570 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt @@ -16,31 +16,24 @@ package com.android.wm.shell.flicker -import android.content.ComponentName - const val IME_WINDOW_NAME = "InputMethod" -const val PIP_WINDOW_NAME = "PipMenuActivity" -const val SPLITSCREEN_PRIMARY_WINDOW_NAME = "SplitScreenActivity" -const val SPLITSCREEN_SECONDARY_WINDOW_NAME = "SplitScreenSecondaryActivity" +const val PIP_MENU_WINDOW_NAME = "PipMenuActivity" const val SYSTEM_UI_PACKAGE_NAME = "com.android.systemui" const val TEST_APP_PACKAGE_NAME = "com.android.wm.shell.flicker.testapp" // Test App > Pip Activity -val TEST_APP_PIP_ACTIVITY_COMPONENT_NAME: ComponentName = ComponentName.createRelative( - TEST_APP_PACKAGE_NAME, ".PipActivity") const val TEST_APP_PIP_ACTIVITY_LABEL = "PipApp" -const val TEST_APP_PIP_ACTIVITY_WINDOW_NAME = "PipActivity" +const val TEST_APP_PIP_MENU_ACTION_NO_OP = "No-Op" +const val TEST_APP_PIP_MENU_ACTION_ON = "On" +const val TEST_APP_PIP_MENU_ACTION_OFF = "Off" +const val TEST_APP_PIP_MENU_ACTION_CLEAR = "Clear" // Test App > Ime Activity -val TEST_APP_IME_ACTIVITY_COMPONENT_NAME: ComponentName = ComponentName.createRelative( - TEST_APP_PACKAGE_NAME, ".ImeActivity") const val TEST_APP_IME_ACTIVITY_LABEL = "ImeApp" +// Test App > Test Activity +const val TEST_APP_FIXED_ACTIVITY_LABEL = "FixedApp" // Test App > SplitScreen Activity -val TEST_APP_SPLITSCREEN_PRIMARY_COMPONENT_NAME: ComponentName = ComponentName.createRelative( - TEST_APP_PACKAGE_NAME, ".$SPLITSCREEN_PRIMARY_WINDOW_NAME") -val TEST_APP_SPLITSCREEN_SECONDARY_COMPONENT_NAME: ComponentName = ComponentName.createRelative( - TEST_APP_PACKAGE_NAME, ".$SPLITSCREEN_SECONDARY_WINDOW_NAME") const val TEST_APP_SPLITSCREEN_PRIMARY_LABEL = "SplitScreenPrimaryApp" const val TEST_APP_SPLITSCREEN_SECONDARY_LABEL = "SplitScreenSecondaryApp" diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt index 2e6037d148a8..54b8fdc83a1f 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt @@ -130,6 +130,7 @@ abstract class FlickerTestBase { const val NAVIGATION_BAR_WINDOW_TITLE = "NavigationBar" const val STATUS_BAR_WINDOW_TITLE = "StatusBar" const val DOCKED_STACK_DIVIDER = "DockedStackDivider" + const val APP_PAIRS_DIVIDER = "AppPairDivider" const val IMAGE_WALLPAPER = "ImageWallpaper" } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt new file mode 100644 index 000000000000..2fc6944a3a5f --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.apppairs + +import android.os.SystemClock +import android.util.Log +import android.view.Surface +import androidx.test.filters.RequiresDevice +import com.android.compatibility.common.util.SystemUtil +import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.server.wm.flicker.dsl.runWithFlicker +import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen +import com.android.wm.shell.flicker.helpers.AppPairsHelper +import com.android.wm.shell.flicker.helpers.AppPairsHelper.Companion.TEST_REPETITIONS +import com.android.wm.shell.flicker.appPairsDividerIsInvisible +import com.android.wm.shell.flicker.appPairsDividerIsVisible +import com.android.wm.shell.flicker.navBarLayerIsAlwaysVisible +import com.android.wm.shell.flicker.navBarWindowIsAlwaysVisible +import com.android.wm.shell.flicker.statusBarLayerIsAlwaysVisible +import com.android.wm.shell.flicker.statusBarWindowIsAlwaysVisible +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized +import java.io.IOException + +/** + * Test AppPairs launch. + * To run this test: `atest WMShellFlickerTests:AppPairsTest` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class AppPairsTest( + rotationName: String, + rotation: Int +) : AppPairsTestBase(rotationName, rotation) { + private val appPairsSetup: FlickerBuilder + get() = FlickerBuilder(instrumentation).apply { + val testLaunchActivity = "launch_appPairs_primary_secondary_activities" + withTestName { + testLaunchActivity + } + setup { + eachRun { + uiDevice.wakeUpAndGoToHomeScreen() + primaryApp.open() + uiDevice.pressHome() + secondaryApp.open() + uiDevice.pressHome() + updateTaskId() + } + } + teardown { + eachRun { + executeShellCommand(composePairsCommand( + primaryTaskId, secondaryTaskId, false /* pair */)) + primaryApp.exit() + secondaryApp.exit() + } + } + assertions { + layersTrace { + navBarLayerIsAlwaysVisible() + statusBarLayerIsAlwaysVisible() + } + windowManagerTrace { + navBarWindowIsAlwaysVisible() + statusBarWindowIsAlwaysVisible() + } + } + } + + @Test + fun testAppPairs_pairPrimaryAndSecondaryApps() { + val testTag = "testAppPaired_pairPrimaryAndSecondary" + runWithFlicker(appPairsSetup) { + withTestName { testTag } + repeat { + TEST_REPETITIONS + } + transitions { + // TODO pair apps through normal UX flow + executeShellCommand(composePairsCommand( + primaryTaskId, secondaryTaskId, true /* pair */)) + SystemClock.sleep(AppPairsHelper.TIMEOUT_MS) + } + assertions { + layersTrace { + appPairsDividerIsVisible() + end("appsEndingBounds", enabled = false) { + val entry = this.trace.entries.firstOrNull() + ?: throw IllegalStateException("Trace is empty") + val dividerRegion = entry.getVisibleBounds(APP_PAIRS_DIVIDER) + this.hasVisibleRegion(primaryApp.defaultWindowName, + appPairsHelper.getPrimaryBounds(dividerRegion)) + .and() + .hasVisibleRegion(secondaryApp.defaultWindowName, + appPairsHelper.getSecondaryBounds(dividerRegion)) + } + } + windowManagerTrace { + end { + showsAppWindow(primaryApp.defaultWindowName) + .and() + .showsAppWindow(secondaryApp.defaultWindowName) + } + } + } + } + } + + @Test + fun testAppPairs_unpairPrimaryAndSecondary() { + val testTag = "testAppPairs_unpairPrimaryAndSecondary" + runWithFlicker(appPairsSetup) { + withTestName { testTag } + repeat { + TEST_REPETITIONS + } + setup { + executeShellCommand(composePairsCommand( + primaryTaskId, secondaryTaskId, true /* pair */)) + SystemClock.sleep(AppPairsHelper.TIMEOUT_MS) + } + transitions { + // TODO pair apps through normal UX flow + executeShellCommand(composePairsCommand( + primaryTaskId, secondaryTaskId, false /* pair */)) + SystemClock.sleep(AppPairsHelper.TIMEOUT_MS) + } + assertions { + layersTrace { + appPairsDividerIsInvisible() + start("appsStartingBounds", enabled = false) { + val entry = this.trace.entries.firstOrNull() + ?: throw IllegalStateException("Trace is empty") + val dividerRegion = entry.getVisibleBounds(APP_PAIRS_DIVIDER) + this.hasVisibleRegion(primaryApp.defaultWindowName, + appPairsHelper.getPrimaryBounds(dividerRegion)) + .and() + .hasVisibleRegion(secondaryApp.defaultWindowName, + appPairsHelper.getSecondaryBounds(dividerRegion)) + } + end("appsEndingBounds", enabled = false) { + this.hasNotLayer(primaryApp.defaultWindowName) + .and() + .hasNotLayer(secondaryApp.defaultWindowName) + } + } + windowManagerTrace { + end { + hidesAppWindow(primaryApp.defaultWindowName) + .and() + .hidesAppWindow(secondaryApp.defaultWindowName) + } + } + } + } + } + + private fun composePairsCommand( + primaryApp: String, + secondaryApp: String, + pair: Boolean + ): String = buildString { + // dumpsys activity service SystemUIService WMShell {pair|unpair} ${TASK_ID_1} ${TASK_ID_2} + append("dumpsys activity service SystemUIService WMShell ") + if (pair) { + append("pair ") + } else { + append("unpair ") + } + append(primaryApp + " " + secondaryApp) + } + + private fun executeShellCommand(cmd: String) { + try { + SystemUtil.runShellCommand(instrumentation, cmd) + } catch (e: IOException) { + Log.d("AppPairsTest", "executeShellCommand error!" + e) + } + } + + private fun updateTaskId() { + val primaryAppComponent = primaryApp.openAppIntent.component + val secondaryAppComponent = secondaryApp.openAppIntent.component + if (primaryAppComponent != null) { + primaryTaskId = appPairsHelper.getTaskIdForActivity( + primaryAppComponent.packageName, primaryAppComponent.className).toString() + } + if (secondaryAppComponent != null) { + secondaryTaskId = appPairsHelper.getTaskIdForActivity( + secondaryAppComponent.packageName, secondaryAppComponent.className).toString() + } + } + + companion object { + var primaryTaskId = "" + var secondaryTaskId = "" + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<Array<Any>> { + val supportedRotations = intArrayOf(Surface.ROTATION_0) + return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) } + } + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestBase.kt new file mode 100644 index 000000000000..f32cd8842074 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestBase.kt @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.apppairs + +import com.android.wm.shell.flicker.NonRotationTestBase +import com.android.wm.shell.flicker.TEST_APP_SPLITSCREEN_PRIMARY_LABEL +import com.android.wm.shell.flicker.TEST_APP_SPLITSCREEN_SECONDARY_LABEL +import com.android.wm.shell.flicker.helpers.AppPairsHelper +import com.android.wm.shell.flicker.helpers.SplitScreenHelper +import com.android.wm.shell.flicker.testapp.Components + +abstract class AppPairsTestBase( + rotationName: String, + rotation: Int +) : NonRotationTestBase(rotationName, rotation) { + protected val appPairsHelper = AppPairsHelper(instrumentation, + TEST_APP_SPLITSCREEN_PRIMARY_LABEL, + Components.SplitScreenActivity()) + protected val primaryApp = SplitScreenHelper(instrumentation, + TEST_APP_SPLITSCREEN_PRIMARY_LABEL, + Components.SplitScreenActivity()) + protected val secondaryApp = SplitScreenHelper(instrumentation, + TEST_APP_SPLITSCREEN_SECONDARY_LABEL, + Components.SplitScreenSecondaryActivity()) +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt new file mode 100644 index 000000000000..e2cda7ad123d --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.helpers + +import android.app.Instrumentation +import android.graphics.Region +import android.system.helpers.ActivityHelper +import com.android.server.wm.flicker.helpers.WindowUtils +import com.android.wm.shell.flicker.testapp.Components + +class AppPairsHelper( + instrumentation: Instrumentation, + activityLabel: String, + componentsInfo: Components.ComponentsInfo +) : BaseAppHelper( + instrumentation, + activityLabel, + componentsInfo +) { + val activityHelper = ActivityHelper.getInstance() + + fun getPrimaryBounds(dividerBounds: Region): android.graphics.Region { + val primaryAppBounds = Region(0, 0, dividerBounds.bounds.right, + dividerBounds.bounds.bottom + WindowUtils.dockedStackDividerInset) + return primaryAppBounds + } + + fun getSecondaryBounds(dividerBounds: Region): android.graphics.Region { + val displayBounds = WindowUtils.displayBounds + val secondaryAppBounds = Region(0, + dividerBounds.bounds.bottom - WindowUtils.dockedStackDividerInset, + displayBounds.right, displayBounds.bottom - WindowUtils.navigationBarHeight) + return secondaryAppBounds + } + + fun getTaskIdForActivity(pkgName: String, activityName: String): Int { + return activityHelper.getTaskIdForActivity(pkgName, activityName) + } + + companion object { + const val TEST_REPETITIONS = 1 + const val TIMEOUT_MS = 3_000L + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt index 6f008ce64fb4..6fd1df3b3f30 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt @@ -16,9 +16,7 @@ package com.android.wm.shell.flicker.helpers -import android.app.ActivityManager import android.app.Instrumentation -import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.pm.PackageManager.FEATURE_LEANBACK @@ -29,15 +27,15 @@ import androidx.test.uiautomator.UiDevice import androidx.test.uiautomator.UiObject2 import androidx.test.uiautomator.Until import com.android.server.wm.flicker.helpers.StandardAppHelper -import com.android.wm.shell.flicker.TEST_APP_PACKAGE_NAME +import com.android.wm.shell.flicker.testapp.Components abstract class BaseAppHelper( instrumentation: Instrumentation, launcherName: String, - private val launcherActivityComponent: ComponentName + private val componentsInfo: Components.ComponentsInfo ) : StandardAppHelper( instrumentation, - TEST_APP_PACKAGE_NAME, + Components.PACKAGE_NAME, launcherName, LauncherStrategyFactory.getInstance(instrumentation).launcherStrategy ) { @@ -46,9 +44,6 @@ abstract class BaseAppHelper( protected val context: Context get() = mInstrumentation.context - private val activityManager: ActivityManager? - get() = context.getSystemService(ActivityManager::class.java) - private val appSelector = By.pkg(packageName).depth(0) protected val isTelevision: Boolean @@ -57,7 +52,7 @@ abstract class BaseAppHelper( } val defaultWindowName: String - get() = launcherActivityComponent.className + get() = componentsInfo.activityName val label: String get() = context.packageManager.run { @@ -67,8 +62,12 @@ abstract class BaseAppHelper( val ui: UiObject2? get() = uiDevice.findObject(appSelector) - fun launchViaIntent() { - context.startActivity(openAppIntent) + fun launchViaIntent(stringExtras: Map<String, String> = mapOf()) { + val intent = openAppIntent + stringExtras.forEach() { + intent.putExtra(it.key, it.value) + } + context.startActivity(intent) uiDevice.wait(Until.hasObject(appSelector), APP_LAUNCH_WAIT_TIME_MS) } @@ -77,13 +76,9 @@ abstract class BaseAppHelper( return uiDevice.wait(Until.gone(appSelector), APP_CLOSE_WAIT_TIME_MS) } - fun forceStop() { - activityManager?.forceStopPackage(packageName) - } - override fun getOpenAppIntent(): Intent { val intent = Intent() - intent.component = launcherActivityComponent + intent.component = componentsInfo.componentName intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) return intent } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/FixedAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/FixedAppHelper.kt new file mode 100644 index 000000000000..c7f19a5d2620 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/FixedAppHelper.kt @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.helpers + +import android.app.Instrumentation +import com.android.wm.shell.flicker.TEST_APP_FIXED_ACTIVITY_LABEL +import com.android.wm.shell.flicker.testapp.Components + +class FixedAppHelper( + instrumentation: Instrumentation +) : BaseAppHelper( + instrumentation, + TEST_APP_FIXED_ACTIVITY_LABEL, + Components.FixedActivity() +)
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt index a6650d7f13d1..d580104ade19 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt @@ -21,8 +21,8 @@ import androidx.test.uiautomator.By import androidx.test.uiautomator.Until import com.android.server.wm.flicker.helpers.FIND_TIMEOUT import com.android.server.wm.flicker.helpers.waitForIME -import com.android.wm.shell.flicker.TEST_APP_IME_ACTIVITY_COMPONENT_NAME import com.android.wm.shell.flicker.TEST_APP_IME_ACTIVITY_LABEL +import com.android.wm.shell.flicker.testapp.Components import org.junit.Assert open class ImeAppHelper( @@ -30,7 +30,7 @@ open class ImeAppHelper( ) : BaseAppHelper( instrumentation, TEST_APP_IME_ACTIVITY_LABEL, - TEST_APP_IME_ACTIVITY_COMPONENT_NAME + Components.ImeActivity() ) { fun openIME() { val editText = uiDevice.wait( diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt index 532b3de6c99e..ed5f8a42258b 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt @@ -26,8 +26,8 @@ import androidx.test.uiautomator.Until import com.android.server.wm.flicker.helpers.closePipWindow import com.android.server.wm.flicker.helpers.hasPipWindow import com.android.wm.shell.flicker.SYSTEM_UI_PACKAGE_NAME -import com.android.wm.shell.flicker.TEST_APP_PIP_ACTIVITY_COMPONENT_NAME import com.android.wm.shell.flicker.TEST_APP_PIP_ACTIVITY_LABEL +import com.android.wm.shell.flicker.testapp.Components import org.junit.Assert.assertNotNull import org.junit.Assert.fail @@ -36,7 +36,7 @@ class PipAppHelper( ) : BaseAppHelper( instrumentation, TEST_APP_PIP_ACTIVITY_LABEL, - TEST_APP_PIP_ACTIVITY_COMPONENT_NAME + Components.PipActivity() ) { private val mediaSessionManager: MediaSessionManager get() = context.getSystemService(MediaSessionManager::class.java) @@ -70,6 +70,12 @@ class PipAppHelper( startButton.click() } + fun checkWithCustomActionsCheckbox() = uiDevice + .findObject(By.res(packageName, "with_custom_actions")) + ?.takeIf { it.isCheckable } + ?.apply { if (!isChecked) click() } + ?: error("'With custom actions' checkbox not found") + fun pauseMedia() = mediaController?.transportControls?.pause() ?: error("No active media session found") diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt index 10daa675ce36..e67fc97dad2e 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt @@ -17,19 +17,19 @@ package com.android.wm.shell.flicker.helpers import android.app.Instrumentation -import android.content.ComponentName import android.graphics.Region import android.os.SystemClock import com.android.server.wm.flicker.helpers.WindowUtils +import com.android.wm.shell.flicker.testapp.Components class SplitScreenHelper( instrumentation: Instrumentation, activityLabel: String, - componentName: ComponentName + componentsInfo: Components.ComponentsInfo ) : BaseAppHelper( instrumentation, activityLabel, - componentName + componentsInfo ) { /** diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AppTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AppTestBase.kt new file mode 100644 index 000000000000..2015f4941cea --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AppTestBase.kt @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.pip + +import android.app.ActivityTaskManager +import android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT +import android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS +import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD +import android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED +import android.os.SystemClock +import com.android.wm.shell.flicker.NonRotationTestBase + +abstract class AppTestBase( + rotationName: String, + rotation: Int +) : NonRotationTestBase(rotationName, rotation) { + companion object { + fun removeAllTasksButHome() { + val ALL_ACTIVITY_TYPE_BUT_HOME = intArrayOf( + ACTIVITY_TYPE_STANDARD, ACTIVITY_TYPE_ASSISTANT, ACTIVITY_TYPE_RECENTS, + ACTIVITY_TYPE_UNDEFINED) + val atm = ActivityTaskManager.getService() + atm.removeRootTasksWithActivityTypes(ALL_ACTIVITY_TYPE_BUT_HOME) + } + + fun waitForAnimationComplete() { + // TODO: UiDevice doesn't have reliable way to wait for the completion of animation. + // Consider to introduce WindowManagerStateHelper to access Activity state. + SystemClock.sleep(1000) + } + } +} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/CommonAssertions.kt index 6bc9dcb5d96c..2a660747bc1d 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/CommonAssertions.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/CommonAssertions.kt @@ -14,6 +14,6 @@ * limitations under the License. */ -package com.android.server.wm.flicker.pip +package com.android.wm.shell.flicker.pip internal const val PIP_WINDOW_TITLE = "PipMenuActivity" diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt new file mode 100644 index 000000000000..0663eb344f46 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.pip + +import android.view.Surface +import androidx.test.filters.RequiresDevice +import com.android.server.wm.flicker.dsl.runFlicker +import com.android.server.wm.flicker.helpers.WindowUtils +import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen +import com.android.wm.shell.flicker.helpers.FixedAppHelper +import com.android.wm.shell.flicker.helpers.PipAppHelper +import com.android.wm.shell.flicker.navBarLayerIsAlwaysVisible +import com.android.wm.shell.flicker.navBarWindowIsAlwaysVisible +import com.android.wm.shell.flicker.statusBarLayerIsAlwaysVisible +import com.android.wm.shell.flicker.statusBarWindowIsAlwaysVisible +import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test Pip launch and exit. + * To run this test: `atest WMShellFlickerTests:EnterExitPipTest` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class EnterExitPipTest( + rotationName: String, + rotation: Int +) : AppTestBase(rotationName, rotation) { + private val pipApp = PipAppHelper(instrumentation) + private val testApp = FixedAppHelper(instrumentation) + + @Test + fun testDisplayMetricsPinUnpin() { + runFlicker(instrumentation) { + withTestName { "testDisplayMetricsPinUnpin" } + setup { + test { + removeAllTasksButHome() + device.wakeUpAndGoToHomeScreen() + pipApp.launchViaIntent(stringExtras = mapOf(EXTRA_ENTER_PIP to "true")) + testApp.launchViaIntent() + waitForAnimationComplete() + } + } + transitions { + // This will bring PipApp to fullscreen + pipApp.launchViaIntent() + waitForAnimationComplete() + } + teardown { + test { + removeAllTasksButHome() + } + } + assertions { + val displayBounds = WindowUtils.getDisplayBounds(rotation) + windowManagerTrace { + all("pipApp must remain inside visible bounds") { + coversAtMostRegion(pipApp.defaultWindowName, displayBounds) + } + all("Initially shows both app windows then pipApp hides testApp") { + showsAppWindow(testApp.defaultWindowName) + .and().showsAppWindowOnTop(pipApp.defaultWindowName) + .then() + .hidesAppWindow(testApp.defaultWindowName) + } + navBarWindowIsAlwaysVisible() + statusBarWindowIsAlwaysVisible() + } + layersTrace { + all("Initially shows both app layers then pipApp hides testApp") { + showsLayer(testApp.defaultWindowName) + .and().showsLayer(pipApp.defaultWindowName) + .then() + .hidesLayer(testApp.defaultWindowName) + } + start("testApp covers the fullscreen, pipApp remains inside display") { + hasVisibleRegion(testApp.defaultWindowName, displayBounds) + coversAtMostRegion(displayBounds, pipApp.defaultWindowName) + } + end("pipApp covers the fullscreen") { + hasVisibleRegion(pipApp.defaultWindowName, displayBounds) + } + navBarLayerIsAlwaysVisible() + statusBarLayerIsAlwaysVisible() + } + } + } + } + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<Array<Any>> { + val supportedRotations = intArrayOf(Surface.ROTATION_0) + return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) } + } + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt index a1da7c939f60..cb1fe4e2981d 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt @@ -19,104 +19,113 @@ package com.android.wm.shell.flicker.pip import android.view.Surface import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.dsl.flicker +import androidx.test.platform.app.InstrumentationRegistry +import com.android.server.wm.flicker.Flicker +import com.android.server.wm.flicker.FlickerTestRunner +import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.helpers.closePipWindow import com.android.server.wm.flicker.helpers.expandPipWindow import com.android.server.wm.flicker.helpers.hasPipWindow +import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen -import com.android.wm.shell.flicker.navBarLayerIsAlwaysVisible -import com.android.wm.shell.flicker.navBarLayerRotatesAndScales -import com.android.wm.shell.flicker.navBarWindowIsAlwaysVisible -import com.android.wm.shell.flicker.noUncoveredRegions -import com.android.wm.shell.flicker.statusBarLayerIsAlwaysVisible -import com.android.wm.shell.flicker.statusBarLayerRotatesScales -import com.android.wm.shell.flicker.statusBarWindowIsAlwaysVisible -import com.android.wm.shell.flicker.PIP_WINDOW_NAME +import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.navBarLayerRotatesAndScales +import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.statusBarLayerRotatesScales +import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.noUncoveredRegions +import com.android.server.wm.flicker.repetitions +import com.android.server.wm.flicker.startRotation +import com.android.wm.shell.flicker.helpers.PipAppHelper import org.junit.FixMethodOrder -import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized /** * Test Pip launch. - * To run this test: `atest WMShellFlickerTests:PipToAppTest` + * To run this test: `atest WMShellFlickerTests:EnterPipTest` */ @RequiresDevice @RunWith(Parameterized::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) @FlakyTest(bugId = 152738416) class EnterPipTest( - rotationName: String, - rotation: Int -) : PipTestBase(rotationName, rotation) { - @Test - fun test() { - flicker(instrumentation) { - withTag { buildTestTag("enterPip", testApp, rotation) } - repeat { 1 } - setup { - test { - device.wakeUpAndGoToHomeScreen() - } - eachRun { - device.pressHome() - testApp.open() - this.setRotation(rotation) - } - } - teardown { - eachRun { - if (device.hasPipWindow()) { - device.closePipWindow() + testName: String, + flickerSpec: Flicker +) : FlickerTestRunner(testName, flickerSpec) { + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): List<Array<Any>> { + val instrumentation = InstrumentationRegistry.getInstrumentation() + val testApp = PipAppHelper(instrumentation) + return FlickerTestRunnerFactory(instrumentation, listOf(Surface.ROTATION_0)) + .buildTest { configuration -> + withTestName { buildTestTag("enterPip", testApp, configuration) } + repeat { configuration.repetitions } + setup { + test { + device.wakeUpAndGoToHomeScreen() + } + eachRun { + device.pressHome() + testApp.open() + this.setRotation(configuration.startRotation) + } } - testApp.exit() - this.setRotation(Surface.ROTATION_0) - } - test { - if (device.hasPipWindow()) { - device.closePipWindow() + teardown { + eachRun { + if (device.hasPipWindow()) { + device.closePipWindow() + } + testApp.exit() + this.setRotation(Surface.ROTATION_0) + } + test { + if (device.hasPipWindow()) { + device.closePipWindow() + } + } } - } - } - transitions { - testApp.clickEnterPipButton() - device.expandPipWindow() - } - assertions { - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - all("pipWindowBecomesVisible") { - this.showsAppWindow(testApp.`package`) - .then() - .showsAppWindow(PIP_WINDOW_NAME) + transitions { + testApp.clickEnterPipButton() + device.expandPipWindow() } - } + assertions { + windowManagerTrace { + navBarWindowIsAlwaysVisible() + statusBarWindowIsAlwaysVisible() - layersTrace { - navBarLayerIsAlwaysVisible() - statusBarLayerIsAlwaysVisible() - noUncoveredRegions(rotation, Surface.ROTATION_0, allStates = false) - navBarLayerRotatesAndScales(rotation, Surface.ROTATION_0) - statusBarLayerRotatesScales(rotation, Surface.ROTATION_0) + all("pipWindowBecomesVisible") { + this.showsAppWindow(testApp.`package`) + .then() + .showsAppWindow(PIP_WINDOW_TITLE) + } + } - all("pipLayerBecomesVisible") { - this.showsLayer(testApp.launcherName) - .then() - .showsLayer(PIP_WINDOW_NAME) + layersTrace { + navBarLayerIsAlwaysVisible(bugId = 140855415) + statusBarLayerIsAlwaysVisible() + noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0, + enabled = false) + navBarLayerRotatesAndScales(configuration.startRotation, + Surface.ROTATION_0, bugId = 140855415) + statusBarLayerRotatesScales(configuration.startRotation, + Surface.ROTATION_0) + } + + layersTrace { + all("pipLayerBecomesVisible") { + this.showsLayer(testApp.launcherName) + .then() + .showsLayer(PIP_WINDOW_TITLE) + } + } } } - } - } - } - - companion object { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<Array<Any>> { - val supportedRotations = intArrayOf(Surface.ROTATION_0) - return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) } } } -} +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt index d343f2a7093b..6c4e65818e49 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt @@ -16,28 +16,21 @@ package com.android.wm.shell.flicker.pip -import android.content.ComponentName -import android.graphics.Region -import android.util.Log import android.view.Surface -import android.view.WindowManager import androidx.test.filters.RequiresDevice -import com.android.compatibility.common.util.SystemUtil import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.dsl.runWithFlicker +import com.android.server.wm.flicker.helpers.WindowUtils import com.android.server.wm.flicker.helpers.closePipWindow import com.android.server.wm.flicker.helpers.hasPipWindow import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen -import com.android.wm.shell.flicker.TEST_APP_IME_ACTIVITY_COMPONENT_NAME import com.android.wm.shell.flicker.IME_WINDOW_NAME -import com.android.wm.shell.flicker.TEST_APP_PIP_ACTIVITY_WINDOW_NAME import com.android.wm.shell.flicker.helpers.ImeAppHelper import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized -import java.io.IOException /** * Test Pip launch. @@ -50,9 +43,6 @@ class PipKeyboardTest( rotationName: String, rotation: Int ) : PipTestBase(rotationName, rotation) { - private val windowManager: WindowManager = - instrumentation.context.getSystemService(WindowManager::class.java) - private val keyboardApp = ImeAppHelper(instrumentation) private val keyboardScenario: FlickerBuilder @@ -71,7 +61,7 @@ class PipKeyboardTest( // open an app with an input field and a keyboard // UiAutomator doesn't support to launch the multiple Activities in a task. // So use launchActivity() for the Keyboard Activity. - launchActivity(TEST_APP_IME_ACTIVITY_COMPONENT_NAME) + keyboardApp.launchViaIntent() } } teardown { @@ -103,10 +93,8 @@ class PipKeyboardTest( assertions { windowManagerTrace { all("PiP window must remain inside visible bounds") { - coversAtMostRegion( - partialWindowTitle = "PipActivity", - region = Region(windowManager.maximumWindowMetrics.bounds) - ) + val displayBounds = WindowUtils.getDisplayBounds(rotation) + coversAtMostRegion(testApp.defaultWindowName, displayBounds) } } } @@ -132,74 +120,13 @@ class PipKeyboardTest( assertions { windowManagerTrace { end { - isAboveWindow(IME_WINDOW_NAME, TEST_APP_PIP_ACTIVITY_WINDOW_NAME) + isAboveWindow(IME_WINDOW_NAME, testApp.defaultWindowName) } } } } } - private fun launchActivity( - activity: ComponentName? = null, - action: String? = null, - flags: Set<Int> = setOf(), - boolExtras: Map<String, Boolean> = mapOf(), - intExtras: Map<String, Int> = mapOf(), - stringExtras: Map<String, String> = mapOf() - ) { - require(activity != null || !action.isNullOrBlank()) { - "Cannot launch an activity with neither activity name nor action!" - } - val command = composeCommand( - "start", activity, action, flags, boolExtras, intExtras, stringExtras) - executeShellCommand(command) - } - - private fun composeCommand( - command: String, - activity: ComponentName?, - action: String?, - flags: Set<Int>, - boolExtras: Map<String, Boolean>, - intExtras: Map<String, Int>, - stringExtras: Map<String, String> - ): String = buildString { - append("am ") - append(command) - activity?.let { - append(" -n ") - append(it.flattenToShortString()) - } - action?.let { - append(" -a ") - append(it) - } - flags.forEach { - append(" -f ") - append(it) - } - boolExtras.forEach { - append(it.withFlag("ez")) - } - intExtras.forEach { - append(it.withFlag("ei")) - } - stringExtras.forEach { - append(it.withFlag("es")) - } - } - - private fun Map.Entry<String, *>.withFlag(flag: String): String = " --$flag $key $value" - - private fun executeShellCommand(cmd: String): String { - try { - return SystemUtil.runShellCommand(instrumentation, cmd) - } catch (e: IOException) { - Log.e("FlickerTests", "Error running shell command: $cmd") - throw e - } - } - companion object { private const val TEST_REPETITIONS = 10 @@ -210,4 +137,4 @@ class PipKeyboardTest( return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) } } } -}
\ No newline at end of file +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipOrientationTest.kt new file mode 100644 index 000000000000..322034ce7688 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipOrientationTest.kt @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.pip + +import android.content.Intent +import android.view.Surface +import androidx.test.filters.RequiresDevice +import com.android.server.wm.flicker.dsl.runFlicker +import com.android.server.wm.flicker.helpers.WindowUtils +import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen +import com.android.wm.shell.flicker.helpers.FixedAppHelper +import com.android.wm.shell.flicker.helpers.PipAppHelper +import com.android.wm.shell.flicker.navBarLayerIsAlwaysVisible +import com.android.wm.shell.flicker.navBarWindowIsAlwaysVisible +import com.android.wm.shell.flicker.statusBarLayerIsAlwaysVisible +import com.android.wm.shell.flicker.statusBarWindowIsAlwaysVisible +import com.android.wm.shell.flicker.testapp.Components.PipActivity.ACTION_ENTER_PIP +import com.android.wm.shell.flicker.testapp.Components.PipActivity.ACTION_SET_REQUESTED_ORIENTATION +import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP +import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_PIP_ORIENTATION +import com.android.wm.shell.flicker.testapp.Components.FixedActivity.EXTRA_FIXED_ORIENTATION +import org.junit.Assert.assertEquals +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test Pip with orientation changes. + * To run this test: `atest WMShellFlickerTests:PipOrientationTest` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class PipOrientationTest( + rotationName: String, + rotation: Int +) : AppTestBase(rotationName, rotation) { + // Helper class to process test actions by broadcast. + private inner class BroadcastActionTrigger { + private fun createIntentWithAction(broadcastAction: String): Intent { + return Intent(broadcastAction).setFlags(Intent.FLAG_RECEIVER_FOREGROUND) + } + fun doAction(broadcastAction: String) { + instrumentation.getContext().sendBroadcast(createIntentWithAction(broadcastAction)) + } + fun requestOrientationForPip(orientation: Int) { + instrumentation.getContext() + .sendBroadcast(createIntentWithAction(ACTION_SET_REQUESTED_ORIENTATION) + .putExtra(EXTRA_PIP_ORIENTATION, orientation.toString())) + } + } + private val broadcastActionTrigger = BroadcastActionTrigger() + + // Corresponds to ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE + private val ORIENTATION_LANDSCAPE = 0 + // Corresponds to ActivityInfo.SCREEN_ORIENTATION_PORTRAIT + private val ORIENTATION_PORTRAIT = 1 + + private val testApp = FixedAppHelper(instrumentation) + private val pipApp = PipAppHelper(instrumentation) + + @Test + fun testEnterPipToOtherOrientation() { + runFlicker(instrumentation) { + withTestName { "testEnterPipToOtherOrientation" } + setup { + test { + removeAllTasksButHome() + device.wakeUpAndGoToHomeScreen() + // Launch a portrait only app on the fullscreen stack + testApp.launchViaIntent(stringExtras = mapOf( + EXTRA_FIXED_ORIENTATION to ORIENTATION_PORTRAIT.toString())) + waitForAnimationComplete() + // Launch the PiP activity fixed as landscape + pipApp.launchViaIntent(stringExtras = mapOf( + EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString())) + waitForAnimationComplete() + } + } + transitions { + // Enter PiP, and assert that the PiP is within bounds now that the device is back + // in portrait + broadcastActionTrigger.doAction(ACTION_ENTER_PIP) + waitForAnimationComplete() + } + teardown { + test { + removeAllTasksButHome() + } + } + assertions { + windowManagerTrace { + all("pipApp window is always on top") { + showsAppWindowOnTop(pipApp.defaultWindowName) + } + start("pipApp window hides testApp") { + hidesAppWindow(testApp.defaultWindowName) + } + end("testApp windows is shown") { + showsAppWindow(testApp.defaultWindowName) + } + navBarWindowIsAlwaysVisible() + statusBarWindowIsAlwaysVisible() + } + layersTrace { + val startingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_90) + val endingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_0) + start("pipApp layer hides testApp") { + hasVisibleRegion(pipApp.defaultWindowName, startingBounds) + hidesLayer(testApp.defaultWindowName) + } + end("testApp layer covers fullscreen") { + hasVisibleRegion(testApp.defaultWindowName, endingBounds) + } + navBarLayerIsAlwaysVisible(bugId = 140855415) + statusBarLayerIsAlwaysVisible(bugId = 140855415) + } + } + } + } + + @Test + fun testSetRequestedOrientationWhilePinned() { + runFlicker(instrumentation) { + withTestName { "testSetRequestedOrientationWhilePinned" } + setup { + test { + removeAllTasksButHome() + device.wakeUpAndGoToHomeScreen() + // Launch the PiP activity fixed as landscape + pipApp.launchViaIntent(stringExtras = mapOf( + EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString(), + EXTRA_ENTER_PIP to "true")) + waitForAnimationComplete() + assertEquals(Surface.ROTATION_0, device.displayRotation) + } + } + transitions { + // Request that the orientation is set to landscape + broadcastActionTrigger.requestOrientationForPip(ORIENTATION_LANDSCAPE) + + // Launch the activity back into fullscreen and ensure that it is now in landscape + pipApp.launchViaIntent() + waitForAnimationComplete() + assertEquals(Surface.ROTATION_90, device.displayRotation) + } + teardown { + test { + removeAllTasksButHome() + } + } + assertions { + val startingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_0) + val endingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_90) + windowManagerTrace { + start("PIP window must remain inside display") { + coversAtMostRegion(pipApp.defaultWindowName, startingBounds) + } + end("pipApp shows on top") { + showsAppWindowOnTop(pipApp.defaultWindowName) + } + navBarWindowIsAlwaysVisible() + statusBarWindowIsAlwaysVisible() + } + layersTrace { + start("PIP layer must remain inside display") { + coversAtMostRegion(startingBounds, pipApp.defaultWindowName) + } + end("pipApp layer covers fullscreen") { + hasVisibleRegion(pipApp.defaultWindowName, endingBounds) + } + navBarLayerIsAlwaysVisible(bugId = 140855415) + statusBarLayerIsAlwaysVisible(bugId = 140855415) + } + } + } + } + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<Array<Any>> { + val supportedRotations = intArrayOf(Surface.ROTATION_0) + return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) } + } + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt new file mode 100644 index 000000000000..96d98d56e069 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.pip + +import android.view.Surface +import androidx.test.filters.RequiresDevice +import androidx.test.platform.app.InstrumentationRegistry +import com.android.server.wm.flicker.Flicker +import com.android.server.wm.flicker.FlickerTestRunner +import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.endRotation +import com.android.server.wm.flicker.helpers.WindowUtils +import com.android.server.wm.flicker.helpers.buildTestTag +import com.android.server.wm.flicker.helpers.setRotation +import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen +import com.android.server.wm.flicker.repetitions +import com.android.server.wm.flicker.startRotation +import com.android.wm.shell.flicker.helpers.FixedAppHelper +import com.android.wm.shell.flicker.helpers.PipAppHelper +import com.android.wm.shell.flicker.navBarLayerIsAlwaysVisible +import com.android.wm.shell.flicker.navBarLayerRotatesAndScales +import com.android.wm.shell.flicker.navBarWindowIsAlwaysVisible +import com.android.wm.shell.flicker.noUncoveredRegions +import com.android.wm.shell.flicker.statusBarLayerIsAlwaysVisible +import com.android.wm.shell.flicker.statusBarLayerRotatesScales +import com.android.wm.shell.flicker.statusBarWindowIsAlwaysVisible +import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP +import org.junit.FixMethodOrder +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test Pip Stack in bounds after rotations. + * To run this test: `atest WMShellFlickerTests:PipRotationTest` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class PipRotationTest( + testName: String, + flickerSpec: Flicker +) : FlickerTestRunner(testName, flickerSpec) { + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<Array<Any>> { + val instrumentation = InstrumentationRegistry.getInstrumentation() + val testApp = FixedAppHelper(instrumentation) + val pipApp = PipAppHelper(instrumentation) + return FlickerTestRunnerFactory(instrumentation, + listOf(Surface.ROTATION_0, Surface.ROTATION_90)) + .buildRotationTest { configuration -> + withTestName { buildTestTag("PipRotationTest", testApp, configuration) } + repeat { configuration.repetitions } + setup { + test { + AppTestBase.removeAllTasksButHome() + device.wakeUpAndGoToHomeScreen() + pipApp.launchViaIntent(stringExtras = mapOf( + EXTRA_ENTER_PIP to "true")) + testApp.launchViaIntent() + AppTestBase.waitForAnimationComplete() + } + eachRun { + setRotation(configuration.startRotation) + } + } + transitions { + setRotation(configuration.endRotation) + } + teardown { + eachRun { + setRotation(Surface.ROTATION_0) + } + test { + AppTestBase.removeAllTasksButHome() + } + } + assertions { + windowManagerTrace { + navBarWindowIsAlwaysVisible() + statusBarWindowIsAlwaysVisible() + } + layersTrace { + navBarLayerIsAlwaysVisible(bugId = 140855415) + statusBarLayerIsAlwaysVisible(bugId = 140855415) + noUncoveredRegions(configuration.startRotation, + configuration.endRotation, allStates = false) + navBarLayerRotatesAndScales(configuration.startRotation, + configuration.endRotation) + statusBarLayerRotatesScales(configuration.startRotation, + configuration.endRotation) + } + layersTrace { + val startingBounds = WindowUtils.getDisplayBounds( + configuration.startRotation) + val endingBounds = WindowUtils.getDisplayBounds( + configuration.endRotation) + start("appLayerRotates_StartingBounds") { + hasVisibleRegion(testApp.defaultWindowName, startingBounds) + coversAtMostRegion(startingBounds, pipApp.defaultWindowName) + } + end("appLayerRotates_EndingBounds") { + hasVisibleRegion(testApp.defaultWindowName, endingBounds) + coversAtMostRegion(endingBounds, pipApp.defaultWindowName) + } + } + } + } + } + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipSplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipSplitScreenTest.kt new file mode 100644 index 000000000000..d20552f0739d --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipSplitScreenTest.kt @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.pip + +import android.view.Surface +import androidx.test.filters.FlakyTest +import androidx.test.filters.RequiresDevice +import com.android.server.wm.flicker.dsl.runFlicker +import com.android.server.wm.flicker.helpers.WindowUtils +import com.android.server.wm.flicker.helpers.exitSplitScreen +import com.android.server.wm.flicker.helpers.isInSplitScreen +import com.android.server.wm.flicker.helpers.launchSplitScreen +import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen +import com.android.wm.shell.flicker.helpers.ImeAppHelper +import com.android.wm.shell.flicker.helpers.FixedAppHelper +import com.android.wm.shell.flicker.helpers.PipAppHelper +import com.android.wm.shell.flicker.navBarLayerIsAlwaysVisible +import com.android.wm.shell.flicker.navBarWindowIsAlwaysVisible +import com.android.wm.shell.flicker.statusBarLayerIsAlwaysVisible +import com.android.wm.shell.flicker.statusBarWindowIsAlwaysVisible +import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test Pip with split-screen. + * To run this test: `atest WMShellFlickerTests:PipSplitScreenTest` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@FlakyTest(bugId = 161435597) +class PipSplitScreenTest( + rotationName: String, + rotation: Int +) : AppTestBase(rotationName, rotation) { + private val pipApp = PipAppHelper(instrumentation) + private val imeApp = ImeAppHelper(instrumentation) + private val testApp = FixedAppHelper(instrumentation) + + @Test + fun testShowsPipLaunchingToSplitScreen() { + runFlicker(instrumentation) { + withTestName { "testShowsPipLaunchingToSplitScreen" } + repeat { TEST_REPETITIONS } + setup { + test { + removeAllTasksButHome() + device.wakeUpAndGoToHomeScreen() + pipApp.launchViaIntent(stringExtras = mapOf(EXTRA_ENTER_PIP to "true")) + waitForAnimationComplete() + } + } + transitions { + testApp.launchViaIntent() + device.launchSplitScreen() + imeApp.launchViaIntent() + waitForAnimationComplete() + } + teardown { + eachRun { + imeApp.exit() + if (device.isInSplitScreen()) { + device.exitSplitScreen() + } + testApp.exit() + } + test { + removeAllTasksButHome() + } + } + assertions { + val displayBounds = WindowUtils.getDisplayBounds(rotation) + windowManagerTrace { + all("PIP window must remain inside visible bounds") { + coversAtMostRegion(pipApp.defaultWindowName, displayBounds) + } + end("Both app windows should be visible") { + showsAppWindow(testApp.defaultWindowName) + showsAppWindow(imeApp.defaultWindowName) + noWindowsOverlap(testApp.defaultWindowName, imeApp.defaultWindowName) + } + navBarWindowIsAlwaysVisible() + statusBarWindowIsAlwaysVisible() + } + layersTrace { + all("PIP layer must remain inside visible bounds") { + coversAtMostRegion(displayBounds, pipApp.defaultWindowName) + } + end("Both app layers should be visible") { + coversAtMostRegion(displayBounds, testApp.defaultWindowName) + coversAtMostRegion(displayBounds, imeApp.defaultWindowName) + } + navBarLayerIsAlwaysVisible() + statusBarLayerIsAlwaysVisible() + } + } + } + } + + companion object { + const val TEST_REPETITIONS = 2 + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<Array<Any>> { + val supportedRotations = intArrayOf(Surface.ROTATION_0) + return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) } + } + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTestBase.kt index c1c34ecfbaec..03a92119fc86 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTestBase.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTestBase.kt @@ -16,12 +16,11 @@ package com.android.wm.shell.flicker.pip -import com.android.wm.shell.flicker.NonRotationTestBase import com.android.wm.shell.flicker.helpers.PipAppHelper abstract class PipTestBase( rotationName: String, rotation: Int -) : NonRotationTestBase(rotationName, rotation) { +) : AppTestBase(rotationName, rotation) { protected val testApp = PipAppHelper(instrumentation) } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt index ac54a0a29350..e1fa6578e552 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToAppTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.wm.flicker.pip +package com.android.wm.shell.flicker.pip import android.view.Surface import androidx.test.filters.FlakyTest @@ -24,7 +24,6 @@ import com.android.server.wm.flicker.Flicker import com.android.server.wm.flicker.FlickerTestRunner import com.android.server.wm.flicker.FlickerTestRunnerFactory import com.android.server.wm.flicker.focusChanges -import com.android.server.wm.flicker.helpers.PipAppHelper import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.helpers.closePipWindow import com.android.server.wm.flicker.helpers.expandPipWindow @@ -40,6 +39,7 @@ import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible +import com.android.wm.shell.flicker.helpers.PipAppHelper import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -47,7 +47,7 @@ import org.junit.runners.Parameterized /** * Test Pip launch. - * To run this test: `atest FlickerTests:PipToAppTest` + * To run this test: `atest WMShellFlickerTests:PipToAppTest` */ @RequiresDevice @RunWith(Parameterized::class) @@ -75,7 +75,7 @@ class PipToAppTest( } eachRun { this.setRotation(configuration.startRotation) - testApp.clickEnterPipButton(device) + testApp.clickEnterPipButton() device.hasPipWindow() } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToHomeTest.kt index f14a27d31ad1..bf1193786a59 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToHomeTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToHomeTest.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.wm.flicker.pip +package com.android.wm.shell.flicker.pip import android.view.Surface import androidx.test.filters.FlakyTest @@ -24,7 +24,6 @@ import com.android.server.wm.flicker.Flicker import com.android.server.wm.flicker.FlickerTestRunner import com.android.server.wm.flicker.FlickerTestRunnerFactory import com.android.server.wm.flicker.focusChanges -import com.android.server.wm.flicker.helpers.PipAppHelper import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.helpers.closePipWindow import com.android.server.wm.flicker.helpers.hasPipWindow @@ -39,6 +38,7 @@ import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible +import com.android.wm.shell.flicker.helpers.PipAppHelper import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -46,7 +46,7 @@ import org.junit.runners.Parameterized /** * Test Pip launch. - * To run this test: `atest FlickerTests:PipToHomeTest` + * To run this test: `atest WMShellFlickerTests:PipToHomeTest` */ @RequiresDevice @RunWith(Parameterized::class) @@ -74,7 +74,7 @@ class PipToHomeTest( eachRun { testApp.open() this.setRotation(configuration.startRotation) - testApp.clickEnterPipButton(device) + testApp.clickEnterPipButton() device.hasPipWindow() } } @@ -93,7 +93,7 @@ class PipToHomeTest( } } transitions { - testApp.closePipWindow(device) + testApp.closePipWindow() } assertions { windowManagerTrace { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt index 871732cf7460..4cb6447f7d7e 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt @@ -20,7 +20,12 @@ import android.graphics.Rect import androidx.test.filters.RequiresDevice import androidx.test.uiautomator.UiObject2 import com.android.wm.shell.flicker.SYSTEM_UI_PACKAGE_NAME +import com.android.wm.shell.flicker.TEST_APP_PIP_MENU_ACTION_CLEAR +import com.android.wm.shell.flicker.TEST_APP_PIP_MENU_ACTION_NO_OP +import com.android.wm.shell.flicker.TEST_APP_PIP_MENU_ACTION_OFF +import com.android.wm.shell.flicker.TEST_APP_PIP_MENU_ACTION_ON import com.android.wm.shell.flicker.wait +import org.junit.Assert.assertNull import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test @@ -128,6 +133,7 @@ class TvPipMenuTests : TvPipTestBase() { testApp.clickStartMediaSessionButton() enterPip_openMenu_assertShown() + assertFullscreenAndCloseButtonsAreShown() // PiP menu should contain the Pause button val pauseButton = uiDevice.findTvPipMenuElementWithDescription(pauseButtonDescription) @@ -137,6 +143,7 @@ class TvPipMenuTests : TvPipTestBase() { // When we pause media, the button should change from Pause to Play pauseButton.click() + assertFullscreenAndCloseButtonsAreShown() // PiP menu should contain the Play button now uiDevice.waitForTvPipMenuElementWithDescription(playButtonDescription) ?: fail("\"Play\" button should be shown in Pip menu if there is an active " + @@ -145,10 +152,99 @@ class TvPipMenuTests : TvPipTestBase() { testApp.closePipWindow() } + @Test + fun pipMenu_withCustomActions() { + // Enter PiP with custom actions. + testApp.checkWithCustomActionsCheckbox() + enterPip_openMenu_assertShown() + + // PiP menu should contain "No-Op", "Off" and "Clear" buttons... + uiDevice.findTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_NO_OP) + ?: fail("\"No-Op\" button should be shown in Pip menu") + val offButton = uiDevice.findTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_OFF) + ?: fail("\"Off\" button should be shown in Pip menu") + uiDevice.findTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_CLEAR) + ?: fail("\"Clear\" button should be shown in Pip menu") + // ... and should also contain the "Full screen" and "Close" buttons. + assertFullscreenAndCloseButtonsAreShown() + + offButton.click() + // Invoking the "Off" action should replace it with the "On" action/button and should + // remove the "No-Op" action/button. "Clear" action/button should remain in the menu ... + uiDevice.waitForTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_ON) + ?: fail("\"On\" button should be shown in Pip for a corresponding custom action") + assertNull("\"No-Op\" button should not be shown in Pip menu", + uiDevice.findTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_NO_OP)) + val clearButton = + uiDevice.findTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_CLEAR) + ?: fail("\"Clear\" button should be shown in Pip menu") + // ... as well as the "Full screen" and "Close" buttons. + assertFullscreenAndCloseButtonsAreShown() + + clearButton.click() + // Invoking the "Clear" action should remove all the custom actions and their corresponding + // buttons, ... + uiDevice.waitUntilTvPipMenuElementWithDescriptionIsGone(TEST_APP_PIP_MENU_ACTION_ON)?.also { + isGone -> if (!isGone) fail("\"On\" button should not be shown in Pip menu") + } + assertNull("\"Off\" button should not be shown in Pip menu", + uiDevice.findTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_OFF)) + assertNull("\"Clear\" button should not be shown in Pip menu", + uiDevice.findTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_CLEAR)) + assertNull("\"No-Op\" button should not be shown in Pip menu", + uiDevice.findTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_NO_OP)) + // ... but the menu should still contain the "Full screen" and "Close" buttons. + assertFullscreenAndCloseButtonsAreShown() + + testApp.closePipWindow() + } + + @Test + fun pipMenu_customActions_override_mediaControls() { + // Start media session before entering PiP with custom actions. + testApp.clickStartMediaSessionButton() + testApp.checkWithCustomActionsCheckbox() + enterPip_openMenu_assertShown() + + // PiP menu should contain "No-Op", "Off" and "Clear" buttons for the custom actions... + uiDevice.findTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_NO_OP) + ?: fail("\"No-Op\" button should be shown in Pip menu") + uiDevice.findTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_OFF) + ?: fail("\"Off\" button should be shown in Pip menu") + val clearButton = + uiDevice.findTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_CLEAR) + ?: fail("\"Clear\" button should be shown in Pip menu") + // ... should also contain the "Full screen" and "Close" buttons, ... + assertFullscreenAndCloseButtonsAreShown() + // ... but should not contain media buttons. + assertNull("\"Play\" button should not be shown in menu when there are custom actions", + uiDevice.findTvPipMenuElementWithDescription(playButtonDescription)) + assertNull("\"Pause\" button should not be shown in menu when there are custom actions", + uiDevice.findTvPipMenuElementWithDescription(pauseButtonDescription)) + + clearButton.click() + // Invoking the "Clear" action should remove all the custom actions, which should bring up + // media buttons... + uiDevice.waitForTvPipMenuElementWithDescription(pauseButtonDescription) + ?: fail("\"Pause\" button should be shown in Pip menu if there is an active " + + "playing media session.") + // ... while the "Full screen" and "Close" buttons should remain in the menu. + assertFullscreenAndCloseButtonsAreShown() + + testApp.closePipWindow() + } + private fun enterPip_openMenu_assertShown(): UiObject2 { testApp.clickEnterPipButton() // Pressing the Window key should bring up Pip menu uiDevice.pressWindowKey() return uiDevice.waitForTvPipMenu() ?: fail("Pip menu should have been shown") } + + private fun assertFullscreenAndCloseButtonsAreShown() { + uiDevice.findTvPipMenuCloseButton() + ?: fail("\"Close PIP\" button should be shown in Pip menu") + uiDevice.findTvPipMenuFullscreenButton() + ?: fail("\"Full screen\" button should be shown in Pip menu") + } }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt index d1906ba2e27e..251bc064352d 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt @@ -53,7 +53,7 @@ abstract class TvPipTestBase : PipTestBase(rotationToString(ROTATION_0), ROTATIO open fun tearDown() { if (!isTelevision) return - testApp.forceStop() + testApp.exit() // Wait for 1 second, and check if the SystemUI has been alive and well since the start. SystemClock.sleep(AFTER_TEXT_PROCESS_CHECK_DELAY) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt index 8db8bc67da14..0732794903b7 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt @@ -58,6 +58,9 @@ fun UiDevice.waitForTvPipMenuElementWithDescription(desc: String): UiObject2? { ?.findObject(buttonSelector) } +fun UiDevice.waitUntilTvPipMenuElementWithDescriptionIsGone(desc: String): Boolean? = + wait(Until.gone(By.copy(tvPipMenuSelector).hasDescendant(By.desc(desc))), WAIT_TIME_MS) + fun UiObject2.isFullscreen(uiDevice: UiDevice): Boolean = visibleBounds.run { height() == uiDevice.displayHeight && width() == uiDevice.displayWidth }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenTest.kt index 6e10f932c745..a0056dfc0948 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenTest.kt @@ -60,8 +60,8 @@ class EnterSplitScreenTest( } teardown { eachRun { - splitScreenApp.forceStop() - secondaryApp.forceStop() + splitScreenApp.exit() + secondaryApp.exit() } } assertions { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/ExitSplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/ExitSplitScreenTest.kt index 17fc862fffec..32e112dbd598 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/ExitSplitScreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/ExitSplitScreenTest.kt @@ -65,8 +65,8 @@ class ExitSplitScreenTest( } teardown { eachRun { - splitScreenApp.forceStop()!! - secondaryApp.forceStop()!! + splitScreenApp.exit() + secondaryApp.exit() } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/OpenAppToSplitScreenTest.kt index ae9fcf9a3b6e..1e328a8dae40 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/OpenAppToSplitScreenTest.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.wm.flicker.splitscreen +package com.android.wm.shell.flicker.splitscreen import android.platform.test.annotations.Presubmit import androidx.test.filters.FlakyTest @@ -48,7 +48,7 @@ import org.junit.runners.Parameterized /** * Test open app to split screen. - * To run this test: `atest FlickerTests:OpenAppToSplitScreenTest` + * To run this test: `atest WMShellFlickerTests:OpenAppToSplitScreenTest` */ @Presubmit @RequiresDevice @@ -65,7 +65,7 @@ class OpenAppToSplitScreenTest( fun getParams(): Collection<Array<Any>> { val instrumentation = InstrumentationRegistry.getInstrumentation() val testApp = StandardAppHelper(instrumentation, - "com.android.server.wm.flicker.testapp", "SimpleApp") + "com.android.wm.shell.flicker.testapp", "SimpleApp") return FlickerTestRunnerFactory(instrumentation) .buildTest { configuration -> diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/ResizeSplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/ResizeSplitScreenTest.kt index 4b9f02412f2a..7c83846621de 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/ResizeSplitScreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/ResizeSplitScreenTest.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.wm.flicker.splitscreen +package com.android.wm.shell.flicker.splitscreen import android.graphics.Region import android.util.Rational @@ -29,8 +29,8 @@ import com.android.server.wm.flicker.FlickerTestRunner import com.android.server.wm.flicker.FlickerTestRunnerFactory import com.android.server.wm.flicker.endRotation import com.android.server.wm.flicker.helpers.StandardAppHelper -import com.android.server.wm.flicker.focusDoesNotChange import com.android.server.wm.flicker.helpers.ImeAppHelper +import com.android.server.wm.flicker.focusDoesNotChange import com.android.server.wm.flicker.helpers.WindowUtils import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.helpers.exitSplitScreen @@ -55,7 +55,7 @@ import org.junit.runners.Parameterized /** * Test split screen resizing window transitions. - * To run this test: `atest FlickerTests:ResizeSplitScreenTest` + * To run this test: `atest WMShellFlickerTests:ResizeSplitScreenTest` * * Currently it runs only in 0 degrees because of b/156100803 */ @@ -78,7 +78,7 @@ class ResizeSplitScreenTest( fun getParams(): Collection<Array<Any>> { val instrumentation = InstrumentationRegistry.getInstrumentation() val testAppTop = StandardAppHelper(instrumentation, - "com.android.server.wm.flicker.testapp", "SimpleApp") + "com.android.wm.shell.flicker.testapp", "SimpleApp") val testAppBottom = ImeAppHelper(instrumentation) return FlickerTestRunnerFactory(instrumentation, listOf(Surface.ROTATION_0)) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenTestBase.kt index 496fe94ba951..a3440df9ddf8 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenTestBase.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenTestBase.kt @@ -17,11 +17,10 @@ package com.android.wm.shell.flicker.splitscreen import com.android.wm.shell.flicker.NonRotationTestBase -import com.android.wm.shell.flicker.TEST_APP_SPLITSCREEN_PRIMARY_COMPONENT_NAME import com.android.wm.shell.flicker.TEST_APP_SPLITSCREEN_PRIMARY_LABEL -import com.android.wm.shell.flicker.TEST_APP_SPLITSCREEN_SECONDARY_COMPONENT_NAME import com.android.wm.shell.flicker.TEST_APP_SPLITSCREEN_SECONDARY_LABEL import com.android.wm.shell.flicker.helpers.SplitScreenHelper +import com.android.wm.shell.flicker.testapp.Components abstract class SplitScreenTestBase( rotationName: String, @@ -29,8 +28,8 @@ abstract class SplitScreenTestBase( ) : NonRotationTestBase(rotationName, rotation) { protected val splitScreenApp = SplitScreenHelper(instrumentation, TEST_APP_SPLITSCREEN_PRIMARY_LABEL, - TEST_APP_SPLITSCREEN_PRIMARY_COMPONENT_NAME) + Components.SplitScreenActivity()) protected val secondaryApp = SplitScreenHelper(instrumentation, TEST_APP_SPLITSCREEN_SECONDARY_LABEL, - TEST_APP_SPLITSCREEN_SECONDARY_COMPONENT_NAME) + Components.SplitScreenSecondaryActivity()) } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/SplitScreenToLauncherTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenToLauncherTest.kt index f966a66aa0a1..00979fa9fac0 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/SplitScreenToLauncherTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenToLauncherTest.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.wm.flicker.splitscreen +package com.android.wm.shell.flicker.splitscreen import android.platform.test.annotations.Presubmit import android.view.Surface @@ -48,7 +48,7 @@ import org.junit.runners.Parameterized /** * Test open app to split screen. - * To run this test: `atest FlickerTests:SplitScreenToLauncherTest` + * To run this test: `atest WMShellFlickerTests:SplitScreenToLauncherTest` */ @Presubmit @RequiresDevice @@ -64,7 +64,7 @@ class SplitScreenToLauncherTest( fun getParams(): Collection<Array<Any>> { val instrumentation = InstrumentationRegistry.getInstrumentation() val testApp = StandardAppHelper(instrumentation, - "com.android.server.wm.flicker.testapp", "SimpleApp") + "com.android.wm.shell.flicker.testapp", "SimpleApp") // b/161435597 causes the test not to work on 90 degrees return FlickerTestRunnerFactory(instrumentation, listOf(Surface.ROTATION_0)) diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/Android.bp b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/Android.bp index d12b49245277..26627a47ee62 100644 --- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/Android.bp +++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/Android.bp @@ -18,3 +18,9 @@ android_test { sdk_version: "current", test_suites: ["device-tests"], } + +java_library { + name: "wmshell-flicker-test-components", + srcs: ["src/**/Components.java"], + sdk_version: "test_current", +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml index 2ce120448ac4..a583b725899b 100644 --- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml +++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml @@ -21,13 +21,25 @@ android:targetSdkVersion="29"/> <application android:allowBackup="false" android:supportsRtl="true"> + <activity android:name=".FixedActivity" + android:resizeableActivity="true" + android:supportsPictureInPicture="true" + android:launchMode="singleTop" + android:label="FixedApp" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> + </activity> <activity android:name=".PipActivity" - android:resizeableActivity="true" - android:supportsPictureInPicture="true" - android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation" - android:taskAffinity="com.android.wm.shell.flicker.testapp.PipActivity" - android:label="PipApp" - android:exported="true"> + android:resizeableActivity="true" + android:supportsPictureInPicture="true" + android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation" + android:taskAffinity="com.android.wm.shell.flicker.testapp.PipActivity" + android:launchMode="singleTop" + android:label="PipApp" + android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> @@ -39,9 +51,9 @@ </activity> <activity android:name=".ImeActivity" - android:taskAffinity="com.android.wm.shell.flicker.testapp.ImeActivity" - android:label="ImeApp" - android:exported="true"> + android:taskAffinity="com.android.wm.shell.flicker.testapp.ImeActivity" + android:label="ImeApp" + android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> @@ -73,5 +85,15 @@ <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> + + <activity android:name=".SimpleActivity" + android:taskAffinity="com.android.wm.shell.flicker.testapp.SimpleActivity" + android:label="SimpleApp" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> + </activity> </application> </manifest> diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_pip.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_pip.xml index b4a4c165cc7b..e5d2f82080a2 100644 --- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_pip.xml +++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_pip.xml @@ -28,6 +28,12 @@ android:text="Enter PIP" android:onClick="enterPip"/> + <CheckBox + android:id="@+id/with_custom_actions" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="With custom actions"/> + <RadioGroup android:layout_width="match_parent" android:layout_height="wrap_content" diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_simple.xml index 2c58d91e34fe..5d94e5177dcc 100644 --- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml +++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_simple.xml @@ -18,9 +18,6 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@android:color/holo_blue_bright"> - <Button android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:id="@+id/enter_pip" - android:text="Enter PIP"/> + android:background="@android:color/holo_orange_light"> + </LinearLayout> diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/Components.java b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/Components.java new file mode 100644 index 000000000000..8e9b4cb2d53e --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/Components.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.testapp; + +import android.content.ComponentName; + +public class Components { + public abstract static class ComponentsInfo { + public ComponentName getComponentName() { + return ComponentName.createRelative(PACKAGE_NAME, "." + getActivityName()); + } + public abstract String getActivityName(); + } + + public static final String PACKAGE_NAME = "com.android.wm.shell.flicker.testapp"; + + public static class FixedActivity extends ComponentsInfo { + // Sets the fixed orientation (can be one of {@link ActivityInfo.ScreenOrientation} + public static final String EXTRA_FIXED_ORIENTATION = "fixed_orientation"; + + @Override + public String getActivityName() { + return FixedActivity.class.getSimpleName(); + } + } + + public static class PipActivity extends ComponentsInfo { + // Intent action that this activity dynamically registers to enter picture-in-picture + public static final String ACTION_ENTER_PIP = PACKAGE_NAME + ".PipActivity.ENTER_PIP"; + // Intent action that this activity dynamically registers to set requested orientation. + // Will apply the oriention to the value set in the EXTRA_FIXED_ORIENTATION extra. + public static final String ACTION_SET_REQUESTED_ORIENTATION = + PACKAGE_NAME + ".PipActivity.SET_REQUESTED_ORIENTATION"; + + // Calls enterPictureInPicture() on creation + public static final String EXTRA_ENTER_PIP = "enter_pip"; + // Sets the fixed orientation (can be one of {@link ActivityInfo.ScreenOrientation} + public static final String EXTRA_PIP_ORIENTATION = "fixed_orientation"; + // Adds a click listener to finish this activity when it is clicked + public static final String EXTRA_TAP_TO_FINISH = "tap_to_finish"; + + @Override + public String getActivityName() { + return PipActivity.class.getSimpleName(); + } + } + + public static class ImeActivity extends ComponentsInfo { + @Override + public String getActivityName() { + return ImeActivity.class.getSimpleName(); + } + } + + public static class SplitScreenActivity extends ComponentsInfo { + @Override + public String getActivityName() { + return SplitScreenActivity.class.getSimpleName(); + } + } + + public static class SplitScreenSecondaryActivity extends ComponentsInfo { + @Override + public String getActivityName() { + return SplitScreenSecondaryActivity.class.getSimpleName(); + } + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/FixedActivity.java b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/FixedActivity.java new file mode 100644 index 000000000000..d4ae6c1313bf --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/FixedActivity.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.testapp; + +import static com.android.wm.shell.flicker.testapp.Components.FixedActivity.EXTRA_FIXED_ORIENTATION; + +import android.os.Bundle; + +public class FixedActivity extends SimpleActivity { + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + + // Set the fixed orientation if requested + if (getIntent().hasExtra(EXTRA_FIXED_ORIENTATION)) { + final int ori = Integer.parseInt(getIntent().getStringExtra(EXTRA_FIXED_ORIENTATION)); + setRequestedOrientation(ori); + } + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/PipActivity.java b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/PipActivity.java index d2fcd0d31558..a6ba7823e22d 100644 --- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/PipActivity.java +++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/PipActivity.java @@ -24,18 +24,38 @@ import static android.media.session.PlaybackState.STATE_PAUSED; import static android.media.session.PlaybackState.STATE_PLAYING; import static android.media.session.PlaybackState.STATE_STOPPED; +import static com.android.wm.shell.flicker.testapp.Components.PipActivity.ACTION_ENTER_PIP; +import static com.android.wm.shell.flicker.testapp.Components.PipActivity.ACTION_SET_REQUESTED_ORIENTATION; +import static com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP; +import static com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_PIP_ORIENTATION; + import android.app.Activity; +import android.app.PendingIntent; import android.app.PictureInPictureParams; +import android.app.RemoteAction; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.graphics.drawable.Icon; import android.media.MediaMetadata; import android.media.session.MediaSession; import android.media.session.PlaybackState; import android.os.Bundle; +import android.util.Log; import android.util.Rational; import android.view.View; import android.view.Window; import android.view.WindowManager; +import android.widget.CheckBox; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; public class PipActivity extends Activity { + private static final String TAG = PipActivity.class.getSimpleName(); /** * A media session title for when the session is in {@link STATE_PLAYING}. * TvPipNotificationTests check whether the actual notification title matches this string. @@ -52,7 +72,19 @@ public class PipActivity extends Activity { private static final Rational RATIO_WIDE = new Rational(2, 1); private static final Rational RATIO_TALL = new Rational(1, 2); - private PictureInPictureParams.Builder mPipParamsBuilder; + private static final String PIP_ACTION_NO_OP = "No-Op"; + private static final String PIP_ACTION_OFF = "Off"; + private static final String PIP_ACTION_ON = "On"; + private static final String PIP_ACTION_CLEAR = "Clear"; + private static final String ACTION_NO_OP = "com.android.wm.shell.flicker.testapp.NO_OP"; + private static final String ACTION_SWITCH_OFF = + "com.android.wm.shell.flicker.testapp.SWITCH_OFF"; + private static final String ACTION_SWITCH_ON = "com.android.wm.shell.flicker.testapp.SWITCH_ON"; + private static final String ACTION_CLEAR = "com.android.wm.shell.flicker.testapp.CLEAR"; + + private final PictureInPictureParams.Builder mPipParamsBuilder = + new PictureInPictureParams.Builder() + .setAspectRatio(RATIO_DEFAULT); private MediaSession mMediaSession; private final PlaybackState.Builder mPlaybackStateBuilder = new PlaybackState.Builder() .setActions(ACTION_PLAY | ACTION_PAUSE | ACTION_STOP) @@ -60,6 +92,46 @@ public class PipActivity extends Activity { private PlaybackState mPlaybackState = mPlaybackStateBuilder.build(); private final MediaMetadata.Builder mMediaMetadataBuilder = new MediaMetadata.Builder(); + private final List<RemoteAction> mSwitchOffActions = new ArrayList<>(); + private final List<RemoteAction> mSwitchOnActions = new ArrayList<>(); + private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (isInPictureInPictureMode()) { + switch (intent.getAction()) { + case ACTION_SWITCH_ON: + mPipParamsBuilder.setActions(mSwitchOnActions); + break; + case ACTION_SWITCH_OFF: + mPipParamsBuilder.setActions(mSwitchOffActions); + break; + case ACTION_CLEAR: + mPipParamsBuilder.setActions(Collections.emptyList()); + break; + case ACTION_NO_OP: + return; + default: + Log.w(TAG, "Unhandled action=" + intent.getAction()); + return; + } + setPictureInPictureParams(mPipParamsBuilder.build()); + } else { + switch (intent.getAction()) { + case ACTION_ENTER_PIP: + enterPip(null); + break; + case ACTION_SET_REQUESTED_ORIENTATION: + setRequestedOrientation(Integer.parseInt(intent.getStringExtra( + EXTRA_PIP_ORIENTATION))); + break; + default: + Log.w(TAG, "Unhandled action=" + intent.getAction()); + return; + } + } + } + }; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -72,9 +144,6 @@ public class PipActivity extends Activity { setContentView(R.layout.activity_pip); - mPipParamsBuilder = new PictureInPictureParams.Builder() - .setAspectRatio(RATIO_DEFAULT); - findViewById(R.id.media_session_start) .setOnClickListener(v -> updateMediaSessionState(STATE_PLAYING)); findViewById(R.id.media_session_stop) @@ -98,9 +167,52 @@ public class PipActivity extends Activity { updateMediaSessionState(STATE_STOPPED); } }); + + // Build two sets of the custom actions. We'll replace one with the other when 'On'/'Off' + // action is invoked. + // The first set consists of 3 actions: 1) Off; 2) No-Op; 3) Clear. + // The second set consists of 2 actions: 1) On; 2) Clear. + // Upon invocation 'Clear' action clear-off all the custom actions, including itself. + final Icon icon = Icon.createWithResource(this, android.R.drawable.ic_menu_help); + final RemoteAction noOpAction = buildRemoteAction(icon, PIP_ACTION_NO_OP, ACTION_NO_OP); + final RemoteAction switchOnAction = + buildRemoteAction(icon, PIP_ACTION_ON, ACTION_SWITCH_ON); + final RemoteAction switchOffAction = + buildRemoteAction(icon, PIP_ACTION_OFF, ACTION_SWITCH_OFF); + final RemoteAction clearAllAction = buildRemoteAction(icon, PIP_ACTION_CLEAR, ACTION_CLEAR); + mSwitchOffActions.addAll(Arrays.asList(switchOnAction, clearAllAction)); + mSwitchOnActions.addAll(Arrays.asList(noOpAction, switchOffAction, clearAllAction)); + + final IntentFilter filter = new IntentFilter(); + filter.addAction(ACTION_NO_OP); + filter.addAction(ACTION_SWITCH_ON); + filter.addAction(ACTION_SWITCH_OFF); + filter.addAction(ACTION_CLEAR); + filter.addAction(ACTION_SET_REQUESTED_ORIENTATION); + filter.addAction(ACTION_ENTER_PIP); + registerReceiver(mBroadcastReceiver, filter); + + handleIntentExtra(getIntent()); + } + + @Override + protected void onDestroy() { + unregisterReceiver(mBroadcastReceiver); + super.onDestroy(); + } + + private RemoteAction buildRemoteAction(Icon icon, String label, String action) { + final Intent intent = new Intent(action); + final PendingIntent pendingIntent = + PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT); + return new RemoteAction(icon, label, label, pendingIntent); } public void enterPip(View v) { + final boolean withCustomActions = + ((CheckBox) findViewById(R.id.with_custom_actions)).isChecked(); + mPipParamsBuilder.setActions( + withCustomActions ? mSwitchOnActions : Collections.emptyList()); enterPictureInPictureMode(mPipParamsBuilder.build()); } @@ -153,4 +265,17 @@ public class PipActivity extends Activity { mMediaSession.setMetadata(mMediaMetadataBuilder.build()); mMediaSession.setActive(newState != STATE_STOPPED); } + + private void handleIntentExtra(Intent intent) { + // Set the fixed orientation if requested + if (intent.hasExtra(EXTRA_PIP_ORIENTATION)) { + final int ori = Integer.parseInt(getIntent().getStringExtra(EXTRA_PIP_ORIENTATION)); + setRequestedOrientation(ori); + } + // Enter picture in picture with the given aspect ratio if provided + if (intent.hasExtra(EXTRA_ENTER_PIP)) { + mPipParamsBuilder.setActions(mSwitchOnActions); + enterPip(null); + } + } } diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/SimpleActivity.java index 9a8f39907877..5343c1893d4e 100644 --- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java +++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/SimpleActivity.java @@ -14,17 +14,13 @@ * limitations under the License. */ -package com.android.server.wm.flicker.testapp; +package com.android.wm.shell.flicker.testapp; import android.app.Activity; -import android.app.PictureInPictureParams; -import android.graphics.Rect; import android.os.Bundle; -import android.util.Rational; import android.view.WindowManager; -import android.widget.Button; -public class PipActivity extends Activity { +public class SimpleActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -32,14 +28,6 @@ public class PipActivity extends Activity { p.layoutInDisplayCutoutMode = WindowManager.LayoutParams .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; getWindow().setAttributes(p); - setContentView(R.layout.activity_pip); - Button enterPip = (Button) findViewById(R.id.enter_pip); - - PictureInPictureParams params = new PictureInPictureParams.Builder() - .setAspectRatio(new Rational(1, 1)) - .setSourceRectHint(new Rect(0, 0, 100, 100)) - .build(); - - enterPip.setOnClickListener((v) -> enterPictureInPictureMode(params)); + setContentView(R.layout.activity_simple); } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java index 7a6e0c1b41fc..a65d832359d2 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java @@ -50,6 +50,7 @@ public class PipBoundsAlgorithmTest extends ShellTestCase { private static final float DEFAULT_ASPECT_RATIO = 1f; private static final float MIN_ASPECT_RATIO = 0.5f; private static final float MAX_ASPECT_RATIO = 2f; + private static final int DEFAULT_MIN_EDGE_SIZE = 100; private PipBoundsAlgorithm mPipBoundsAlgorithm; private DisplayInfo mDefaultDisplayInfo; @@ -73,7 +74,8 @@ public class PipBoundsAlgorithmTest extends ShellTestCase { com.android.internal.R.integer.config_defaultPictureInPictureGravity, Gravity.END | Gravity.BOTTOM); res.addOverride( - com.android.internal.R.dimen.default_minimal_size_pip_resizable_task, 100); + com.android.internal.R.dimen.default_minimal_size_pip_resizable_task, + DEFAULT_MIN_EDGE_SIZE); res.addOverride( com.android.internal.R.string.config_defaultPictureInPictureScreenEdgeInsets, "16x16"); @@ -112,6 +114,127 @@ public class PipBoundsAlgorithmTest extends ShellTestCase { } @Test + public void getDefaultBounds_noOverrideMinSize_matchesDefaultSizeAndAspectRatio() { + final Size defaultSize = mPipBoundsAlgorithm.getSizeForAspectRatio(DEFAULT_ASPECT_RATIO, + DEFAULT_MIN_EDGE_SIZE, mDefaultDisplayInfo.logicalWidth, + mDefaultDisplayInfo.logicalHeight); + + mPipBoundsState.setOverrideMinSize(null); + final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds(); + + assertEquals(defaultSize, new Size(defaultBounds.width(), defaultBounds.height())); + assertEquals(DEFAULT_ASPECT_RATIO, getRectAspectRatio(defaultBounds), + ASPECT_RATIO_ERROR_MARGIN); + } + + @Test + public void getDefaultBounds_widerOverrideMinSize_matchesMinSizeWidthAndDefaultAspectRatio() { + overrideDefaultAspectRatio(1.0f); + // The min size's aspect ratio is greater than the default aspect ratio. + final Size overrideMinSize = new Size(150, 120); + + mPipBoundsState.setOverrideMinSize(overrideMinSize); + final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds(); + + // The default aspect ratio should trump the min size aspect ratio. + assertEquals(DEFAULT_ASPECT_RATIO, getRectAspectRatio(defaultBounds), + ASPECT_RATIO_ERROR_MARGIN); + // The width of the min size is still used with the default aspect ratio. + assertEquals(overrideMinSize.getWidth(), defaultBounds.width()); + } + + @Test + public void getDefaultBounds_tallerOverrideMinSize_matchesMinSizeHeightAndDefaultAspectRatio() { + overrideDefaultAspectRatio(1.0f); + // The min size's aspect ratio is greater than the default aspect ratio. + final Size overrideMinSize = new Size(120, 150); + + mPipBoundsState.setOverrideMinSize(overrideMinSize); + final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds(); + + // The default aspect ratio should trump the min size aspect ratio. + assertEquals(DEFAULT_ASPECT_RATIO, getRectAspectRatio(defaultBounds), + ASPECT_RATIO_ERROR_MARGIN); + // The height of the min size is still used with the default aspect ratio. + assertEquals(overrideMinSize.getHeight(), defaultBounds.height()); + } + + @Test + public void getDefaultBounds_imeShowing_offsetByImeHeight() { + final int imeHeight = 30; + mPipBoundsState.setImeVisibility(false, 0); + final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds(); + + mPipBoundsState.setImeVisibility(true, imeHeight); + final Rect defaultBoundsWithIme = mPipBoundsAlgorithm.getDefaultBounds(); + + assertEquals(imeHeight, defaultBounds.top - defaultBoundsWithIme.top); + } + + @Test + public void getDefaultBounds_shelfShowing_offsetByShelfHeight() { + final int shelfHeight = 30; + mPipBoundsState.setShelfVisibility(false, 0); + final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds(); + + mPipBoundsState.setShelfVisibility(true, shelfHeight); + final Rect defaultBoundsWithShelf = mPipBoundsAlgorithm.getDefaultBounds(); + + assertEquals(shelfHeight, defaultBounds.top - defaultBoundsWithShelf.top); + } + + @Test + public void getDefaultBounds_imeAndShelfShowing_offsetByTallest() { + final int imeHeight = 30; + final int shelfHeight = 40; + mPipBoundsState.setImeVisibility(false, 0); + mPipBoundsState.setShelfVisibility(false, 0); + final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds(); + + mPipBoundsState.setImeVisibility(true, imeHeight); + mPipBoundsState.setShelfVisibility(true, shelfHeight); + final Rect defaultBoundsWithIme = mPipBoundsAlgorithm.getDefaultBounds(); + + assertEquals(shelfHeight, defaultBounds.top - defaultBoundsWithIme.top); + } + + @Test + public void getDefaultBounds_boundsAtDefaultGravity() { + final Rect insetBounds = new Rect(); + mPipBoundsAlgorithm.getInsetBounds(insetBounds); + overrideDefaultStackGravity(Gravity.END | Gravity.BOTTOM); + + final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds(); + + assertEquals(insetBounds.bottom, defaultBounds.bottom); + assertEquals(insetBounds.right, defaultBounds.right); + } + + @Test + public void getNormalBounds_invalidAspectRatio_returnsDefaultBounds() { + final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds(); + + // Set an invalid current aspect ratio. + mPipBoundsState.setAspectRatio(MIN_ASPECT_RATIO / 2); + final Rect normalBounds = mPipBoundsAlgorithm.getNormalBounds(); + + assertEquals(defaultBounds, normalBounds); + } + + @Test + public void getNormalBounds_validAspectRatio_returnsAdjustedDefaultBounds() { + final Rect defaultBoundsAdjustedToAspectRatio = mPipBoundsAlgorithm.getDefaultBounds(); + mPipBoundsAlgorithm.transformBoundsToAspectRatio(defaultBoundsAdjustedToAspectRatio, + MIN_ASPECT_RATIO, false /* useCurrentMinEdgeSize */, false /* useCurrentSize */); + + // Set a valid current aspect ratio different that the default. + mPipBoundsState.setAspectRatio(MIN_ASPECT_RATIO); + final Rect normalBounds = mPipBoundsAlgorithm.getNormalBounds(); + + assertEquals(defaultBoundsAdjustedToAspectRatio, normalBounds); + } + + @Test public void getEntryDestinationBounds_returnBoundsMatchesAspectRatio() { final float[] aspectRatios = new float[] { (MIN_ASPECT_RATIO + DEFAULT_ASPECT_RATIO) / 2, @@ -121,8 +244,7 @@ public class PipBoundsAlgorithmTest extends ShellTestCase { for (float aspectRatio : aspectRatios) { mPipBoundsState.setAspectRatio(aspectRatio); final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds(); - final float actualAspectRatio = - destinationBounds.width() / (destinationBounds.height() * 1f); + final float actualAspectRatio = getRectAspectRatio(destinationBounds); assertEquals("Destination bounds matches the given aspect ratio", aspectRatio, actualAspectRatio, ASPECT_RATIO_ERROR_MARGIN); } @@ -274,6 +396,22 @@ public class PipBoundsAlgorithmTest extends ShellTestCase { assertBoundsInclusionWithMargin("useDefaultBounds", defaultBounds, actualBounds); } + private void overrideDefaultAspectRatio(float aspectRatio) { + final TestableResources res = mContext.getOrCreateTestableResources(); + res.addOverride( + com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio, + aspectRatio); + mPipBoundsAlgorithm.onConfigurationChanged(mContext); + } + + private void overrideDefaultStackGravity(int stackGravity) { + final TestableResources res = mContext.getOrCreateTestableResources(); + res.addOverride( + com.android.internal.R.integer.config_defaultPictureInPictureGravity, + stackGravity); + mPipBoundsAlgorithm.onConfigurationChanged(mContext); + } + private void assertBoundsInclusionWithMargin(String from, Rect expected, Rect actual) { final Rect expectedWithMargin = new Rect(expected); expectedWithMargin.inset(-ROUNDING_ERROR_MARGIN, -ROUNDING_ERROR_MARGIN); @@ -282,4 +420,8 @@ public class PipBoundsAlgorithmTest extends ShellTestCase { + " with error margin " + ROUNDING_ERROR_MARGIN, expectedWithMargin.contains(actual)); } + + private static float getRectAspectRatio(Rect rect) { + return rect.width() / (rect.height() * 1f); + } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java index 59e10c189046..4bcca06b592f 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java @@ -135,4 +135,38 @@ public class PipBoundsStateTest extends ShellTestCase { verify(callback, never()).accept(true, 100); } + + @Test + public void testSetOverrideMinSize_changed_callbackInvoked() { + final Runnable callback = mock(Runnable.class); + mPipBoundsState.setOverrideMinSize(new Size(5, 5)); + mPipBoundsState.setOnMinimalSizeChangeCallback(callback); + + mPipBoundsState.setOverrideMinSize(new Size(10, 10)); + + verify(callback).run(); + } + + @Test + public void testSetOverrideMinSize_notChanged_callbackNotInvoked() { + final Runnable callback = mock(Runnable.class); + mPipBoundsState.setOverrideMinSize(new Size(5, 5)); + mPipBoundsState.setOnMinimalSizeChangeCallback(callback); + + mPipBoundsState.setOverrideMinSize(new Size(5, 5)); + + verify(callback, never()).run(); + } + + @Test + public void testGetOverrideMinEdgeSize() { + mPipBoundsState.setOverrideMinSize(null); + assertEquals(0, mPipBoundsState.getOverrideMinEdgeSize()); + + mPipBoundsState.setOverrideMinSize(new Size(5, 10)); + assertEquals(5, mPipBoundsState.getOverrideMinEdgeSize()); + + mPipBoundsState.setOverrideMinSize(new Size(15, 10)); + assertEquals(10, mPipBoundsState.getOverrideMinEdgeSize()); + } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipSnapAlgorithmTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipSnapAlgorithmTest.java new file mode 100644 index 000000000000..dcee2e1847b2 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipSnapAlgorithmTest.java @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.pip; + +import static org.junit.Assert.assertEquals; + +import android.graphics.Rect; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import androidx.test.filters.SmallTest; + +import com.android.wm.shell.ShellTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** Tests for {@link PipSnapAlgorithm}. **/ +@RunWith(AndroidTestingRunner.class) +@SmallTest +@TestableLooper.RunWithLooper(setAsMainLooper = true) +public class PipSnapAlgorithmTest extends ShellTestCase { + private static final int DEFAULT_STASH_OFFSET = 32; + private static final Rect DISPLAY_BOUNDS = new Rect(0, 0, 2000, 2000); + private static final Rect STACK_BOUNDS_CENTERED = new Rect(900, 900, 1100, 1100); + private static final Rect MOVEMENT_BOUNDS = new Rect(0, 0, + DISPLAY_BOUNDS.width() - STACK_BOUNDS_CENTERED.width(), + DISPLAY_BOUNDS.width() - STACK_BOUNDS_CENTERED.width()); + + private PipSnapAlgorithm mPipSnapAlgorithm; + + @Before + public void setUp() { + mPipSnapAlgorithm = new PipSnapAlgorithm(); + } + + @Test + public void testApplySnapFraction_topEdge() { + final float snapFraction = 0.25f; + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + + mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, snapFraction); + + assertEquals(MOVEMENT_BOUNDS.width() / 4, bounds.left); + assertEquals(MOVEMENT_BOUNDS.top, bounds.top); + } + + @Test + public void testApplySnapFraction_rightEdge() { + final float snapFraction = 1.5f; + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + + mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, snapFraction); + + assertEquals(MOVEMENT_BOUNDS.right, bounds.left); + assertEquals(MOVEMENT_BOUNDS.height() / 2, bounds.top); + } + + @Test + public void testApplySnapFraction_bottomEdge() { + final float snapFraction = 2.25f; + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + + mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, snapFraction); + + assertEquals((int) (MOVEMENT_BOUNDS.width() * 0.75f), bounds.left); + assertEquals(MOVEMENT_BOUNDS.bottom, bounds.top); + } + + @Test + public void testApplySnapFraction_leftEdge() { + final float snapFraction = 3.75f; + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + + mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, snapFraction); + + assertEquals(MOVEMENT_BOUNDS.left, bounds.left); + assertEquals((int) (MOVEMENT_BOUNDS.height() * 0.25f), bounds.top); + } + + @Test + public void testApplySnapFraction_notStashed_isNotOffBounds() { + final float snapFraction = 2f; + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + + mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, snapFraction, + PipBoundsState.STASH_TYPE_NONE, DEFAULT_STASH_OFFSET, DISPLAY_BOUNDS); + + assertEquals(MOVEMENT_BOUNDS.right, bounds.left); + assertEquals(MOVEMENT_BOUNDS.bottom, bounds.top); + } + + @Test + public void testApplySnapFraction_stashedLeft() { + final float snapFraction = 3f; + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + + mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, snapFraction, + PipBoundsState.STASH_TYPE_LEFT, DEFAULT_STASH_OFFSET, DISPLAY_BOUNDS); + + final int offBoundsWidth = bounds.width() - DEFAULT_STASH_OFFSET; + assertEquals(MOVEMENT_BOUNDS.left - offBoundsWidth, bounds.left); + assertEquals(MOVEMENT_BOUNDS.bottom, bounds.top); + } + + @Test + public void testApplySnapFraction_stashedRight() { + final float snapFraction = 2f; + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + + mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, snapFraction, + PipBoundsState.STASH_TYPE_RIGHT, DEFAULT_STASH_OFFSET, DISPLAY_BOUNDS); + + assertEquals(DISPLAY_BOUNDS.right - DEFAULT_STASH_OFFSET, bounds.left); + assertEquals(MOVEMENT_BOUNDS.bottom, bounds.top); + } + + @Test + public void testSnapRectToClosestEdge_rightEdge() { + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + // Move the centered rect slightly to the right side. + bounds.offset(10, 0); + + mPipSnapAlgorithm.snapRectToClosestEdge(bounds, MOVEMENT_BOUNDS, bounds, + PipBoundsState.STASH_TYPE_NONE); + + assertEquals(MOVEMENT_BOUNDS.right, bounds.left); + } + + @Test + public void testSnapRectToClosestEdge_leftEdge() { + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + // Move the centered rect slightly to the left side. + bounds.offset(-10, 0); + + mPipSnapAlgorithm.snapRectToClosestEdge(bounds, MOVEMENT_BOUNDS, bounds, + PipBoundsState.STASH_TYPE_NONE); + + assertEquals(MOVEMENT_BOUNDS.left, bounds.left); + } + + @Test + public void testSnapRectToClosestEdge_topEdge() { + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + // Move the centered rect slightly to the top half. + bounds.offset(0, -10); + + mPipSnapAlgorithm.snapRectToClosestEdge(bounds, MOVEMENT_BOUNDS, bounds, + PipBoundsState.STASH_TYPE_NONE); + + assertEquals(MOVEMENT_BOUNDS.top, bounds.top); + } + + @Test + public void testSnapRectToClosestEdge_bottomEdge() { + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + // Move the centered rect slightly to the bottom half. + bounds.offset(0, 10); + + mPipSnapAlgorithm.snapRectToClosestEdge(bounds, MOVEMENT_BOUNDS, bounds, + PipBoundsState.STASH_TYPE_NONE); + + assertEquals(MOVEMENT_BOUNDS.bottom, bounds.top); + } + + @Test + public void testSnapRectToClosestEdge_stashed_unStahesBounds() { + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + // Stash it on the left side. + mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, 3.5f, + PipBoundsState.STASH_TYPE_LEFT, DEFAULT_STASH_OFFSET, DISPLAY_BOUNDS); + + mPipSnapAlgorithm.snapRectToClosestEdge(bounds, MOVEMENT_BOUNDS, bounds, + PipBoundsState.STASH_TYPE_LEFT); + + assertEquals(MOVEMENT_BOUNDS.left, bounds.left); + } + + @Test + public void testGetSnapFraction_leftEdge() { + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + // Move it slightly to the left side. + bounds.offset(-10, 0); + + final float snapFraction = mPipSnapAlgorithm.getSnapFraction(bounds, MOVEMENT_BOUNDS); + + assertEquals(3.5f, snapFraction, 0.1f); + } + + @Test + public void testGetSnapFraction_rightEdge() { + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + // Move it slightly to the right side. + bounds.offset(10, 0); + + final float snapFraction = mPipSnapAlgorithm.getSnapFraction(bounds, MOVEMENT_BOUNDS); + + assertEquals(1.5f, snapFraction, 0.1f); + } + + @Test + public void testGetSnapFraction_topEdge() { + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + // Move it slightly to the top half. + bounds.offset(0, -10); + + final float snapFraction = mPipSnapAlgorithm.getSnapFraction(bounds, MOVEMENT_BOUNDS); + + assertEquals(0.5f, snapFraction, 0.1f); + } + + @Test + public void testGetSnapFraction_bottomEdge() { + final Rect bounds = new Rect(STACK_BOUNDS_CENTERED); + // Move it slightly to the bottom half. + bounds.offset(0, 10); + + final float snapFraction = mPipSnapAlgorithm.getSnapFraction(bounds, MOVEMENT_BOUNDS); + + assertEquals(2.5f, snapFraction, 0.1f); + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java index b25c74d12818..abbc681f53fe 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java @@ -91,7 +91,7 @@ public class PipTouchHandlerTest extends ShellTestCase { mPipBoundsState = new PipBoundsState(mContext); mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState); mPipSnapAlgorithm = mPipBoundsAlgorithm.getSnapAlgorithm(); - mPipSnapAlgorithm = new PipSnapAlgorithm(mContext); + mPipSnapAlgorithm = new PipSnapAlgorithm(); mPipTouchHandler = new PipTouchHandler(mContext, mPipMenuActivityController, mPipBoundsAlgorithm, mPipBoundsState, mPipTaskOrganizer, mFloatingContentCoordinator, mPipUiEventLogger); @@ -115,7 +115,8 @@ public class PipTouchHandlerTest extends ShellTestCase { @Test public void updateMovementBounds_minBounds() { Rect expectedMinMovementBounds = new Rect(); - mPipSnapAlgorithm.getMovementBounds(mMinBounds, mInsetBounds, expectedMinMovementBounds, 0); + mPipBoundsAlgorithm.getMovementBounds(mMinBounds, mInsetBounds, expectedMinMovementBounds, + 0); mPipTouchHandler.onMovementBoundsChanged(mInsetBounds, mMinBounds, mCurBounds, mFromImeAdjustment, mFromShelfAdjustment, mDisplayRotation); @@ -129,12 +130,13 @@ public class PipTouchHandlerTest extends ShellTestCase { public void updateMovementBounds_maxBounds() { Point displaySize = new Point(); mContext.getDisplay().getRealSize(displaySize); - Size maxSize = mPipSnapAlgorithm.getSizeForAspectRatio(1, + Size maxSize = mPipBoundsAlgorithm.getSizeForAspectRatio(1, mContext.getResources().getDimensionPixelSize( R.dimen.pip_expanded_shortest_edge_size), displaySize.x, displaySize.y); Rect maxBounds = new Rect(0, 0, maxSize.getWidth(), maxSize.getHeight()); Rect expectedMaxMovementBounds = new Rect(); - mPipSnapAlgorithm.getMovementBounds(maxBounds, mInsetBounds, expectedMaxMovementBounds, 0); + mPipBoundsAlgorithm.getMovementBounds(maxBounds, mInsetBounds, expectedMaxMovementBounds, + 0); mPipTouchHandler.onMovementBoundsChanged(mInsetBounds, mMinBounds, mCurBounds, mFromImeAdjustment, mFromShelfAdjustment, mDisplayRotation); diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 8c2a632b9fd7..a545b3d5e134 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -1041,7 +1041,9 @@ base::expected<const ResolvedBag*, NullOrIOError> AssetManager2::ResolveBag( base::expected<const ResolvedBag*, NullOrIOError> AssetManager2::GetBag(uint32_t resid) const { std::vector<uint32_t> found_resids; - return GetBag(resid, found_resids); + const auto bag = GetBag(resid, found_resids); + cached_bag_resid_stacks_.emplace(resid, found_resids); + return bag; } base::expected<const ResolvedBag*, NullOrIOError> AssetManager2::GetBag( diff --git a/libs/hwui/DisplayListOps.in b/libs/hwui/DisplayListOps.in index 49817925d9b4..c6c4ba8a6493 100644 --- a/libs/hwui/DisplayListOps.in +++ b/libs/hwui/DisplayListOps.in @@ -19,7 +19,6 @@ X(Save) X(Restore) X(SaveLayer) X(SaveBehind) -X(Concat44) X(Concat) X(SetMatrix) X(Scale) diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp index 473dc53dc4bf..a495ec4ac411 100644 --- a/libs/hwui/RecordingCanvas.cpp +++ b/libs/hwui/RecordingCanvas.cpp @@ -125,24 +125,18 @@ struct SaveBehind final : Op { } }; -struct Concat44 final : Op { - static const auto kType = Type::Concat44; - Concat44(const SkM44& m) : matrix(m) {} - SkM44 matrix; - void draw(SkCanvas* c, const SkMatrix&) const { c->concat(matrix); } -}; struct Concat final : Op { static const auto kType = Type::Concat; - Concat(const SkMatrix& matrix) : matrix(matrix) {} - SkMatrix matrix; + Concat(const SkM44& matrix) : matrix(matrix) {} + SkM44 matrix; void draw(SkCanvas* c, const SkMatrix&) const { c->concat(matrix); } }; struct SetMatrix final : Op { static const auto kType = Type::SetMatrix; - SetMatrix(const SkMatrix& matrix) : matrix(matrix) {} - SkMatrix matrix; + SetMatrix(const SkM44& matrix) : matrix(matrix) {} + SkM44 matrix; void draw(SkCanvas* c, const SkMatrix& original) const { - c->setMatrix(SkMatrix::Concat(original, matrix)); + c->setMatrix(SkM44(original) * matrix); } }; struct Scale final : Op { @@ -569,12 +563,9 @@ void DisplayListData::saveBehind(const SkRect* subset) { } void DisplayListData::concat(const SkM44& m) { - this->push<Concat44>(0, m); -} -void DisplayListData::concat(const SkMatrix& matrix) { - this->push<Concat>(0, matrix); + this->push<Concat>(0, m); } -void DisplayListData::setMatrix(const SkMatrix& matrix) { +void DisplayListData::setMatrix(const SkM44& matrix) { this->push<SetMatrix>(0, matrix); } void DisplayListData::scale(SkScalar sx, SkScalar sy) { @@ -834,10 +825,7 @@ bool RecordingCanvas::onDoSaveBehind(const SkRect* subset) { void RecordingCanvas::didConcat44(const SkM44& m) { fDL->concat(m); } -void RecordingCanvas::didConcat(const SkMatrix& matrix) { - fDL->concat(matrix); -} -void RecordingCanvas::didSetMatrix(const SkMatrix& matrix) { +void RecordingCanvas::didSetM44(const SkM44& matrix) { fDL->setMatrix(matrix); } void RecordingCanvas::didScale(SkScalar sx, SkScalar sy) { diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h index 63d120c4ca19..4851148cd4d8 100644 --- a/libs/hwui/RecordingCanvas.h +++ b/libs/hwui/RecordingCanvas.h @@ -82,8 +82,7 @@ private: void restore(); void concat(const SkM44&); - void concat(const SkMatrix&); - void setMatrix(const SkMatrix&); + void setMatrix(const SkM44&); void scale(SkScalar, SkScalar); void translate(SkScalar, SkScalar); void translateZ(SkScalar); @@ -154,8 +153,7 @@ public: void onFlush() override; void didConcat44(const SkM44&) override; - void didConcat(const SkMatrix&) override; - void didSetMatrix(const SkMatrix&) override; + void didSetM44(const SkM44&) override; void didScale(SkScalar, SkScalar) override; void didTranslate(SkScalar, SkScalar) override; diff --git a/libs/hwui/WebViewFunctorManager.cpp b/libs/hwui/WebViewFunctorManager.cpp index 68541b4b31f0..671c66f11e32 100644 --- a/libs/hwui/WebViewFunctorManager.cpp +++ b/libs/hwui/WebViewFunctorManager.cpp @@ -26,6 +26,35 @@ namespace android::uirenderer { +namespace { +class ScopedCurrentFunctor { +public: + ScopedCurrentFunctor(WebViewFunctor* functor) { + ALOG_ASSERT(!sCurrentFunctor); + ALOG_ASSERT(functor); + sCurrentFunctor = functor; + } + ~ScopedCurrentFunctor() { + ALOG_ASSERT(sCurrentFunctor); + sCurrentFunctor = nullptr; + } + + static ASurfaceControl* getSurfaceControl() { + ALOG_ASSERT(sCurrentFunctor); + return sCurrentFunctor->getSurfaceControl(); + } + static void mergeTransaction(ASurfaceTransaction* transaction) { + ALOG_ASSERT(sCurrentFunctor); + sCurrentFunctor->mergeTransaction(transaction); + } + +private: + static WebViewFunctor* sCurrentFunctor; +}; + +WebViewFunctor* ScopedCurrentFunctor::sCurrentFunctor = nullptr; +} // namespace + RenderMode WebViewFunctor_queryPlatformRenderMode() { auto pipelineType = Properties::getRenderPipelineType(); switch (pipelineType) { @@ -83,7 +112,15 @@ void WebViewFunctor::drawGl(const DrawGlInfo& drawInfo) { if (!mHasContext) { mHasContext = true; } - mCallbacks.gles.draw(mFunctor, mData, drawInfo); + ScopedCurrentFunctor currentFunctor(this); + + WebViewOverlayData overlayParams = { + // TODO: + .overlaysMode = OverlaysMode::Disabled, + .getSurfaceControl = currentFunctor.getSurfaceControl, + .mergeTransaction = currentFunctor.mergeTransaction, + }; + mCallbacks.gles.draw(mFunctor, mData, drawInfo, overlayParams); } void WebViewFunctor::initVk(const VkFunctorInitParams& params) { @@ -98,7 +135,15 @@ void WebViewFunctor::initVk(const VkFunctorInitParams& params) { void WebViewFunctor::drawVk(const VkFunctorDrawParams& params) { ATRACE_NAME("WebViewFunctor::drawVk"); - mCallbacks.vk.draw(mFunctor, mData, params); + ScopedCurrentFunctor currentFunctor(this); + + WebViewOverlayData overlayParams = { + // TODO + .overlaysMode = OverlaysMode::Disabled, + .getSurfaceControl = currentFunctor.getSurfaceControl, + .mergeTransaction = currentFunctor.mergeTransaction, + }; + mCallbacks.vk.draw(mFunctor, mData, params, overlayParams); } void WebViewFunctor::postDrawVk() { @@ -118,6 +163,20 @@ void WebViewFunctor::destroyContext() { } } +void WebViewFunctor::removeOverlays() { + ScopedCurrentFunctor currentFunctor(this); + mCallbacks.removeOverlays(mFunctor, mData, currentFunctor.mergeTransaction); +} + +ASurfaceControl* WebViewFunctor::getSurfaceControl() { + // TODO + return nullptr; +} + +void WebViewFunctor::mergeTransaction(ASurfaceTransaction* transaction) { + // TODO +} + WebViewFunctorManager& WebViewFunctorManager::instance() { static WebViewFunctorManager sInstance; return sInstance; diff --git a/libs/hwui/WebViewFunctorManager.h b/libs/hwui/WebViewFunctorManager.h index 675b738c6406..737d60525aa9 100644 --- a/libs/hwui/WebViewFunctorManager.h +++ b/libs/hwui/WebViewFunctorManager.h @@ -56,6 +56,8 @@ public: void postDrawVk() { mReference.postDrawVk(); } + void removeOverlays() { mReference.removeOverlays(); } + private: friend class WebViewFunctor; @@ -71,6 +73,10 @@ public: void drawVk(const VkFunctorDrawParams& params); void postDrawVk(); void destroyContext(); + void removeOverlays(); + + ASurfaceControl* getSurfaceControl(); + void mergeTransaction(ASurfaceTransaction* transaction); sp<Handle> createHandle() { LOG_ALWAYS_FATAL_IF(mCreatedHandle); diff --git a/libs/hwui/jni/Typeface.cpp b/libs/hwui/jni/Typeface.cpp index b88ffa6e79b8..a2964d6627a1 100644 --- a/libs/hwui/jni/Typeface.cpp +++ b/libs/hwui/jni/Typeface.cpp @@ -94,13 +94,27 @@ static jint Typeface_getWeight(CRITICAL_JNI_PARAMS_COMMA jlong faceHandle) { } static jlong Typeface_createFromArray(JNIEnv *env, jobject, jlongArray familyArray, - int weight, int italic) { + jlong fallbackPtr, int weight, int italic) { ScopedLongArrayRO families(env, familyArray); std::vector<std::shared_ptr<minikin::FontFamily>> familyVec; - familyVec.reserve(families.size()); - for (size_t i = 0; i < families.size(); i++) { - FontFamilyWrapper* family = reinterpret_cast<FontFamilyWrapper*>(families[i]); - familyVec.emplace_back(family->family); + Typeface* typeface = (fallbackPtr == 0) ? nullptr : toTypeface(fallbackPtr); + if (typeface != nullptr) { + const std::vector<std::shared_ptr<minikin::FontFamily>>& fallbackFamilies = + toTypeface(fallbackPtr)->fFontCollection->getFamilies(); + familyVec.reserve(families.size() + fallbackFamilies.size()); + for (size_t i = 0; i < families.size(); i++) { + FontFamilyWrapper* family = reinterpret_cast<FontFamilyWrapper*>(families[i]); + familyVec.emplace_back(family->family); + } + for (size_t i = 0; i < fallbackFamilies.size(); i++) { + familyVec.emplace_back(fallbackFamilies[i]); + } + } else { + familyVec.reserve(families.size()); + for (size_t i = 0; i < families.size(); i++) { + FontFamilyWrapper* family = reinterpret_cast<FontFamilyWrapper*>(families[i]); + familyVec.emplace_back(family->family); + } } return toJLong(Typeface::createFromFamilies(std::move(familyVec), weight, italic)); } @@ -145,6 +159,13 @@ static std::function<std::shared_ptr<minikin::MinikinFont>()> readMinikinFontSki return [fontPath, fontIndex, axesPtr, axesCount]() -> std::shared_ptr<minikin::MinikinFont> { std::string path(fontPath.data(), fontPath.size()); sk_sp<SkData> data = SkData::MakeFromFileName(path.c_str()); + if (data == nullptr) { + // This may happen if: + // 1. When the process failed to open the file (e.g. invalid path or permission). + // 2. When the process failed to map the file (e.g. hitting max_map_count limit). + ALOGE("Failed to make SkData from file name: %s", path.c_str()); + return nullptr; + } const void* fontPtr = data->data(); size_t fontSize = data->size(); std::vector<minikin::FontVariation> axes(axesPtr, axesPtr + axesCount); @@ -243,7 +264,7 @@ static const JNINativeMethod gTypefaceMethods[] = { { "nativeGetReleaseFunc", "()J", (void*)Typeface_getReleaseFunc }, { "nativeGetStyle", "(J)I", (void*)Typeface_getStyle }, { "nativeGetWeight", "(J)I", (void*)Typeface_getWeight }, - { "nativeCreateFromArray", "([JII)J", + { "nativeCreateFromArray", "([JJII)J", (void*)Typeface_createFromArray }, { "nativeSetDefault", "(J)V", (void*)Typeface_setDefault }, { "nativeGetSupportedAxes", "(J)[I", (void*)Typeface_getSupportedAxes }, diff --git a/libs/hwui/jni/pdf/PdfEditor.cpp b/libs/hwui/jni/pdf/PdfEditor.cpp index e65921ac8e0a..427bafa1bd83 100644 --- a/libs/hwui/jni/pdf/PdfEditor.cpp +++ b/libs/hwui/jni/pdf/PdfEditor.cpp @@ -129,8 +129,8 @@ static void nativeSetTransformAndClip(JNIEnv* env, jclass thiz, jlong documentPt // PDF's coordinate system origin is left-bottom while in graphics it // is the top-left. So, translate the PDF coordinates to ours. - SkMatrix reflectOnX = SkMatrix::MakeScale(1, -1); - SkMatrix moveUp = SkMatrix::MakeTrans(0, FPDF_GetPageHeight(page)); + SkMatrix reflectOnX = SkMatrix::Scale(1, -1); + SkMatrix moveUp = SkMatrix::Translate(0, FPDF_GetPageHeight(page)); SkMatrix coordinateChange = SkMatrix::Concat(moveUp, reflectOnX); // Apply the transformation what was created in our coordinates. diff --git a/libs/hwui/private/hwui/WebViewFunctor.h b/libs/hwui/private/hwui/WebViewFunctor.h index 96da947ace08..22ae59e5137b 100644 --- a/libs/hwui/private/hwui/WebViewFunctor.h +++ b/libs/hwui/private/hwui/WebViewFunctor.h @@ -17,6 +17,15 @@ #ifndef FRAMEWORKS_BASE_WEBVIEWFUNCTOR_H #define FRAMEWORKS_BASE_WEBVIEWFUNCTOR_H +#ifdef __ANDROID__ // Layoutlib does not support surface control +#include <android/surface_control.h> +#else +// To avoid ifdefs around overlay implementation all over the place we typedef these to void *. They +// won't be used. +typedef void* ASurfaceControl; +typedef void* ASurfaceTransaction; +#endif + #include <cutils/compiler.h> #include <private/hwui/DrawGlInfo.h> #include <private/hwui/DrawVkInfo.h> @@ -28,6 +37,14 @@ enum class RenderMode { Vulkan, }; +enum class OverlaysMode { + // Indicated that webview should not promote anything to overlays this draw + // and remove all visible overlays. + Disabled, + // Indicates that webview can use overlays. + Enabled +}; + // Static for the lifetime of the process ANDROID_API RenderMode WebViewFunctor_queryPlatformRenderMode(); @@ -35,6 +52,23 @@ struct WebViewSyncData { bool applyForceDark; }; +struct WebViewOverlayData { + // Desired overlay mode for this draw. + OverlaysMode overlaysMode; + + // Returns parent ASurfaceControl for WebView overlays. It will be have same + // geometry as the surface we draw into and positioned below it (underlay). + // This does not pass ownership to webview, but guaranteed to be alive until + // transaction from next removeOverlays call or functor destruction will be + // finished. + ASurfaceControl* (*getSurfaceControl)(); + + // Merges WebView transaction to be applied synchronously with current draw. + // This doesn't pass ownership of the transaction, changes will be copied and + // webview can free transaction right after the call. + void (*mergeTransaction)(ASurfaceTransaction*); +}; + struct WebViewFunctorCallbacks { // kModeSync, called on RenderThread void (*onSync)(int functor, void* data, const WebViewSyncData& syncData); @@ -48,16 +82,23 @@ struct WebViewFunctorCallbacks { // this functor had ever been drawn. void (*onDestroyed)(int functor, void* data); + // Called on render thread to force webview hide all overlays and stop updating them. + // Should happen during hwui draw (e.g can be called instead of draw if webview + // isn't visible and won't receive draw) and support MergeTransaction call. + void (*removeOverlays)(int functor, void* data, void (*mergeTransaction)(ASurfaceTransaction*)); + union { struct { // Called on RenderThread. initialize is guaranteed to happen before this call - void (*draw)(int functor, void* data, const DrawGlInfo& params); + void (*draw)(int functor, void* data, const DrawGlInfo& params, + const WebViewOverlayData& overlayParams); } gles; struct { // Called either the first time the functor is used or the first time it's used after // a call to onContextDestroyed. void (*initialize)(int functor, void* data, const VkFunctorInitParams& params); - void (*draw)(int functor, void* data, const VkFunctorDrawParams& params); + void (*draw)(int functor, void* data, const VkFunctorDrawParams& params, + const WebViewOverlayData& overlayParams); void (*postDraw)(int functor, void*); } vk; }; diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h index 36c5a8c1b3de..c1d8b761514b 100644 --- a/libs/hwui/tests/common/TestUtils.h +++ b/libs/hwui/tests/common/TestUtils.h @@ -323,7 +323,8 @@ public: }; switch (mode) { case RenderMode::OpenGL_ES: - callbacks.gles.draw = [](int functor, void* client_data, const DrawGlInfo& params) { + callbacks.gles.draw = [](int functor, void* client_data, const DrawGlInfo& params, + const WebViewOverlayData& overlay_params) { expectOnRenderThread("draw"); sMockFunctorCounts[functor].glesDraw++; }; diff --git a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp index f4c3e13b0ea6..955a5e7d8b3a 100644 --- a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp +++ b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp @@ -39,7 +39,7 @@ RENDERTHREAD_TEST(DeferredLayerUpdater, updateLayer) { EXPECT_EQ(Matrix4::identity(), layerUpdater->backingLayer()->getTexTransform()); // push the deferred updates to the layer - SkMatrix scaledMatrix = SkMatrix::MakeScale(0.5, 0.5); + SkMatrix scaledMatrix = SkMatrix::Scale(0.5, 0.5); SkBitmap bitmap; bitmap.allocN32Pixels(16, 16); sk_sp<SkImage> layerImage = SkImage::MakeFromBitmap(bitmap); diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp index abdf9d587189..26bc65915c7a 100644 --- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp +++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp @@ -206,7 +206,7 @@ TEST(RenderNodeDrawable, saveLayerClipAndMatrixRestore) { ASSERT_EQ(SkRect::MakeLTRB(50, 50, 350, 350), getRecorderClipBounds(recorder)); recorder.translate(300.0f, 400.0f); - EXPECT_EQ(SkMatrix::MakeTrans(300.0f, 400.0f), getRecorderMatrix(recorder)); + EXPECT_EQ(SkMatrix::Translate(300.0f, 400.0f), getRecorderMatrix(recorder)); recorder.restore(); ASSERT_EQ(SkRect::MakeLTRB(0, 0, 400, 800), getRecorderClipBounds(recorder)); @@ -1107,27 +1107,27 @@ TEST(ReorderBarrierDrawable, testShadowMatrix) { EXPECT_EQ(dy, TRANSLATE_Y); } - virtual void didSetMatrix(const SkMatrix& matrix) override { + virtual void didSetM44(const SkM44& matrix) override { mDrawCounter++; // First invocation is EndReorderBarrierDrawable::drawShadow to apply shadow matrix. // Second invocation is preparing the matrix for an elevated RenderNodeDrawable. - EXPECT_TRUE(matrix.isIdentity()); + EXPECT_TRUE(matrix == SkM44()); EXPECT_TRUE(getTotalMatrix().isIdentity()); } - virtual void didConcat(const SkMatrix& matrix) override { + virtual void didConcat44(const SkM44& matrix) override { mDrawCounter++; if (mFirstDidConcat) { // First invocation is EndReorderBarrierDrawable::drawShadow to apply shadow matrix. mFirstDidConcat = false; - EXPECT_EQ(SkMatrix::MakeTrans(CASTER_X + TRANSLATE_X, CASTER_Y + TRANSLATE_Y), + EXPECT_EQ(SkM44::Translate(CASTER_X + TRANSLATE_X, CASTER_Y + TRANSLATE_Y), matrix); - EXPECT_EQ(SkMatrix::MakeTrans(CASTER_X + TRANSLATE_X, CASTER_Y + TRANSLATE_Y), + EXPECT_EQ(SkMatrix::Translate(CASTER_X + TRANSLATE_X, CASTER_Y + TRANSLATE_Y), getTotalMatrix()); } else { // Second invocation is preparing the matrix for an elevated RenderNodeDrawable. - EXPECT_EQ(SkMatrix::MakeTrans(TRANSLATE_X, TRANSLATE_Y), matrix); - EXPECT_EQ(SkMatrix::MakeTrans(TRANSLATE_X, TRANSLATE_Y), getTotalMatrix()); + EXPECT_EQ(SkM44::Translate(TRANSLATE_X, TRANSLATE_Y), matrix); + EXPECT_EQ(SkMatrix::Translate(TRANSLATE_X, TRANSLATE_Y), getTotalMatrix()); } } diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp index 74a565439f85..2d34b0980546 100644 --- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp +++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp @@ -263,10 +263,10 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren_vdOffscr } // Another way to be offscreen: a matrix from the draw call. - for (const SkMatrix translate : { SkMatrix::MakeTrans(width, 0), - SkMatrix::MakeTrans(0, height), - SkMatrix::MakeTrans(-width, 0), - SkMatrix::MakeTrans(0, -height)}) { + for (const SkMatrix translate : { SkMatrix::Translate(width, 0), + SkMatrix::Translate(0, height), + SkMatrix::Translate(-width, 0), + SkMatrix::Translate(0, -height)}) { SkiaDisplayList skiaDL; VectorDrawableRoot dirtyVD(new VectorDrawable::Group()); dirtyVD.mutateProperties()->setBounds(bounds); @@ -291,7 +291,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren_vdOffscr SkiaDisplayList skiaDL; VectorDrawableRoot dirtyVD(new VectorDrawable::Group()); dirtyVD.mutateProperties()->setBounds(bounds); - SkMatrix translate = SkMatrix::MakeTrans(50, 50); + SkMatrix translate = SkMatrix::Translate(50, 50); skiaDL.appendVD(&dirtyVD, translate); ASSERT_TRUE(dirtyVD.isDirty()); diff --git a/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp b/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp index eec25c6bd40d..15ecf5831f3a 100644 --- a/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp +++ b/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp @@ -111,11 +111,11 @@ TEST(RenderNodeDrawable, renderPropTransform) { [](RenderProperties& properties) { properties.setLeftTopRightBottom(10, 10, 110, 110); - SkMatrix staticMatrix = SkMatrix::MakeScale(1.2f, 1.2f); + SkMatrix staticMatrix = SkMatrix::Scale(1.2f, 1.2f); properties.setStaticMatrix(&staticMatrix); // ignored, since static overrides animation - SkMatrix animationMatrix = SkMatrix::MakeTrans(15, 15); + SkMatrix animationMatrix = SkMatrix::Translate(15, 15); properties.setAnimationMatrix(&animationMatrix); properties.setTranslationX(10); diff --git a/location/java/android/location/util/identity/CallerIdentity.java b/location/java/android/location/util/identity/CallerIdentity.java index e023aa1dcd22..9dbdedeb8a03 100644 --- a/location/java/android/location/util/identity/CallerIdentity.java +++ b/location/java/android/location/util/identity/CallerIdentity.java @@ -150,8 +150,8 @@ public final class CallerIdentity { return mListenerId; } - /** Returns true if this represents a system identity. */ - public boolean isSystem() { + /** Returns true if this represents a system server identity. */ + public boolean isSystemServer() { return mUid == Process.SYSTEM_UID; } diff --git a/location/java/com/android/internal/location/timezone/ILocationTimeZoneProviderManager.aidl b/location/java/com/android/internal/location/timezone/ILocationTimeZoneProviderManager.aidl index f89a64fc586c..b5450b7a0f88 100644 --- a/location/java/com/android/internal/location/timezone/ILocationTimeZoneProviderManager.aidl +++ b/location/java/com/android/internal/location/timezone/ILocationTimeZoneProviderManager.aidl @@ -16,7 +16,7 @@ package com.android.internal.location.timezone; -import android.location.timezone.LocationTimeZoneEvent; +import com.android.internal.location.timezone.LocationTimeZoneEvent; /** * Binder interface for the manager of location time zone provider implementations. diff --git a/location/java/android/location/timezone/LocationTimeZoneEvent.aidl b/location/java/com/android/internal/location/timezone/LocationTimeZoneEvent.aidl index 538658822930..199e0671f910 100644 --- a/location/java/android/location/timezone/LocationTimeZoneEvent.aidl +++ b/location/java/com/android/internal/location/timezone/LocationTimeZoneEvent.aidl @@ -14,6 +14,6 @@ * limitations under the License. */ -package android.location.timezone; +package com.android.internal.location.timezone; parcelable LocationTimeZoneEvent; diff --git a/location/java/android/location/timezone/LocationTimeZoneEvent.java b/location/java/com/android/internal/location/timezone/LocationTimeZoneEvent.java index 922a38921048..31c27d1bb977 100644 --- a/location/java/android/location/timezone/LocationTimeZoneEvent.java +++ b/location/java/com/android/internal/location/timezone/LocationTimeZoneEvent.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.location.timezone; +package com.android.internal.location.timezone; import android.annotation.IntDef; import android.annotation.NonNull; @@ -24,6 +24,7 @@ import android.os.Parcelable; import com.android.internal.util.Preconditions; +import java.time.Duration; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -70,30 +71,30 @@ public final class LocationTimeZoneEvent implements Parcelable { @NonNull private final List<String> mTimeZoneIds; - private final long mElapsedRealtimeNanos; + private final long mElapsedRealtimeMillis; private LocationTimeZoneEvent(@EventType int eventType, @NonNull List<String> timeZoneIds, - long elapsedRealtimeNanos) { + long elapsedRealtimeMillis) { mEventType = checkValidEventType(eventType); mTimeZoneIds = immutableList(timeZoneIds); boolean emptyTimeZoneIdListExpected = eventType != EVENT_TYPE_SUCCESS; Preconditions.checkState(!emptyTimeZoneIdListExpected || timeZoneIds.isEmpty()); - mElapsedRealtimeNanos = elapsedRealtimeNanos; + mElapsedRealtimeMillis = elapsedRealtimeMillis; } /** * Returns the time of this fix, in elapsed real-time since system boot. * * <p>This value can be reliably compared to {@link - * android.os.SystemClock#elapsedRealtimeNanos}, to calculate the age of a fix and to compare + * android.os.SystemClock#elapsedRealtime()}, to calculate the age of a fix and to compare * {@link LocationTimeZoneEvent} instances. * - * @return elapsed real-time of fix, in nanoseconds since system boot. + * @return elapsed real-time of fix, in milliseconds */ - public long getElapsedRealtimeNanos() { - return mElapsedRealtimeNanos; + public long getElapsedRealtimeMillis() { + return mElapsedRealtimeMillis; } /** @@ -118,7 +119,8 @@ public final class LocationTimeZoneEvent implements Parcelable { return "LocationTimeZoneEvent{" + "mEventType=" + mEventType + ", mTimeZoneIds=" + mTimeZoneIds - + ", mElapsedRealtimeNanos=" + mElapsedRealtimeNanos + + ", mElapsedRealtimeMillis=" + mElapsedRealtimeMillis + + "(" + Duration.ofMillis(mElapsedRealtimeMillis) + ")" + '}'; } @@ -130,8 +132,8 @@ public final class LocationTimeZoneEvent implements Parcelable { @SuppressWarnings("unchecked") ArrayList<String> timeZoneIds = (ArrayList<String>) in.readArrayList(null /* classLoader */); - long elapsedRealtimeNanos = in.readLong(); - return new LocationTimeZoneEvent(eventType, timeZoneIds, elapsedRealtimeNanos); + long elapsedRealtimeMillis = in.readLong(); + return new LocationTimeZoneEvent(eventType, timeZoneIds, elapsedRealtimeMillis); } @Override @@ -149,7 +151,7 @@ public final class LocationTimeZoneEvent implements Parcelable { public void writeToParcel(Parcel parcel, int flags) { parcel.writeInt(mEventType); parcel.writeList(mTimeZoneIds); - parcel.writeLong(mElapsedRealtimeNanos); + parcel.writeLong(mElapsedRealtimeMillis); } @Override @@ -162,13 +164,13 @@ public final class LocationTimeZoneEvent implements Parcelable { } LocationTimeZoneEvent that = (LocationTimeZoneEvent) o; return mEventType == that.mEventType - && mElapsedRealtimeNanos == that.mElapsedRealtimeNanos + && mElapsedRealtimeMillis == that.mElapsedRealtimeMillis && mTimeZoneIds.equals(that.mTimeZoneIds); } @Override public int hashCode() { - return Objects.hash(mEventType, mTimeZoneIds, mElapsedRealtimeNanos); + return Objects.hash(mEventType, mTimeZoneIds, mElapsedRealtimeMillis); } /** @hide */ @@ -176,7 +178,7 @@ public final class LocationTimeZoneEvent implements Parcelable { private @EventType int mEventType = EVENT_TYPE_UNKNOWN; private @NonNull List<String> mTimeZoneIds = Collections.emptyList(); - private long mElapsedRealtimeNanos; + private long mElapsedRealtimeMillis; public Builder() { } @@ -187,7 +189,7 @@ public final class LocationTimeZoneEvent implements Parcelable { public Builder(@NonNull LocationTimeZoneEvent ltz) { mEventType = ltz.mEventType; mTimeZoneIds = ltz.mTimeZoneIds; - mElapsedRealtimeNanos = ltz.mElapsedRealtimeNanos; + mElapsedRealtimeMillis = ltz.mElapsedRealtimeMillis; } /** @@ -210,8 +212,8 @@ public final class LocationTimeZoneEvent implements Parcelable { /** * Sets the time of this event, in elapsed real-time since system boot. */ - public Builder setElapsedRealtimeNanos(long time) { - mElapsedRealtimeNanos = time; + public Builder setElapsedRealtimeMillis(long time) { + mElapsedRealtimeMillis = time; return this; } @@ -219,7 +221,7 @@ public final class LocationTimeZoneEvent implements Parcelable { * Builds a {@link LocationTimeZoneEvent} instance. */ public LocationTimeZoneEvent build() { - return new LocationTimeZoneEvent(mEventType, mTimeZoneIds, mElapsedRealtimeNanos); + return new LocationTimeZoneEvent(mEventType, mTimeZoneIds, mElapsedRealtimeMillis); } } diff --git a/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneEventUnbundled.java b/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneEventUnbundled.java index 07396333b05c..55f554554474 100644 --- a/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneEventUnbundled.java +++ b/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneEventUnbundled.java @@ -18,9 +18,10 @@ package com.android.location.timezone.provider; import android.annotation.IntDef; import android.annotation.NonNull; -import android.location.timezone.LocationTimeZoneEvent; import android.os.SystemClock; +import com.android.internal.location.timezone.LocationTimeZoneEvent; + import java.util.Collections; import java.util.List; import java.util.Objects; @@ -146,7 +147,7 @@ public final class LocationTimeZoneEventUnbundled { LocationTimeZoneEvent event = new LocationTimeZoneEvent.Builder() .setEventType(internalEventType) .setTimeZoneIds(mTimeZoneIds) - .setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos()) + .setElapsedRealtimeMillis(SystemClock.elapsedRealtime()) .build(); return new LocationTimeZoneEventUnbundled(event); } diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java index 65e6feaa31d3..ea691a727d25 100644 --- a/media/java/android/media/AudioRecord.java +++ b/media/java/android/media/AudioRecord.java @@ -166,6 +166,7 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection, //-------------------- /** * Accessed by native methods: provides access to C++ AudioRecord object + * Is 0 after release() */ @SuppressWarnings("unused") @UnsupportedAppUsage @@ -1872,7 +1873,11 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection, if (mNativeRecorderInJavaObj == 0) { return 0; } - return native_getPortId(); + try { + return native_getPortId(); + } catch (IllegalStateException e) { + return 0; + } } //-------------------------------------------------------------------------- @@ -2055,6 +2060,9 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection, private native final int native_get_active_microphones( ArrayList<MicrophoneInfo> activeMicrophones); + /** + * @throws IllegalStateException + */ private native int native_getPortId(); private native int native_set_preferred_microphone_direction(int direction); diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index 973c2a82c549..4b11e3231aba 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -4237,14 +4237,14 @@ public class MediaPlayer extends PlayerBase * @hide */ @SystemApi - @RequiresPermission("android.permission.BIND_IMS_SERVICE") + @RequiresPermission(BIND_IMS_SERVICE) public void setOnRtpRxNoticeListener( @NonNull Context context, @NonNull OnRtpRxNoticeListener listener, @Nullable Handler handler) { Objects.requireNonNull(context); Preconditions.checkArgument( context.checkSelfPermission(BIND_IMS_SERVICE) == PERMISSION_GRANTED, - "android.permission.BIND_IMS_SERVICE permission not granted."); + BIND_IMS_SERVICE + " permission not granted."); mOnRtpRxNoticeListener = Objects.requireNonNull(listener); mOnRtpRxNoticeHandler = handler; } diff --git a/media/java/android/media/MediaTranscodeManager.java b/media/java/android/media/MediaTranscodeManager.java index 4544d049e09f..705da19b65e4 100644 --- a/media/java/android/media/MediaTranscodeManager.java +++ b/media/java/android/media/MediaTranscodeManager.java @@ -29,6 +29,7 @@ import android.net.Uri; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.ServiceSpecificException; import android.system.Os; import android.util.Log; @@ -616,15 +617,24 @@ public final class MediaTranscodeManager { /* Writes the TranscodingRequest to a parcel. */ private TranscodingRequestParcel writeToParcel(@NonNull Context context) { TranscodingRequestParcel parcel = new TranscodingRequestParcel(); - // TODO(hkuang): Implement all the fields here to pass to service. parcel.priority = mPriority; parcel.transcodingType = mType; parcel.sourceFilePath = mSourceUri.toString(); parcel.destinationFilePath = mDestinationUri.toString(); parcel.clientUid = mClientUid; parcel.clientPid = mClientPid; - parcel.clientPackageName = mClientUid < 0 ? context.getPackageName() : - context.getPackageManager().getNameForUid(mClientUid); + if (mClientUid < 0) { + parcel.clientPackageName = context.getPackageName(); + } else { + String packageName = context.getPackageManager().getNameForUid(mClientUid); + // PackageName is optional as some uid does not have package name. Set to + // "Unavailable" string in this case. + if (packageName == null) { + Log.w(TAG, "Failed to find package for uid: " + mClientUid); + packageName = "Unavailable"; + } + parcel.clientPackageName = packageName; + } parcel.requestedVideoTrackFormat = convertToVideoTrackFormat(mVideoTrackFormat); if (mTestConfig != null) { parcel.isForTesting = true; @@ -753,7 +763,7 @@ public final class MediaTranscodeManager { */ @NonNull public Builder setClientUid(int uid) { - if (uid <= 0) { + if (uid < 0) { throw new IllegalArgumentException("Invalid Uid"); } mClientUid = uid; @@ -769,7 +779,7 @@ public final class MediaTranscodeManager { */ @NonNull public Builder setClientPid(int pid) { - if (pid <= 0) { + if (pid < 0) { throw new IllegalArgumentException("Invalid pid"); } mClientPid = pid; @@ -1417,7 +1427,7 @@ public final class MediaTranscodeManager { mPendingTranscodingSessions.put(session.getSessionId(), session); return session; } - } catch (RemoteException re) { + } catch (RemoteException | ServiceSpecificException ex) { throw new UnsupportedOperationException( "Failed to submit request to Transcoding service"); } diff --git a/media/java/android/media/Rating.java b/media/java/android/media/Rating.java index be752583eae5..4da23a1319af 100644 --- a/media/java/android/media/Rating.java +++ b/media/java/android/media/Rating.java @@ -206,11 +206,12 @@ public final class Rating implements Parcelable { Log.e(TAG, "Invalid rating style (" + starRatingStyle + ") for a star rating"); return null; } - if ((starRating < 0.0f) || (starRating > maxRating)) { + if (starRating >= 0.0f && starRating <= maxRating) { + return new Rating(starRatingStyle, starRating); + } else { Log.e(TAG, "Trying to set out of range star-based rating"); return null; } - return new Rating(starRatingStyle, starRating); } /** @@ -221,11 +222,11 @@ public final class Rating implements Parcelable { * @return null if the rating is out of range, a new Rating instance otherwise. */ public static Rating newPercentageRating(float percent) { - if ((percent < 0.0f) || (percent > 100.0f)) { + if (percent >= 0.0f && percent <= 100.0f) { + return new Rating(RATING_PERCENTAGE, percent); + } else { Log.e(TAG, "Invalid percentage-based rating value"); return null; - } else { - return new Rating(RATING_PERCENTAGE, percent); } } diff --git a/media/java/android/media/tv/tuner/Descrambler.java b/media/java/android/media/tv/tuner/Descrambler.java index 5f79dc52a2f1..2217eb3a1dc1 100644 --- a/media/java/android/media/tv/tuner/Descrambler.java +++ b/media/java/android/media/tv/tuner/Descrambler.java @@ -22,6 +22,7 @@ import android.annotation.Nullable; import android.annotation.SystemApi; import android.media.tv.tuner.Tuner.Result; import android.media.tv.tuner.filter.Filter; +import android.util.Log; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -115,9 +116,9 @@ public class Descrambler implements AutoCloseable { * keys for different purposes. * * @param keyToken the token to be used to link the key slot. Use {@link Tuner.INVALID_KEYTOKEN} - * to remove the to remove the current key from descrambler. If the current keyToken - * comes from MediaCas session, use {@link Tuner.INVALID_KEYTOKEN} to remove current key - * before close MediaCas session. + * to remove the current key from descrambler. If the current keyToken comes from a + * MediaCas session, use {@link Tuner.INVALID_KEYTOKEN} to remove current key before + * closing the MediaCas session. * @return result status of the operation. */ @Result @@ -125,6 +126,9 @@ public class Descrambler implements AutoCloseable { synchronized (mLock) { TunerUtils.checkResourceState(TAG, mIsClosed); Objects.requireNonNull(keyToken, "key token must not be null"); + if (!isValidKeyToken(keyToken)) { + return Tuner.RESULT_INVALID_ARGUMENT; + } return nativeSetKeyToken(keyToken); } } @@ -147,4 +151,17 @@ public class Descrambler implements AutoCloseable { } } + private boolean isValidKeyToken(byte[] keyToken) { + if (keyToken.length == 0 || keyToken.length > 16) { + Log.d(TAG, "Invalid key token size: " + (keyToken.length * 8) + " bit."); + return false; + } + for (int i = 0; i < keyToken.length; i++) { + if (keyToken[i] < 0) { + Log.d(TAG, "Invalid key token."); + return false; + } + } + return true; + } } diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java index 82cc78d7e47d..451f54caf7f9 100644 --- a/media/java/android/media/tv/tuner/filter/Filter.java +++ b/media/java/android/media/tv/tuner/filter/Filter.java @@ -186,7 +186,7 @@ public class Filter implements AutoCloseable { value = {SCRAMBLING_STATUS_UNKNOWN, SCRAMBLING_STATUS_NOT_SCRAMBLED, SCRAMBLING_STATUS_SCRAMBLED}) @Retention(RetentionPolicy.SOURCE) - public @interface ScramblingStatusMask {} + public @interface ScramblingStatus {} /** * Content’s scrambling status is unknown @@ -204,6 +204,23 @@ public class Filter implements AutoCloseable { public static final int SCRAMBLING_STATUS_SCRAMBLED = android.hardware.tv.tuner.V1_1.Constants.ScramblingStatus.SCRAMBLED; + /** @hide */ + @IntDef(flag = true, + prefix = "MONITOR_EVENT_", + value = {MONITOR_EVENT_SCRAMBLING_STATUS, MONITOR_EVENT_IP_CID_CHANGE}) + @Retention(RetentionPolicy.SOURCE) + public @interface MonitorEventTypeMask {} + + /** + * Monitor scrambling status change. + */ + public static final int MONITOR_EVENT_SCRAMBLING_STATUS = + android.hardware.tv.tuner.V1_1.Constants.DemuxFilterMonitorEventType.SCRAMBLING_STATUS; + /** + * Monitor ip cid change. + */ + public static final int MONITOR_EVENT_IP_CID_CHANGE = + android.hardware.tv.tuner.V1_1.Constants.DemuxFilterMonitorEventType.IP_CID_CHANGE; private static final String TAG = "Filter"; @@ -222,7 +239,7 @@ public class Filter implements AutoCloseable { int type, int subType, FilterConfiguration settings); private native int nativeGetId(); private native long nativeGetId64Bit(); - private native int nativeconfigureScramblingEvent(int scramblingStatusMask); + private native int nativeConfigureMonitorEvent(int monitorEventTypesMask); private native int nativeSetDataSource(Filter source); private native int nativeStartFilter(); private native int nativeStopFilter(); @@ -306,34 +323,40 @@ public class Filter implements AutoCloseable { } /** - * Configure the Filter to monitor specific Scrambling Status through - * {@link ScramblingStatusEvent}. + * Configure the Filter to monitor scrambling status and ip cid change. Set corresponding bit of + * {@link MonitorEventTypeMask} to monitor the change. Reset to stop monitoring. * * <p>{@link ScramblingStatusEvent} should be sent at the following two scenarios: + * <ul> + * <li>When this method is called with {@link MONITOR_EVENT_SCRAMBLING_STATUS}, the first + * detected scrambling status should be sent. + * <li>When the Scrambling status transits into different status, event should be sent. + * <ul/> * + * <p>{@link IpCidChangeEvent} should be sent at the following two scenarios: * <ul> - * <li>When this method is called, the first detected scrambling status should be sent. - * <li>When the filter transits into the monitored statuses configured in - * {@code scramblingStatusMask}, event should be sent. + * <li>When this method is called with {@link MONITOR_EVENT_IP_CID_CHANGE}, the first detected + * CID for the IP should be sent. + * <li>When the CID is changed to different value for the IP filter, event should be sent. * <ul/> * * <p>This configuration is only supported in Tuner 1.1 or higher version. Unsupported version * will cause no-op. Use {@link TunerVersionChecker.getTunerVersion()} to get the version * information. * - * @param scramblingStatusMask Scrambling Statuses to be monitored. Set corresponding bit to - * monitor it. Reset to stop monitoring. + * @param monitorEventTypesMask Types of event to be monitored. Set corresponding bit to + * monitor it. Reset to stop monitoring. * @return result status of the operation. */ @Result - public int configureScramblingStatusEvent(@ScramblingStatusMask int scramblingStatusMask) { + public int configureMonitorEvent(@MonitorEventTypeMask int monitorEventTypesMask) { synchronized (mLock) { TunerUtils.checkResourceState(TAG, mIsClosed); if (!TunerVersionChecker.checkHigherOrEqualVersionTo( - TunerVersionChecker.TUNER_VERSION_1_1, "configureScramblingStatusEvent")) { + TunerVersionChecker.TUNER_VERSION_1_1, "configureMonitorEvent")) { return Tuner.RESULT_UNAVAILABLE; } - return nativeconfigureScramblingEvent(scramblingStatusMask); + return nativeConfigureMonitorEvent(monitorEventTypesMask); } } diff --git a/media/java/android/media/tv/tuner/filter/IpCidChangeEvent.java b/media/java/android/media/tv/tuner/filter/IpCidChangeEvent.java new file mode 100644 index 000000000000..c0043f3b15cd --- /dev/null +++ b/media/java/android/media/tv/tuner/filter/IpCidChangeEvent.java @@ -0,0 +1,46 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.tuner.filter; + +import android.annotation.SystemApi; + +/** + * Ip Cid Change event sent from {@link Filter} objects new ip cid. + * + * <p>This event is only sent in Tuner 1.1 or higher version. Use + * {@link TunerVersionChecker.getTunerVersion()} to get the version information. + * + * @hide + */ +@SystemApi +public final class IpCidChangeEvent extends FilterEvent { + private final int mCid; + + private IpCidChangeEvent(int cid) { + mCid = cid; + } + + /** + * Gets ip cid. + * + * <p>This event is only sent in Tuner 1.1 or higher version. Use + * {@link TunerVersionChecker.getTunerVersion()} to get the version information. + */ + public int getIpCid() { + return mCid; + } +} diff --git a/media/java/android/media/tv/tuner/filter/RecordSettings.java b/media/java/android/media/tv/tuner/filter/RecordSettings.java index 52ce208f251e..91992afadb3e 100644 --- a/media/java/android/media/tv/tuner/filter/RecordSettings.java +++ b/media/java/android/media/tv/tuner/filter/RecordSettings.java @@ -51,7 +51,7 @@ public class RecordSettings extends Settings { public @interface TsIndexMask {} /** - * Invalid TS index. + * Invalid Transport Stream (TS) index. */ public static final int TS_INDEX_INVALID = 0; /** diff --git a/media/java/android/media/tv/tuner/filter/ScramblingStatusEvent.java b/media/java/android/media/tv/tuner/filter/ScramblingStatusEvent.java index a78b96e9cf51..fef539638db8 100644 --- a/media/java/android/media/tv/tuner/filter/ScramblingStatusEvent.java +++ b/media/java/android/media/tv/tuner/filter/ScramblingStatusEvent.java @@ -30,18 +30,17 @@ import android.annotation.SystemApi; public final class ScramblingStatusEvent extends FilterEvent { private final int mScramblingStatus; - private ScramblingStatusEvent(@Filter.ScramblingStatusMask int scramblingStatus) { + private ScramblingStatusEvent(@Filter.ScramblingStatus int scramblingStatus) { mScramblingStatus = scramblingStatus; } /** * Gets Scrambling Status Type. * - * <p>This event field is only sent in Tuner 1.1 or higher version. Unsupported version returns - * default value 0. Use {@link TunerVersionChecker.getTunerVersion()} to get the version - * information. + * <p>This event field is only sent in Tuner 1.1 or higher version. Use + * {@link TunerVersionChecker.getTunerVersion()} to get the version information. */ - @Filter.ScramblingStatusMask + @Filter.ScramblingStatus public int getScramblingStatus() { return mScramblingStatus; } diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java index ed56b4398c22..798bf6e2f8ee 100755 --- a/media/java/android/mtp/MtpDatabase.java +++ b/media/java/android/mtp/MtpDatabase.java @@ -19,8 +19,7 @@ package android.mtp; import android.annotation.NonNull; import android.content.BroadcastReceiver; import android.content.ContentProviderClient; -import android.content.ContentUris; -import android.content.ContentValues; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -32,7 +31,6 @@ import android.media.ExifInterface; import android.media.ThumbnailUtils; import android.net.Uri; import android.os.BatteryManager; -import android.os.RemoteException; import android.os.SystemProperties; import android.os.storage.StorageVolume; import android.provider.MediaStore; @@ -103,8 +101,6 @@ public class MtpDatabase implements AutoCloseable { private MtpStorageManager mManager; private static final String PATH_WHERE = Files.FileColumns.DATA + "=?"; - private static final String[] ID_PROJECTION = new String[] {Files.FileColumns._ID}; - private static final String[] PATH_PROJECTION = new String[] {Files.FileColumns.DATA}; private static final String NO_MEDIA = ".nomedia"; static { @@ -431,7 +427,7 @@ public class MtpDatabase implements AutoCloseable { } // Add the new file to MediaProvider if (succeeded) { - MediaStore.scanFile(mContext.getContentResolver(), obj.getPath().toFile()); + updateMediaStore(mContext, obj.getPath().toFile()); } } @@ -580,32 +576,8 @@ public class MtpDatabase implements AutoCloseable { return MtpConstants.RESPONSE_GENERAL_ERROR; } - // finally update MediaProvider - ContentValues values = new ContentValues(); - values.put(Files.FileColumns.DATA, newPath.toString()); - String[] whereArgs = new String[]{oldPath.toString()}; - try { - // note - we are relying on a special case in MediaProvider.update() to update - // the paths for all children in the case where this is a directory. - final Uri objectsUri = MediaStore.Files.getContentUri(obj.getVolumeName()); - mMediaProvider.update(objectsUri, values, PATH_WHERE, whereArgs); - } catch (RemoteException e) { - Log.e(TAG, "RemoteException in mMediaProvider.update", e); - } - - // check if nomedia status changed - if (obj.isDir()) { - // for directories, check if renamed from something hidden to something non-hidden - if (oldPath.getFileName().startsWith(".") && !newPath.startsWith(".")) { - MediaStore.scanFile(mContext.getContentResolver(), newPath.toFile()); - } - } else { - // for files, check if renamed from .nomedia to something else - if (oldPath.getFileName().toString().toLowerCase(Locale.US).equals(NO_MEDIA) - && !newPath.getFileName().toString().toLowerCase(Locale.US).equals(NO_MEDIA)) { - MediaStore.scanFile(mContext.getContentResolver(), newPath.getParent().toFile()); - } - } + updateMediaStore(mContext, oldPath.toFile()); + updateMediaStore(mContext, newPath.toFile()); return MtpConstants.RESPONSE_OK; } @@ -635,48 +607,15 @@ public class MtpDatabase implements AutoCloseable { Log.e(TAG, "Failed to end move object"); return; } - obj = mManager.getObject(objId); if (!success || obj == null) return; - // Get parent info from MediaProvider, since the id is different from MTP's - ContentValues values = new ContentValues(); + Path path = newParentObj.getPath().resolve(name); Path oldPath = oldParentObj.getPath().resolve(name); - values.put(Files.FileColumns.DATA, path.toString()); - if (obj.getParent().isRoot()) { - values.put(Files.FileColumns.PARENT, 0); - } else { - int parentId = findInMedia(newParentObj, path.getParent()); - if (parentId != -1) { - values.put(Files.FileColumns.PARENT, parentId); - } else { - // The new parent isn't in MediaProvider, so delete the object instead - deleteFromMedia(obj, oldPath, obj.isDir()); - return; - } - } - // update MediaProvider - Cursor c = null; - String[] whereArgs = new String[]{oldPath.toString()}; - try { - int parentId = -1; - if (!oldParentObj.isRoot()) { - parentId = findInMedia(oldParentObj, oldPath.getParent()); - } - if (oldParentObj.isRoot() || parentId != -1) { - // Old parent exists in MediaProvider - perform a move - // note - we are relying on a special case in MediaProvider.update() to update - // the paths for all children in the case where this is a directory. - final Uri objectsUri = MediaStore.Files.getContentUri(obj.getVolumeName()); - mMediaProvider.update(objectsUri, values, PATH_WHERE, whereArgs); - } else { - // Old parent doesn't exist - add the object - MediaStore.scanFile(mContext.getContentResolver(), path.toFile()); - } - } catch (RemoteException e) { - Log.e(TAG, "RemoteException in mMediaProvider.update", e); - } + + updateMediaStore(mContext, oldPath.toFile()); + updateMediaStore(mContext, path.toFile()); } @VisibleForNative @@ -699,7 +638,19 @@ public class MtpDatabase implements AutoCloseable { if (!success) { return; } - MediaStore.scanFile(mContext.getContentResolver(), obj.getPath().toFile()); + + updateMediaStore(mContext, obj.getPath().toFile()); + } + + private static void updateMediaStore(@NonNull Context context, @NonNull File file) { + final ContentResolver resolver = context.getContentResolver(); + // For file, check whether the file name is .nomedia or not. + // If yes, scan the parent directory to update all files in the directory. + if (!file.isDirectory() && file.getName().toLowerCase(Locale.ROOT).endsWith(NO_MEDIA)) { + MediaStore.scanFile(resolver, file.getParentFile()); + } else { + MediaStore.scanFile(resolver, file); + } } @VisibleForNative @@ -928,26 +879,6 @@ public class MtpDatabase implements AutoCloseable { deleteFromMedia(obj, obj.getPath(), obj.isDir()); } - private int findInMedia(MtpStorageManager.MtpObject obj, Path path) { - final Uri objectsUri = MediaStore.Files.getContentUri(obj.getVolumeName()); - - int ret = -1; - Cursor c = null; - try { - c = mMediaProvider.query(objectsUri, ID_PROJECTION, PATH_WHERE, - new String[]{path.toString()}, null, null); - if (c != null && c.moveToNext()) { - ret = c.getInt(0); - } - } catch (RemoteException e) { - Log.e(TAG, "Error finding " + path + " in MediaProvider"); - } finally { - if (c != null) - c.close(); - } - return ret; - } - private void deleteFromMedia(MtpStorageManager.MtpObject obj, Path path, boolean isDir) { final Uri objectsUri = MediaStore.Files.getContentUri(obj.getVolumeName()); try { @@ -963,13 +894,10 @@ public class MtpDatabase implements AutoCloseable { } String[] whereArgs = new String[]{path.toString()}; - if (mMediaProvider.delete(objectsUri, PATH_WHERE, whereArgs) > 0) { - if (!isDir && path.toString().toLowerCase(Locale.US).endsWith(NO_MEDIA)) { - MediaStore.scanFile(mContext.getContentResolver(), path.getParent().toFile()); - } - } else { - Log.i(TAG, "Mediaprovider didn't delete " + path); + if (mMediaProvider.delete(objectsUri, PATH_WHERE, whereArgs) == 0) { + Log.i(TAG, "MediaProvider didn't delete " + path); } + updateMediaStore(mContext, path.toFile()); } catch (Exception e) { Log.d(TAG, "Failed to delete " + path + " from MediaProvider"); } diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp index b255a48d34d0..72dc32e7998f 100644 --- a/media/jni/android_media_tv_Tuner.cpp +++ b/media/jni/android_media_tv_Tuner.cpp @@ -604,11 +604,16 @@ jobjectArray FilterCallback::getTsRecordEvent( jlong byteNumber = static_cast<jlong>(tsRecordEvent.byteNumber); - jlong pts = (eventsExt.size() > i) ? static_cast<jlong>(eventsExt[i].tsRecord().pts) - : static_cast<jlong>(Constant64Bit::INVALID_PRESENTATION_TIME_STAMP); - jlong firstMbInSlice = (eventsExt.size() > i) - ? static_cast<jint>(eventsExt[i].tsRecord().firstMbInSlice) - : static_cast<jint>(Constant::INVALID_FIRST_MACROBLOCK_IN_SLICE); + jlong pts; + jlong firstMbInSlice; + if (eventsExt.size() > i && eventsExt[i].getDiscriminator() == + DemuxFilterEventExt::Event::hidl_discriminator::tsRecord) { + pts = static_cast<jlong>(eventsExt[i].tsRecord().pts); + firstMbInSlice = static_cast<jint>(eventsExt[i].tsRecord().firstMbInSlice); + } else { + pts = static_cast<jlong>(Constant64Bit::INVALID_PRESENTATION_TIME_STAMP); + firstMbInSlice = static_cast<jint>(Constant::INVALID_FIRST_MACROBLOCK_IN_SLICE); + } jobject obj = env->NewObject(eventClazz, eventInit, jpid, ts, sc, byteNumber, @@ -632,16 +637,25 @@ jobjectArray FilterCallback::getMmtpRecordEvent( jint scHevcIndexMask = static_cast<jint>(mmtpRecordEvent.scHevcIndexMask); jlong byteNumber = static_cast<jlong>(mmtpRecordEvent.byteNumber); - jint mpuSequenceNumber = (eventsExt.size() > i) - ? static_cast<jint>(eventsExt[i].mmtpRecord().mpuSequenceNumber) - : static_cast<jint>(Constant::INVALID_MMTP_RECORD_EVENT_MPT_SEQUENCE_NUM); - jlong pts = (eventsExt.size() > i) ? static_cast<jlong>(eventsExt[i].mmtpRecord().pts) - : static_cast<jlong>(Constant64Bit::INVALID_PRESENTATION_TIME_STAMP); - jlong firstMbInSlice = (eventsExt.size() > i) - ? static_cast<jint>(eventsExt[i].mmtpRecord().firstMbInSlice) - : static_cast<jint>(Constant::INVALID_FIRST_MACROBLOCK_IN_SLICE); - jlong tsIndexMask = (eventsExt.size() > i) - ? static_cast<jint>(eventsExt[i].mmtpRecord().tsIndexMask) : 0; + + jint mpuSequenceNumber; + jlong pts; + jlong firstMbInSlice; + jlong tsIndexMask; + + if (eventsExt.size() > i && eventsExt[i].getDiscriminator() == + DemuxFilterEventExt::Event::hidl_discriminator::mmtpRecord) { + mpuSequenceNumber = static_cast<jint>(eventsExt[i].mmtpRecord().mpuSequenceNumber); + pts = static_cast<jlong>(eventsExt[i].mmtpRecord().pts); + firstMbInSlice = static_cast<jint>(eventsExt[i].mmtpRecord().firstMbInSlice); + tsIndexMask = static_cast<jint>(eventsExt[i].mmtpRecord().tsIndexMask); + } else { + mpuSequenceNumber = + static_cast<jint>(Constant::INVALID_MMTP_RECORD_EVENT_MPT_SEQUENCE_NUM); + pts = static_cast<jlong>(Constant64Bit::INVALID_PRESENTATION_TIME_STAMP); + firstMbInSlice = static_cast<jint>(Constant::INVALID_FIRST_MACROBLOCK_IN_SLICE); + tsIndexMask = 0; + } jobject obj = env->NewObject(eventClazz, eventInit, scHevcIndexMask, byteNumber, @@ -720,11 +734,21 @@ jobjectArray FilterCallback::getScramblingStatusEvent( jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/ScramblingStatusEvent"); jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(I)V"); - for (int i = 0; i < eventsExt.size(); i++) { - auto scramblingStatus = eventsExt[i].scramblingStatus(); - jobject obj = env->NewObject(eventClazz, eventInit, static_cast<jint>(scramblingStatus)); - env->SetObjectArrayElement(arr, i, obj); - } + auto scramblingStatus = eventsExt[0].monitorEvent().scramblingStatus(); + jobject obj = env->NewObject(eventClazz, eventInit, static_cast<jint>(scramblingStatus)); + env->SetObjectArrayElement(arr, 0, obj); + return arr; +} + +jobjectArray FilterCallback::getIpCidChangeEvent( + jobjectArray& arr, const std::vector<DemuxFilterEventExt::Event>& eventsExt) { + JNIEnv *env = AndroidRuntime::getJNIEnv(); + jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/IpCidChangeEvent"); + jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(I)V"); + + auto cid = eventsExt[0].monitorEvent().cid(); + jobject obj = env->NewObject(eventClazz, eventInit, static_cast<jint>(cid)); + env->SetObjectArrayElement(arr, 0, obj); return arr; } @@ -734,11 +758,9 @@ jobjectArray FilterCallback::getRestartEvent( jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/RestartEvent"); jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(I)V"); - for (int i = 0; i < eventsExt.size(); i++) { - auto startId = eventsExt[i].startId(); - jobject obj = env->NewObject(eventClazz, eventInit, static_cast<jint>(startId)); - env->SetObjectArrayElement(arr, i, obj); - } + auto startId = eventsExt[0].startId(); + jobject obj = env->NewObject(eventClazz, eventInit, static_cast<jint>(startId)); + env->SetObjectArrayElement(arr, 0, obj); return arr; } @@ -747,17 +769,31 @@ Return<void> FilterCallback::onFilterEvent_1_1(const DemuxFilterEvent& filterEve ALOGD("FilterCallback::onFilterEvent_1_1"); JNIEnv *env = AndroidRuntime::getJNIEnv(); + jobjectArray array; std::vector<DemuxFilterEvent::Event> events = filterEvent.events; std::vector<DemuxFilterEventExt::Event> eventsExt = filterEventExt.events; jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/FilterEvent"); - jobjectArray array = env->NewObjectArray(events.size(), eventClazz, NULL); if (events.empty() && !eventsExt.empty()) { + // Monitor event should be sent with one DemuxFilterMonitorEvent in DemuxFilterEventExt. + array = env->NewObjectArray(1, eventClazz, NULL); auto eventExt = eventsExt[0]; switch (eventExt.getDiscriminator()) { - case DemuxFilterEventExt::Event::hidl_discriminator::scramblingStatus: { - array = getScramblingStatusEvent(array, eventsExt); + case DemuxFilterEventExt::Event::hidl_discriminator::monitorEvent: { + switch (eventExt.monitorEvent().getDiscriminator()) { + case DemuxFilterMonitorEvent::hidl_discriminator::scramblingStatus: { + array = getScramblingStatusEvent(array, eventsExt); + break; + } + case DemuxFilterMonitorEvent::hidl_discriminator::cid: { + array = getIpCidChangeEvent(array, eventsExt); + break; + } + default: { + break; + } + } break; } case DemuxFilterEventExt::Event::hidl_discriminator::startId: { @@ -771,6 +807,7 @@ Return<void> FilterCallback::onFilterEvent_1_1(const DemuxFilterEvent& filterEve } if (!events.empty()) { + array = env->NewObjectArray(events.size(), eventClazz, NULL); auto event = events[0]; switch (event.getDiscriminator()) { case DemuxFilterEvent::Event::hidl_discriminator::media: { @@ -4069,8 +4106,8 @@ static jlong android_media_tv_Tuner_get_filter_64bit_id(JNIEnv* env, jobject fil ::android::hardware::tv::tuner::V1_1::Constant64Bit::INVALID_FILTER_ID_64BIT); } -static jint android_media_tv_Tuner_configure_scrambling_status_event( - JNIEnv* env, jobject filter, int scramblingStatusMask) { +static jint android_media_tv_Tuner_configure_monitor_event( + JNIEnv* env, jobject filter, int monitorEventType) { sp<IFilter> iFilterSp = getFilter(env, filter)->getIFilter(); if (iFilterSp == NULL) { ALOGD("Failed to configure scrambling event: filter not found"); @@ -4082,7 +4119,7 @@ static jint android_media_tv_Tuner_configure_scrambling_status_event( Result res; if (iFilterSp_1_1 != NULL) { - res = iFilterSp_1_1->configureScramblingEvent(scramblingStatusMask); + res = iFilterSp_1_1->configureMonitorEvent(monitorEventType); } else { ALOGW("configureScramblingEvent is not supported with the current HAL implementation."); return (jint) Result::INVALID_STATE; @@ -4762,8 +4799,8 @@ static const JNINativeMethod gFilterMethods[] = { { "nativeGetId", "()I", (void *)android_media_tv_Tuner_get_filter_id }, { "nativeGetId64Bit", "()J", (void *)android_media_tv_Tuner_get_filter_64bit_id }, - { "nativeconfigureScramblingEvent", "(I)I", - (void *)android_media_tv_Tuner_configure_scrambling_status_event }, + { "nativeConfigureMonitorEvent", "(I)I", + (void *)android_media_tv_Tuner_configure_monitor_event }, { "nativeSetDataSource", "(Landroid/media/tv/tuner/filter/Filter;)I", (void *)android_media_tv_Tuner_set_filter_data_source }, { "nativeStartFilter", "()I", (void *)android_media_tv_Tuner_start_filter }, diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h index ebb95f43c52d..e79b5c2e391a 100644 --- a/media/jni/android_media_tv_Tuner.h +++ b/media/jni/android_media_tv_Tuner.h @@ -44,7 +44,6 @@ using ::android::hardware::hidl_handle; using ::android::hardware::hidl_vec; using ::android::hardware::kSynchronizedReadWrite; using ::android::hardware::tv::tuner::V1_0::DemuxFilterEvent; -using ::android::hardware::tv::tuner::V1_1::DemuxFilterEventExt; using ::android::hardware::tv::tuner::V1_0::DemuxFilterStatus; using ::android::hardware::tv::tuner::V1_0::DemuxFilterType; using ::android::hardware::tv::tuner::V1_0::DemuxPid; @@ -73,6 +72,8 @@ using ::android::hardware::tv::tuner::V1_0::LnbId; using ::android::hardware::tv::tuner::V1_0::PlaybackStatus; using ::android::hardware::tv::tuner::V1_0::RecordStatus; using ::android::hardware::tv::tuner::V1_0::Result; +using ::android::hardware::tv::tuner::V1_1::DemuxFilterEventExt; +using ::android::hardware::tv::tuner::V1_1::DemuxFilterMonitorEvent; using ::android::hardware::tv::tuner::V1_1::FrontendScanMessageExt1_1; using ::android::hardware::tv::tuner::V1_1::FrontendScanMessageTypeExt1_1; using ::android::hardware::tv::tuner::V1_1::IFrontendCallback; @@ -188,6 +189,8 @@ private: jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events); jobjectArray getScramblingStatusEvent( jobjectArray& arr, const std::vector<DemuxFilterEventExt::Event>& eventsExt); + jobjectArray getIpCidChangeEvent( + jobjectArray& arr, const std::vector<DemuxFilterEventExt::Event>& eventsExt); jobjectArray getRestartEvent( jobjectArray& arr, const std::vector<DemuxFilterEventExt::Event>& eventsExt); }; diff --git a/media/native/midi/MidiDeviceInfo.cpp b/media/native/midi/MidiDeviceInfo.cpp index ac68d26c935b..8a573fba322b 100644 --- a/media/native/midi/MidiDeviceInfo.cpp +++ b/media/native/midi/MidiDeviceInfo.cpp @@ -90,13 +90,13 @@ status_t MidiDeviceInfo::readFromParcel(const Parcel* parcel) { status_t MidiDeviceInfo::readStringVector( const Parcel* parcel, Vector<String16> *vectorPtr, size_t defaultLength) { - std::unique_ptr<std::vector<std::unique_ptr<String16>>> v; + std::optional<std::vector<std::optional<String16>>> v; status_t result = parcel->readString16Vector(&v); if (result != OK) return result; vectorPtr->clear(); - if (v.get() != nullptr) { + if (v) { for (const auto& iter : *v) { - if (iter.get() != nullptr) { + if (iter) { vectorPtr->push_back(*iter); } else { vectorPtr->push_back(String16()); diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt index d134b37c6b70..eca67bd7d211 100644 --- a/native/android/libandroid.map.txt +++ b/native/android/libandroid.map.txt @@ -151,6 +151,7 @@ LIBANDROID { AHardwareBuffer_allocate; # introduced=26 AHardwareBuffer_describe; # introduced=26 AHardwareBuffer_fromHardwareBuffer; # introduced=26 + AHardwareBuffer_getId; # introduced=31 AHardwareBuffer_getNativeHandle; # introduced=26 AHardwareBuffer_isSupported; # introduced=29 AHardwareBuffer_lock; # introduced=26 diff --git a/native/webview/plat_support/draw_fn.h b/native/webview/plat_support/draw_fn.h index 42cad43af8fc..44fe56fcaf4b 100644 --- a/native/webview/plat_support/draw_fn.h +++ b/native/webview/plat_support/draw_fn.h @@ -10,6 +10,7 @@ #ifndef ANDROID_WEBVIEW_PUBLIC_BROWSER_DRAW_FN_H_ #define ANDROID_WEBVIEW_PUBLIC_BROWSER_DRAW_FN_H_ +#include <android/surface_control.h> #include <vulkan/vulkan.h> #ifdef __cplusplus @@ -21,7 +22,31 @@ extern "C" { // // 1 is Android Q. This matches kAwDrawGLInfoVersion version 3. // 2 Adds transfer_function_* and color_space_toXYZD50 to AwDrawFn_DrawGLParams. -static const int kAwDrawFnVersion = 2; +// 3 Adds SurfaceControl related functions. +static const int kAwDrawFnVersion = 3; + +// Returns parent ASurfaceControl for WebView overlays. It will be have same +// geometry as the surface we draw into and positioned below it (underlay). +// This does not pass ownership to webview, but guaranteed to be alive until +// transaction from next removeOverlays call or functor destruction will be +// finished. +typedef ASurfaceControl* AwDrawFn_GetSurfaceControl(); + +// Merges WebView transaction to be applied synchronously with current draw. +// This doesn't pass ownership of the transaction, changes will be copied and +// webview can free transaction right after the call. +typedef void AwDrawFn_MergeTransaction(ASurfaceTransaction* transaction); + +enum AwDrawFnOverlaysMode { + // Indicated that webview should not promote anything to overlays this draw + // and remove all visible overlays. + // Added in version 3. + AW_DRAW_FN_OVERLAYS_MODE_DISABLED = 0, + + // Indicates that webview can use overlays. + // Added in version 3. + AW_DRAW_FN_OVERLAYS_MODE_ENABLED = 1, +}; struct AwDrawFn_OnSyncParams { int version; @@ -60,6 +85,19 @@ struct AwDrawFn_DrawGLParams { float transfer_function_e; float transfer_function_f; float color_space_toXYZD50[9]; + + // Input: Indicates how webview should use overlays for this draw. + // Added in version 3. + AwDrawFnOverlaysMode overlays_mode; + + // Input: WebView can call it to obtain parent surface control for overlays. + // Added in version 3. + AwDrawFn_GetSurfaceControl* get_surface_control; + + // Input: WebView call this to apply ASurfaceTransaction synchronously with + // the draw. + // Added in version 3. + AwDrawFn_MergeTransaction* merge_transaction; }; struct AwDrawFn_InitVkParams { @@ -122,12 +160,33 @@ struct AwDrawFn_DrawVkParams { int clip_top; int clip_right; int clip_bottom; + + // Input: Indicates how webview should use overlays for this draw. + // Added in version 3. + AwDrawFnOverlaysMode overlays_mode; + + // Input: WebView can call it to obtain parent surface control for overlays. + // Added in version 3. + AwDrawFn_GetSurfaceControl* get_surface_control; + + // Input: WebView call this to apply ASurfaceTransaction synchronously with + // the draw. + // Added in version 3. + AwDrawFn_MergeTransaction* merge_transaction; }; struct AwDrawFn_PostDrawVkParams { int version; }; +struct AwDrawFn_RemoveOverlaysParams { + int version; + // Input: WebView call this to apply ASurfaceTransaction synchronously with + // the draw. + // Added in version 3. + AwDrawFn_MergeTransaction* merge_transaction; +}; + // Called on render thread while UI thread is blocked. Called for both GL and // VK. typedef void AwDrawFn_OnSync(int functor, @@ -166,8 +225,15 @@ typedef void AwDrawFn_PostDrawVk(int functor, void* data, AwDrawFn_PostDrawVkParams* params); +// Can be called to make webview hide all overlays and stop updating them until +// next draw. WebView must obtain new ASurfaceControl after this call to use as +// parent for the overlays on next draw. +typedef void AwDrawFn_RemoveOverlays(int functor, + void* data, + AwDrawFn_RemoveOverlaysParams* params); + struct AwDrawFnFunctorCallbacks { - // No version here since this is passed from chromium to android. + // version is passed in CreateFunctor call. AwDrawFn_OnSync* on_sync; AwDrawFn_OnContextDestroyed* on_context_destroyed; AwDrawFn_OnDestroyed* on_destroyed; @@ -175,6 +241,8 @@ struct AwDrawFnFunctorCallbacks { AwDrawFn_InitVk* init_vk; AwDrawFn_DrawVk* draw_vk; AwDrawFn_PostDrawVk* post_draw_vk; + // Added in version 3. + AwDrawFn_RemoveOverlays* remove_overlays; }; enum AwDrawFnRenderMode { @@ -185,10 +253,16 @@ enum AwDrawFnRenderMode { // Get the render mode. Result is static for the process. typedef AwDrawFnRenderMode AwDrawFn_QueryRenderMode(void); -// Create a functor. |functor_callbacks| should be valid until OnDestroyed. +// This available up to version 3, use the one below. typedef int AwDrawFn_CreateFunctor(void* data, AwDrawFnFunctorCallbacks* functor_callbacks); +// Create a functor. |functor_callbacks| should be valid until OnDestroyed. +typedef int AwDrawFn_CreateFunctor_v3( + void* data, + int version, + AwDrawFnFunctorCallbacks* functor_callbacks); + // May be called on any thread to signal that the functor should be destroyed. // The functor will receive an onDestroyed when the last usage of it is // released, and it should be considered alive & active until that point. @@ -197,8 +271,11 @@ typedef void AwDrawFn_ReleaseFunctor(int functor); struct AwDrawFnFunctionTable { int version; AwDrawFn_QueryRenderMode* query_render_mode; + // Available up to version 3. AwDrawFn_CreateFunctor* create_functor; AwDrawFn_ReleaseFunctor* release_functor; + // Added in version 3. + AwDrawFn_CreateFunctor_v3* create_functor_v3; }; #ifdef __cplusplus diff --git a/native/webview/plat_support/draw_functor.cpp b/native/webview/plat_support/draw_functor.cpp index 7cce61b87d12..ea57ea070369 100644 --- a/native/webview/plat_support/draw_functor.cpp +++ b/native/webview/plat_support/draw_functor.cpp @@ -32,6 +32,15 @@ struct SupportData { AwDrawFnFunctorCallbacks callbacks; }; +AwDrawFnOverlaysMode GetOverlaysMode(uirenderer::OverlaysMode overlays_mode) { + switch (overlays_mode) { + case uirenderer::OverlaysMode::Disabled: + return AW_DRAW_FN_OVERLAYS_MODE_DISABLED; + case uirenderer::OverlaysMode::Enabled: + return AW_DRAW_FN_OVERLAYS_MODE_ENABLED; + } +} + void onSync(int functor, void* data, const uirenderer::WebViewSyncData& syncData) { AwDrawFn_OnSyncParams params = { @@ -53,8 +62,20 @@ void onDestroyed(int functor, void* data) { delete support; } +void removeOverlays(int functor, void* data, + AwDrawFn_MergeTransaction merge_transaction) { + AwDrawFn_RemoveOverlaysParams params = { + .version = kAwDrawFnVersion, + .merge_transaction = merge_transaction + }; + SupportData* support = static_cast<SupportData*>(data); + if (support->callbacks.remove_overlays) + support->callbacks.remove_overlays(functor, support->data, ¶ms); +} + void draw_gl(int functor, void* data, - const uirenderer::DrawGlInfo& draw_gl_params) { + const uirenderer::DrawGlInfo& draw_gl_params, + const uirenderer::WebViewOverlayData& overlay_params) { float gabcdef[7]; draw_gl_params.color_space_ptr->transferFn(gabcdef); AwDrawFn_DrawGLParams params = { @@ -73,6 +94,9 @@ void draw_gl(int functor, void* data, .transfer_function_d = gabcdef[4], .transfer_function_e = gabcdef[5], .transfer_function_f = gabcdef[6], + .overlays_mode = GetOverlaysMode(overlay_params.overlaysMode), + .get_surface_control = overlay_params.getSurfaceControl, + .merge_transaction = overlay_params.mergeTransaction }; COMPILE_ASSERT(NELEM(params.transform) == NELEM(draw_gl_params.transform), mismatched_transform_matrix_sizes); @@ -118,7 +142,9 @@ void initializeVk(int functor, void* data, support->callbacks.init_vk(functor, support->data, ¶ms); } -void drawVk(int functor, void* data, const uirenderer::VkFunctorDrawParams& draw_vk_params) { +void drawVk(int functor, void* data, + const uirenderer::VkFunctorDrawParams& draw_vk_params, + const uirenderer::WebViewOverlayData& overlay_params) { SupportData* support = static_cast<SupportData*>(data); float gabcdef[7]; draw_vk_params.color_space_ptr->transferFn(gabcdef); @@ -142,6 +168,9 @@ void drawVk(int functor, void* data, const uirenderer::VkFunctorDrawParams& draw .clip_top = draw_vk_params.clip_top, .clip_right = draw_vk_params.clip_right, .clip_bottom = draw_vk_params.clip_bottom, + .overlays_mode = GetOverlaysMode(overlay_params.overlaysMode), + .get_surface_control = overlay_params.getSurfaceControl, + .merge_transaction = overlay_params.mergeTransaction }; COMPILE_ASSERT(sizeof(params.color_space_toXYZD50) == sizeof(skcms_Matrix3x3), gamut_transform_size_mismatch); @@ -161,12 +190,14 @@ void postDrawVk(int functor, void* data) { support->callbacks.post_draw_vk(functor, support->data, ¶ms); } -int CreateFunctor(void* data, AwDrawFnFunctorCallbacks* functor_callbacks) { +int CreateFunctor_v3(void* data, int version, + AwDrawFnFunctorCallbacks* functor_callbacks) { static bool callbacks_initialized = false; static uirenderer::WebViewFunctorCallbacks webview_functor_callbacks = { .onSync = &onSync, .onContextDestroyed = &onContextDestroyed, .onDestroyed = &onDestroyed, + .removeOverlays = &removeOverlays, }; if (!callbacks_initialized) { switch (uirenderer::WebViewFunctor_queryPlatformRenderMode()) { @@ -183,8 +214,23 @@ int CreateFunctor(void* data, AwDrawFnFunctorCallbacks* functor_callbacks) { } SupportData* support = new SupportData{ .data = data, - .callbacks = *functor_callbacks, }; + + // These callbacks are available on all versions. + support->callbacks = { + .on_sync = functor_callbacks->on_sync, + .on_context_destroyed = functor_callbacks->on_context_destroyed, + .on_destroyed = functor_callbacks->on_destroyed, + .draw_gl = functor_callbacks->draw_gl, + .init_vk = functor_callbacks->init_vk, + .draw_vk = functor_callbacks->draw_vk, + .post_draw_vk = functor_callbacks->post_draw_vk, + }; + + if (version >= 3) { + support->callbacks.remove_overlays = functor_callbacks->remove_overlays; + } + int functor = uirenderer::WebViewFunctor_create( support, webview_functor_callbacks, uirenderer::WebViewFunctor_queryPlatformRenderMode()); @@ -192,6 +238,12 @@ int CreateFunctor(void* data, AwDrawFnFunctorCallbacks* functor_callbacks) { return functor; } +int CreateFunctor(void* data, AwDrawFnFunctorCallbacks* functor_callbacks) { + const int kVersionForDeprecatedCreateFunctor = 2; + return CreateFunctor_v3(data, kVersionForDeprecatedCreateFunctor, + functor_callbacks); +} + void ReleaseFunctor(int functor) { uirenderer::WebViewFunctor_release(functor); } @@ -211,6 +263,7 @@ jlong GetDrawFnFunctionTable() { .query_render_mode = &QueryRenderMode, .create_functor = &CreateFunctor, .release_functor = &ReleaseFunctor, + .create_functor_v3 = &CreateFunctor_v3, }; return reinterpret_cast<intptr_t>(&function_table); } diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java index c7f5e9a5ceec..f11febc15d04 100644 --- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java +++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java @@ -20,6 +20,7 @@ import android.content.Intent; import android.os.PersistableBundle; import android.telephony.CarrierConfigManager; import android.telephony.TelephonyManager; +import android.telephony.data.ApnSetting; import android.text.TextUtils; import android.util.Log; @@ -85,8 +86,8 @@ public class CustomConfigLoader { case TelephonyManager.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED: configs = b.getStringArray(CarrierConfigManager .KEY_CARRIER_DEFAULT_ACTIONS_ON_DCFAILURE_STRING_ARRAY); - arg1 = intent.getStringExtra(TelephonyManager.EXTRA_APN_TYPE); - arg2 = intent.getStringExtra(TelephonyManager.EXTRA_ERROR_CODE); + arg1 = String.valueOf(intent.getIntExtra(TelephonyManager.EXTRA_APN_TYPE, -1)); + arg2 = intent.getStringExtra(TelephonyManager.EXTRA_DATA_FAIL_CAUSE); break; case TelephonyManager.ACTION_CARRIER_SIGNAL_RESET: configs = b.getStringArray(CarrierConfigManager @@ -141,10 +142,24 @@ public class CustomConfigLoader { // case 1 actionStr = splitStr[0]; } else if (splitStr.length == 2 && arg1 != null && arg2 != null) { - // case 2 + // case 2. The only thing that uses this is CARRIER_SIGNAL_REQUEST_NETWORK_FAILED, + // and the carrier config for that can provide either an int or string for the apn type, + // depending on when it was introduced. Therefore, return a positive match if either + // the int version or the string version of the apn type in the broadcast matches. + String apnInIntFormat = arg1; + String apnInStringFormat = null; + try { + int apnInt = Integer.parseInt(apnInIntFormat); + apnInStringFormat = ApnSetting.getApnTypeString(apnInt); + } catch (NumberFormatException e) { + Log.e(TAG, "Got invalid apn type from broadcast: " + apnInIntFormat); + } + String[] args = splitStr[0].split(INTRA_GROUP_DELIMITER); - if (args.length == 2 && TextUtils.equals(arg1, args[0]) && - TextUtils.equals(arg2, args[1])) { + boolean doesArg1Match = TextUtils.equals(apnInIntFormat, args[0]) + || (apnInStringFormat != null && TextUtils.equals(apnInStringFormat, args[0])); + if (args.length == 2 && doesArg1Match + && TextUtils.equals(arg2, args[1])) { actionStr = splitStr[1]; } } else if ((splitStr.length == 2) && (arg1 != null) && (arg2 == null)) { diff --git a/packages/CompanionDeviceManager/res/layout/buttons.xml b/packages/CompanionDeviceManager/res/layout/buttons.xml index b190a7f675ca..a80720c40737 100644 --- a/packages/CompanionDeviceManager/res/layout/buttons.xml +++ b/packages/CompanionDeviceManager/res/layout/buttons.xml @@ -29,14 +29,15 @@ android:id="@+id/button_cancel" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="@android:string/cancel" + android:text="@string/consent_no" + android:textColor="?android:attr/textColorSecondary" style="@android:style/Widget.Material.Button.Borderless.Colored" /> <Button android:id="@+id/button_pair" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="@android:string/ok" + android:text="@string/consent_yes" style="@android:style/Widget.Material.Button.Borderless.Colored" /> </LinearLayout>
\ No newline at end of file diff --git a/packages/CompanionDeviceManager/res/layout/device_chooser.xml b/packages/CompanionDeviceManager/res/layout/device_chooser.xml index db014aef6e8b..273347af6119 100644 --- a/packages/CompanionDeviceManager/res/layout/device_chooser.xml +++ b/packages/CompanionDeviceManager/res/layout/device_chooser.xml @@ -23,11 +23,13 @@ <include layout="@layout/title" /> + <include layout="@layout/profile_summary" /> + <ListView android:id="@+id/device_list" android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_below="@+id/title" + android:layout_below="@+id/profile_summary" android:layout_above="@+id/buttons" style="@android:style/Widget.Material.ListView" /> diff --git a/packages/CompanionDeviceManager/res/layout/device_confirmation.xml b/packages/CompanionDeviceManager/res/layout/device_confirmation.xml index 7cde41ac2894..1336e79a855b 100644 --- a/packages/CompanionDeviceManager/res/layout/device_confirmation.xml +++ b/packages/CompanionDeviceManager/res/layout/device_confirmation.xml @@ -23,6 +23,8 @@ <include layout="@layout/title" /> + <include layout="@layout/profile_summary" /> + <include layout="@layout/buttons" /> </LinearLayout>
\ No newline at end of file diff --git a/packages/CompanionDeviceManager/res/layout/profile_summary.xml b/packages/CompanionDeviceManager/res/layout/profile_summary.xml new file mode 100644 index 000000000000..80fec59fbc45 --- /dev/null +++ b/packages/CompanionDeviceManager/res/layout/profile_summary.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + + +<TextView + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/profile_summary" + android:layout_below="@+id/title" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:layout_marginEnd="16dp" + android:textColor="?android:attr/textColorSecondary" + android:textSize="14sp" + android:gravity="center" +/>
\ No newline at end of file diff --git a/packages/CompanionDeviceManager/res/layout/title.xml b/packages/CompanionDeviceManager/res/layout/title.xml index 0a44fbb34a9c..9a5036622468 100644 --- a/packages/CompanionDeviceManager/res/layout/title.xml +++ b/packages/CompanionDeviceManager/res/layout/title.xml @@ -20,5 +20,6 @@ android:id="@+id/title" android:layout_width="match_parent" android:layout_height="wrap_content" + android:gravity="center" style="@*android:style/TextAppearance.Widget.Toolbar.Title" />
\ No newline at end of file diff --git a/packages/CompanionDeviceManager/res/values/strings.xml b/packages/CompanionDeviceManager/res/values/strings.xml index c4372ebef663..1b96b0045ef0 100644 --- a/packages/CompanionDeviceManager/res/values/strings.xml +++ b/packages/CompanionDeviceManager/res/values/strings.xml @@ -20,9 +20,21 @@ <string name="app_label">Companion Device Manager</string> <!-- Title of the device selection dialog. --> - <string name="chooser_title">Link with <strong><xliff:g id="app_name" example="Android Wear">%1$s</xliff:g></strong></string> + <string name="chooser_title">Choose a <xliff:g id="profile_name" example="watch">%1$s</xliff:g> to be managed by <strong><xliff:g id="app_name" example="Android Wear">%2$s</xliff:g></strong></string> - <!-- Title of the device pairing confirmation dialog. --> - <string name="confirmation_title">Link <strong><xliff:g id="app_name" example="Android Wear">%1$s</xliff:g></strong> with <strong><xliff:g id="device_name" example="ASUS ZenWatch 2">%2$s</xliff:g></strong></string> + <!-- The generic placeholder for a device type when nothing specific is known about it [CHAR LIMIT=30] --> + <string name="profile_name_generic">device</string> + + <!-- Title of the device association confirmation dialog. --> + <string name="confirmation_title">Set <strong><xliff:g id="app_name" example="Android Wear">%1$s</xliff:g></strong> to manage your <xliff:g id="profile_name" example="watch">%2$s</xliff:g> - <strong><xliff:g id="device_name" example="ASUS ZenWatch 2">%3$s</xliff:g></strong></string> + + <!-- Text of the device profile permissions explanation in the association dialog. --> + <string name="profile_summary"><xliff:g id="app_name" example="Android Wear">%1$s</xliff:g> is needed to manage your <xliff:g id="profile_name" example="watch">%2$s</xliff:g>. <xliff:g id="app_name2" example="Android Wear">%3$s</xliff:g> will get access to <xliff:g id="permissions" example="Notifications, Calendar and Phone">%4$s</xliff:g> while the <xliff:g id="profile_name2" example="watch">%5$s</xliff:g> is connected.</string> + + <!-- Positive button for the device-app association consent dialog [CHAR LIMIT=30] --> + <string name="consent_yes">Yes</string> + + <!-- Negative button for the device-app association consent dialog [CHAR LIMIT=30] --> + <string name="consent_no">No thanks</string> </resources> diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java index bdfbf82145c7..f42a51d6593a 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java @@ -17,11 +17,13 @@ package com.android.companiondevicemanager; import static android.companion.BluetoothDeviceFilterUtils.getDeviceMacAddress; +import static android.text.TextUtils.withoutPrefix; import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; import static java.util.Objects.requireNonNull; import android.app.Activity; +import android.companion.AssociationRequest; import android.companion.CompanionDeviceManager; import android.content.Intent; import android.content.pm.PackageManager; @@ -62,12 +64,19 @@ public class DeviceChooserActivity extends Activity { getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); - if (getService().mRequest.isSingleDevice()) { + String deviceProfile = getRequest().getDeviceProfile(); + String profileName = deviceProfile == null + ? getString(R.string.profile_name_generic) + //TODO introduce PermissionController APIs to resolve UI values + : withoutPrefix("android.app.role.COMPANION_DEVICE_", deviceProfile).toLowerCase(); + + if (getRequest().isSingleDevice()) { setContentView(R.layout.device_confirmation); final DeviceFilterPair selectedDevice = getService().mDevicesFound.get(0); setTitle(Html.fromHtml(getString( R.string.confirmation_title, getCallingAppName(), + profileName, selectedDevice.getDisplayName()), 0)); mPairButton = findViewById(R.id.button_pair); mPairButton.setOnClickListener(v -> onDeviceConfirmed(getService().mSelectedDevice)); @@ -77,7 +86,9 @@ public class DeviceChooserActivity extends Activity { setContentView(R.layout.device_chooser); mPairButton = findViewById(R.id.button_pair); mPairButton.setVisibility(View.GONE); - setTitle(Html.fromHtml(getString(R.string.chooser_title, getCallingAppName()), 0)); + setTitle(Html.fromHtml(getString(R.string.chooser_title, + profileName, + getCallingAppName()), 0)); mDeviceListView = findViewById(R.id.device_list); final DeviceDiscoveryService.DevicesAdapter adapter = getService().mDevicesAdapter; mDeviceListView.setAdapter(adapter); @@ -97,12 +108,33 @@ public class DeviceChooserActivity extends Activity { }); mDeviceListView.addFooterView(mLoadingIndicator = getProgressBar(), null, false); } + + TextView profileSummary = findViewById(R.id.profile_summary); + + if (deviceProfile != null) { + //TODO introduce PermissionController APIs to resolve UI values + String privileges = "Notifications, Phone, Contacts and Calendar"; + profileSummary.setVisibility(View.VISIBLE); + profileSummary.setText(getString(R.string.profile_summary, + getCallingAppName(), + profileName, + getCallingAppName(), + privileges, + profileName)); + } else { + profileSummary.setVisibility(View.GONE); + } + getService().mActivity = this; mCancelButton = findViewById(R.id.button_cancel); mCancelButton.setOnClickListener(v -> cancel()); } + private AssociationRequest getRequest() { + return getService().mRequest; + } + private void cancel() { getService().onCancel(); setResult(RESULT_CANCELED); @@ -132,7 +164,7 @@ public class DeviceChooserActivity extends Activity { @Override public String getCallingPackage() { - return requireNonNull(getService().mRequest.getCallingPackage()); + return requireNonNull(getRequest().getCallingPackage()); } @Override diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java index a7e397e9563c..2fe351edb080 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java @@ -64,7 +64,7 @@ import android.util.SparseArray; import android.util.TypedValue; import android.view.View; import android.view.ViewGroup; -import android.widget.ArrayAdapter; +import android.widget.BaseAdapter; import android.widget.TextView; import com.android.internal.infra.AndroidFuture; @@ -329,7 +329,7 @@ public class DeviceDiscoveryService extends Service { mServiceCallback.cancel(true); } - class DevicesAdapter extends ArrayAdapter<DeviceFilterPair> { + class DevicesAdapter extends BaseAdapter { private Drawable BLUETOOTH_ICON = icon(android.R.drawable.stat_sys_data_bluetooth); private Drawable WIFI_ICON = icon(com.android.internal.R.drawable.ic_wifi_signal_3); @@ -341,10 +341,6 @@ public class DeviceDiscoveryService extends Service { return icon; } - public DevicesAdapter() { - super(DeviceDiscoveryService.this, 0, mDevicesFound); - } - @Override public View getView( int position, @@ -391,6 +387,21 @@ public class DeviceDiscoveryService extends Service { mColors.put(colorAttr, result); return result; } + + @Override + public int getCount() { + return mDevicesFound.size(); + } + + @Override + public DeviceFilterPair getItem(int position) { + return mDevicesFound.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } } /** diff --git a/packages/InputDevices/res/values-mn/strings.xml b/packages/InputDevices/res/values-mn/strings.xml index fcaf321c6b6c..16970971243a 100644 --- a/packages/InputDevices/res/values-mn/strings.xml +++ b/packages/InputDevices/res/values-mn/strings.xml @@ -22,9 +22,9 @@ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Болгар хэл, Авиа зүй"</string> <string name="keyboard_layout_italian" msgid="6497079660449781213">"Итали"</string> <string name="keyboard_layout_danish" msgid="8036432066627127851">"Дани"</string> - <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Норвеги"</string> + <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Норвег"</string> <string name="keyboard_layout_swedish" msgid="732959109088479351">"Швед"</string> - <string name="keyboard_layout_finnish" msgid="5585659438924315466">"Финлянд"</string> + <string name="keyboard_layout_finnish" msgid="5585659438924315466">"Финланд"</string> <string name="keyboard_layout_croatian" msgid="4172229471079281138">"Хорват"</string> <string name="keyboard_layout_czech" msgid="1349256901452975343">"Чех"</string> <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Чех хэлний QWERTY загвар"</string> diff --git a/packages/PackageInstaller/res/values-ne/strings.xml b/packages/PackageInstaller/res/values-ne/strings.xml index 495a05b941d6..0b73271a0f34 100644 --- a/packages/PackageInstaller/res/values-ne/strings.xml +++ b/packages/PackageInstaller/res/values-ne/strings.xml @@ -37,7 +37,7 @@ <string name="install_failed_msg" product="tv" msgid="1920009940048975221">"तपाईंको टिभी मा <xliff:g id="APP_NAME">%1$s</xliff:g> स्थापना गर्न सकिएन।"</string> <string name="install_failed_msg" product="default" msgid="6484461562647915707">"तपाईंको फोनमा <xliff:g id="APP_NAME">%1$s</xliff:g> स्थापना गर्न सकिएन।"</string> <string name="launch" msgid="3952550563999890101">"खोल्नुहोस्"</string> - <string name="unknown_apps_admin_dlg_text" msgid="4456572224020176095">"तपाईंका प्रशासकले अज्ञात स्रोतहरूबाट प्राप्त अनुप्रयोगहरूलाई स्थापना गर्ने अनुमति दिनुहुन्न"</string> + <string name="unknown_apps_admin_dlg_text" msgid="4456572224020176095">"तपाईंका प्रशासकले अज्ञात स्रोतहरूबाट प्राप्त एपहरूलाई स्थापना गर्ने अनुमति दिनुहुन्न"</string> <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"यी प्रयोगकर्ता अज्ञात एपहरू स्थापना गर्न सक्नुहुन्न"</string> <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"यो प्रयोगकर्तालाई एपहरू स्थापना गर्ने अनुमति छैन"</string> <string name="ok" msgid="7871959885003339302">"ठिक छ"</string> diff --git a/packages/PackageInstaller/res/values-pl/strings.xml b/packages/PackageInstaller/res/values-pl/strings.xml index 5ce30c29638f..f67cb0838358 100644 --- a/packages/PackageInstaller/res/values-pl/strings.xml +++ b/packages/PackageInstaller/res/values-pl/strings.xml @@ -57,7 +57,7 @@ <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Chcesz odinstalować tę aplikację dla "<b>"wszystkich"</b>" użytkowników? Ta aplikacja i jej dane zostaną usunięte dla "<b>"wszystkich"</b>" użytkowników na urządzeniu."</string> <string name="uninstall_application_text_user" msgid="498072714173920526">"Chcesz odinstalować tę aplikację dla użytkownika <xliff:g id="USERNAME">%1$s</xliff:g>?"</string> <string name="uninstall_update_text" msgid="863648314632448705">"Przywrócić fabryczną wersję tej aplikacji? Wszystkie dane zostaną usunięte."</string> - <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Przywrócić fabryczną wersję tej aplikacji? Wszystkie dane zostaną usunięte. Dotyczy to wszystkich użytkowników tego urządzenia, również tych korzystających z profilu do pracy."</string> + <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Przywrócić fabryczną wersję tej aplikacji? Wszystkie dane zostaną usunięte. Dotyczy to wszystkich użytkowników tego urządzenia, również tych korzystających z profilu służbowego."</string> <string name="uninstall_keep_data" msgid="7002379587465487550">"Zachowaj <xliff:g id="SIZE">%1$s</xliff:g> danych aplikacji."</string> <string name="uninstalling_notification_channel" msgid="840153394325714653">"Aktywne odinstalowania"</string> <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Nieudane odinstalowania"</string> diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp index 3af4b25fb40c..e6492aadd765 100644 --- a/packages/SettingsLib/Android.bp +++ b/packages/SettingsLib/Android.bp @@ -51,7 +51,9 @@ java_defaults { "SettingsLibRadioButtonPreference", "SettingsLibDisplayDensityUtils", "SettingsLibUtils", + "SettingsLibEmergencyNumber", "SettingsLibTopIntroPreference", + "SettingsLibBannerMessagePreference", ], } diff --git a/packages/SettingsLib/BannerMessagePreference/Android.bp b/packages/SettingsLib/BannerMessagePreference/Android.bp new file mode 100644 index 000000000000..095975afa13a --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/Android.bp @@ -0,0 +1,13 @@ +android_library { + name: "SettingsLibBannerMessagePreference", + + srcs: ["src/**/*.java"], + resource_dirs: ["res"], + + static_libs: [ + "androidx.preference_preference", + ], + + sdk_version: "system_current", + min_sdk_version: "21", +} diff --git a/packages/SettingsLib/BannerMessagePreference/AndroidManifest.xml b/packages/SettingsLib/BannerMessagePreference/AndroidManifest.xml new file mode 100644 index 000000000000..56b886f7efb1 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/AndroidManifest.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.settingslib.widget"> + + <uses-sdk android:minSdkVersion="21"/> + +</manifest> diff --git a/packages/SettingsLib/BannerMessagePreference/res/drawable/ic_warning.xml b/packages/SettingsLib/BannerMessagePreference/res/drawable/ic_warning.xml new file mode 100644 index 000000000000..c1f2c9f9f9d0 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/drawable/ic_warning.xml @@ -0,0 +1,26 @@ +<!-- + Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M1,21L12,2L23,21H1ZM19.53,19L12,5.99L4.47,19H19.53ZM11,16V18H13V16H11ZM11,10H13V14H11V10Z" + android:fillColor="?android:attr/colorError" + android:fillType="evenOdd"/> +</vector> diff --git a/packages/SettingsLib/BannerMessagePreference/res/layout/banner_message.xml b/packages/SettingsLib/BannerMessagePreference/res/layout/banner_message.xml new file mode 100644 index 000000000000..977e19687960 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/layout/banner_message.xml @@ -0,0 +1,73 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingStart="24dp" + android:paddingEnd="16dp" + android:paddingTop="24dp" + android:paddingBottom="8dp" + android:orientation="vertical"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + + <ImageView + android:layout_width="24dp" + android:layout_height="24dp" + android:src="@drawable/ic_warning"/> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="24dp" + android:orientation="vertical"> + + <TextView + android:id="@+id/banner_title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="@style/Banner.Text.Title"/> + <TextView + android:id="@+id/banner_summary" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="@style/Banner.Text.Summary"/> + </LinearLayout> + </LinearLayout> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:gravity="end"> + <Button + android:id="@+id/banner_negative_btn" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + style="@android:style/Widget.DeviceDefault.Button.Borderless.Colored"/> + + <Button + android:id="@+id/banner_positive_btn" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + style="@android:style/Widget.DeviceDefault.Button.Borderless.Colored"/> + </LinearLayout> +</LinearLayout>
\ No newline at end of file diff --git a/packages/SettingsLib/BannerMessagePreference/res/values/styles.xml b/packages/SettingsLib/BannerMessagePreference/res/values/styles.xml new file mode 100644 index 000000000000..df47c642e402 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values/styles.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources> + <style name="Banner.Text.Title" + parent="@android:style/TextAppearance.Material.Subhead"> + <item name="android:textSize">16sp</item> + <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item> + <item name="android:textColor">?android:attr/textColorPrimary</item> + </style> + + <style name="Banner.Text.Summary" + parent="@*android:style/TextAppearance.DeviceDefault.Body1"> + <item name="android:textColor">?android:attr/textColorSecondary</item> + <item name="android:textSize">14sp</item> + </style> +</resources>
\ No newline at end of file diff --git a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java new file mode 100644 index 000000000000..5352552d7c66 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.widget; + +import android.content.Context; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.view.View; +import android.widget.Button; +import android.widget.TextView; + +import androidx.annotation.StringRes; +import androidx.preference.Preference; +import androidx.preference.PreferenceViewHolder; + +/** + * Banner message is a banner displaying important information (permission request, page error etc), + * and provide actions for user to address. It requires a user action to be dismissed. + */ +public class BannerMessagePreference extends Preference { + + private static final String TAG = "BannerPreference"; + private BannerMessagePreference.ButtonInfo mPositiveButtonInfo; + private BannerMessagePreference.ButtonInfo mNegativeButtonInfo; + + public BannerMessagePreference(Context context) { + super(context); + init(); + } + + public BannerMessagePreference(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + public BannerMessagePreference(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(); + } + + public BannerMessagePreference(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + init(); + } + + @Override + public void onBindViewHolder(PreferenceViewHolder holder) { + super.onBindViewHolder(holder); + holder.setDividerAllowedAbove(true); + holder.setDividerAllowedBelow(true); + + mPositiveButtonInfo.mButton = (Button) holder.findViewById(R.id.banner_positive_btn); + mNegativeButtonInfo.mButton = (Button) holder.findViewById(R.id.banner_negative_btn); + + mPositiveButtonInfo.setUpButton(); + mNegativeButtonInfo.setUpButton(); + + final TextView titleView = (TextView) holder.findViewById(R.id.banner_title); + final TextView summaryView = (TextView) holder.findViewById(R.id.banner_summary); + + titleView.setText(getTitle()); + summaryView.setText(getSummary()); + } + + private void init() { + mPositiveButtonInfo = new BannerMessagePreference.ButtonInfo(); + mNegativeButtonInfo = new BannerMessagePreference.ButtonInfo(); + setSelectable(false); + setLayoutResource(R.layout.banner_message); + } + + /** + * Set the visibility state of positive button. + */ + public BannerMessagePreference setPositiveButtonVisible(boolean isVisible) { + if (isVisible != mPositiveButtonInfo.mIsVisible) { + mPositiveButtonInfo.mIsVisible = isVisible; + notifyChanged(); + } + return this; + } + + /** + * Set the visibility state of negative button. + */ + public BannerMessagePreference setNegativeButtonVisible(boolean isVisible) { + if (isVisible != mNegativeButtonInfo.mIsVisible) { + mNegativeButtonInfo.mIsVisible = isVisible; + notifyChanged(); + } + return this; + } + + /** + * Register a callback to be invoked when positive button is clicked. + */ + public BannerMessagePreference setPositiveButtonOnClickListener( + View.OnClickListener listener) { + if (listener != mPositiveButtonInfo.mListener) { + mPositiveButtonInfo.mListener = listener; + notifyChanged(); + } + return this; + } + + /** + * Register a callback to be invoked when negative button is clicked. + */ + public BannerMessagePreference setNegativeButtonOnClickListener( + View.OnClickListener listener) { + if (listener != mNegativeButtonInfo.mListener) { + mNegativeButtonInfo.mListener = listener; + notifyChanged(); + } + return this; + } + + /** + * Sets the text to be displayed in positive button. + */ + public BannerMessagePreference setPositiveButtonText(@StringRes int textResId) { + final String newText = getContext().getString(textResId); + if (!TextUtils.equals(newText, mPositiveButtonInfo.mText)) { + mPositiveButtonInfo.mText = newText; + notifyChanged(); + } + return this; + } + + /** + * Sets the text to be displayed in negative button. + */ + public BannerMessagePreference setNegativeButtonText(@StringRes int textResId) { + final String newText = getContext().getString(textResId); + if (!TextUtils.equals(newText, mNegativeButtonInfo.mText)) { + mNegativeButtonInfo.mText = newText; + notifyChanged(); + } + return this; + } + + static class ButtonInfo { + private Button mButton; + private CharSequence mText; + private View.OnClickListener mListener; + private boolean mIsVisible = true; + + void setUpButton() { + mButton.setText(mText); + mButton.setOnClickListener(mListener); + + if (shouldBeVisible()) { + mButton.setVisibility(View.VISIBLE); + } else { + mButton.setVisibility(View.GONE); + } + } + + /** + * By default, two buttons are visible. + * If user didn't set a text for a button, then it should not be shown. + */ + private boolean shouldBeVisible() { + return mIsVisible && (!TextUtils.isEmpty(mText)); + } + } +} diff --git a/packages/SettingsLib/EmergencyNumber/Android.bp b/packages/SettingsLib/EmergencyNumber/Android.bp new file mode 100644 index 000000000000..3c41f7834d6c --- /dev/null +++ b/packages/SettingsLib/EmergencyNumber/Android.bp @@ -0,0 +1,10 @@ +android_library { + name: "SettingsLibEmergencyNumber", + + srcs: ["src/**/*.java"], + static_libs: [ + "androidx.annotation_annotation", + ], + sdk_version: "system_current", + min_sdk_version: "21", +} diff --git a/packages/SettingsLib/EmergencyNumber/AndroidManifest.xml b/packages/SettingsLib/EmergencyNumber/AndroidManifest.xml new file mode 100644 index 000000000000..b877fc4ba531 --- /dev/null +++ b/packages/SettingsLib/EmergencyNumber/AndroidManifest.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.settingslib.emergencynumber"> + + <uses-sdk android:minSdkVersion="21" /> + +</manifest> diff --git a/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java b/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java new file mode 100644 index 000000000000..12d21cae48d4 --- /dev/null +++ b/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.emergencynumber; + +import static android.telephony.emergency.EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE; +import static android.telephony.emergency.EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE; + +import android.content.Context; +import android.content.pm.PackageManager; +import android.provider.Settings; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; +import android.telephony.emergency.EmergencyNumber; +import android.text.TextUtils; +import android.util.ArrayMap; +import android.util.Log; + +import androidx.annotation.VisibleForTesting; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * Util class to help manage emergency numbers + */ +public class EmergencyNumberUtils { + private static final String TAG = "EmergencyNumberUtils"; + private static final String EMERGENCY_GESTURE_CALL_NUMBER = "emergency_gesture_call_number"; + @VisibleForTesting + static final String FALL_BACK_NUMBER = "112"; + + private final Context mContext; + private final TelephonyManager mTelephonyManager; + + + public EmergencyNumberUtils(Context context) { + mContext = context; + if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { + mTelephonyManager = context.getSystemService(TelephonyManager.class); + } else { + mTelephonyManager = null; + } + } + + /** + * Returns the most appropriate number for police. + */ + public String getDefaultPoliceNumber() { + if (mTelephonyManager == null) { + return FALL_BACK_NUMBER; + } + final List<EmergencyNumber> promotedPoliceNumber = getPromotedEmergencyNumbers( + EMERGENCY_SERVICE_CATEGORY_POLICE); + if (promotedPoliceNumber == null || promotedPoliceNumber.isEmpty()) { + return FALL_BACK_NUMBER; + } + return promotedPoliceNumber.get(0).getNumber(); + } + + /** + * Returns the number chosen by user. If user has not provided any number, use default ({@link + * #getDefaultPoliceNumber()}). + */ + public String getPoliceNumber() { + final String userProvidedNumber = Settings.Secure.getString(mContext.getContentResolver(), + EMERGENCY_GESTURE_CALL_NUMBER); + return TextUtils.isEmpty(userProvidedNumber) + ? getDefaultPoliceNumber() : userProvidedNumber; + } + + private List<EmergencyNumber> getPromotedEmergencyNumbers(int categories) { + Map<Integer, List<EmergencyNumber>> allLists = mTelephonyManager.getEmergencyNumberList( + categories); + if (allLists == null || allLists.isEmpty()) { + Log.w(TAG, "Unable to retrieve emergency number lists!"); + return new ArrayList<>(); + } + Map<Integer, List<EmergencyNumber>> promotedEmergencyNumberLists = new ArrayMap<>(); + for (Map.Entry<Integer, List<EmergencyNumber>> entry : allLists.entrySet()) { + if (entry.getKey() == null || entry.getValue() == null) { + continue; + } + List<EmergencyNumber> emergencyNumberList = entry.getValue(); + Log.d(TAG, "Emergency numbers for subscription id " + entry.getKey()); + + // The list of promoted emergency numbers which will be visible on shortcut view. + List<EmergencyNumber> promotedList = new ArrayList<>(); + // A temporary list for non-prioritized emergency numbers. + List<EmergencyNumber> tempList = new ArrayList<>(); + + for (EmergencyNumber emergencyNumber : emergencyNumberList) { + // Emergency numbers in DATABASE are prioritized since they were well-categorized. + boolean isFromPrioritizedSource = + emergencyNumber.getEmergencyNumberSources().contains( + EMERGENCY_NUMBER_SOURCE_DATABASE); + + Log.d(TAG, String.format("Number %s, isFromPrioritizedSource %b", + emergencyNumber, isFromPrioritizedSource)); + if (isFromPrioritizedSource) { + promotedList.add(emergencyNumber); + } else { + tempList.add(emergencyNumber); + } + } + // Puts numbers in temp list after prioritized numbers. + promotedList.addAll(tempList); + + if (!promotedList.isEmpty()) { + promotedEmergencyNumberLists.put(entry.getKey(), promotedList); + } + } + + if (promotedEmergencyNumberLists.isEmpty()) { + Log.w(TAG, "No promoted emergency number found!"); + } + return promotedEmergencyNumberLists.get(SubscriptionManager.getDefaultSubscriptionId()); + } +} diff --git a/packages/SettingsLib/Utils/src/com/android/settingslib/utils/applications/AppUtils.java b/packages/SettingsLib/Utils/src/com/android/settingslib/utils/applications/AppUtils.java index e7c0d9659d11..4505dad8ea12 100644 --- a/packages/SettingsLib/Utils/src/com/android/settingslib/utils/applications/AppUtils.java +++ b/packages/SettingsLib/Utils/src/com/android/settingslib/utils/applications/AppUtils.java @@ -52,6 +52,9 @@ public class AppUtils { public static String getAppContentDescription(Context context, String packageName, int userId) { final CharSequence appLabel = getApplicationLabel(context.getPackageManager(), packageName); + if (appLabel == null) { + return ""; + } return context.getSystemService(UserManager.class).isManagedProfile(userId) ? context.getString(R.string.accessibility_work_profile_app_description, appLabel) : appLabel.toString(); diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index fa0fa5c93155..41af185da0b7 100644 --- a/packages/SettingsLib/res/values-af/strings.xml +++ b/packages/SettingsLib/res/values-af/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Onaktief. Tik om te wissel."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktief. Tik om te wissel."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Programbystandstatus:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Mediakodewisselinginstellings"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Aktiveer kodewisseling vir alle programme"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Deaktiveer kodewisseling vir programme"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Mediakodewisselinginstellings"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Deaktiveer kodewisseling"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Aktiveer kodewisseling vir programme"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Lopende dienste"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Sien en beheer dienste wat tans aktief is"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView-implementering"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> oor tot battery gelaai is"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> tot battery gelaai is"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Battery word tydelik beperk"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Onbekend"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Laai"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Laai tans vinnig"</string> diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml index f98b80124c5e..8e4e402c2e16 100644 --- a/packages/SettingsLib/res/values-am/strings.xml +++ b/packages/SettingsLib/res/values-am/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"ቦዝኗል። ለመቀያየር ነካ ያድርጉ።"</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"ገቢር። ለመቀያየር ነካ ያድርጉ።"</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"የመተግበሪያ ዝግጁ የመሆን ሁኔታ፦<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"የሚዲያ ትራንስኮድ ቅንብሮች"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"ለሁሉም መተግበሪያዎች ትራንስኮዲንግን ያንቁ"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"ለመተግበሪያዎች ትራንስኮዲንግን ያሰናክሉ"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"የሚዲያ ትራንስኮዲንግ ቅንብሮች"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"ትራንስኮንግን አሰናክል"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"ለመተግበሪያዎች ትራንስኮዲንግን ያንቁ"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"አሂድ አገልግሎቶች"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"በአሁኑጊዜ እየሄዱ ያሉ አገልግሎቶችን ተቆጣጠር እና እይ"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"የWebView ትግበራ"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> ኃይል እስከሚሞላ ድረስ ይቀራል"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ኃይል እስከሚሞላ ድረስ"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - ባትሪ ለጊዜው ተገድቧል"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"ያልታወቀ"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"ኃይል በመሙላት ላይ"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ኃይል በፍጥነት በመሙላት ላይ"</string> diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index a1432bf1e7f1..691ec046da02 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"غير نشط، انقر للتبديل."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"نشط، انقر للتبديل."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"حالة تطبيق وضع الاستعداد:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"إعدادات تحويل ترميز الوسائط"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"تفعيل تحويل الترميز لكل التطبيقات"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"إيقاف تحويل الترميز للتطبيقات"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"إعدادات تحويل ترميز الوسائط"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"إيقاف تحويل الترميز"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"تفعيل تحويل الترميز للتطبيقات"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"الخدمات قيد التشغيل"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"عرض الخدمات قيد التشغيل في الوقت الحالي والتحكم فيها"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"تطبيق WebView"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> إلى أن يتم شحن الجهاز بالكامل"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> إلى أن يتم شحن الجهاز بالكامل"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - تأثير محدود على البطارية مؤقتًا"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"غير معروف"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"جارٍ الشحن"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"جارٍ الشحن سريعًا"</string> diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml index c7000dd8d23f..61214e09ba26 100644 --- a/packages/SettingsLib/res/values-as/strings.xml +++ b/packages/SettingsLib/res/values-as/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"নিষ্ক্ৰিয়। ট\'গল কৰিবলৈ টিপক।"</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"সক্ৰিয়। ট\'গল কৰিবলৈ টিপক।"</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"এপ্ ষ্টেণ্ডবাই অৱস্থাত আছে:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"মিডিয়া ট্ৰান্সক\'ডৰ ছেটিং"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"আটাইবোৰ এপৰ বাবে ট্ৰান্সক\'ডিং সক্ষম কৰক"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"এপৰ বাবে ট্ৰান্সক\'ডিং অক্ষম কৰক"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"মিডিয়া ট্ৰান্সক\'ডিঙৰ ছেটিং"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"ট্ৰান্সক\'ডিং অক্ষম কৰক"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"এপৰ বাবে ট্ৰান্সক\'ডিং সক্ষম কৰক"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"চলিত সেৱা"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"বৰ্তমান চলি থকা সেৱাসমূহ চাওক আৰু নিয়ন্ত্ৰণ কৰক"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"ৱেবভিউ প্ৰয়োগ"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"চাৰ্জ হ’বলৈ <xliff:g id="TIME">%1$s</xliff:g> বাকী আছে"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> চাৰ্জ হ\'বলৈ"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - বেটাৰী সাময়িকভাৱে সীমিত কৰা হৈছে"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"অজ্ঞাত"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"চাৰ্জ কৰি থকা হৈছে"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"দ্ৰুততাৰে চাৰ্জ হৈছে"</string> diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml index c30b7b5a31a4..db61527a54d8 100644 --- a/packages/SettingsLib/res/values-az/strings.xml +++ b/packages/SettingsLib/res/values-az/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Deaktivdir. Keçid etmək üçün basın."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktivdir. Keçid etmək üçün basın."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Tətbiqin gözləmə rejimi:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Media yenidən kodlaşdırma ayarları"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Bütün tətbiqlər üçün yenidən kodlaşdırmanı aktiv edin"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Tətbiqlər üçün yenidən kodlaşdırmanı deaktiv edin"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Media yenidən kodlaşdırma ayarları"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Yenidən kodlaşdırmanı deaktiv edin"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Tətbiqlər üçün yenidən kodlaşdırmanı aktiv edin"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"İşləyən xidmətlər"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Hazırda prosesdə olan xidmətləri görüntüləyin və onlara nəzarət edin"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView icrası"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Enerjinin dolmasına <xliff:g id="TIME">%1$s</xliff:g> qalıb"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - Enerjinin dolmasına <xliff:g id="TIME">%2$s</xliff:g> qalıb"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Batareya müvəqqəti məhdudlaşdırılıb"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Naməlum"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Enerji doldurma"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Sürətlə doldurulur"</string> diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml index 5230a8019582..70fb3636b492 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Neaktivna. Dodirnite da biste je aktivirali."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktivna. Dodirnite da biste je deaktivirali."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Stanje pripravnosti aplikacije: <xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Podešavanja transkodiranja medija"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Omogući transkodiranje za sve aplikacije"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Onemogućite transkodiranje za aplikacije"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Podešavanja transkodiranja medija"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Onemogući transkodiranje"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Omogućite transkodiranje za aplikacije"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Pokrenute usluge"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Prikaz i kontrola trenutno pokrenutih usluga"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Primena WebView-a"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Napuniće se za <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – napuniće se za <xliff:g id="TIME">%2$s</xliff:g>"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – baterija je trenutno ograničena"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Nepoznato"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Puni se"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Brzo se puni"</string> diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml index 04f6ef00a355..8a0db8261a71 100644 --- a/packages/SettingsLib/res/values-be/strings.xml +++ b/packages/SettingsLib/res/values-be/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Неактыўная. Краніце, каб пераключыць."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Актыўная. Краніце, каб пераключыць."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Стан праграмы ў рэжыме чакання: <xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Налады перакадзіравання мультымедыя"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Дазволіць перакадзіраванне для ўсіх праграм"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Адключыць перакадзіраванне для праграм"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Налады перакадзіравання мультымедыя"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Выключыць перакадзіраванне"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Уключыць перакадзіраванне для праграм"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Запушчаныя службы"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Прагляд запушчаных службаў i кіраванне iмi"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Рэалізацыя WebView"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Засталося <xliff:g id="TIME">%1$s</xliff:g> да поўнай зарадкі"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> да поўнай зарадкі"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – зарад акумулятара часова абмежаваны"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Невядома"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Зарадка"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Хуткая зарадка"</string> diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml index e1f18a172194..27dcf105fd2e 100644 --- a/packages/SettingsLib/res/values-bg/strings.xml +++ b/packages/SettingsLib/res/values-bg/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Неактивно. Докоснете, за да превключите."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Активно. Докоснете, за да превключите."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Състояние на готовност на приложението: <xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Настройки за прекодирането на мултимедийно съдържание"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Активиране на прекодирането за всички приложения"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Деактивиране на прекодирането за приложения"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Настройки за прекодирането на мултимедия"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Деактивиране на прекодирането"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Активиране на прекодирането за приложения"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Изпълнявани услуги"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Преглед и контрол върху изпълняващите се понастоящем услуги"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Внедряване на WebView"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Оставащо време до пълно зареждане: <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до пълно зареждане"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Батерията е временно ограничена"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Неизвестно"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Зарежда се"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Зарежда се бързо"</string> diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml index b27d29c60d07..29862e6efd31 100644 --- a/packages/SettingsLib/res/values-bn/strings.xml +++ b/packages/SettingsLib/res/values-bn/strings.xml @@ -211,9 +211,9 @@ <string name="adb_wireless_error" msgid="721958772149779856">"সমস্যা"</string> <string name="adb_wireless_settings" msgid="2295017847215680229">"ওয়্যারলেস ডিবাগিং"</string> <string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"কোন কোন ডিভাইস উপলভ্য আছে তা দেখে নিয়ে ব্যবহার করার জন্য, ওয়্যারলেস ডিবাগিং চালু করুন"</string> - <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"QR কোড ব্যবহার করে ডিভাইস যোগ করুন"</string> + <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"QR কোড ব্যবহার করে ডিভাইসের সাথে পেয়ার করুন"</string> <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"QR কোড স্ক্যানার ব্যবহার করে নতুন ডিভাইস যোগ করুন"</string> - <string name="adb_pair_method_code_title" msgid="1122590300445142904">"যোগ করার কোড ব্যবহার করে ডিভাইস যোগ করুন"</string> + <string name="adb_pair_method_code_title" msgid="1122590300445142904">"পেয়ারিং কোড ব্যবহার করে ডিভাইসের সাথে পেয়ার করুন"</string> <string name="adb_pair_method_code_summary" msgid="6370414511333685185">"ছয় সংখ্যার কোড ব্যবহার করে নতুন ডিভাইস যোগ করুন"</string> <string name="adb_paired_devices_title" msgid="5268997341526217362">"যোগ করা ডিভাইস"</string> <string name="adb_wireless_device_connected_summary" msgid="3039660790249148713">"এখন কানেক্ট রয়েছে"</string> @@ -222,16 +222,16 @@ <string name="adb_device_fingerprint_title_format" msgid="291504822917843701">"ডিভাইসে আঙ্গুলের ছাপ: <xliff:g id="FINGERPRINT_PARAM">%1$s</xliff:g>"</string> <string name="adb_wireless_connection_failed_title" msgid="664211177427438438">"কানেক্ট করা যায়নি"</string> <string name="adb_wireless_connection_failed_message" msgid="9213896700171602073">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>টি সঠিক নেটওয়ার্কে কানেক্ট আছে কিনা দেখে নিন"</string> - <string name="adb_pairing_device_dialog_title" msgid="7141739231018530210">"ডিভাইসের সাথে যোগ করুন"</string> - <string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"ওয়াই-ফাই যোগ করার কোড"</string> - <string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"যোগ করা যায়নি"</string> + <string name="adb_pairing_device_dialog_title" msgid="7141739231018530210">"ডিভাইসের সাথে পেয়ার করুন"</string> + <string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"ওয়াই-ফাইয়ের সাথে পেয়ার করার কোড"</string> + <string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"পেয়ার করা যায়নি"</string> <string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"ডিভাইসটি একই নেটওয়ার্কে কানেক্ট আছে কিনা দেখে নিন।"</string> - <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"QR কোড স্ক্যান করে ওয়াই-ফাই ব্যবহার করে ডিভাইস যোগ করুন"</string> + <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"QR কোড স্ক্যান করে ওয়াই-ফাই ব্যবহার করে ডিভাইসের সাথে পেয়ার করুন"</string> <string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"ডিভাইস যোগ করা হচ্ছে…"</string> <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"ডিভাইস যোগ করা যায়নি। এটি দুটি কারণে হয়ে থাকে - QR কোডটি সঠিক নয় বা ডিভাইসটি একই নেটওয়ার্কে কানেক্ট করা নেই।"</string> <string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"IP অ্যাড্রেস ও পোর্ট"</string> <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"QR কোড স্ক্যান করুন"</string> - <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"QR কোড স্ক্যান করে ওয়াই-ফাইয়ের সাহায্যে ডিভাইস যোগ করুন"</string> + <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"QR কোড স্ক্যান করে ওয়াই-ফাই ব্যবহার করে ডিভাইসের সাথে পেয়ার করুন"</string> <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"একটি ওয়াই-ফাই নেটওয়ার্কের সাথে কানেক্ট করুন"</string> <string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, debug, dev"</string> <string name="bugreport_in_power" msgid="8664089072534638709">"ত্রুটি প্রতিবেদনের শর্টকাট"</string> @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"নিষ্ক্রিয় রয়েছে৷ টগল করতে আলতো চাপুন৷"</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"সক্রিয় রয়েছে৷ টগল করতে আলতো চাপুন৷"</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"অ্যাপ স্ট্যান্ডবাই-এর অবস্থা:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"মিডিয়া ট্রান্সকোড সেটিংস"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"সব অ্যাপের জন্য ট্রান্সকোডিং চালু করুন"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"অ্যাপের ট্রান্সকোডিং বন্ধ করুন"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"মিডিয়া ট্রান্সকোডিং সেটিংস"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"ট্রান্সকোডিং বন্ধ করুন"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"অ্যাপের জন্য ট্রান্সকোডিং চালু করুন"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"এখন চলছে যে পরিষেবাগুলি"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"বর্তমান চলমান পরিষেবাগুলি দেখুন এবং নিয়ন্ত্রণ করুন"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"ওয়েবভিউ প্রয়োগ"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"সম্পূর্ণ চার্জ হতে <xliff:g id="TIME">%1$s</xliff:g> বাকি আছে"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>-এ সম্পূর্ণ চার্জ হয়ে যাবে"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - ব্যাটারি কিছুক্ষণের জন্য সীমিত করা হয়েছে"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"অজানা"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"চার্জ হচ্ছে"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"দ্রুত চার্জ হচ্ছে"</string> diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index 8d467587f3d6..4704ec8db79a 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Neaktivno. Dodirnite za promjenu opcije."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktivno. Dodirnite za promjenu opcije."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Stanje mirovanja aplikacije:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Postavke transkodiranja medijskih fajlova"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Omogućite transkodiranje za sve aplikacije"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Onemogućite transkodiranje za aplikacije"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Postavke transkodiranja medija"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Onemogućite transkodiranje"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Omogućite transkodiranje za aplikacije"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Pokrenute usluge"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Prikaz i kontrola trenutno pokrenutih usluga"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Postavljanje WebViewa"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Napunit će se za <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – napunit će se za <xliff:g id="TIME">%2$s</xliff:g>"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Baterija je privremeno ograničena"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Nepoznato"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Punjenje"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Brzo punjenje"</string> diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index f4fc7d6d47d6..5d04963d9b4a 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -66,7 +66,7 @@ <string name="bluetooth_disconnected" msgid="7739366554710388701">"Desconnectat"</string> <string name="bluetooth_disconnecting" msgid="7638892134401574338">"S\'està desconnectant..."</string> <string name="bluetooth_connecting" msgid="5871702668260192755">"S\'està connectant…"</string> - <string name="bluetooth_connected" msgid="8065345572198502293">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> connectat"</string> + <string name="bluetooth_connected" msgid="8065345572198502293">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> Connectat"</string> <string name="bluetooth_pairing" msgid="4269046942588193600">"S\'està vinculant..."</string> <string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> connectat (sense accés al telèfon)"</string> <string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> connectat (sense contingut multimèdia)"</string> @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Aplicació inactiva. Toca per activar-la."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aplicació activa. Toca per desactivar-la."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Estat de les aplicacions inactives: <xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Configuració de la transcodificació de contingut multimèdia"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Activa la transcodificació per a totes les aplicacions"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Desactiva la transcodificació per a les aplicacions"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Configuració de la transcodificació de contingut multimèdia"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Desactiva la transcodificació"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Activa la transcodificació per a les aplicacions"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Serveis en execució"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Visualitza i controla els serveis en execució"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Implementació de WebView"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> per completar la càrrega"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> per completar la càrrega"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g>: bateria limitada temporalment"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Desconegut"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"S\'està carregant"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Carregant ràpidament"</string> diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml index be0bd47edb3e..a99a4dabbc60 100644 --- a/packages/SettingsLib/res/values-cs/strings.xml +++ b/packages/SettingsLib/res/values-cs/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Neaktivní. Klepnutím možnost přepnete."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktivní. Klepnutím možnost přepnete."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Stav pohotovostního režimu aplikace: <xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Nastavení překódování médií"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Povolit překódování u všech aplikací"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Zakázat překódování aplikací"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Nastavení překódování médií"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Zakázat překódování"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Povolit překódování pro aplikace"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Spuštěné služby"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Umožňuje zobrazit a ovládat aktuálně spuštěné služby"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Implementace WebView"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Do nabití zbývá: <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do nabití"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Baterie dočasně omezena"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Neznámé"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Nabíjí se"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Rychlé nabíjení"</string> diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml index 2725598a0b57..c90155eb2b68 100644 --- a/packages/SettingsLib/res/values-da/strings.xml +++ b/packages/SettingsLib/res/values-da/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Inaktiv. Tryk for at skifte."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktiv. Tryk for at skifte."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Standbystatus for appen:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Indstillinger for omkodning af medier"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Aktivér omkodning for alle apps"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Deaktiver omkodning for apps"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Indstillinger for omkodning af medier"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Deaktiver omkodning"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Aktivér omkodning for apps"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Kørende tjenester"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Vis og administrer kørende tjenester"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView-implementering"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Opladet om <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – opladet om <xliff:g id="TIME">%2$s</xliff:g>"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Batteriet er midlertidigt begrænset"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Ukendt"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Oplader"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Oplader hurtigt"</string> diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml index fcd12227620f..b5b9fc480607 100644 --- a/packages/SettingsLib/res/values-de/strings.xml +++ b/packages/SettingsLib/res/values-de/strings.xml @@ -399,12 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Inaktiv. Zum Wechseln tippen."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktiv. Zum Wechseln tippen."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Standby-Status der App:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <!-- no translation found for transcode_settings_title (6700974145733932357) --> - <skip /> - <!-- no translation found for transcode_enable_all (4719796495995795404) --> - <skip /> - <!-- no translation found for transcode_skip_apps (5680997722349545778) --> - <skip /> + <string name="transcode_settings_title" msgid="2581975870429850549">"Einstellungen für Medientranscodierung"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Transcodierung deaktivieren"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Transcodierung für Apps aktivieren"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Aktive Dienste"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Momentan ausgeführte Dienste anzeigen und steuern"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView-Implementierung"</string> @@ -452,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Noch <xliff:g id="TIME">%1$s</xliff:g> bis zur Aufladung"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> bis zur Aufladung"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Akku vorübergehend eingeschränkt"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Unbekannt"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Wird aufgeladen"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Schnelles Aufladen"</string> diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml index 9e8845a584ac..203ec40c5a42 100644 --- a/packages/SettingsLib/res/values-el/strings.xml +++ b/packages/SettingsLib/res/values-el/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Ανενεργό. Πατήστε για εναλλαγή."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Ενεργό. Πατήστε για εναλλαγή."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Κατάσταση αναμονής εφαρμογής:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Ρυθμίσεις διακωδικοποίησης μέσων"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Ενεργοποίηση διακωδικοποίησης για όλες τις εφαρμογές"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Απενεργοποίηση της διακωδικοποίησης για εφαρμογές"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Ρυθμίσεις διακωδικοποίησης μέσων"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Απενεργοποίηση διακωδικοποίησης"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Ενεργοποίηση διακωδικοποίησης για εφαρμογές"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Υπηρεσίες που εκτελούνται"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Προβολή και έλεγχος των εφαρμογών που εκτελούνται αυτή τη στιγμή"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Υλοποίηση WebView"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Απομένουν <xliff:g id="TIME">%1$s</xliff:g> για ολοκλήρωση της φόρτισης"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> για την ολοκλήρωση της φόρτισης"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Η μπαταρία περιορίστηκε προσωρινά."</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Άγνωστο"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Φόρτιση"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Ταχεία φόρτιση"</string> diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml index 7729f8e2d834..f8019d21c65b 100644 --- a/packages/SettingsLib/res/values-en-rAU/strings.xml +++ b/packages/SettingsLib/res/values-en-rAU/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Inactive. Tap to toggle."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Active. Tap to toggle."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"App standby state:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Media transcode settings"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Enable transcoding for all apps"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Disable transcoding for apps"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Media transcoding settings"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Disable transcoding"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Enable transcoding for apps"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Running services"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"View and control currently running services"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView implementation"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> left until charged"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until charged"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Battery limited temporarily"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Unknown"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Charging"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Charging rapidly"</string> diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml index 00be3559a92f..7135de5676b2 100644 --- a/packages/SettingsLib/res/values-en-rCA/strings.xml +++ b/packages/SettingsLib/res/values-en-rCA/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Inactive. Tap to toggle."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Active. Tap to toggle."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"App standby state:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Media transcode settings"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Enable transcoding for all apps"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Disable transcoding for apps"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Media transcoding settings"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Disable transcoding"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Enable transcoding for apps"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Running services"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"View and control currently running services"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView implementation"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> left until charged"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until charged"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Battery limited temporarily"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Unknown"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Charging"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Charging rapidly"</string> diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml index 7729f8e2d834..f8019d21c65b 100644 --- a/packages/SettingsLib/res/values-en-rGB/strings.xml +++ b/packages/SettingsLib/res/values-en-rGB/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Inactive. Tap to toggle."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Active. Tap to toggle."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"App standby state:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Media transcode settings"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Enable transcoding for all apps"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Disable transcoding for apps"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Media transcoding settings"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Disable transcoding"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Enable transcoding for apps"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Running services"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"View and control currently running services"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView implementation"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> left until charged"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until charged"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Battery limited temporarily"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Unknown"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Charging"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Charging rapidly"</string> diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml index 7729f8e2d834..f8019d21c65b 100644 --- a/packages/SettingsLib/res/values-en-rIN/strings.xml +++ b/packages/SettingsLib/res/values-en-rIN/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Inactive. Tap to toggle."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Active. Tap to toggle."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"App standby state:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Media transcode settings"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Enable transcoding for all apps"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Disable transcoding for apps"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Media transcoding settings"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Disable transcoding"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Enable transcoding for apps"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Running services"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"View and control currently running services"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView implementation"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> left until charged"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until charged"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Battery limited temporarily"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Unknown"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Charging"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Charging rapidly"</string> diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml index dacc782bb030..54290d269f44 100644 --- a/packages/SettingsLib/res/values-en-rXC/strings.xml +++ b/packages/SettingsLib/res/values-en-rXC/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Inactive. Tap to toggle."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Active. Tap to toggle."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"App standby state:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Media transcode settings"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Enable transcoding for all apps"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Disable transcoding for apps"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Media transcoding settings"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Disable transcoding"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Enable transcoding for apps"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Running services"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"View and control currently running services"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView implementation"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> left until charged"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until charged"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Battery limited temporarily"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Unknown"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Charging"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Charging rapidly"</string> diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml index 40eb0c71bbb6..08ff55099c9c 100644 --- a/packages/SettingsLib/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/res/values-es-rUS/strings.xml @@ -196,7 +196,7 @@ <string name="choose_profile" msgid="343803890897657450">"Elegir perfil"</string> <string name="category_personal" msgid="6236798763159385225">"Personal"</string> <string name="category_work" msgid="4014193632325996115">"Trabajo"</string> - <string name="development_settings_title" msgid="140296922921597393">"Opciones para programadores"</string> + <string name="development_settings_title" msgid="140296922921597393">"Opciones para desarrolladores"</string> <string name="development_settings_enable" msgid="4285094651288242183">"Activar opciones para programador"</string> <string name="development_settings_summary" msgid="8718917813868735095">"Establecer opciones para desarrollar aplicaciones"</string> <string name="development_settings_not_available" msgid="355070198089140951">"Las opciones de programador no están disponibles para este usuario."</string> @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Inactiva. Presiona para activar o desactivar."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Activa. Presiona para activar o desactivar."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Estado de la app en espera: <xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Configuración de la transcodificación multimedia"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Habilitar la transcodificación para todas las apps"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Inhabilitar la transcodificación para las apps"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Configuración de transcodificación de contenido multimedia"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Inhabilitar transcodificación"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Habilitar transcodificación en apps"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"En ejecución"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Ver y controlar servicios actuales en ejecución"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Implementación de WebView"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> para completar la carga"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar la carga"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Batería limitada temporalmente"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Desconocido"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Cargando"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Cargando rápido"</string> diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index 83669172679c..6f6a1a1c9b17 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Inactiva. Toca para alternar."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Activa. Toca para alternar."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Estado de la aplicación en espera: <xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Configuración de la transcodificación multimedia"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Habilitar transcodificación en todas las aplicaciones"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Inhabilitar transcodificación en las aplicaciones"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Configuración de la transcodificación multimedia"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Inhabilitar transcodificación"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Habilitar transcodificación en las aplicaciones"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Servicios en ejecución"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Ver y controlar los servicios en ejecución"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Implementación de WebView"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> hasta cargarse completamente"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> (<xliff:g id="TIME">%2$s</xliff:g> hasta cargarse completamente)"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g>: batería limitada temporalmente"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Desconocido"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Cargando"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Cargando rápidamente"</string> diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml index f876b957b62d..9d6326e45706 100644 --- a/packages/SettingsLib/res/values-et/strings.xml +++ b/packages/SettingsLib/res/values-et/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Passiivne. Puudutage vahetamiseks."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktiivne. Puudutage vahetamiseks."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Rakenduse ootelolek:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Meedia transkodeerimise seaded"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Luba transkodeerimine kõikide rakenduste puhul"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Keela rakenduste puhul transkodeerimine"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Meedia transkodeerimise seaded"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Keela transkodeerimine"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Luba rakenduste puhul transkodeerimine"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Käitatud teenused"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Praegu käitatud teenuste vaatamine ja juhtimine"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView\' rakendamine"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Täislaadimiseni on jäänud <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> täislaadimiseni"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – akutase on ajutiselt piiratud"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Tundmatu"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Laadimine"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Kiirlaadimine"</string> diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml index 0a7bc3eb8b06..01afa1706e3e 100644 --- a/packages/SettingsLib/res/values-eu/strings.xml +++ b/packages/SettingsLib/res/values-eu/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Inaktibo. Aldatzeko, sakatu hau."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktibo. Aldatzeko, sakatu hau."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Egonean moduko aplikazioaren egoera: <xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Multimedia-edukia transkodetzeko ezarpenak"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Gaitu transkodetzeko aukera aplikazio guztietan"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Desgaitu aplikazioak transkodetzeko aukera"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Multimedia-edukia transkodetzeko ezarpenak"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Desgaitu transkodetzeko aukera"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Gaitu aplikazioak transkodetzeko aukera"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Abian diren zerbitzuak"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Ikusi eta kontrolatu une honetan abian diren zerbitzuak"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView inplementazioa"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> guztiz kargatu arte"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> guztiz kargatu arte"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g>: bateria mugatuta egongo da aldi batez"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Ezezaguna"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Kargatzen"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Bizkor kargatzen"</string> diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index e0f328065d99..dd7ab9672e96 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"غیرفعال. برای تغییر حالت ضربه بزنید."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"فعال. برای تغییر حالت ضربه بزنید."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"وضعیت حالت آماده بهکار برنامه:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"تنظیمات تراتبدیل رسانه"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"فعال کردن تراتبدیل برای همه برنامهها"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"غیرفعال کردن تراتبدیل برای برنامهها"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"تنظیمات تراتبدیل رسانه"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"غیرفعال کردن تراتبدیل"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"فعال کردن تراتبدیل برای برنامهها"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"سرویسهای در حال اجرا"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"مشاهده و کنترل سرویسهای در حال اجرای فعلی"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"اجرای وبنما"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> مانده تا شارژ کامل"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> تا شارژ کامل"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - باتری موقتاً محدود شده است"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"ناشناس"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"در حال شارژ شدن"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"درحال شارژ شدن سریع"</string> diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml index 651faa54614c..b86b02d8c25f 100644 --- a/packages/SettingsLib/res/values-fi/strings.xml +++ b/packages/SettingsLib/res/values-fi/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Ei päällä. Ota käyttöön koskettamalla."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktiivinen. Vaihda koskettamalla."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Sovelluksen valmiusluokka: <xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Median transkoodausasetukset"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Ota transkoodaus käyttöön kaikissa sovelluksissa"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Poista sovellusten transkoodaus käytöstä"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Median transkoodausasetukset"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Poista transkoodaus käytöstä"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Ota sovellusten transkoodaus käyttöön"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Käynnissä olevat palvelut"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Tarkastele ja hallitse käynnissä olevia palveluita."</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView-käyttöönotto"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> jäljellä täyteen lataukseen"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> täyteen lataukseen"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Akun käyttöä rajoitettu tilapäisesti"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Tuntematon"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Ladataan"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Nopea lataus"</string> diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml index c37d6cc59f1d..0bca1dba1aa4 100644 --- a/packages/SettingsLib/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml @@ -142,7 +142,7 @@ <string name="process_kernel_label" msgid="950292573930336765">"Système d\'exploitation Android"</string> <string name="data_usage_uninstalled_apps" msgid="1933665711856171491">"Applications supprimées"</string> <string name="data_usage_uninstalled_apps_users" msgid="5533981546921913295">"Applications et utilisateurs supprimés"</string> - <string name="data_usage_ota" msgid="7984667793701597001">"Mises à jour système"</string> + <string name="data_usage_ota" msgid="7984667793701597001">"Mises à jour du système"</string> <string name="tether_settings_title_usb" msgid="3728686573430917722">"Partage de connexion par USB"</string> <string name="tether_settings_title_wifi" msgid="4803402057533895526">"Point d\'accès Wi-Fi mobile"</string> <string name="tether_settings_title_bluetooth" msgid="916519902721399656">"Partage connexion Bluetooth"</string> @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Application inactive. Touchez ici pour l\'activer."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Application active. Touchez ici pour la désactiver."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"État de l\'application en veille :<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Paramètres de transcodage pour les éléments multimédias"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Activer le transcodage pour toutes les applications"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Désactiver le transcodage pour toutes les applications"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Paramètres de transcodage des éléments multimédias"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Désactiver le transcodage"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Activer le transcodage pour les applications"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Services en cours d\'exécution"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Afficher et contrôler les services en cours d\'exécution"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Mise en œuvre WebView"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> jusqu\'à la charge complète"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> : <xliff:g id="TIME">%2$s</xliff:g> jusqu\'à la charge complète"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Pile limitée temporairement"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Inconnu"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Charge en cours…"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Recharge rapide"</string> @@ -501,7 +502,7 @@ <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Activer"</string> <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Activer le mode Ne pas déranger"</string> <string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Jamais"</string> - <string name="zen_interruption_level_priority" msgid="5392140786447823299">"Priorités seulement"</string> + <string name="zen_interruption_level_priority" msgid="5392140786447823299">"Prioritaires seulement"</string> <string name="zen_mode_and_condition" msgid="8877086090066332516">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string> <string name="zen_alarm_warning_indef" msgid="4146527909616457163">"Vous n\'entendrez pas votre prochaine alarme à <xliff:g id="WHEN">%1$s</xliff:g> sauf si vous désactivez préalablement cette option"</string> <string name="zen_alarm_warning" msgid="245729928048586280">"Vous n\'entendrez pas votre prochaine alarme à <xliff:g id="WHEN">%1$s</xliff:g>"</string> diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml index ef52ec562b89..ebde43ba52a2 100644 --- a/packages/SettingsLib/res/values-fr/strings.xml +++ b/packages/SettingsLib/res/values-fr/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Application inactive. Appuyez ici pour l\'activer."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Application active. Appuyez ici pour la désactiver."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"État de mise en veille de l\'application : <xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Paramètres de transcodage des contenus multimédias"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Activer le transcodage pour toutes les applications"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Désactiver le transcodage pour les applications"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Paramètres de transcodage des contenus multimédias"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Désactiver le transcodage"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Activer le transcodage pour les applications"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Services en cours d\'exécution"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Afficher et contrôler les services en cours d\'exécution"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Mise en œuvre WebView"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> jusqu\'à ce que la batterie soit chargée"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> jusqu\'à la charge complète"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Batterie limitée temporairement"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Inconnu"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Batterie en charge"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Charge rapide"</string> diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml index 16777aae2bb8..b040817b2801 100644 --- a/packages/SettingsLib/res/values-gl/strings.xml +++ b/packages/SettingsLib/res/values-gl/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Aplicación inactiva. Toca para alternar a configuración."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aplicación activa. Toca para alternar a configuración."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Estado en espera da aplicación: <xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Configuración de transcodificación de contido multimedia"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Activar transcodificación para todas as aplicacións"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Desactivar transcodificación para determinadas aplicacións"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Configuración de transcodificación de contido multimedia"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Desactivar transcodificación"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Activar transcodificación para as aplicacións"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Servizos en uso"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Comproba e controla os servizos actualmente en uso"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Implementación de WebView"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> para completar a carga"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> para completar a carga"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> (batería limitada temporalmente)"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Descoñecido"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Cargando"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Cargando rapidamente"</string> diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml index ef88ab13c2f0..c1bd7cac30a6 100644 --- a/packages/SettingsLib/res/values-gu/strings.xml +++ b/packages/SettingsLib/res/values-gu/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"નિષ્ક્રિય. ટોગલ કરવા માટે ટૅપ કરો."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"સક્રિય. ટોગલ કરવા માટે ટૅપ કરો."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"ઍપ સ્ટૅન્ડબાયની સ્થિતિ:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"મીડિયાનું ફૉર્મેટ બદલવાની પ્રક્રિયાના સેટિંગ"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"બધી ઍપ માટે ફૉર્મેટ બદલવાની પ્રક્રિયા ચાલુ કરો"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"ઍપ માટે ફૉર્મેટ બદલવાની પ્રક્રિયા બંધ કરો"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"મીડિયાનું ફૉર્મેટ બદલવાની પ્રક્રિયાના સેટિંગ"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"ફૉર્મેટ બદલવાની પ્રક્રિયા બંધ કરો"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"ઍપ માટે ફૉર્મેટ બદલવાની પ્રક્રિયા ચાલુ કરો"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"ચાલુ સેવાઓ"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"હાલમાં ચાલતી સેવાઓ જુઓ અને નિયંત્રિત કરો"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView અમલીકરણ"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"ચાર્જ થવામાં <xliff:g id="TIME">%1$s</xliff:g> બાકી છે"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - ચાર્જ થવા માટે <xliff:g id="TIME">%2$s</xliff:g>"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - બૅટરીનો વપરાશ હંગામી રૂપે મર્યાદિત છે"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"અજાણ્યું"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"ચાર્જ થઈ રહ્યું છે"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ઝડપથી ચાર્જ થાય છે"</string> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index 3aa413b3452a..d9153a1ab542 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"बंद है. टॉगल करने के लिए टैप करें."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"सक्रिय. टॉगल करने के लिए टैप करें."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"ऐप्लिकेशन स्टैंडबाय की स्थिति:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"मीडिया ऐप्लिकेशन को ट्रांसकोड करने से जुड़ी सेटिंग"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"सभी ऐप्लिकेशन के लिए ट्रांसकोडिंग चालू करें"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"ऐप्लिकेशन के लिए ट्रांसकोडिंग बंद करें"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"मीडिया ट्रांसकोडिंग सेटिंग"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"ट्रांसकोडिंग को बंद करें"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"ऐप्लिकेशन के लिए ट्रांसकोडिंग चालू करें"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"चल रही सेवाएं"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"इस समय चल रही सेवाओं को देखें और नियंत्रित करें"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"वेबव्यू लागू करें"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"चार्ज पूरा होने में <xliff:g id="TIME">%1$s</xliff:g> बचा है"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> में पूरा चार्ज हो जाएगा"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - कुछ समय के लिए, बैटरी का सीमित इस्तेमाल होगा"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"अज्ञात"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"चार्ज हो रही है"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"तेज़ चार्ज हो रही है"</string> diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml index bc0c2bc1b9a5..b51e096ca6b7 100644 --- a/packages/SettingsLib/res/values-hr/strings.xml +++ b/packages/SettingsLib/res/values-hr/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Nije aktivno. Dodirnite da biste to promijenili."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktivno. Dodirnite da biste to promijenili."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Stanje aplikacije u mirovanju: <xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Postavke konvertiranja medija"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Omogućavanje konvertiranja za sve aplikacije"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Onemogućivanje konvertiranja za aplikacije"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Postavke konvertiranja medija"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Onemogućivanje konvertiranja"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Omogućivanje konvertiranja za aplikacije"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Pokrenute usluge"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Pregledajte i kontrolirajte pokrenute usluge"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Implementacija WebViewa"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Napunit će se za <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – napunit će se za <xliff:g id="TIME">%2$s</xliff:g>"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Baterija je privremeno ograničena"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Nepoznato"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Punjenje"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Brzo punjenje"</string> diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml index 37b5b50d2cb7..33770fc1bdd8 100644 --- a/packages/SettingsLib/res/values-hu/strings.xml +++ b/packages/SettingsLib/res/values-hu/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Kikapcsolva. Koppintson ide a váltáshoz."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Bekapcsolva. Koppintson ide a váltáshoz."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Alkalmazás készenléti állapota:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Média átkódolásához tartozó beállítások"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Átkódolás engedélyezése minden alkalmazásnál"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Átkódolás letiltása az alkalmazásoknál"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Médiaátkódolási beállítások"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Átkódolás letiltása"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Átkódolás engedélyezése az alkalmazásoknál"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Futó szolgáltatások"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"A jelenleg futó szolgáltatások megtekintése és vezérlése"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView-megvalósítás"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> van hátra a feltöltésből"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> a feltöltésig"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Akkumulátor ideiglenesen korlátozva"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Ismeretlen"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Töltés"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Gyorstöltés"</string> diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml index f2744b26a4ea..06d70f854f27 100644 --- a/packages/SettingsLib/res/values-hy/strings.xml +++ b/packages/SettingsLib/res/values-hy/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Ակտիվ չէ: Հպեք՝ փոխելու համար:"</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Ակտիվ է: Հպեք՝ փոխելու համար:"</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Հավելվածի սպասման կարգավիճակ՝ <xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Մեդիա բովանդակության վերակոդավորման կարգավորումներ"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Միացնել վերակոդավորումը բոլոր հավելվածների համար"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Անջատել վերակոդավորումը հավելվածների համար"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Մեդիա ֆայլերի վերակոդավորման կարգավորումներ"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Անջատել վերակոդավորումը"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Միացնել վերակոդավորումը հավելվածների համար"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Աշխատող ծառայություններ"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Դիտել և վերահսկել ընթացիկ աշխատող ծառայությունները"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView ծառայություն"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> մինչև լիցքավորումը"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> մինչև լիցքավորումը"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Մարտկոցը ժամանակավորապես սահմանափակված է"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Անհայտ"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Լիցքավորում"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Արագ լիցքավորում"</string> diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml index e1298271d464..4123d9e3b755 100644 --- a/packages/SettingsLib/res/values-in/strings.xml +++ b/packages/SettingsLib/res/values-in/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Tidak aktif. Ketuk untuk beralih."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktif. Ketuk untuk beralih."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Status standby aplikasi:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Setelan transcoding media"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Mengaktifkan transcoding untuk semua aplikasi"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Menonaktifkan transcoding untuk aplikasi"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Setelan transcoding media"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Menonaktifkan transcoding"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Mengaktifkan transcoding untuk aplikasi"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Layanan yang sedang berjalan"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Melihat dan mengontrol layanan yang sedang berjalan"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Penerapan WebView"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Sisa <xliff:g id="TIME">%1$s</xliff:g> hingga terisi penuh"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> lagi terisi penuh"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Daya baterai terbatas untuk sementara"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Tidak diketahui"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Mengisi daya"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Mengisi daya cepat"</string> diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml index 724cf638de6a..3b9bef872177 100644 --- a/packages/SettingsLib/res/values-is/strings.xml +++ b/packages/SettingsLib/res/values-is/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Óvirkt. Ýttu til að breyta."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Virkt. Ýttu til að breyta."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Biðstaða forrits: <xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Umkóðunarstillingar efnis"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Kveikja á umkóðun í öllum forritum"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Slökkva á umkóðun í forritum"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Stillingar efnisumkóðunar"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Slökkva á umkóðun"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Kveikja á umkóðun í forritum"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Þjónustur í gangi"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Skoða og stjórna þjónustum í gangi"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Innleiðing WebView"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> að fullri hleðslu"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> að fullri hleðslu"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Rafhlaða takmörkuð tímabundið"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Óþekkt"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Í hleðslu"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Hröð hleðsla"</string> diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml index 4edb887d3db3..1ab6b24456f3 100644 --- a/packages/SettingsLib/res/values-it/strings.xml +++ b/packages/SettingsLib/res/values-it/strings.xml @@ -66,7 +66,7 @@ <string name="bluetooth_disconnected" msgid="7739366554710388701">"Disconnesso"</string> <string name="bluetooth_disconnecting" msgid="7638892134401574338">"Disconnessione…"</string> <string name="bluetooth_connecting" msgid="5871702668260192755">"Connessione…"</string> - <string name="bluetooth_connected" msgid="8065345572198502293">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> connesso"</string> + <string name="bluetooth_connected" msgid="8065345572198502293">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> Connesso"</string> <string name="bluetooth_pairing" msgid="4269046942588193600">"Accoppiamento…"</string> <string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> connesso (telefono escluso)"</string> <string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> connesso (contenuti multimediali esclusi)"</string> @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Non attiva. Tocca per attivare/disattivare."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Attiva. Tocca per attivare/disattivare."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Stato di standby dell\'app: <xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Impostazioni transcodifica contenuti multimediali"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Attiva transcodifica per tutte le app"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Disattiva transcodifica per le app"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Impostazioni transcodifica contenuti multimediali"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Disattiva transcodifica"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Attiva transcodifica per le app"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Servizi in esecuzione"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Visualizza e controlla i servizi attualmente in esecuzione"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Implementazione di WebView"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Tempo rimanente alla carica completa: <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> alla carica completa"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Batteria momentaneamente limitata"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Sconosciuta"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"In carica"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Ricarica veloce"</string> diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml index 6c916d9ab399..15868d5fc997 100644 --- a/packages/SettingsLib/res/values-iw/strings.xml +++ b/packages/SettingsLib/res/values-iw/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"אפליקציה לא פעילה. הקש כדי להחליף מצב."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"אפליקציה פעילה. הקש כדי להחליף מצב."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"אפליקציה במצב המתנה:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"הגדרות המרת קידוד במדיה"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"הפעלת המרת קידוד בכל האפליקציות"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"השבתת המרת קידוד באפליקציות"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"הגדרות של המרת קידוד למדיה"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"השבתה של המרת קידוד"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"הפעלה של המרת קידוד לאפליקציות"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"שירותים פועלים"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"הצגת השירותים הפועלים כעת ושליטה בהם"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"יישום WebView"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"נשארו <xliff:g id="TIME">%1$s</xliff:g> עד הטעינה"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> עד הטעינה"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - הסוללה מוגבלת באופן זמני"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"לא ידוע"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"בטעינה"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"הסוללה נטענת מהר"</string> diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml index 69392ce1de7a..89ee98eeeb42 100644 --- a/packages/SettingsLib/res/values-ja/strings.xml +++ b/packages/SettingsLib/res/values-ja/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"無効です。タップすると切り替わります。"</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"有効です。タップすると切り替わります。"</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"アプリ スタンバイ状態: <xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"メディアのコード変換設定"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"すべてのアプリに対しコード変換を有効にする"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"アプリに対しコード変換を無効にする"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"メディアのコード変換設定"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"コード変換を無効にする"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"アプリに対しコード変換を有効にする"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"実行中のサービス"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"現在実行中のサービスを表示して制御する"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView の実装"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"充電完了まであと <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - 充電完了まで <xliff:g id="TIME">%2$s</xliff:g>"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - 電池の使用が一時的に制限されています"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"不明"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"充電中"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"急速充電中"</string> diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml index e81562136a97..d1885789779a 100644 --- a/packages/SettingsLib/res/values-ka/strings.xml +++ b/packages/SettingsLib/res/values-ka/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"უმოქმედო. შეეხეთ გადასართავად."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"აქტიური. შეეხეთ გადასართავად."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"აპის მოლოდინის რეჟიმის მდგომარეობა:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"მედიის ტრანსკოდირების პარამეტრები"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"ტრანსკოდირების ჩართვა ყველა აპისთვის"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"ტრანსკოდირების გამორთვა ყველა აპისთვის"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"მედიის ტრანსკოდირების პარამეტრები"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"ტრანსკოდირების გათიშვა"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"ტრანსკოდირების ჩართვა აპებისთვის"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"მიმდინარე სერვისები"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"ამჟამად მოქმედი სერვისების ნახვა და მართვა"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView რეალიზაცია"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"დატენვამდე დარჩა <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> დატენვამდე"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> — ბატარეა დროებით შეზღუდულია"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"უცნობი"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"იტენება"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"სწრაფად იტენება"</string> diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml index c75c722f5b1c..2bc1c4d20728 100644 --- a/packages/SettingsLib/res/values-kk/strings.xml +++ b/packages/SettingsLib/res/values-kk/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Белсенді емес. Ауыстырып қосу үшін түртіңіз."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Белсенді. Ауыстырып қосу үшін түртіңіз."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Қолданбаның күту режимі: <xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Медиамазмұнды қайта кодтау параметрлері"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Барлық қолданба үшін қайта кодтауға рұқсат ету"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Қолданбалар үшін қайта кодтауды өшіру"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Медиамазмұнды қайта кодтау параметрлері"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Қайта кодтауды өшіру"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Қолданбалар үшін қайта кодтауға рұқсат ету"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Қосылып тұрған қызметтер"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Қазір істеп тұрған қызметтерді көру және басқару"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView қызметі"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Зарядталғанға дейін <xliff:g id="TIME">%1$s</xliff:g> қалды"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – зарядталғанға дейін <xliff:g id="TIME">%2$s</xliff:g>"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Батарея жұмысы уақытша шектелген"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Белгісіз"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Зарядталуда"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Жылдам зарядталуда"</string> diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml index 2590a9fe46d7..7306cf8c5d3f 100644 --- a/packages/SettingsLib/res/values-km/strings.xml +++ b/packages/SettingsLib/res/values-km/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"សកម្ម។ ប៉ះដើម្បីបិទ/បើក។"</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"សកម្ម។ ប៉ះដើម្បីបិទ/បើក។"</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"ស្ថានភាពមុខងារផ្អាកដំណើរការកម្មវិធី៖<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"ការកំណត់ការបំប្លែងកូដមេឌៀ"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"បើកការបំប្លែងកូដសម្រាប់កម្មវិធីទាំងអស់"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"បិទការបំប្លែងកូដសម្រាប់កម្មវិធី"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"ការកំណត់ការបំប្លែងកូដមេឌៀ"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"បិទការបំប្លែងកូដ"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"បើកការបំប្លែងកូដសម្រាប់កម្មវិធី"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"សេវាកម្មកំពុងដំណើរការ"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"មើល និងគ្រប់គ្រងសេវាកម្មកំពុងដំណើរការបច្ចុប្បន្ន"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"ការអនុវត្ត WebView"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> ទៀតទើបសាកថ្មពេញ"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ទៀតទើបសាកថ្មពេញ"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - បានដាក់កម្រិតថ្មជាបណ្ដោះអាសន្ន"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"មិនស្គាល់"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"កំពុងបញ្ចូលថ្ម"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"កំពុងសាកថ្មយ៉ាងឆាប់រហ័ស"</string> diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index dad3e03e94c7..0c1502ca620d 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"ನಿಷ್ಕ್ರಿಯ. ಟಾಗಲ್ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"ಸಕ್ರಿಯ. ಟಾಗಲ್ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"ಅಪ್ಲಿಕೇಶನ್ ಸ್ಟ್ಯಾಂಡ್ಬೈ ಸ್ಥಿತಿ:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"ಮಾಧ್ಯಮ ಟ್ರಾನ್ಸ್ಕೋಡ್ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"ಎಲ್ಲಾ ಆ್ಯಪ್ಗಳಿಗಾಗಿ ಟ್ರಾನ್ಸ್ಕೋಡಿಂಗ್ ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿ"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"ಆ್ಯಪ್ಗಳಿಗಾಗಿ ಟ್ರಾನ್ಸ್ಕೋಡಿಂಗ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"ಮೀಡಿಯಾ ಟ್ರಾನ್ಸ್ಕೋಡಿಂಗ್ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"ಟ್ರಾನ್ಸ್ಕೋಡಿಂಗ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"ಆ್ಯಪ್ಗಳಿಗಾಗಿ ಟ್ರಾನ್ಸ್ಕೋಡಿಂಗ್ ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿ"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"ರನ್ ಆಗುತ್ತಿರುವ ಸೇವೆಗಳು"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"ಈಗ ರನ್ ಆಗುತ್ತಿರುವ ಸೇವೆಗಳನ್ನು ವೀಕ್ಷಿಸಿ ಮತ್ತು ನಿಯಂತ್ರಿಸಿ"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView ಹೊಂದಿಸಿ"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"ಚಾರ್ಜ್ ಆಗಲು <xliff:g id="TIME">%1$s</xliff:g> ಸಮಯ ಬಾಕಿ ಇದೆ"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - ಚಾರ್ಜ್ ಆಗಲು <xliff:g id="TIME">%2$s</xliff:g> ಸಮಯ ಬೇಕು"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - ಬ್ಯಾಟರಿ ತಾತ್ಕಾಲಿಕವಾಗಿ ಸೀಮಿತವಾಗಿದೆ"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"ಅಪರಿಚಿತ"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ವೇಗದ ಚಾರ್ಜಿಂಗ್"</string> diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml index d41e7dbc1e5d..13223440d098 100644 --- a/packages/SettingsLib/res/values-ko/strings.xml +++ b/packages/SettingsLib/res/values-ko/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"비활성화 상태입니다. 전환하려면 탭하세요."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"활성화되었습니다. 전환하려면 탭하세요."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"앱 대기 상태:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"미디어 트랜스코딩 설정"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"모든 앱에서 트랜스코딩 사용 설정"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"모든 앱에서 트랜스코딩 사용 중지"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"미디어 트랜스코딩 설정"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"트랜스코딩 사용 중지"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"앱 트랜스코딩 사용 설정"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"실행 중인 서비스"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"현재 실행 중인 서비스 보기 및 제어"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView 구현"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"충전 완료까지 <xliff:g id="TIME">%1$s</xliff:g> 남음"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - 충전 완료까지 <xliff:g id="TIME">%2$s</xliff:g> 남음"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - 일시적으로 배터리 사용 제한"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"알 수 없음"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"충전 중"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"고속 충전 중"</string> diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml index c07f334abdf2..9697aa70e44f 100644 --- a/packages/SettingsLib/res/values-ky/strings.xml +++ b/packages/SettingsLib/res/values-ky/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Иштеген жок. Күйгүзүү үчүн басып коюңуз."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Иштеп турат. Өчүрүү үчүн басып коюңуз."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Көшүү режиминдеги колдонмонун абалы:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Медианы транскоддоо жөндөөлөрү"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Бардык колдонмолор үчүн транскоддоону иштетүү"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Бардык колдонмолор үчүн транскоддоону өчүрүү"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Медиа файлдарды транскоддоо жөндөөлөрү"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Транскоддоону өчүрүү"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Колдонмолорду транскоддоону күйгүзүү"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Иштеп жаткан кызматтар"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Учурда иштеп жаткан кызматтарды көрүп, көзөмөлдөп турасыз"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView кызматы"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> кийин толук кубатталып бүтөт"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> кийин толук кубатталып бүтөт"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Батареяны колдонуу убактлуу чектелген"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Белгисиз"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Кубатталууда"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Ыкчам кубатталууда"</string> diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml index 5aa27bf2a67a..720122262dcf 100644 --- a/packages/SettingsLib/res/values-lo/strings.xml +++ b/packages/SettingsLib/res/values-lo/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"ບໍ່ໄດ້ນຳໃຊ້. ແຕະບໍ່ສັບປ່ຽນ."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"ນຳໃຊ້ຢູ່. ແຕະເພື່ອສັບປ່ຽນ."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"ສະຖານະສະແຕນບາຍແອັບ:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"ການຕັ້ງຄ່າການປ່ຽນຮູບແບບລະຫັດມີເດຍ"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"ເປີດການນຳໃຊ້ການປ່ຽນຮູບແບບລະຫັດສຳລັບທຸກແອັບ"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"ປິດການນຳໃຊ້ການປ່ຽນຮູບແບບລະຫັດສຳລັບແອັບ"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"ການຕັ້ງຄ່າການປ່ຽນຮູບແບບລະຫັດມີເດຍ"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"ປິດການນຳໃຊ້ການປ່ຽນຮູບແບບລະຫັດ"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"ເປີດການນຳໃຊ້ການປ່ຽນຮູບແບບລະຫັດສຳລັບແອັບ"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"ບໍລິການທີ່ເຮັດວຽກຢູ່"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"ເບິ່ງ ແລະຈັດການບໍລິການທີ່ກຳລັງເຮັດວຽກຢູ່ໃນປັດຈຸບັນ"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"ການຈັດຕັ້ງປະຕິບັດ WebView"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> ຈົນກວ່າຈະສາກເຕັມ"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ຈົນກວ່າຈະສາກເຕັມ"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - ຈຳກັດແບັດເຕີຣີຊົ່ວຄາວ"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"ບໍ່ຮູ້ຈັກ"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"ກຳລັງສາກໄຟ"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ກຳລັງສາກໄຟດ່ວນ"</string> diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml index ff4d336440eb..e62eeb2dbd97 100644 --- a/packages/SettingsLib/res/values-lt/strings.xml +++ b/packages/SettingsLib/res/values-lt/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Neaktyvi. Palieskite, kad perjungtumėte."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktyvi. Palieskite, kad perjungtumėte."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Programų budėjimo režimo būsena: <xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Medijos perkodavimo nustatymai"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Įjungti visų programų perkodavimą"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Išjungti programų perkodavimą"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Medijos perkodavimo nustatymai"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Išjungti perkodavimą"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Įjungti programų perkodavimą"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Vykdomos paslaugos"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Žiūrėti ir valdyti dabar vykdomas paslaugas"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"„WebView“ diegimas"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Iki visiškos įkrovos liko <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – iki visiškos įkrovos liko <xliff:g id="TIME">%2$s</xliff:g>"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – akumuliatorius laikinai apribotas"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Nežinomas"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Kraunasi..."</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Greitai įkraunama"</string> diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml index e876cef9a98a..2f305612a5fc 100644 --- a/packages/SettingsLib/res/values-lv/strings.xml +++ b/packages/SettingsLib/res/values-lv/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Neaktīva. Pieskarieties, lai pārslēgtu."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktīva. Pieskarieties, lai pārslēgtu."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Lietotnes gaidstāves stāvoklis: <xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Multivides failu pārkodēšanas iestatījumi"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Iespējot pārkodēšanu visām lietotnēm"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Atspējot pārkodēšanu noteiktām lietotnēm"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Multivides failu pārkodēšanas iestatījumi"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Atspējot pārkodēšanu"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Iespējot pārkodēšanu noteiktām lietotnēm"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Aktīvie pakalpojumi"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Pašreiz darbojošos pakalpojumu skatīšana un vadība"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView ieviešana"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> — <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Vēl <xliff:g id="TIME">%1$s</xliff:g> līdz pilnai uzlādei"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> — <xliff:g id="TIME">%2$s</xliff:g> līdz pilnai uzlādei"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g>, akumulatora uzlāde pagaidām ierobežota"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Nezināms"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Uzlāde"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Notiek ātrā uzlāde"</string> diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml index 3507ae723f2a..1936fe2d32a4 100644 --- a/packages/SettingsLib/res/values-mk/strings.xml +++ b/packages/SettingsLib/res/values-mk/strings.xml @@ -51,9 +51,9 @@ <string name="connected_via_carrier" msgid="1968057009076191514">"Поврзано преку %1$s"</string> <string name="available_via_carrier" msgid="465598683092718294">"Достапно преку %1$s"</string> <string name="osu_opening_provider" msgid="4318105381295178285">"Се отвора <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string> - <string name="osu_connect_failed" msgid="9107873364807159193">"Не можеше да се поврзе"</string> + <string name="osu_connect_failed" msgid="9107873364807159193">"Не може да се поврзе"</string> <string name="osu_completing_sign_up" msgid="8412636665040390901">"Се завршува регистрацијата…"</string> - <string name="osu_sign_up_failed" msgid="5605453599586001793">"Не можеше да се заврши регистрацијата. Допрете за да се обидете повторно."</string> + <string name="osu_sign_up_failed" msgid="5605453599586001793">"Не може да се заврши регистрацијата. Допрете за да се обидете повторно."</string> <string name="osu_sign_up_complete" msgid="7640183358878916847">"Регистрацијата е завршена. Се поврзува…"</string> <string name="speed_label_very_slow" msgid="8526005255731597666">"Многу бавна"</string> <string name="speed_label_slow" msgid="6069917670665664161">"Бавна"</string> @@ -116,8 +116,8 @@ <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"СПАРИ"</string> <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Откажи"</string> <string name="bluetooth_pairing_will_share_phonebook" msgid="3064334458659165176">"Кога е поврзано, спарувањето одобрува пристап до контактите и историјата на повиците."</string> - <string name="bluetooth_pairing_error_message" msgid="6626399020672335565">"Не можеше да се спари со <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> - <string name="bluetooth_pairing_pin_error_message" msgid="264422127613704940">"Не можеше да се спари со <xliff:g id="DEVICE_NAME">%1$s</xliff:g> поради погрешен PIN или лозинка."</string> + <string name="bluetooth_pairing_error_message" msgid="6626399020672335565">"Не може да се спари со <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="bluetooth_pairing_pin_error_message" msgid="264422127613704940">"Не може да се спари со <xliff:g id="DEVICE_NAME">%1$s</xliff:g> поради погрешен PIN или лозинка."</string> <string name="bluetooth_pairing_device_down_error_message" msgid="2554424863101358857">"Не може да комуницира со <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> <string name="bluetooth_pairing_rejected_error_message" msgid="5943444352777314442">"Спарувањето е одбиено од <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> <string name="bluetooth_talkback_computer" msgid="3736623135703893773">"Компјутер"</string> @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Неактивно. Допрете за да смените."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Активно. Допрете за да смените."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Состојба на мирување на апликацијата: <xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Поставки за транскодирање на аудиовизуелни содржини"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Овозможи транскодирање за сите апликации"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Оневозможи транскодирање за сите апликации"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Поставки за транскодирање аудиовизуелни содржини"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Оневозможи транскодирање"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Овозможете транскодирање за апликациите"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Активни услуги"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Погледнете и контролирајте услуги што се моментално активни"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Воведување WebView"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Уште <xliff:g id="TIME">%1$s</xliff:g> до целосно полнење"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> до целосно полнење"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Батеријата е привремено ограничена"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Непознато"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Се полни"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Брзо полнење"</string> diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml index 4aae6ee47930..de4a49a2ed06 100644 --- a/packages/SettingsLib/res/values-ml/strings.xml +++ b/packages/SettingsLib/res/values-ml/strings.xml @@ -399,12 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"നിഷ്ക്രിയം. മാറ്റുന്നതിനു ടാപ്പുചെയ്യുക."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"സജീവം. മാറ്റുന്നതിന് ടാപ്പുചെയ്യുക."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"ആപ്പ് സ്റ്റാൻഡ്ബൈ നില:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <!-- no translation found for transcode_settings_title (6700974145733932357) --> - <skip /> - <!-- no translation found for transcode_enable_all (4719796495995795404) --> - <skip /> - <!-- no translation found for transcode_skip_apps (5680997722349545778) --> - <skip /> + <string name="transcode_settings_title" msgid="2581975870429850549">"മീഡിയ ട്രാൻസ്കോഡ് ചെയ്യൽ ക്രമീകരണം"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"ട്രാൻസ്കോഡ് ചെയ്യൽ പ്രവർത്തനരഹിതമാക്കുക"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"ആപ്പുകൾക്കായി ട്രാൻസ്കോഡ് ചെയ്യുന്നത് പ്രവർത്തനക്ഷമമാക്കുക"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"പ്രവർത്തിക്കുന്ന സേവനങ്ങൾ"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"നിലവിൽ പ്രവർത്തിക്കുന്ന സേവനങ്ങൾ കാണുക, നിയന്ത്രിക്കുക"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView നടപ്പാക്കൽ"</string> @@ -452,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"പൂർണ്ണമായി ചാർജാവാൻ <xliff:g id="TIME">%1$s</xliff:g> ശേഷിക്കുന്നു"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - പൂർണ്ണമായി ചാർജാവാൻ <xliff:g id="TIME">%2$s</xliff:g>"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - ബാറ്ററി താൽക്കാലം പരിമിതപ്പെടുത്തി"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"അജ്ഞാതം"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"ചാർജ് ചെയ്യുന്നു"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"അതിവേഗ ചാർജിംഗ്"</string> diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml index 9cdf5bf98ede..dee8add685f9 100644 --- a/packages/SettingsLib/res/values-mn/strings.xml +++ b/packages/SettingsLib/res/values-mn/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Идэвхгүй байна. Унтраах/асаахын тулд дарна уу."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Идэвхтэй байна. Унтраах/асаахын тулд дарна уу."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Апп зогсолтын горимын төлөв:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Медиагийн хөрвүүлгийн тохиргоо"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Бүх аппад хөрвүүлгийг идэвхжүүлэх"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Аппуудад хөрвүүлгийг идэвхгүй болгох"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Медиа хөрвүүлгийн тохиргоо"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Хөрвүүлгийг идэвхгүй болгох"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Аппуудад хөрвүүлгийг идэвхжүүлэх"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Ажиллаж байгаа үйлчилгээнүүд"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Одоо ажиллаж байгаа үйлчилгээнүүдийг харах болон хянах"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView хэрэгжилт"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Цэнэглэх хүртэл үлдсэн <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - цэнэглэх хүртэл <xliff:g id="TIME">%2$s</xliff:g>"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Батарейг түр хугацаанд хязгаарласан"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Тодорхойгүй"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Цэнэглэж байна"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Хурдан цэнэглэж байна"</string> diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml index d6563ae41832..60ad68e42c09 100644 --- a/packages/SettingsLib/res/values-mr/strings.xml +++ b/packages/SettingsLib/res/values-mr/strings.xml @@ -399,12 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"निष्क्रिय. टॉगल करण्यासाठी टॅप करा."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"सक्रिय. टॉगल करण्यासाठी टॅप करा."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"अॅप स्टँडबाय स्थिती: <xliff:g id="BUCKET"> %s</xliff:g>"</string> - <!-- no translation found for transcode_settings_title (6700974145733932357) --> - <skip /> - <!-- no translation found for transcode_enable_all (4719796495995795404) --> - <skip /> - <!-- no translation found for transcode_skip_apps (5680997722349545778) --> - <skip /> + <string name="transcode_settings_title" msgid="2581975870429850549">"मीडिया ट्रान्सकोडिंगची सेटिंग्ज"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"ट्रान्सकोडिंग बंद करा"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"ॲप्ससाठी ट्रान्सकोडिंग सुरू करा"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"सुरू सेवा"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"सध्या सुरू असलेल्या सेवा पहा आणि नियंत्रित करा"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"वेबदृश्य अंमलबजावणी"</string> @@ -452,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> पर्यंत पूर्ण चार्ज होईल"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> पर्यंत पूर्ण चार्ज होईल"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - बॅटरी तात्पुरती मर्यादित आहे"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"अज्ञात"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"चार्ज होत आहे"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"वेगाने चार्ज होत आहे"</string> diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml index 6bcc5d2fa3e8..e4d591207937 100644 --- a/packages/SettingsLib/res/values-ms/strings.xml +++ b/packages/SettingsLib/res/values-ms/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Tidak aktif. Ketik untuk menogol."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktif. Ketik untuk menogol."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Keadaan tunggu sedia apl:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Tetapan transpengekodan media"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Dayakan transpengekodan untuk semua apl"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Lumpuhkan transpengekodan untuk apl"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Tetapan transpengekodan media"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Lumpuhkan transpengekodan"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Dayakan transpengekodan untuk apl"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Perkhidmatan dijalankan"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Lihat dan kawal perkhidmatan yang sedang dijalankan"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Pelaksanaan WebView"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> lagi sehingga dicas penuh"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> sehingga dicas"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Bateri terhad untuk sementara"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Tidak diketahui"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Mengecas"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Mengecas dgn cepat"</string> diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml index 8bb717a57789..09a8bf87700b 100644 --- a/packages/SettingsLib/res/values-my/strings.xml +++ b/packages/SettingsLib/res/values-my/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"ပွင့်မနေပါ။ ပြောင်းရန်တို့ပါ။"</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"ပွင့်နေသည်။ ပြောင်းရန်တို့ပါ။"</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"အက်ပ်ကို အရန်သင့်ထားရှိခြင်း အခြေအနေ-<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"မီဒီယာအမျိုးအစားပြောင်းခြင်း ဆက်တင်များ"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"အက်ပ်အားလုံးအတွက် အမျိုးအစားပြောင်းခြင်းကို ဖွင့်ရန်"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"အက်ပ်များအတွက် အမျိုးအစားပြောင်းခြင်းကို ပိတ်ရန်"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"မီဒီယာအမျိုးအစားပြောင်းခြင်း ဆက်တင်များ"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"အမျိုးအစားပြောင်းခြင်းကို ပိတ်ရန်"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"အက်ပ်များအတွက် အမျိုးအစားပြောင်းခြင်းကို ဖွင့်ရန်"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"အလုပ်လုပ်နေသောဝန်ဆောင်မှုများ"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"လက်ရှိ ဝန်ဆောင်မှုများကို ကြည့်ရှု ထိန်းသိမ်းသည်"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView အကောင်အထည်ဖော်မှု"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"အားပြည့်ရန် <xliff:g id="TIME">%1$s</xliff:g> ကျန်သည်"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - အားပြည့်ရန် <xliff:g id="TIME">%2$s</xliff:g> ကျန်သည်"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - ဘက်ထရီ ယာယီကန့်သတ်ထားသည်"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"မသိ"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"အားသွင်းနေပါသည်"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"အမြန် အားသွင်းနေသည်"</string> diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml index 6a3aa29e0561..d1af28940f1b 100644 --- a/packages/SettingsLib/res/values-nb/strings.xml +++ b/packages/SettingsLib/res/values-nb/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Ikke aktiv. Trykk for å slå av/på."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktiv. Trykk for å slå av/på."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Hvilemodus:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Innstillinger for omkoding av medier"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Aktiver omkoding for alle apper"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Deaktiver omkoding for apper"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Innstillinger for omkoding av medier"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Deaktiver omkoding"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Aktiver omkoding for apper"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Aktive tjenester"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Se og kontrollér tjenester som kjører"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView-implementering"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> til batteriet er fulladet"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> til batteriet er fulladet"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Batteriet er midlertidig begrenset"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Ukjent"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Lader"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Lader raskt"</string> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index 5bc98c1b26cf..0c6114b9e220 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -305,7 +305,7 @@ <string name="adbwifi_warning_message" msgid="8005936574322702388">"वायरलेस डिबगिङ डिभलपमेन्ट प्रयोजनका लागि मात्रै हो। यसलाई आफ्ना कम्प्युटर र उपकरणका बिच डेटा प्रतिलिपि गर्न, सूचना नदिई आफ्नो उपकरणमा एपहरू स्थापना गर्न र लग डेटा पढ्न प्रयोग गर्नुहोस्।"</string> <string name="adb_keys_warning_message" msgid="2968555274488101220">"तपाईं पहिले नै अधिकृत गर्नुभएका सबै कम्प्यूटरबाट USB डिबग गर्नको लागि पहुँच रद्द गर्ने हो?"</string> <string name="dev_settings_warning_title" msgid="8251234890169074553">"विकास सेटिङहरू अनुमति दिने हो?"</string> - <string name="dev_settings_warning_message" msgid="37741686486073668">"यी सेटिङहरू केवल विकास प्रयोगको लागि विचार गरिएको हो। तिनीहरूले तपाईंको उपकरण र अनुप्रयोगहरूलाई विच्छेदन गर्न वा दुर्व्यवहार गर्न सक्दछ।"</string> + <string name="dev_settings_warning_message" msgid="37741686486073668">"यी सेटिङहरू केवल विकास प्रयोगको लागि विचार गरिएको हो। तिनीहरूले तपाईंको उपकरण र एपहरूलाई विच्छेदन गर्न वा दुर्व्यवहार गर्न सक्दछ।"</string> <string name="verify_apps_over_usb_title" msgid="6031809675604442636">"USB मा एपहरू रुजु गर्नुहोस्"</string> <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"हानिकारक व्यवहारको लागि ADB/ADT को माध्यमबाट स्थापित अनुप्रयोगहरूको जाँच गर्नुहोस्।"</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"नामकरण नगरिएका ब्लुटुथ यन्त्रहरू (MAC ठेगाना भएका मात्र) देखाइनेछ"</string> @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"निष्क्रिय। टगल गर्न ट्याप गर्नुहोस्।"</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"सक्रिय। टगल गर्न ट्याप गर्नुहोस्।"</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"एपको स्ट्यान्डबाई अवस्था:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"मिडिया फाइल ट्रान्सकोड गर्नेसम्बन्धी सेटिङ"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"सबै एपमा ट्रान्सकोडिङ अन गर्नुहोस्"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"एपमा ट्रान्सकोडिङ अफ गर्नुहोस्"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"मिडिया ट्रान्सकोडिङ सेटिङ"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"ट्रान्सकोडिङ अफ गर्नुहोस्"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"एपहरूमा ट्रान्सकोडिङ अन गर्नुहोस्"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"चलिरहेका सेवाहरू"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"हाल चालु भइरहेका सेवाहरू हेर्नुहोस् र नियन्त्रण गर्नुहोस्"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView कार्यान्वयन"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"पूर्ण चार्ज हुन <xliff:g id="TIME">%1$s</xliff:g> बाँकी"</string> <string name="power_charging_duration" msgid="5005740040558984057">"पूर्ण चार्ज हुन <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> लाग्छ"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - केही समयका लागि ब्याट्री प्रयोग सीमित गरिएको छ"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"अज्ञात"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"चार्ज हुँदै"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"द्रुत गतिमा चार्ज गरिँदै"</string> diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml index bf40f40f1b21..a409756e5b5f 100644 --- a/packages/SettingsLib/res/values-nl/strings.xml +++ b/packages/SettingsLib/res/values-nl/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Inactief. Tik om te schakelen."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Actief. Tik om te schakelen."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Stand-bystatus app: <xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Instellingen voor mediatranscodering"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Transcodering inschakelen voor alle apps"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Transcodering uitschakelen voor alle apps"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Instellingen voor mediatranscodering"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Transcodering uitschakelen"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Transcodering inschakelen voor apps"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Actieve services"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Services die momenteel actief zijn, weergeven en beheren"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView-implementatie"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Nog <xliff:g id="TIME">%1$s</xliff:g> tot opgeladen"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> tot opgeladen"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Batterij tijdelijk beperkt"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Onbekend"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Opladen"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Snel opladen"</string> diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml index a65a7d80eb7b..0a0eeff6de60 100644 --- a/packages/SettingsLib/res/values-or/strings.xml +++ b/packages/SettingsLib/res/values-or/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"ନିଷ୍କ୍ରିୟ। ଟୋଗଲ୍ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ।"</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"ସକ୍ରିୟ। ବଦଳାଇବା ପାଇଁ ଟାପ୍ କରନ୍ତୁ"</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"ଆପ୍ ଷ୍ଟାଣ୍ଡବାଏ ଅବସ୍ଥା:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"ମିଡିଆ ଟ୍ରାନ୍ସକୋଡିଂ ସେଟିଂସ୍"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"ସମସ୍ତ ଆପ୍ ପାଇଁ ଟ୍ରାନ୍ସକୋଡିଂ ସକ୍ଷମ କରନ୍ତୁ"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"ଆପଗୁଡ଼ିକ ପାଇଁ ଟ୍ରାନ୍ସକୋଡିଂ ଅକ୍ଷମ କରନ୍ତୁ"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"ମିଡିଆ ଟ୍ରାନ୍ସକୋଡିଂ ସେଟିଂସ୍"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"ଟ୍ରାନ୍ସକୋଡିଂ ଅକ୍ଷମ କରନ୍ତୁ"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"ଆପଗୁଡ଼ିକ ପାଇଁ ଟ୍ରାନ୍ସକୋଡିଂ ସକ୍ଷମ କରନ୍ତୁ"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"ଚାଲୁଥିବା ସେବାଗୁଡ଼ିକ"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"ଏବେ ଚାଲୁଥିବା ସେବାଗୁଡ଼ିକୁ ଦେଖନ୍ତୁ ଓ ନିୟନ୍ତ୍ରଣ କରନ୍ତୁ"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"ୱେବ୍ଭ୍ୟୁ ପ୍ରୟୋଗ"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"ଚାର୍ଜ ହେବା ପାଇଁ <xliff:g id="TIME">%1$s</xliff:g> ବାକି ଅଛି"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ଚାର୍ଜ ହେବା ପର୍ଯ୍ୟନ୍ତ"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - ବ୍ୟାଟେରୀ ଅସ୍ଥାୟୀ ଭାବେ ସୀମିତ ଅଛି"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"ଅଜ୍ଞାତ"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"ଚାର୍ଜ ହେଉଛି"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ଶୀଘ୍ର ଚାର୍ଜ ହେଉଛି"</string> diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index 5a009bea08af..c9c37d922537 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"ਅਕਿਰਿਆਸ਼ੀਲ। ਟੌਗਲ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"ਕਿਰਿਆਸ਼ੀਲ। ਟੌਗਲ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"ਐਪ ਸਟੈਂਡਬਾਈ ਸਥਿਤੀ:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"ਮੀਡੀਆ ਦੀਆਂ ਟ੍ਰਾਂਸਕੋਡ ਸੈਟਿੰਗਾਂ"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"ਸਾਰੀਆਂ ਐਪਾਂ ਲਈ ਟ੍ਰਾਂਸਕੋਡਿੰਗ ਚਾਲੂ ਕਰੋ"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"ਸਾਰੀਆਂ ਐਪਾਂ ਲਈ ਟ੍ਰਾਂਸਕੋਡਿੰਗ ਬੰਦ ਕਰੋ"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"ਮੀਡੀਆ ਟ੍ਰਾਂਸਕੋਡਿੰਗ ਸੈਟਿੰਗਾਂ"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"ਟ੍ਰਾਂਸਕੋਡਿੰਗ ਬੰਦ ਕਰੋ"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"ਐਪਾਂ ਲਈ ਟ੍ਰਾਂਸਕੋਡਿੰਗ ਚਾਲੂ ਕਰੋ"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"ਚੱਲ ਰਹੀਆਂ ਸੇਵਾਵਾਂ"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"ਇਸ ਵੇਲੇ ਚੱਲ ਰਹੀਆਂ ਸੇਵਾਵਾਂ ਦੇਖੋ ਅਤੇ ਇਹਨਾਂ ਨੂੰ ਕੰਟਰੋਲ ਕਰੋ"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView ਅਮਲ"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"ਚਾਰਜ ਹੋਣ ਵਿੱਚ <xliff:g id="TIME">%1$s</xliff:g> ਬਾਕੀ"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ਤੱਕ ਚਾਰਜ ਹੋ ਜਾਵੇਗੀ"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - ਬੈਟਰੀ ਕੁਝ ਸਮੇਂ ਲਈ ਸੀਮਤ"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"ਅਗਿਆਤ"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ਤੇਜ਼ ਚਾਰਜ ਹੋ ਰਹੀ ਹੈ"</string> diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index 294e628ef567..e894c2cbc34c 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -148,7 +148,7 @@ <string name="tether_settings_title_bluetooth" msgid="916519902721399656">"Tethering przez Bluetooth"</string> <string name="tether_settings_title_usb_bluetooth" msgid="1727111807207577322">"Tethering"</string> <string name="tether_settings_title_all" msgid="8910259483383010470">"Tethering i punkt dostępu"</string> - <string name="managed_user_title" msgid="449081789742645723">"Wszystkie aplikacje do pracy"</string> + <string name="managed_user_title" msgid="449081789742645723">"Wszystkie aplikacje służbowe"</string> <string name="user_guest" msgid="6939192779649870792">"Gość"</string> <string name="unknown" msgid="3544487229740637809">"Nieznana"</string> <string name="running_process_item_user_label" msgid="3988506293099805796">"Użytkownik: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string> @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Nieaktywna. Dotknij, by zmienić."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktywna. Dotknij, by zmienić."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Stan aplikacji w trybie czuwania: <xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Ustawienia transkodowania multimediów"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Włącz transkodowanie dla wszystkich aplikacji"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Wyłącz transkodowanie dla aplikacji"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Ustawienia transkodowania multimediów"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Wyłącz transkodowanie"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Włącz transkodowanie dla aplikacji"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Uruchomione usługi"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Wyświetl obecnie uruchomione usługi i nimi zarządzaj"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Implementacja WebView"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Do naładowania <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – do naładowania <xliff:g id="TIME">%2$s</xliff:g>"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – bateria tymczasowo ograniczona"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Nieznane"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Ładowanie"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Szybkie ładowanie"</string> diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml index d9d1973a4457..acaabc793970 100644 --- a/packages/SettingsLib/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Inativo. Tocar para alternar."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Ativo. Tocar para alternar."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Estado em espera do app:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Configurações de transcodificação de mídia"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Ativar a transcodificação para todos os apps"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Desativar a transcodificação para apps"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Configurações de transcodificação de mídia"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Desativar transcodificação"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Ativar transcodificação para apps"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Serviços em execução"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Visualizar e controlar os serviços em execução no momento"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Implementação do WebView"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Tempo restante até a carga completa: <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> até a carga completa"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g>: bateria limitada temporariamente"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Desconhecido"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Carregando"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Carregando rápido"</string> diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml index ee6e5fec7eae..2c1a9678f158 100644 --- a/packages/SettingsLib/res/values-pt-rPT/strings.xml +++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Inativo. Toque para ativar/desativar."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Ativo. Toque para ativar/desativar."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Estado do Modo de espera das apps:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Definições da transcodificação multimédia"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Ativar transcodificação para todas as apps"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Desative a transcodificação para apps"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Definições da transcodificação de multimédia"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Desativar a transcodificação"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Ative a transcodificação para apps"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Serviços em execução"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Ver e controlar os serviços actualmente em execução"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Implementação WebView"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Falta(m) <xliff:g id="TIME">%1$s</xliff:g> até ficar carregada"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> até ficar carregada"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Bateria limitada temporariamente."</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Desconhecido"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"A carregar"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Carregamento rápido"</string> diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml index d9d1973a4457..acaabc793970 100644 --- a/packages/SettingsLib/res/values-pt/strings.xml +++ b/packages/SettingsLib/res/values-pt/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Inativo. Tocar para alternar."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Ativo. Tocar para alternar."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Estado em espera do app:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Configurações de transcodificação de mídia"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Ativar a transcodificação para todos os apps"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Desativar a transcodificação para apps"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Configurações de transcodificação de mídia"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Desativar transcodificação"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Ativar transcodificação para apps"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Serviços em execução"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Visualizar e controlar os serviços em execução no momento"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Implementação do WebView"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Tempo restante até a carga completa: <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> até a carga completa"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g>: bateria limitada temporariamente"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Desconhecido"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Carregando"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Carregando rápido"</string> diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index 8f6ddeb11fe3..57a0454f3465 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Inactivă. Atingeți pentru a comuta."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Activă. Atingeți pentru a comuta."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Stare Standby aplicații: <xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Setări pentru transcodarea conținutului media"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Activați transcodarea pentru toate aplicațiile"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Dezactivați transcodarea pentru aplicații"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Setări pentru transcodarea conținutului media"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Dezactivați transcodarea"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Activați transcodarea pentru aplicații"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Servicii în curs de funcționare"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Vedeți și controlați serviciile care funcționează în prezent"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Implementare WebView"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Au mai rămas <xliff:g id="TIME">%1$s</xliff:g> până la încărcare"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> până la încărcare"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – baterie limitată temporar"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Necunoscut"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Se încarcă"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Se încarcă rapid"</string> diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index e61eb29db2f5..09b8de04388b 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Выключено. Нажмите, чтобы включить."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Включено. Нажмите, чтобы отключить."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Статус приложения в режиме ожидания:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Настройки перекодирования медиаконтента"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Разрешить перекодирование для всех приложений"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Приложения, для которых нужно запретить перекодирование"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Настройки перекодирования медиафайлов"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Отключить перекодирование"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Включить перекодирование для приложений"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Работающие службы"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Просмотр и управление работающими службами"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Сервис WebView"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> до полной зарядки"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до полной зарядки"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> • Уровень заряда временно ограничен"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Неизвестно"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Идет зарядка"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Быстрая зарядка"</string> diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml index cfea2aac17cb..d36140f66b54 100644 --- a/packages/SettingsLib/res/values-si/strings.xml +++ b/packages/SettingsLib/res/values-si/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"අක්රියයි. ටොගල කිරීමට තට්ටු කරන්න."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"සක්රියයි. ටොගල කිරීමට තට්ටු කරන්න."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"යෙදුම් පොරොත්තු තත්ත්වය:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"මාධ්ය ට්රාන්ස්කෝඩ් සැකසීම්"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"සියලු යෙදුම් සඳහා ට්රාන්ස්කෝඩින් සබල කරන්න"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"සියලු යෙදුම් සඳහා ට්රාන්ස්කෝඩින් අබල කරන්න"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"මාධ්ය ට්රාන්ස්කෝඩින් සැකසීම්"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"ට්රාන්ස්කෝඩින් අබල කරන්න"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"යෙදුම් සඳහා ට්රාන්ස්කෝඩින් සබල කරන්න"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"ධාවනය වන සේවා"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"දැනට ධාවනය වන සේවා බලන්න සහ පාලනය කරන්න"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView ක්රියාත්මක කිරීම"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"ආරෝපණය වන තෙක් <xliff:g id="TIME">%1$s</xliff:g> ඇත"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - ආරෝපණය වන තෙක් <xliff:g id="TIME">%2$s</xliff:g>"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - බැටරිය තාවකාලිකව සීමිතයි"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"නොදනී"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"ආරෝපණය වෙමින්"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ශීඝ්ර ආරෝපණය"</string> diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml index 56fc8116ad8a..096e95e7050b 100644 --- a/packages/SettingsLib/res/values-sk/strings.xml +++ b/packages/SettingsLib/res/values-sk/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Neaktívne. Prepnite klepnutím."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktívne. Prepnite klepnutím."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Stav pohotovostného režimu aplikácie: <xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Nastavenie prekódovania médií"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Povoliť prekódovanie všetkých aplikácií"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Zakázať prekódovanie všetkých aplikácií"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Nastavenia prekódovania médií"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Vypnúť prekódovanie"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Zapnúť prekódovanie aplikácií"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Spustené služby"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Zobrazovať a riadiť aktuálne spustené služby"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Implementácia WebView"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Zostávajúci čas do úplného nabitia: <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabitia"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Batéria je dočasne obmedzená"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Neznáme"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Nabíja sa"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Rýchle nabíjanie"</string> diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml index 22a65702b3c2..345fa36d3bd6 100644 --- a/packages/SettingsLib/res/values-sl/strings.xml +++ b/packages/SettingsLib/res/values-sl/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Neaktivno. Dotaknite se za preklop."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktivno. Dotaknite se za preklop."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Stanje pripravljenosti aplikacije: <xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Nastavitve prekodiranja predstavnosti"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Omogočanje prekodiranja za vse aplikacije"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Onemogočanje prekodiranja za aplikacije"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Nastavitve prekodiranja predstavnosti"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Onemogočanje prekodiranja"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Omogočanje prekodiranja za aplikacije"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Zagnane storitve"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Preglejte in nadzorujte storitve, ki so trenutno zagnane"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Izvedba spletnega pogleda"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Še <xliff:g id="TIME">%1$s</xliff:g> do polne napolnjenosti"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do polne napolnjenosti"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – baterija je začasno omejena"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Neznano"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Polnjenje"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Hitro polnjenje"</string> diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml index 2d846c8ae178..3953a6a4f6e0 100644 --- a/packages/SettingsLib/res/values-sq/strings.xml +++ b/packages/SettingsLib/res/values-sq/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Joaktiv. Trokit për ta ndryshuar."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktiv. Trokit për ta ndryshuar."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Gjendja e gatishmërisë e aplikacionit:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Cilësimet e transkodimit të medias"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Aktivizo transkodimin për të gjitha aplikacionet"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Çaktivizo transkodimin për aplikacionet"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Cilësimet e transkodimit të multimediave"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Çaktivizo transkodimin"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Aktivizo transkodimin për aplikacionet"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Shërbimet në ekzekutim"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Shiko dhe kontrollo shërbimet që po ekzekutohen aktualisht"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Zbatimi i WebView"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> të mbetura deri në karikim"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> derisa të karikohet"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Bateria e kufizuar përkohësisht"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"I panjohur"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Po karikohet"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Po ngarkon me shpejtësi"</string> diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml index 34fd5552ff43..70b287933c93 100644 --- a/packages/SettingsLib/res/values-sr/strings.xml +++ b/packages/SettingsLib/res/values-sr/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Неактивна. Додирните да бисте је активирали."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Активна. Додирните да бисте је деактивирали."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Стање приправности апликације: <xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Подешавања транскодирања медија"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Омогући транскодирање за све апликације"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Онемогућите транскодирање за апликације"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Подешавања транскодирања медија"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Онемогући транскодирање"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Омогућите транскодирање за апликације"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Покренуте услуге"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Приказ и контрола тренутно покренутих услуга"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Примена WebView-а"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Напуниће се за <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – напуниће се за <xliff:g id="TIME">%2$s</xliff:g>"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – батерија је тренутно ограничена"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Непознато"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Пуни се"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Брзо се пуни"</string> diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index 05e455a8e5e3..f466e882c2fc 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Inaktiv. Tryck om du vill aktivera."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktiv. Tryck om du vill inaktivera."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Status för strömsparfunktion för appar:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Inställningar för medieomkodning"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Aktivera omkodning för alla appar"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Inaktivera omkodning för appar"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Inställningar för medieomkodning"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Inaktivera omkodning"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Aktivera omkodning för appar"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Aktiva tjänster"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Visa och styr aktiva tjänster"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView-implementering"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> kvar till full laddning"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> till full laddning"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – batteriet är tillfälligt begränsat"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Okänd"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Laddar"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Laddas snabbt"</string> diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml index 365b0fd85835..e1060fec2a66 100644 --- a/packages/SettingsLib/res/values-sw/strings.xml +++ b/packages/SettingsLib/res/values-sw/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Haitumika. Gusa ili ugeuze."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Inatumika. Gusa ili ugeuze."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Hali ya kisitisha programu:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Mipangilio ya kubadilisha muundo wa faili ya maudhui"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Washa ubadilishaji muundo wa faili kwenye programu zote"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Zima ubadilishaji muundo wa faili kwenye programu"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Mipangilio ya kubadilisha muundo wa faili ya maudhui"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Zima ubadilishaji muundo wa faili"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Washa ubadilishaji muundo wa faili kwenye programu"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Huduma zinazoendeshwa"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Onyesha na udhibiti huduma zinazoendeshwa kwa sasa"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Utekelezaji wa WebView"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Imebakisha <xliff:g id="TIME">%1$s</xliff:g> ijae chaji"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ijae chaji"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Betri imedhibitiwa kwa muda"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Haijulikani"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Inachaji"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Inachaji kwa kasi"</string> diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index e546ad7ba697..ba98d79ff7e4 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"செயலில் இல்லை. மாற்ற, தட்டவும்."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"செயலில் உள்ளது. மாற்ற, தட்டவும்."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"காத்திருப்பில் உள்ள ஆப்ஸின் நிலை:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"மீடியா குறிமாற்ற அமைப்புகள்"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"அனைத்து ஆப்ஸுக்கும் குறிமாற்றத்தை இயக்கு"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"அனைத்து ஆப்ஸுக்கும் குறிமாற்றத்தை முடக்கு"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"மீடியா குறிமாற்ற அமைப்புகள்"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"குறிமாற்றத்தை முடக்கு"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"ஆப்ஸுக்குக் குறிமாற்றத்தை இயக்கு"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"இயங்கும் சேவைகள்"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"தற்போது இயக்கத்தில் இருக்கும் சேவைகளைப் பார்த்து கட்டுப்படுத்து"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView செயல்படுத்தல்"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"முழு சார்ஜாக <xliff:g id="TIME">%1$s</xliff:g> ஆகும்"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - முழு சார்ஜாக <xliff:g id="TIME">%2$s</xliff:g> ஆகும்"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g>-பேட்டரி தற்காலிகக் கட்டுப்பாட்டிலுள்ளது"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"அறியப்படாத"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"சார்ஜ் ஆகிறது"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"வேகமாக சார்ஜாகிறது"</string> diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index c09d2e142487..0b93979dbd07 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"నిష్క్రియంగా ఉంది. టోగుల్ చేయడానికి నొక్కండి."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"సక్రియంగా ఉంది. టోగుల్ చేయడానికి నొక్కండి."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"యాప్ స్టాండ్బై స్థితి:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"మీడియా ట్రాన్స్కోడింగ్ సెట్టింగ్లు"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"అన్ని యాప్ల కోసం ట్రాన్స్కోడింగ్ని ఎనేబుల్ చేయండి"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"యాప్ల కోసం ట్రాన్స్కోడింగ్ని డిజేబుల్ చేయండి"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"మీడియా ట్రాన్స్కోడింగ్ సెట్టింగ్లు"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"ట్రాన్స్కోడింగ్ను డిజేబుల్ చేయండి"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"యాప్ల కోసం ట్రాన్స్కోడింగ్ను ఎనేబుల్ చేయండి"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"అమలులో ఉన్న సేవలు"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"ప్రస్తుతం అమలులో ఉన్న సేవలను వీక్షించండి మరియు నియంత్రించండి"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"వెబ్ వీక్షణ అమలు"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"ఛార్జ్ అవ్వడానికి <xliff:g id="TIME">%1$s</xliff:g> సమయం మిగిలి ఉంది"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - ఛార్జ్ అవ్వడానికి <xliff:g id="TIME">%2$s</xliff:g> పడుతుంది"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> -బ్యాటరీ తాత్కాలికంగా పరిమితం చేయబడింది"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"తెలియదు"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"ఛార్జ్ అవుతోంది"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"వేగవంతమైన ఛార్జింగ్"</string> diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml index fcfc11eff28e..a31e14611bf6 100644 --- a/packages/SettingsLib/res/values-th/strings.xml +++ b/packages/SettingsLib/res/values-th/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"ไม่ได้ใช้งาน แตะเพื่อสลับ"</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"ใช้งานอยู่ แตะเพื่อสลับ"</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"สถานะการสแตนด์บายของแอป:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"การตั้งค่าการแปลงสื่อ"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"เปิดใช้การแปลงสำหรับแอปทั้งหมด"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"ปิดใช้การแปลงสำหรับแอป"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"การตั้งค่าการแปลงสื่อ"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"ปิดใช้การแปลง"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"เปิดใช้การแปลงสำหรับแอป"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"บริการที่ทำงานอยู่"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"ดูและควบคุมบริการที่ทำงานอยู่"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"การใช้งาน WebView"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"เหลือ <xliff:g id="TIME">%1$s</xliff:g> จนกว่าจะชาร์จเต็ม"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> จนกว่าจะชาร์จ"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - การชาร์จแบตเตอรี่จำกัดชั่วคราว"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"ไม่ทราบ"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"กำลังชาร์จ"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"กำลังชาร์จอย่างเร็ว"</string> diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml index cdd1d621da2c..bbb77790e68a 100644 --- a/packages/SettingsLib/res/values-tl/strings.xml +++ b/packages/SettingsLib/res/values-tl/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Hindi aktibo. I-tap upang i-toggle."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktibo. I-tap upang i-toggle."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Status ng app standby:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Mga setting ng pagta-transcode ng media"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"I-enable ang pagta-transcode para sa lahat ng app"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"I-disable ang pagta-transcode para sa mga app"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Mga setting ng pag-transcode ng media"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"I-disable ang pag-transcode"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"I-enable ang pag-transcode para sa mga app"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Mga tumatakbong serbisyo"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Tingnan at kontrolin ang mga kasalukuyang tumatakbong serbisyo"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Pagpapatupad sa WebView"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> ang natitira bago matapos mag-charge"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> hanggang matapos mag-charge"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Pansamantalang limitado ang baterya"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Hindi Kilala"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Nagcha-charge"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Mabilis na charge"</string> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index 0916d8963b80..fba7f41bbbf5 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Etkin değil. Geçiş yapmak için dokunun."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Etkin. Geçiş yapmak için dokunun."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Uygulamayı beklemeye alma durumu: <xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Medya kod dönüştürme ayarları"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Tüm uygulamalar için kod dönüştürmeyi etkinleştirir"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Seçili uygulamalar için kod dönüştürmeyi devre dışı bırakır"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Medya kod dönüştürme ayarları"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Kod dönüştürmeyi devre dışı bırak"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Uygulamalar için kod dönüştürmeyi etkinleştir"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Çalışan hizmetler"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Şu anda çalışan hizmetleri görüntüle ve denetle"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Web Görünümü kullanımı"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Şarj olmaya <xliff:g id="TIME">%1$s</xliff:g> kaldı"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - şarj olmaya <xliff:g id="TIME">%2$s</xliff:g> kaldı"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Pil geçici olarak sınırlı"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Bilinmiyor"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Şarj oluyor"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Hızlı şarj oluyor"</string> diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml index 1558ce52cda9..b633752d28a9 100644 --- a/packages/SettingsLib/res/values-uk/strings.xml +++ b/packages/SettingsLib/res/values-uk/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Неактивний додаток. Торкніться, щоб активувати."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Активний додаток. Торкніться, щоб дезактивувати."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Режим очікування: <xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Налаштування перекодування медіаконтенту"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Увімкнути перекодування для всіх додатків"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Вимкнути перекодування для додатків"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Налаштування перекодування медіафайлів"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Вимкнути перекодування"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Увімкнути перекодування додатків"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Запущені сервіси"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Переглянути й налаштувати запущені сервіси"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Застосування WebView"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> до повного заряду"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до повного заряду"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – дані акумулятора тимчасово недоступні"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Невідомо"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Заряджається"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Швидке заряджання"</string> diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml index 507f8ccae491..b4d8ad2576bd 100644 --- a/packages/SettingsLib/res/values-ur/strings.xml +++ b/packages/SettingsLib/res/values-ur/strings.xml @@ -399,12 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"غیر فعال۔ ٹوگل کرنے کیلئے تھپتھپائیں۔"</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"فعال۔ ٹوگل کرنے کیلئے تھپتھپائیں۔"</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"ایپ اسٹینڈ بائی کی حالت:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <!-- no translation found for transcode_settings_title (6700974145733932357) --> - <skip /> - <!-- no translation found for transcode_enable_all (4719796495995795404) --> - <skip /> - <!-- no translation found for transcode_skip_apps (5680997722349545778) --> - <skip /> + <string name="transcode_settings_title" msgid="2581975870429850549">"میڈیا ٹرانسکوڈنگ کی ترتیبات"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"ٹرانسکوڈنگ غیر فعال کریں"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"ایپس کے لئے ٹرانسکوڈنگ فعال کریں"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"چل رہی سروسز"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"فی الحال چل رہی سروسز دیکھیں اور انہیں کنٹرول کریں"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView کا نفاذ"</string> @@ -452,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"چارج ہونے میں <xliff:g id="TIME">%1$s</xliff:g> باقی"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> چارج ہونے تک"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - بیٹری عارضی طور پر محدود ہے"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"نامعلوم"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"چارج ہو رہا ہے"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"تیزی سے چارج ہو رہا ہے"</string> diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml index 84207bf99c9f..5bb422ecf96a 100644 --- a/packages/SettingsLib/res/values-uz/strings.xml +++ b/packages/SettingsLib/res/values-uz/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Nofaol. O‘zgartirish uchun bu yerga bosing."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Faol. O‘zgartirish uchun bu yerga bosing."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Kutish rejimi holati: <xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Media transkripsiya sozlamalari"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Barcha ilovalar transkripsiyasini yoqish"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Barcha ilovalar transkripsiyasini faolsizlantirish"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Media transkripsiyasi sozlamalari"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Transkripsiyani faolsizlantirish"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Ilovalar uchun transkripsiyani yoqish"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Ishlab turgan ilovalar"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Ishlab turgan ilovalarni ko‘rish va boshqarish"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView ta’minotchisi"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> ichida toʻladi"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> ichida toʻladi"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Quvvat darajasi vaqtincha cheklangan"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Noma’lum"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Quvvat olmoqda"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Tezkor quvvat olmoqda"</string> diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml index 14a97f3c8f05..0acf9418b718 100644 --- a/packages/SettingsLib/res/values-vi/strings.xml +++ b/packages/SettingsLib/res/values-vi/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Không hoạt động. Nhấn để chuyển đổi."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Hiện hoạt. Nhấn để chuyển đổi."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Trạng thái chờ ứng dụng:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Tùy chọn chuyển mã ứng dụng"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Bật tùy chọn chuyển mã cho tất cả ứng dụng"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Tắt tùy chọn chuyển mã cho ứng dụng"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Cài đặt chuyển mã nội dung nghe nhìn"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Tắt tùy chọn chuyển mã"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Bật tùy chọn chuyển mã cho ứng dụng"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Các dịch vụ đang chạy"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Xem và kiểm soát các dịch vụ đang chạy"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Triển khai WebView"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Còn <xliff:g id="TIME">%1$s</xliff:g> nữa là sạc đầy"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> nữa là sạc đầy"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Thời lượng pin bị hạn chế tạm thời"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Không xác định"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Đang sạc"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Đang sạc nhanh"</string> diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml index 9d312b61e75c..ca2ad87fb864 100644 --- a/packages/SettingsLib/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"未启用。点按即可切换。"</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"已启用。点按即可切换。"</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"应用待机状态:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"媒体转码设置"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"为所有应用启用转码"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"为应用停用转码"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"媒体转码设置"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"停用转码功能"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"为应用启用转码功能"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"正在运行的服务"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"查看和控制当前正在运行的服务"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView 实现"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"还剩 <xliff:g id="TIME">%1$s</xliff:g>充满电"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>后充满电"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - 暂时限用电池"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"未知"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"正在充电"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"正在快速充电"</string> diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml index a8eb24624946..16ca19d313c1 100644 --- a/packages/SettingsLib/res/values-zh-rHK/strings.xml +++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"未啟用。輕按即可切換。"</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"已啟用。輕按即可切換。"</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"備用應用程式狀態:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"媒體轉碼設定"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"為所有應用程式啟用轉碼功能"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"為所有應用程式停用轉碼功能"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"媒體轉碼設定"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"停用轉碼功能"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"為應用程式啟用轉碼功能"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"執行中的服務"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"查看並控制目前正在執行中的服務"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView 設置"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"還需 <xliff:g id="TIME">%1$s</xliff:g>才能充滿電"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - 還需 <xliff:g id="TIME">%2$s</xliff:g>才能充滿電"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - 暫時限制電池充電"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"未知"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"充電中"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"正在快速充電"</string> diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index 73692e46605d..810119b97777 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"未啟用。輕觸即可切換。"</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"已啟用。輕觸即可切換。"</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"應用程式待命狀態:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"媒體轉碼設定"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"替所有應用程式啟用轉碼功能"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"替應用程式停用轉碼功能"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"媒體轉碼功能設定"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"停用轉碼功能"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"替應用程式啟用轉碼功能"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"正在運作的服務"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"查看並管理目前正在執行的服務"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView 實作"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g>後充飽電"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>後充飽電"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - 暫時限制電池用量"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"不明"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"充電中"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"快速充電中"</string> diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml index ead2800b208a..23beeb1aee82 100644 --- a/packages/SettingsLib/res/values-zu/strings.xml +++ b/packages/SettingsLib/res/values-zu/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Akusebenzi. Thepha ukuze ushintshe."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Kuyasebenza. Thepha ukuze ushintshe."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Isimo sokulinda kohlelo lokusebenza:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="6700974145733932357">"Amasethingi emidiya yokudlulisela ikhodi"</string> - <string name="transcode_enable_all" msgid="4719796495995795404">"Nikela amandla ukudlulisela ikhodi kuzonke izinhlelo zokusebenza"</string> - <string name="transcode_skip_apps" msgid="5680997722349545778">"Khubaza ukudlulisela ikhodi kwezinhlelo zokusebenza"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"Amasethingi wemidiya yokudlulisela ikhodi"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Khubaza ukudlulisela ikhodi"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Nika amandla ukudlulisela ikhodi kwezinhlelo zokusebenza"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Amasevisi asebenzayo"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Buka futhi ulawule amasevisi asebenzayo okwamanje"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Ukufakwa ke-WebView"</string> @@ -449,6 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> esele ize ishaje"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ize igcwale"</string> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Ibhethri ikhawulelwe okwesikhashana"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Akwaziwa"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Iyashaja"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Ishaja ngokushesha"</string> diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index cea7903b8726..9f1c96d88905 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -1109,6 +1109,8 @@ <string name="power_remaining_charging_duration_only"><xliff:g id="time">%1$s</xliff:g> left until charged</string> <!-- [CHAR_LIMIT=40] Label for battery level chart when charging with duration --> <string name="power_charging_duration"><xliff:g id="level">%1$s</xliff:g> - <xliff:g id="time">%2$s</xliff:g> until charged</string> + <!-- [CHAR_LIMIT=40] Label for battery level chart when charge been limited --> + <string name="power_charging_limited"><xliff:g id="level">%1$s</xliff:g> - Battery limited temporarily</string> <!-- Battery Info screen. Value for a status item. Used for diagnostic info screens, precise translation isn't needed --> <string name="battery_info_status_unknown">Unknown</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java index 59d8acb82196..8fd1910041c8 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java @@ -49,6 +49,7 @@ import java.util.concurrent.CopyOnWriteArrayList; */ public class BluetoothEventManager { private static final String TAG = "BluetoothEventManager"; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private final LocalBluetoothAdapter mLocalAdapter; private final CachedBluetoothDeviceManager mDeviceManager; @@ -366,6 +367,9 @@ public class BluetoothEventManager { * BluetoothDevice.UNBOND_REASON_* */ private void showUnbondMessage(Context context, String name, int reason) { + if (DEBUG) { + Log.d(TAG, "showUnbondMessage() name : " + name + ", reason : " + reason); + } int errorMsg; switch (reason) { @@ -382,6 +386,7 @@ public class BluetoothEventManager { case BluetoothDevice.UNBOND_REASON_AUTH_TIMEOUT: case BluetoothDevice.UNBOND_REASON_REPEATED_ATTEMPTS: case BluetoothDevice.UNBOND_REASON_REMOTE_AUTH_CANCELED: + case BluetoothDevice.UNBOND_REASON_REMOVED: errorMsg = R.string.bluetooth_pairing_error_message; break; default: diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java index ba1dc64ce1e6..6a4d650ebf31 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java @@ -35,6 +35,8 @@ import android.content.IntentFilter; import android.os.UserHandle; import android.telephony.TelephonyManager; +import com.android.settingslib.R; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -49,6 +51,8 @@ import java.util.List; @RunWith(RobolectricTestRunner.class) public class BluetoothEventManagerTest { + private static final String DEVICE_NAME = "test_device_name"; + @Mock private LocalBluetoothAdapter mLocalAdapter; @Mock @@ -71,6 +75,8 @@ public class BluetoothEventManagerTest { private BluetoothDevice mDevice2; @Mock private LocalBluetoothProfileManager mLocalProfileManager; + @Mock + private BluetoothUtils.ErrorListener mErrorListener; private Context mContext; private Intent mIntent; @@ -92,6 +98,7 @@ public class BluetoothEventManagerTest { mCachedDevice1 = new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice1); mCachedDevice2 = new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice2); + BluetoothUtils.setErrorListener(mErrorListener); } @Test @@ -344,4 +351,80 @@ public class BluetoothEventManagerTest { assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse(); assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.HEARING_AID)).isFalse(); } + + @Test + public void showUnbondMessage_reasonRemoved_showCorrectedErrorCode() { + mIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED); + mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice); + mIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE); + mIntent.putExtra(BluetoothDevice.EXTRA_REASON, BluetoothDevice.UNBOND_REASON_REMOVED); + when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedDevice1); + when(mCachedDevice1.getName()).thenReturn(DEVICE_NAME); + + mContext.sendBroadcast(mIntent); + + verify(mErrorListener).onShowError(any(Context.class), eq(DEVICE_NAME), + eq(R.string.bluetooth_pairing_error_message)); + } + + @Test + public void showUnbondMessage_reasonAuthTimeout_showCorrectedErrorCode() { + mIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED); + mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice); + mIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE); + mIntent.putExtra(BluetoothDevice.EXTRA_REASON, BluetoothDevice.UNBOND_REASON_AUTH_TIMEOUT); + when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedDevice1); + when(mCachedDevice1.getName()).thenReturn(DEVICE_NAME); + + mContext.sendBroadcast(mIntent); + + verify(mErrorListener).onShowError(any(Context.class), eq(DEVICE_NAME), + eq(R.string.bluetooth_pairing_error_message)); + } + + @Test + public void showUnbondMessage_reasonRemoteDeviceDown_showCorrectedErrorCode() { + mIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED); + mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice); + mIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE); + mIntent.putExtra(BluetoothDevice.EXTRA_REASON, + BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN); + when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedDevice1); + when(mCachedDevice1.getName()).thenReturn(DEVICE_NAME); + + mContext.sendBroadcast(mIntent); + + verify(mErrorListener).onShowError(any(Context.class), eq(DEVICE_NAME), + eq(R.string.bluetooth_pairing_device_down_error_message)); + } + + @Test + public void showUnbondMessage_reasonAuthRejected_showCorrectedErrorCode() { + mIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED); + mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice); + mIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE); + mIntent.putExtra(BluetoothDevice.EXTRA_REASON, BluetoothDevice.UNBOND_REASON_AUTH_REJECTED); + when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedDevice1); + when(mCachedDevice1.getName()).thenReturn(DEVICE_NAME); + + mContext.sendBroadcast(mIntent); + + verify(mErrorListener).onShowError(any(Context.class), eq(DEVICE_NAME), + eq(R.string.bluetooth_pairing_rejected_error_message)); + } + + @Test + public void showUnbondMessage_reasonAuthFailed_showCorrectedErrorCode() { + mIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED); + mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice); + mIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE); + mIntent.putExtra(BluetoothDevice.EXTRA_REASON, BluetoothDevice.UNBOND_REASON_AUTH_FAILED); + when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedDevice1); + when(mCachedDevice1.getName()).thenReturn(DEVICE_NAME); + + mContext.sendBroadcast(mIntent); + + verify(mErrorListener).onShowError(any(Context.class), eq(DEVICE_NAME), + eq(R.string.bluetooth_pairing_pin_error_message)); + } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/emergencynumber/EmergencyNumberUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/emergencynumber/EmergencyNumberUtilsTest.java new file mode 100644 index 000000000000..140363127dff --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/emergencynumber/EmergencyNumberUtilsTest.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.emergencynumber; + +import static android.telephony.emergency.EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.pm.PackageManager; +import android.provider.Settings; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; +import android.telephony.emergency.EmergencyNumber; +import android.util.ArrayMap; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@RunWith(RobolectricTestRunner.class) +public class EmergencyNumberUtilsTest { + private static final String TELEPHONY_EMERGENCY_NUMBER = "1234"; + private static final String USER_OVERRIDE_EMERGENCY_NUMBER = "5678"; + + @Mock + private Context mContext; + @Mock + private PackageManager mPackageManager; + @Mock + private TelephonyManager mTelephonyManager; + private EmergencyNumberUtils mUtils; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + when(mContext.getPackageManager()).thenReturn(mPackageManager); + } + + @Test + public void getDefaultPoliceNumber_noTelephony_shouldReturnDefault() { + when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)).thenReturn(false); + mUtils = new EmergencyNumberUtils(mContext); + + assertThat(mUtils.getDefaultPoliceNumber()).isEqualTo( + EmergencyNumberUtils.FALL_BACK_NUMBER); + } + + @Test + public void getDefaultPoliceNumber_hasTelephony_shouldLoadFromTelephony() { + when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)).thenReturn(true); + when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager); + addEmergencyNumberToTelephony(); + mUtils = new EmergencyNumberUtils(mContext); + + + assertThat(mUtils.getDefaultPoliceNumber()).isEqualTo(TELEPHONY_EMERGENCY_NUMBER); + } + + @Test + public void getPoliceNumber_hasUserOverride_shouldLoadFromUserOverride() { + when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)).thenReturn(true); + when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager); + addEmergencyNumberToTelephony(); + + ContentResolver resolver = RuntimeEnvironment.application.getContentResolver(); + when(mContext.getContentResolver()).thenReturn(resolver); + Settings.Secure.putString(resolver, Settings.Secure.EMERGENCY_GESTURE_CALL_NUMBER, + USER_OVERRIDE_EMERGENCY_NUMBER); + + mUtils = new EmergencyNumberUtils(mContext); + + assertThat(mUtils.getPoliceNumber()).isEqualTo(USER_OVERRIDE_EMERGENCY_NUMBER); + } + + @Test + public void getPoliceNumber_noUserOverride_shouldLoadFromTelephony() { + when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)).thenReturn(true); + when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager); + addEmergencyNumberToTelephony(); + + mUtils = new EmergencyNumberUtils(mContext); + + assertThat(mUtils.getPoliceNumber()).isEqualTo(TELEPHONY_EMERGENCY_NUMBER); + } + + private void addEmergencyNumberToTelephony() { + final int subId = SubscriptionManager.getDefaultSubscriptionId(); + EmergencyNumber emergencyNumber = mock(EmergencyNumber.class); + Map<Integer, List<EmergencyNumber>> numbers = new ArrayMap<>(); + List<EmergencyNumber> numbersForSubId = new ArrayList<>(); + numbersForSubId.add(emergencyNumber); + numbers.put(subId, numbersForSubId); + when(mTelephonyManager.getEmergencyNumberList( + EMERGENCY_SERVICE_CATEGORY_POLICE)).thenReturn(numbers); + when(emergencyNumber.getNumber()).thenReturn(TELEPHONY_EMERGENCY_NUMBER); + } +} diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BannerMessagePreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BannerMessagePreferenceTest.java new file mode 100644 index 000000000000..8ab02983bfc2 --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BannerMessagePreferenceTest.java @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.widget; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; +import android.view.View; +import android.widget.Button; +import android.widget.TextView; + +import androidx.preference.PreferenceViewHolder; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +@RunWith(RobolectricTestRunner.class) +public class BannerMessagePreferenceTest { + + private Context mContext; + private View mRootView; + private BannerMessagePreference mBannerPreference; + private PreferenceViewHolder mHolder; + + @Before + public void setUp() { + mContext = RuntimeEnvironment.application; + mRootView = View.inflate(mContext, R.layout.banner_message, null /* parent */); + mHolder = PreferenceViewHolder.createInstanceForTests(mRootView); + mBannerPreference = new BannerMessagePreference(mContext); + } + + @Test + public void onBindViewHolder_shouldSetTitle() { + mBannerPreference.setTitle("test"); + + mBannerPreference.onBindViewHolder(mHolder); + + assertThat(((TextView) mRootView.findViewById(R.id.banner_title)).getText()) + .isEqualTo("test"); + } + + @Test + public void onBindViewHolder_shouldSetSummary() { + mBannerPreference.setSummary("test"); + + mBannerPreference.onBindViewHolder(mHolder); + + assertThat(((TextView) mRootView.findViewById(R.id.banner_summary)).getText()) + .isEqualTo("test"); + } + + @Test + public void setPositiveButtonText_shouldShowPositiveButton() { + mBannerPreference.setPositiveButtonText(R.string.tts_settings_title); + + mBannerPreference.onBindViewHolder(mHolder); + + assertThat(((Button) mRootView.findViewById(R.id.banner_positive_btn)).getVisibility()) + .isEqualTo(View.VISIBLE); + } + + @Test + public void setNegativeButtonText_shouldShowNegativeButton() { + mBannerPreference.setNegativeButtonText(R.string.tts_settings_title); + + mBannerPreference.onBindViewHolder(mHolder); + + assertThat(((Button) mRootView.findViewById(R.id.banner_negative_btn)).getVisibility()) + .isEqualTo(View.VISIBLE); + } + + @Test + public void withoutSetPositiveButtonText_shouldHidePositiveButton() { + mBannerPreference.onBindViewHolder(mHolder); + + assertThat(((Button) mRootView.findViewById(R.id.banner_positive_btn)).getVisibility()) + .isEqualTo(View.GONE); + } + + @Test + public void withoutSetNegativeButtonText_shouldHideNegativeButton() { + mBannerPreference.onBindViewHolder(mHolder); + + assertThat(((Button) mRootView.findViewById(R.id.banner_negative_btn)).getVisibility()) + .isEqualTo(View.GONE); + } + + @Test + public void setPositiveButtonVisible_withTrue_shouldShowPositiveButton() { + mBannerPreference.setPositiveButtonText(R.string.tts_settings_title); + + mBannerPreference.setPositiveButtonVisible(true); + mBannerPreference.onBindViewHolder(mHolder); + + assertThat(((Button) mRootView.findViewById(R.id.banner_positive_btn)).getVisibility()) + .isEqualTo(View.VISIBLE); + } + + @Test + public void setPositiveButtonVisible_withFalse_shouldHidePositiveButton() { + mBannerPreference.setPositiveButtonText(R.string.tts_settings_title); + + mBannerPreference.setPositiveButtonVisible(false); + mBannerPreference.onBindViewHolder(mHolder); + + assertThat(((Button) mRootView.findViewById(R.id.banner_positive_btn)).getVisibility()) + .isEqualTo(View.GONE); + } + + @Test + public void setNegativeButtonVisible_withTrue_shouldShowNegativeButton() { + mBannerPreference.setNegativeButtonText(R.string.tts_settings_title); + + mBannerPreference.setNegativeButtonVisible(true); + mBannerPreference.onBindViewHolder(mHolder); + + assertThat(((Button) mRootView.findViewById(R.id.banner_negative_btn)).getVisibility()) + .isEqualTo(View.VISIBLE); + } + + @Test + public void setNegativeButtonVisible_withFalse_shouldHideNegativeButton() { + mBannerPreference.setNegativeButtonText(R.string.tts_settings_title); + + mBannerPreference.setNegativeButtonVisible(false); + mBannerPreference.onBindViewHolder(mHolder); + + assertThat(((Button) mRootView.findViewById(R.id.banner_negative_btn)).getVisibility()) + .isEqualTo(View.GONE); + } +} diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index a97af4bbe324..2a699ea45abe 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -146,6 +146,7 @@ <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS" /> <uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS" /> <uses-permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" /> + <uses-permission android:name="android.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND" /> <uses-permission android:name="android.permission.ACTIVITY_EMBEDDING" /> <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" /> <uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" /> diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index 014d73f281cc..2ea0c2294f76 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -33,6 +33,13 @@ java_library { srcs: ["src/com/android/systemui/EventLogTags.logtags"], } +java_library { + name: "SystemUI-sensors", + srcs: [ + "src/com/android/systemui/util/sensors/ThresholdSensor.java", + ] +} + android_library { name: "SystemUI-core", srcs: [ diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 3de0fbdf8fb1..7120cc21c821 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -623,8 +623,7 @@ <service android:name=".keyguard.KeyguardService" - android:exported="true" - android:enabled="@bool/config_enableKeyguardService" /> + android:exported="true" /> <activity android:name=".keyguard.WorkLockActivity" android:label="@string/accessibility_desc_work_lock" diff --git a/packages/SystemUI/plugin/Android.bp b/packages/SystemUI/plugin/Android.bp index df5561acbbc4..ab4f800d2586 100644 --- a/packages/SystemUI/plugin/Android.bp +++ b/packages/SystemUI/plugin/Android.bp @@ -19,7 +19,8 @@ java_library { srcs: ["src/**/*.java"], static_libs: [ - "PluginCoreLib" + "PluginCoreLib", + "SystemUI-sensors", ], } diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java index 63f8b1f5dbb8..6e86f268a725 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java @@ -20,7 +20,9 @@ import android.net.Uri; import android.view.MotionEvent; import com.android.systemui.plugins.annotations.ProvidesInterface; +import com.android.systemui.util.sensors.ThresholdSensor; +import java.io.FileDescriptor; import java.io.PrintWriter; /** @@ -30,80 +32,66 @@ import java.io.PrintWriter; */ @ProvidesInterface(version = FalsingManager.VERSION) public interface FalsingManager { - int VERSION = 5; + int VERSION = 6; void onSuccessfulUnlock(); - void onNotificationActive(); - - void setShowingAod(boolean showingAod); - - void onNotificatonStartDraggingDown(); - boolean isUnlockingDisabled(); /** Returns true if the gesture should be rejected. */ boolean isFalseTouch(int interactionType); - void onNotificatonStopDraggingDown(); - - void setNotificationExpanded(); + /** + * Returns true if the FalsingManager thinks the last gesure was not a valid tap. + * + * Accepts one parameter, robustCheck, that distinctly changes behavior. When set to false, + * this method simply looks at the last gesture and returns whether it is a tap or not, (as + * opposed to a swipe or other non-tap gesture). When set to true, a more thorough analysis + * is performed that can include historical interactions and other contextual cues to see + * if the tap looks accidental. + * + * Set robustCheck to true if you want to validate a tap for launching an action, like opening + * a notification. Set to false if you simply want to know if the last gesture looked like a + * tap. + */ + boolean isFalseTap(boolean robustCheck); + + /** + * Returns true if the last two gestures do not look like a double tap. + * + * Only works on data that has already been reported to the FalsingManager. Be sure that + * {@link #onTouchEvent(MotionEvent, int, int)} has already been called for all of the + * taps you want considered. + * + * This looks at the last two gestures on the screen, ensuring that they meet the following + * criteria: + * + * a) There are at least two gestures. + * b) The last two gestures look like taps. + * c) The last two gestures look like a double tap taken together. + * + * This method is _not_ context aware. That is to say, if two taps occur on two neighboring + * views, but are otherwise close to one another, this will report a successful double tap. + * It is up to the caller to decide + * @return + */ + boolean isFalseDoubleTap(); boolean isClassifierEnabled(); - void onQsDown(); - - void setQsExpanded(boolean expanded); - boolean shouldEnforceBouncer(); - void onTrackingStarted(boolean secure); - - void onTrackingStopped(); - - void onLeftAffordanceOn(); - - void onCameraOn(); - - void onAffordanceSwipingStarted(boolean rightCorner); - - void onAffordanceSwipingAborted(); - - void onStartExpandingFromPulse(); - - void onExpansionFromPulseStopped(); - Uri reportRejectedTouch(); - void onScreenOnFromTouch(); - boolean isReportingEnabled(); - void onUnlockHintStarted(); - - void onCameraHintStarted(); - - void onLeftAffordanceHintStarted(); - - void onScreenTurningOn(); - - void onScreenOff(); - - void onNotificationStopDismissing(); - - void onNotificationDismissed(); - - void onNotificationStartDismissing(); - - void onNotificationDoubleTap(boolean accepted, float dx, float dy); - - void onBouncerShown(); - - void onBouncerHidden(); - void onTouchEvent(MotionEvent ev, int width, int height); - void dump(PrintWriter pw); + /** From com.android.systemui.Dumpable. */ + void dump(FileDescriptor fd, PrintWriter pw, String[] args); void cleanup(); + + /** Call to report a ProximityEvent to the FalsingManager. */ + void onProximityEvent(ThresholdSensor.ThresholdSensorEvent proximityEvent); } diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml index 7816d6252267..c82bda6620bd 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml @@ -82,6 +82,28 @@ android:elegantTextHeight="false" /> </FrameLayout> + <FrameLayout + android:id="@+id/new_lockscreen_clock_view_large" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_below="@id/keyguard_status_area" + android:paddingTop="20dp" + android:visibility="gone"> + <com.android.keyguard.AnimatableClockView + android:id="@+id/animatable_clock_view_large" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" + android:gravity="center_horizontal" + android:textSize="180dp" + android:letterSpacing="0.02" + android:lineSpacingMultiplier=".8" + android:includeFontPadding="false" + android:fontFamily="@font/clock" + android:typeface="monospace" + android:elegantTextHeight="false" + /> + </FrameLayout> <include layout="@layout/keyguard_status_area" android:id="@+id/keyguard_status_area" android:layout_width="match_parent" diff --git a/packages/SystemUI/res-keyguard/values-af/strings.xml b/packages/SystemUI/res-keyguard/values-af/strings.xml index 92dd9fd96111..276fa23e8578 100644 --- a/packages/SystemUI/res-keyguard/values-af/strings.xml +++ b/packages/SystemUI/res-keyguard/values-af/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laai tans"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laai tans vinnig"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laai tans stadig"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Koppel jou laaier."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Druk Kieslys om te ontsluit."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Netwerk is gesluit"</string> diff --git a/packages/SystemUI/res-keyguard/values-am/strings.xml b/packages/SystemUI/res-keyguard/values-am/strings.xml index f94c20f9ad01..ae5d1f675c52 100644 --- a/packages/SystemUI/res-keyguard/values-am/strings.xml +++ b/packages/SystemUI/res-keyguard/values-am/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ኃይል በመሙላት ላይ"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • በፍጥነት ኃይልን በመሙላት ላይ"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • በዝግታ ኃይልን በመሙላት ላይ"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"ኃይል መሙያዎን ያያይዙ።"</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"ለመክፈት ምናሌ ተጫን።"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"አውታረ መረብ ተቆልፏል"</string> diff --git a/packages/SystemUI/res-keyguard/values-ar/strings.xml b/packages/SystemUI/res-keyguard/values-ar/strings.xml index 65e3f0dbd176..fb03fe340c3a 100644 --- a/packages/SystemUI/res-keyguard/values-ar/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ar/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • جارٍ الشحن"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • جارٍ الشحن سريعًا"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • جارٍ الشحن ببطء"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"توصيل جهاز الشحن."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"اضغط على \"القائمة\" لإلغاء التأمين."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"الشبكة مؤمّنة"</string> diff --git a/packages/SystemUI/res-keyguard/values-as/strings.xml b/packages/SystemUI/res-keyguard/values-as/strings.xml index 3b51e480b7dd..8e8b13d0a160 100644 --- a/packages/SystemUI/res-keyguard/values-as/strings.xml +++ b/packages/SystemUI/res-keyguard/values-as/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • চ্চার্জ কৰি থকা হৈছে"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • দ্ৰুত গতিৰে চ্চাৰ্জ কৰি থকা হৈছে"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • লাহে লাহে চ্চাৰ্জ কৰি থকা হৈছে"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"আপোনাৰ চ্চার্জাৰ সংযোগ কৰক।"</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"আনলক কৰিবলৈ মেনু টিপক।"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"নেটৱর্ক লক কৰা অৱস্থাত আছে"</string> diff --git a/packages/SystemUI/res-keyguard/values-az/strings.xml b/packages/SystemUI/res-keyguard/values-az/strings.xml index ea07c3db4354..f9bff73a671f 100644 --- a/packages/SystemUI/res-keyguard/values-az/strings.xml +++ b/packages/SystemUI/res-keyguard/values-az/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Enerji yığır"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Sürətlə enerji yığır"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Yavaş enerji yığır"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Adapteri qoşun."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Kilidi açmaq üçün Menyu düyməsinə basın."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Şəbəkə kilidlidir"</string> diff --git a/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml b/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml index e206958d1e95..3434ac67bb73 100644 --- a/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Puni se"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Brzo se puni"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Sporo se puni"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Priključite punjač."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Pritisnite Meni da biste otključali."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Mreža je zaključana"</string> diff --git a/packages/SystemUI/res-keyguard/values-be/strings.xml b/packages/SystemUI/res-keyguard/values-be/strings.xml index 569e705fbcc2..07d682f69f56 100644 --- a/packages/SystemUI/res-keyguard/values-be/strings.xml +++ b/packages/SystemUI/res-keyguard/values-be/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ідзе зарадка"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ідзе хуткая зарадка"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ідзе павольная зарадка"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Падключыце зарадную прыладу."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Націсніце кнопку \"Меню\", каб разблакіраваць."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Сетка заблакіравана"</string> diff --git a/packages/SystemUI/res-keyguard/values-bg/strings.xml b/packages/SystemUI/res-keyguard/values-bg/strings.xml index d015be320e20..3890c11f9450 100644 --- a/packages/SystemUI/res-keyguard/values-bg/strings.xml +++ b/packages/SystemUI/res-keyguard/values-bg/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Зарежда се"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Зарежда се бързо"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Зарежда се бавно"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Свържете зарядното си устройство."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Натиснете „Меню“, за да отключите."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Мрежата е заключена"</string> diff --git a/packages/SystemUI/res-keyguard/values-bn/strings.xml b/packages/SystemUI/res-keyguard/values-bn/strings.xml index 8eae6e6e2e18..f9163747aff7 100644 --- a/packages/SystemUI/res-keyguard/values-bn/strings.xml +++ b/packages/SystemUI/res-keyguard/values-bn/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • চার্জ হচ্ছে"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • দ্রুত চার্জ হচ্ছে"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ধীরে চার্জ হচ্ছে"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"আপনার চার্জার সংযুক্ত করুন।"</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"আনলক করতে মেনুতে টিপুন।"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"নেটওয়ার্ক লক করা আছে"</string> diff --git a/packages/SystemUI/res-keyguard/values-bs/strings.xml b/packages/SystemUI/res-keyguard/values-bs/strings.xml index 286b08be1c5e..59a881db8e33 100644 --- a/packages/SystemUI/res-keyguard/values-bs/strings.xml +++ b/packages/SystemUI/res-keyguard/values-bs/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Punjenje"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Brzo punjenje"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Sporo punjenje"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Priključite punjač."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Pritisnite meni da otključate."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Mreža je zaključana"</string> diff --git a/packages/SystemUI/res-keyguard/values-ca/strings.xml b/packages/SystemUI/res-keyguard/values-ca/strings.xml index cb7fa37b281d..82d63c157441 100644 --- a/packages/SystemUI/res-keyguard/values-ca/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ca/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • S\'està carregant"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • S\'està carregant ràpidament"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • S\'està carregant lentament"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Connecta el carregador."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Prem Menú per desbloquejar."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"La xarxa està bloquejada"</string> diff --git a/packages/SystemUI/res-keyguard/values-cs/strings.xml b/packages/SystemUI/res-keyguard/values-cs/strings.xml index 4f0c0ffa4962..a4e653f19678 100644 --- a/packages/SystemUI/res-keyguard/values-cs/strings.xml +++ b/packages/SystemUI/res-keyguard/values-cs/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Nabíjení"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Rychlé nabíjení"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Pomalé nabíjení"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Připojte dobíjecí zařízení."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Klávesy odemknete stisknutím tlačítka nabídky."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Síť je blokována"</string> diff --git a/packages/SystemUI/res-keyguard/values-da/strings.xml b/packages/SystemUI/res-keyguard/values-da/strings.xml index e486fc625699..604852cfeb45 100644 --- a/packages/SystemUI/res-keyguard/values-da/strings.xml +++ b/packages/SystemUI/res-keyguard/values-da/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Oplader"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Oplader hurtigt"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Oplader langsomt"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Tilslut din oplader."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Tryk på menuen for at låse op."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Netværket er låst"</string> diff --git a/packages/SystemUI/res-keyguard/values-de/strings.xml b/packages/SystemUI/res-keyguard/values-de/strings.xml index 06d012f4e84d..0e2ea136f263 100644 --- a/packages/SystemUI/res-keyguard/values-de/strings.xml +++ b/packages/SystemUI/res-keyguard/values-de/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Wird geladen"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Wird schnell geladen"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Wird langsam geladen"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Ladegerät anschließen."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Zum Entsperren die Menütaste drücken."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Netzwerk gesperrt"</string> diff --git a/packages/SystemUI/res-keyguard/values-el/strings.xml b/packages/SystemUI/res-keyguard/values-el/strings.xml index 176428421476..90e255031dd1 100644 --- a/packages/SystemUI/res-keyguard/values-el/strings.xml +++ b/packages/SystemUI/res-keyguard/values-el/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Φόρτιση"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Γρήγορη φόρτιση"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Αργή φόρτιση"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Συνδέστε τον φορτιστή."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Πατήστε \"Μενού\" για ξεκλείδωμα."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Κλειδωμένο δίκτυο"</string> diff --git a/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml b/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml index 92a15949a8ab..7b0c638dcdf3 100644 --- a/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml +++ b/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml @@ -38,6 +38,7 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging rapidly"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging slowly"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="8190982388514496109">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Battery limited temporarily"</string> <string name="keyguard_low_battery" msgid="1868012396800230904">"Connect your charger."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Press Menu to unlock."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Network locked"</string> diff --git a/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml b/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml index 719f1a18a744..2ef720e44735 100644 --- a/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml +++ b/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml @@ -38,6 +38,7 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging rapidly"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging slowly"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="8190982388514496109">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Battery limited temporarily"</string> <string name="keyguard_low_battery" msgid="1868012396800230904">"Connect your charger."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Press Menu to unlock."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Network locked"</string> diff --git a/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml b/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml index 92a15949a8ab..7b0c638dcdf3 100644 --- a/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml +++ b/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml @@ -38,6 +38,7 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging rapidly"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging slowly"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="8190982388514496109">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Battery limited temporarily"</string> <string name="keyguard_low_battery" msgid="1868012396800230904">"Connect your charger."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Press Menu to unlock."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Network locked"</string> diff --git a/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml b/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml index 92a15949a8ab..7b0c638dcdf3 100644 --- a/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml +++ b/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml @@ -38,6 +38,7 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging rapidly"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging slowly"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="8190982388514496109">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Battery limited temporarily"</string> <string name="keyguard_low_battery" msgid="1868012396800230904">"Connect your charger."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Press Menu to unlock."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Network locked"</string> diff --git a/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml b/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml index 975b1f644ef8..471ef8ba83d9 100644 --- a/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml +++ b/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml @@ -38,6 +38,7 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging rapidly"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging slowly"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="8190982388514496109">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Battery limited temporarily"</string> <string name="keyguard_low_battery" msgid="1868012396800230904">"Connect your charger."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Press Menu to unlock."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Network locked"</string> diff --git a/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml b/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml index 25ab6159998e..a5750b550295 100644 --- a/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml +++ b/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando rápidamente"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando lentamente"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Conecta tu cargador."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Presiona Menú para desbloquear."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Bloqueada para la red"</string> diff --git a/packages/SystemUI/res-keyguard/values-es/strings.xml b/packages/SystemUI/res-keyguard/values-es/strings.xml index 0754681215cc..69488da11576 100644 --- a/packages/SystemUI/res-keyguard/values-es/strings.xml +++ b/packages/SystemUI/res-keyguard/values-es/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando rápidamente"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando lentamente"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Conecta el cargador."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Pulsa el menú para desbloquear la pantalla."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Bloqueada para la red"</string> diff --git a/packages/SystemUI/res-keyguard/values-et/strings.xml b/packages/SystemUI/res-keyguard/values-et/strings.xml index 331a95c73c5e..948e134e68ec 100644 --- a/packages/SystemUI/res-keyguard/values-et/strings.xml +++ b/packages/SystemUI/res-keyguard/values-et/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laadimine"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Kiirlaadimine"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Aeglane laadimine"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Ühendage laadija."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Vajutage avamiseks menüüklahvi."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Võrk on lukus"</string> diff --git a/packages/SystemUI/res-keyguard/values-eu/strings.xml b/packages/SystemUI/res-keyguard/values-eu/strings.xml index 3ff224b9e55a..b1aa206364a4 100644 --- a/packages/SystemUI/res-keyguard/values-eu/strings.xml +++ b/packages/SystemUI/res-keyguard/values-eu/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Kargatzen"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Bizkor kargatzen"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mantso kargatzen"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Konektatu kargagailua."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Desblokeatzeko, sakatu Menua."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Sarea blokeatuta dago"</string> diff --git a/packages/SystemUI/res-keyguard/values-fa/strings.xml b/packages/SystemUI/res-keyguard/values-fa/strings.xml index 5e696369634e..8fecdd3ee980 100644 --- a/packages/SystemUI/res-keyguard/values-fa/strings.xml +++ b/packages/SystemUI/res-keyguard/values-fa/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • درحال شارژ شدن"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • درحال شارژ سریع"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • آهستهآهسته شارژ میشود"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"شارژر را وصل کنید."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"برای باز کردن قفل روی «منو» فشار دهید."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"شبکه قفل شد"</string> diff --git a/packages/SystemUI/res-keyguard/values-fi/strings.xml b/packages/SystemUI/res-keyguard/values-fi/strings.xml index 54bc4d8cba58..c47de5dc8cf7 100644 --- a/packages/SystemUI/res-keyguard/values-fi/strings.xml +++ b/packages/SystemUI/res-keyguard/values-fi/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ladataan"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ladataan nopeasti"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ladataan hitaasti"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Kytke laturi."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Poista lukitus painamalla Valikkoa."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Verkko lukittu"</string> diff --git a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml index 2eafc2faa21a..b330452f5bbe 100644 --- a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"En recharge : <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"En recharge rapide : <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"En recharge lente : <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Branchez votre chargeur."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Appuyez sur la touche Menu pour déverrouiller l\'appareil."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Réseau verrouillé"</string> diff --git a/packages/SystemUI/res-keyguard/values-fr/strings.xml b/packages/SystemUI/res-keyguard/values-fr/strings.xml index 824ea41811f7..0c13c0f0cb0d 100644 --- a/packages/SystemUI/res-keyguard/values-fr/strings.xml +++ b/packages/SystemUI/res-keyguard/values-fr/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Recharge…"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Recharge rapide…"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Recharge lente…"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Branchez votre chargeur."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Appuyez sur \"Menu\" pour déverrouiller le clavier."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Réseau verrouillé"</string> diff --git a/packages/SystemUI/res-keyguard/values-gl/strings.xml b/packages/SystemUI/res-keyguard/values-gl/strings.xml index 09488718fd1a..601b2e23aba1 100644 --- a/packages/SystemUI/res-keyguard/values-gl/strings.xml +++ b/packages/SystemUI/res-keyguard/values-gl/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando rapidamente"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando lentamente"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Conecta o cargador."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Preme Menú para desbloquear."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Bloqueada pola rede"</string> diff --git a/packages/SystemUI/res-keyguard/values-gu/strings.xml b/packages/SystemUI/res-keyguard/values-gu/strings.xml index b02d3d97b39f..296124fc743d 100644 --- a/packages/SystemUI/res-keyguard/values-gu/strings.xml +++ b/packages/SystemUI/res-keyguard/values-gu/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ચાર્જિંગ"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ઝડપથી ચાર્જિંગ"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ધીમેથી ચાર્જિંગ"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"તમારું ચાર્જર કનેક્ટ કરો."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"અનલૉક કરવા માટે મેનૂ દબાવો."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"નેટવર્ક લૉક થયું"</string> diff --git a/packages/SystemUI/res-keyguard/values-hi/strings.xml b/packages/SystemUI/res-keyguard/values-hi/strings.xml index f6b15de0e97e..c1c9dd57a64f 100644 --- a/packages/SystemUI/res-keyguard/values-hi/strings.xml +++ b/packages/SystemUI/res-keyguard/values-hi/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • चार्ज हो रहा है"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • तेज़ चार्ज हो रहा है"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • धीरे चार्ज हो रहा है"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"अपना चार्जर कनेक्ट करें."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"लॉक खोलने के लिए मेन्यू दबाएं."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"नेटवर्क लॉक किया हुआ है"</string> diff --git a/packages/SystemUI/res-keyguard/values-hr/strings.xml b/packages/SystemUI/res-keyguard/values-hr/strings.xml index 49db3f88669a..1b51b33c9843 100644 --- a/packages/SystemUI/res-keyguard/values-hr/strings.xml +++ b/packages/SystemUI/res-keyguard/values-hr/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • punjenje"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • brzo punjenje"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • sporo punjenje"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Priključite punjač."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Pritisnite Izbornik da biste otključali."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Mreža je zaključana"</string> diff --git a/packages/SystemUI/res-keyguard/values-hu/strings.xml b/packages/SystemUI/res-keyguard/values-hu/strings.xml index c26998f01ff0..4dcb22971979 100644 --- a/packages/SystemUI/res-keyguard/values-hu/strings.xml +++ b/packages/SystemUI/res-keyguard/values-hu/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Töltés"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Gyors töltés"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Lassú töltés"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Csatlakoztassa a töltőt."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"A feloldáshoz nyomja meg a Menü gombot."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Hálózat zárolva"</string> diff --git a/packages/SystemUI/res-keyguard/values-hy/strings.xml b/packages/SystemUI/res-keyguard/values-hy/strings.xml index ad949d48fe0c..354d93208f15 100644 --- a/packages/SystemUI/res-keyguard/values-hy/strings.xml +++ b/packages/SystemUI/res-keyguard/values-hy/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Լիցքավորում"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Արագ լիցքավորում"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Դանդաղ լիցքավորում"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Միացրեք լիցքավորիչը:"</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Ապակողպելու համար սեղմեք Ընտրացանկը:"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Ցանցը կողպված է"</string> diff --git a/packages/SystemUI/res-keyguard/values-in/strings.xml b/packages/SystemUI/res-keyguard/values-in/strings.xml index 85b2a4726fa2..d43823cea147 100644 --- a/packages/SystemUI/res-keyguard/values-in/strings.xml +++ b/packages/SystemUI/res-keyguard/values-in/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mengisi daya"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mengisi daya dengan cepat"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mengisi daya dengan lambat"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Hubungkan pengisi daya."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Tekan Menu untuk membuka kunci."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Jaringan terkunci"</string> diff --git a/packages/SystemUI/res-keyguard/values-is/strings.xml b/packages/SystemUI/res-keyguard/values-is/strings.xml index e40cdca33034..233402b33f26 100644 --- a/packages/SystemUI/res-keyguard/values-is/strings.xml +++ b/packages/SystemUI/res-keyguard/values-is/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Í hleðslu"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Hröð hleðsla"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Hæg hleðsla"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Tengdu hleðslutækið."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Ýttu á valmyndarhnappinn til að taka úr lás."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Net læst"</string> diff --git a/packages/SystemUI/res-keyguard/values-it/strings.xml b/packages/SystemUI/res-keyguard/values-it/strings.xml index e1c9ee8b7d34..87fd81cb1581 100644 --- a/packages/SystemUI/res-keyguard/values-it/strings.xml +++ b/packages/SystemUI/res-keyguard/values-it/strings.xml @@ -38,6 +38,7 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • In carica"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ricarica veloce"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ricarica lenta"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="8190982388514496109">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Batteria momentaneamente limitata"</string> <string name="keyguard_low_battery" msgid="1868012396800230904">"Collega il caricabatterie."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Premi Menu per sbloccare."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Rete bloccata"</string> diff --git a/packages/SystemUI/res-keyguard/values-iw/strings.xml b/packages/SystemUI/res-keyguard/values-iw/strings.xml index e054f629836e..6af70b4cebf3 100644 --- a/packages/SystemUI/res-keyguard/values-iw/strings.xml +++ b/packages/SystemUI/res-keyguard/values-iw/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • בטעינה"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • בטעינה מהירה"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • בטעינה איטית"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"חבר את המטען."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"לחץ על \'תפריט\' כדי לבטל את הנעילה."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"הרשת נעולה"</string> diff --git a/packages/SystemUI/res-keyguard/values-ja/strings.xml b/packages/SystemUI/res-keyguard/values-ja/strings.xml index 957d78a8b440..ab5a13c16bad 100644 --- a/packages/SystemUI/res-keyguard/values-ja/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ja/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 充電中"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 急速充電中"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 低速充電中"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"充電してください。"</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"メニューからロックを解除できます。"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"ネットワークがロックされました"</string> diff --git a/packages/SystemUI/res-keyguard/values-ka/strings.xml b/packages/SystemUI/res-keyguard/values-ka/strings.xml index d0d15fec7172..b10ab615abd0 100644 --- a/packages/SystemUI/res-keyguard/values-ka/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ka/strings.xml @@ -38,6 +38,7 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • იტენება"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • სწრაფად იტენება"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ნელა იტენება"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="8190982388514496109">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ბატარეა დროებით შეზღუდულია"</string> <string name="keyguard_low_battery" msgid="1868012396800230904">"შეაერთეთ დამტენი."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"განსაბლოკად დააჭირეთ მენიუს."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"ქსელი ჩაკეტილია"</string> diff --git a/packages/SystemUI/res-keyguard/values-kk/strings.xml b/packages/SystemUI/res-keyguard/values-kk/strings.xml index 62afd1e45df8..c415bbbe1a72 100644 --- a/packages/SystemUI/res-keyguard/values-kk/strings.xml +++ b/packages/SystemUI/res-keyguard/values-kk/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Зарядталуда"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Жылдам зарядталуда"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Баяу зарядталуда"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Зарядтағышты қосыңыз."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Ашу үшін \"Мәзір\" пернесін басыңыз."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Желі құлыптаулы"</string> diff --git a/packages/SystemUI/res-keyguard/values-km/strings.xml b/packages/SystemUI/res-keyguard/values-km/strings.xml index 52b7fab768c5..1a278e8262c8 100644 --- a/packages/SystemUI/res-keyguard/values-km/strings.xml +++ b/packages/SystemUI/res-keyguard/values-km/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • កំពុងសាកថ្ម"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • កំពុងសាកថ្មយ៉ាងឆាប់រហ័ស"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • កំពុងសាកថ្មយឺត"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"សូមសាកថ្មរបស់អ្នក។"</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"ចុចម៉ឺនុយ ដើម្បីដោះសោ។"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"បណ្ដាញជាប់សោ"</string> diff --git a/packages/SystemUI/res-keyguard/values-kn/strings.xml b/packages/SystemUI/res-keyguard/values-kn/strings.xml index 785ca4338326..961fcec69853 100644 --- a/packages/SystemUI/res-keyguard/values-kn/strings.xml +++ b/packages/SystemUI/res-keyguard/values-kn/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ಚಾರ್ಜ್ ಮಾಡಲಾಗುತ್ತಿದೆ"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ವೇಗವಾಗಿ ಚಾರ್ಜ್ಮಾಡಲಾಗುತ್ತಿದೆ"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ನಿಧಾನವಾಗಿ ಚಾರ್ಜ್ ಮಾಡಲಾಗುತ್ತಿದೆ"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"ನಿಮ್ಮ ಚಾರ್ಜರ್ ಸಂಪರ್ಕಗೊಳಿಸಿ."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"ಅನ್ಲಾಕ್ ಮಾಡಲು ಮೆನು ಒತ್ತಿರಿ."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"ನೆಟ್ವರ್ಕ್ ಲಾಕ್ ಆಗಿದೆ"</string> diff --git a/packages/SystemUI/res-keyguard/values-ko/strings.xml b/packages/SystemUI/res-keyguard/values-ko/strings.xml index 848490ebb9b8..2f93af920e17 100644 --- a/packages/SystemUI/res-keyguard/values-ko/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ko/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 충전 중"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 고속 충전 중"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 저속 충전 중"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"충전기를 연결하세요."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"잠금 해제하려면 메뉴를 누르세요."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"네트워크 잠김"</string> diff --git a/packages/SystemUI/res-keyguard/values-ky/strings.xml b/packages/SystemUI/res-keyguard/values-ky/strings.xml index d868788a3eca..7e929199f1bc 100644 --- a/packages/SystemUI/res-keyguard/values-ky/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ky/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Кубатталууда"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Тез кубатталууда"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Жай кубатталууда"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Кубаттагычка туташтырыңыз."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Кулпуну ачуу үчүн Менюну басыңыз."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Тармак кулпуланган"</string> diff --git a/packages/SystemUI/res-keyguard/values-lo/strings.xml b/packages/SystemUI/res-keyguard/values-lo/strings.xml index ebaffb13d5b4..b96a2bb4aa4c 100644 --- a/packages/SystemUI/res-keyguard/values-lo/strings.xml +++ b/packages/SystemUI/res-keyguard/values-lo/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ກຳລັງສາກ"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ກຳລັງສາກແບບດ່ວນ"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ກຳລັງສາກແບບຊ້າ"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"ເຊື່ອມຕໍ່ສາຍສາກຂອງທ່ານ."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"ກົດ \"ເມນູ\" ເພື່ອປົດລັອກ."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"ເຄືອຂ່າຍຖືກລັອກ"</string> diff --git a/packages/SystemUI/res-keyguard/values-lt/strings.xml b/packages/SystemUI/res-keyguard/values-lt/strings.xml index 4d598f6bfb6d..773f7c42ebd1 100644 --- a/packages/SystemUI/res-keyguard/values-lt/strings.xml +++ b/packages/SystemUI/res-keyguard/values-lt/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Įkraunama"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Greitai įkraunama"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Lėtai įkraunama"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Prijunkite kroviklį."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Paspauskite meniu, jei norite atrakinti."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Tinklas užrakintas"</string> diff --git a/packages/SystemUI/res-keyguard/values-lv/strings.xml b/packages/SystemUI/res-keyguard/values-lv/strings.xml index fad67d536e58..68bf4cde3086 100644 --- a/packages/SystemUI/res-keyguard/values-lv/strings.xml +++ b/packages/SystemUI/res-keyguard/values-lv/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Notiek uzlāde"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Notiek ātrā uzlāde"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Notiek lēnā uzlāde"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Pievienojiet uzlādes ierīci."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Lai atbloķētu, nospiediet izvēlnes ikonu."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Tīkls ir bloķēts."</string> diff --git a/packages/SystemUI/res-keyguard/values-mk/strings.xml b/packages/SystemUI/res-keyguard/values-mk/strings.xml index 1397f467e116..e51f77488726 100644 --- a/packages/SystemUI/res-keyguard/values-mk/strings.xml +++ b/packages/SystemUI/res-keyguard/values-mk/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Се полни"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Брзо полнење"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Бавно полнење"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Поврзете го полначот."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Притиснете „Мени“ за отклучување."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Мрежата е заклучена"</string> diff --git a/packages/SystemUI/res-keyguard/values-ml/strings.xml b/packages/SystemUI/res-keyguard/values-ml/strings.xml index f82f822ca26c..4b4391482833 100644 --- a/packages/SystemUI/res-keyguard/values-ml/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ml/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ചാർജ് ചെയ്യുന്നു"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • വേഗത്തിൽ ചാർജ് ചെയ്യുന്നു"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • പതുക്കെ ചാർജ് ചെയ്യുന്നു"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"നിങ്ങളുടെ ചാർജർ കണക്റ്റുചെയ്യുക."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"അൺലോക്കുചെയ്യാൻ മെനു അമർത്തുക."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"നെറ്റ്വർക്ക് ലോക്കുചെയ്തു"</string> diff --git a/packages/SystemUI/res-keyguard/values-mn/strings.xml b/packages/SystemUI/res-keyguard/values-mn/strings.xml index 462017af1922..4667252546c5 100644 --- a/packages/SystemUI/res-keyguard/values-mn/strings.xml +++ b/packages/SystemUI/res-keyguard/values-mn/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Цэнэглэж байна"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Хурдан цэнэглэж байна"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Удаан цэнэглэж байна"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Цэнэглэгчээ холбоно уу."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Түгжээг тайлах бол цэсийг дарна уу."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Сүлжээ түгжигдсэн"</string> diff --git a/packages/SystemUI/res-keyguard/values-mr/strings.xml b/packages/SystemUI/res-keyguard/values-mr/strings.xml index 0166791dc3ba..bdd390475c4a 100644 --- a/packages/SystemUI/res-keyguard/values-mr/strings.xml +++ b/packages/SystemUI/res-keyguard/values-mr/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • चार्ज होत आहे"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • वेगाने चार्ज होत आहे"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • सावकाश चार्ज होत आहे"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"तुमचा चार्जर कनेक्ट करा."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"अनलॉक करण्यासाठी मेनू दाबा."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"नेटवर्क लॉक केले"</string> diff --git a/packages/SystemUI/res-keyguard/values-ms/strings.xml b/packages/SystemUI/res-keyguard/values-ms/strings.xml index 67500866f3d4..a5179887ce5d 100644 --- a/packages/SystemUI/res-keyguard/values-ms/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ms/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mengecas"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mengecas dengan cepat"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mengecas dengan perlahan"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Sambungkan pengecas anda."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Tekan Menu untuk membuka kunci."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Rangkaian dikunci"</string> diff --git a/packages/SystemUI/res-keyguard/values-my/strings.xml b/packages/SystemUI/res-keyguard/values-my/strings.xml index 3b32f06e19cd..07f3d38aa8c8 100644 --- a/packages/SystemUI/res-keyguard/values-my/strings.xml +++ b/packages/SystemUI/res-keyguard/values-my/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • အားသွင်းနေသည်"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • အမြန်အားသွင်းနေသည်"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • နှေးကွေးစွာ အားသွင်းနေသည်"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"အားသွင်းကိရိယာကို ချိတ်ဆက်ပါ။"</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"မီနူးကို နှိပ်၍ လော့ခ်ဖွင့်ပါ။"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"ကွန်ရက်ကို လော့ခ်ချထားသည်"</string> diff --git a/packages/SystemUI/res-keyguard/values-nb/strings.xml b/packages/SystemUI/res-keyguard/values-nb/strings.xml index ebd8f2922ba2..a391b9293c05 100644 --- a/packages/SystemUI/res-keyguard/values-nb/strings.xml +++ b/packages/SystemUI/res-keyguard/values-nb/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Lader"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Lader raskt"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Lader sakte"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Koble til en batterilader."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Trykk på menyknappen for å låse opp."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Nettverket er låst"</string> diff --git a/packages/SystemUI/res-keyguard/values-ne/strings.xml b/packages/SystemUI/res-keyguard/values-ne/strings.xml index ce05e38dca10..812aa38d4622 100644 --- a/packages/SystemUI/res-keyguard/values-ne/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ne/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • चार्ज गरिँदै"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • द्रुत गतिमा चार्ज गरिँदै"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • मन्द गतिमा चार्ज गरिँदै"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"तपाईंको चार्जर जोड्नुहोस्।"</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"अनलक गर्न मेनु थिच्नुहोस्।"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"नेटवर्क लक भएको छ"</string> diff --git a/packages/SystemUI/res-keyguard/values-nl/strings.xml b/packages/SystemUI/res-keyguard/values-nl/strings.xml index aa783e84b892..f1c2033baaa8 100644 --- a/packages/SystemUI/res-keyguard/values-nl/strings.xml +++ b/packages/SystemUI/res-keyguard/values-nl/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Opladen"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Snel opladen"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Langzaam opladen"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Sluit de oplader aan."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Druk op Menu om te ontgrendelen."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Netwerk vergrendeld"</string> diff --git a/packages/SystemUI/res-keyguard/values-or/strings.xml b/packages/SystemUI/res-keyguard/values-or/strings.xml index 8bbdcf1e9eb6..be6e2856bd27 100644 --- a/packages/SystemUI/res-keyguard/values-or/strings.xml +++ b/packages/SystemUI/res-keyguard/values-or/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ଚାର୍ଜ ହେଉଛି"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ଦ୍ରୁତ ଭାବେ ଚାର୍ଜ ହେଉଛି"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ଧୀରେ ଚାର୍ଜ ହେଉଛି"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"ଆପଣଙ୍କ ଚାର୍ଜର୍ ସଂଯୋଗ କରନ୍ତୁ।"</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"ଅନଲକ୍ କରିବା ପାଇଁ ମେନୁକୁ ଦବାନ୍ତୁ।"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"ନେଟୱର୍କକୁ ଲକ୍ କରାଯାଇଛି"</string> diff --git a/packages/SystemUI/res-keyguard/values-pa/strings.xml b/packages/SystemUI/res-keyguard/values-pa/strings.xml index 78e066526840..a6ea6aaf4162 100644 --- a/packages/SystemUI/res-keyguard/values-pa/strings.xml +++ b/packages/SystemUI/res-keyguard/values-pa/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ਤੇਜ਼ੀ ਨਾਲ ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ਹੌਲੀ-ਹੌਲੀ ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"ਆਪਣਾ ਚਾਰਜਰ ਕਨੈਕਟ ਕਰੋ।"</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"ਅਣਲਾਕ ਕਰਨ ਲਈ \"ਮੀਨੂ\" ਦਬਾਓ।"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"ਨੈੱਟਵਰਕ ਲਾਕ ਕੀਤਾ ਗਿਆ"</string> diff --git a/packages/SystemUI/res-keyguard/values-pl/strings.xml b/packages/SystemUI/res-keyguard/values-pl/strings.xml index 5094cf9983a1..9c6f27118600 100644 --- a/packages/SystemUI/res-keyguard/values-pl/strings.xml +++ b/packages/SystemUI/res-keyguard/values-pl/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ładowanie"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Szybkie ładowanie"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Wolne ładowanie"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Podłącz ładowarkę."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Naciśnij Menu, aby odblokować."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Sieć zablokowana"</string> diff --git a/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml b/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml index 5bfc3dbc900d..d2d5fb9d159a 100644 --- a/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml @@ -38,6 +38,7 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carregando"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carregando rapidamente"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carregando lentamente"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="8190982388514496109">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Bateria limitada temporariamente"</string> <string name="keyguard_low_battery" msgid="1868012396800230904">"Conecte o seu carregador."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Pressione Menu para desbloquear."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Rede bloqueada"</string> diff --git a/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml b/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml index 5af8bc09a05b..7f0a4d883ee5 100644 --- a/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml @@ -38,6 +38,7 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • A carregar…"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • A carregar rapidamente…"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • A carregar lentamente…"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="8190982388514496109">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Bateria limitada temporariamente."</string> <string name="keyguard_low_battery" msgid="1868012396800230904">"Ligue o carregador."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Prima Menu para desbloquear."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Rede bloqueada"</string> diff --git a/packages/SystemUI/res-keyguard/values-pt/strings.xml b/packages/SystemUI/res-keyguard/values-pt/strings.xml index 5bfc3dbc900d..d2d5fb9d159a 100644 --- a/packages/SystemUI/res-keyguard/values-pt/strings.xml +++ b/packages/SystemUI/res-keyguard/values-pt/strings.xml @@ -38,6 +38,7 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carregando"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carregando rapidamente"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carregando lentamente"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="8190982388514496109">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Bateria limitada temporariamente"</string> <string name="keyguard_low_battery" msgid="1868012396800230904">"Conecte o seu carregador."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Pressione Menu para desbloquear."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Rede bloqueada"</string> diff --git a/packages/SystemUI/res-keyguard/values-ro/strings.xml b/packages/SystemUI/res-keyguard/values-ro/strings.xml index 8122241e6613..572c1dbd0830 100644 --- a/packages/SystemUI/res-keyguard/values-ro/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ro/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Se încarcă"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Se încarcă rapid"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Se încarcă lent"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Conectați încărcătorul."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Apăsați pe Meniu pentru a debloca."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Rețea blocată"</string> diff --git a/packages/SystemUI/res-keyguard/values-ru/strings.xml b/packages/SystemUI/res-keyguard/values-ru/strings.xml index b80b479ca29a..2c87608f9b9b 100644 --- a/packages/SystemUI/res-keyguard/values-ru/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ru/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"Идет зарядка (<xliff:g id="PERCENTAGE">%s</xliff:g>)"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"Идет быстрая зарядка (<xliff:g id="PERCENTAGE">%s</xliff:g>)"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"Идет медленная зарядка (<xliff:g id="PERCENTAGE">%s</xliff:g>)"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Подключите зарядное устройство."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Для разблокировки нажмите \"Меню\"."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Сеть заблокирована"</string> diff --git a/packages/SystemUI/res-keyguard/values-si/strings.xml b/packages/SystemUI/res-keyguard/values-si/strings.xml index 1cd876f15d47..8682192ce32a 100644 --- a/packages/SystemUI/res-keyguard/values-si/strings.xml +++ b/packages/SystemUI/res-keyguard/values-si/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ආරෝපණය වෙමින්"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • වේගයෙන් ආරෝපණය වෙමින්"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • සෙමින් ආරෝපණය වෙමින්"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"ඔබගේ ආරෝපකයට සම්බන්ධ කරන්න."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"අගුලු හැරීමට මෙනුව ඔබන්න."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"ජාලය අගුළු දමා ඇත"</string> diff --git a/packages/SystemUI/res-keyguard/values-sk/strings.xml b/packages/SystemUI/res-keyguard/values-sk/strings.xml index 801a7dbccf6d..d5e6093b1a83 100644 --- a/packages/SystemUI/res-keyguard/values-sk/strings.xml +++ b/packages/SystemUI/res-keyguard/values-sk/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Nabíja sa"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Nabíja sa rýchlo"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Nabíja sa pomaly"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Pripojte nabíjačku."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Odomknete stlačením tlačidla ponuky."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Sieť je zablokovaná"</string> diff --git a/packages/SystemUI/res-keyguard/values-sl/strings.xml b/packages/SystemUI/res-keyguard/values-sl/strings.xml index 967255cb50e7..82d5c5493010 100644 --- a/packages/SystemUI/res-keyguard/values-sl/strings.xml +++ b/packages/SystemUI/res-keyguard/values-sl/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • polnjenje"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • hitro polnjenje"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • počasno polnjenje"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Priključite napajalnik."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Če želite odkleniti, pritisnite meni."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Omrežje je zaklenjeno"</string> diff --git a/packages/SystemUI/res-keyguard/values-sq/strings.xml b/packages/SystemUI/res-keyguard/values-sq/strings.xml index 382a4dcafed7..a67413fdf5c2 100644 --- a/packages/SystemUI/res-keyguard/values-sq/strings.xml +++ b/packages/SystemUI/res-keyguard/values-sq/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Po karikohet"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Po karikohet me shpejtësi"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Po karikohet ngadalë"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Lidh karikuesin."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Shtyp \"Meny\" për të shkyçur."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Rrjeti është i kyçur"</string> diff --git a/packages/SystemUI/res-keyguard/values-sr/strings.xml b/packages/SystemUI/res-keyguard/values-sr/strings.xml index f83df3f8925e..dec31650308e 100644 --- a/packages/SystemUI/res-keyguard/values-sr/strings.xml +++ b/packages/SystemUI/res-keyguard/values-sr/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Пуни се"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Брзо се пуни"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Споро се пуни"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Прикључите пуњач."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Притисните Мени да бисте откључали."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Мрежа је закључана"</string> diff --git a/packages/SystemUI/res-keyguard/values-sv/strings.xml b/packages/SystemUI/res-keyguard/values-sv/strings.xml index a037bffa4da2..3ef2dfe87e88 100644 --- a/packages/SystemUI/res-keyguard/values-sv/strings.xml +++ b/packages/SystemUI/res-keyguard/values-sv/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laddas"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laddas snabbt"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laddas långsamt"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Anslut laddaren."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Lås upp genom att trycka på Meny."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Nätverk låst"</string> diff --git a/packages/SystemUI/res-keyguard/values-sw/strings.xml b/packages/SystemUI/res-keyguard/values-sw/strings.xml index efa5ecfd44fd..5c5fc10fe77e 100644 --- a/packages/SystemUI/res-keyguard/values-sw/strings.xml +++ b/packages/SystemUI/res-keyguard/values-sw/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Inachaji"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Inachaji kwa kasi"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Inachaji pole pole"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Unganisha chaja yako."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Bonyeza Menyu ili kufungua."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Mtandao umefungwa"</string> diff --git a/packages/SystemUI/res-keyguard/values-ta/strings.xml b/packages/SystemUI/res-keyguard/values-ta/strings.xml index 96dbbb0314d6..c188301bb7ed 100644 --- a/packages/SystemUI/res-keyguard/values-ta/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ta/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • சார்ஜாகிறது"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • வேகமாகச் சார்ஜாகிறது"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • மெதுவாகச் சார்ஜாகிறது"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"சார்ஜரை இணைக்கவும்."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"திறக்க, மெனுவை அழுத்தவும்."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"நெட்வொர்க் பூட்டப்பட்டது"</string> diff --git a/packages/SystemUI/res-keyguard/values-te/strings.xml b/packages/SystemUI/res-keyguard/values-te/strings.xml index d44003bc5b95..27a5b285c485 100644 --- a/packages/SystemUI/res-keyguard/values-te/strings.xml +++ b/packages/SystemUI/res-keyguard/values-te/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ఛార్జ్ అవుతోంది"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • వేగంగా ఛార్జ్ అవుతోంది"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • నెమ్మదిగా ఛార్జ్ అవుతోంది"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"మీ ఛార్జర్ను కనెక్ట్ చేయండి."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"అన్లాక్ చేయడానికి మెనుని నొక్కండి."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"నెట్వర్క్ లాక్ చేయబడింది"</string> diff --git a/packages/SystemUI/res-keyguard/values-th/strings.xml b/packages/SystemUI/res-keyguard/values-th/strings.xml index e157be4ac18e..e506745d637e 100644 --- a/packages/SystemUI/res-keyguard/values-th/strings.xml +++ b/packages/SystemUI/res-keyguard/values-th/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • กำลังชาร์จ"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • กำลังชาร์จอย่างเร็ว"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • กำลังชาร์จอย่างช้าๆ"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"เสียบที่ชาร์จของคุณ"</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"กด \"เมนู\" เพื่อปลดล็อก"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"เครือข่ายถูกล็อก"</string> diff --git a/packages/SystemUI/res-keyguard/values-tl/strings.xml b/packages/SystemUI/res-keyguard/values-tl/strings.xml index 7b7e17dc8dcb..912fd9529f09 100644 --- a/packages/SystemUI/res-keyguard/values-tl/strings.xml +++ b/packages/SystemUI/res-keyguard/values-tl/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Nagcha-charge"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mabilis na nagcha-charge"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mabagal na nagcha-charge"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Ikonekta ang iyong charger."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Pindutin ang Menu upang i-unlock."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Naka-lock ang network"</string> diff --git a/packages/SystemUI/res-keyguard/values-tr/strings.xml b/packages/SystemUI/res-keyguard/values-tr/strings.xml index 8c0caead0bdb..5390c4c5c4eb 100644 --- a/packages/SystemUI/res-keyguard/values-tr/strings.xml +++ b/packages/SystemUI/res-keyguard/values-tr/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Şarj oluyor"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Hızlı şarj oluyor"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Yavaş şarj oluyor"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Şarj cihazınızı takın."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Kilidi açmak için Menü\'ye basın."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Ağ kilitli"</string> diff --git a/packages/SystemUI/res-keyguard/values-uk/strings.xml b/packages/SystemUI/res-keyguard/values-uk/strings.xml index 6e5ce0f142dc..6263d79fd645 100644 --- a/packages/SystemUI/res-keyguard/values-uk/strings.xml +++ b/packages/SystemUI/res-keyguard/values-uk/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Заряджання"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Швидке заряджання"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Повільне заряджання"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Підключіть зарядний пристрій."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Натисніть меню, щоб розблокувати."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Мережу заблоковано"</string> diff --git a/packages/SystemUI/res-keyguard/values-ur/strings.xml b/packages/SystemUI/res-keyguard/values-ur/strings.xml index 0fd5e17c953e..bf936cb86b51 100644 --- a/packages/SystemUI/res-keyguard/values-ur/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ur/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • چارج ہو رہا ہے"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • تیزی سے چارج ہو رہا ہے"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • آہستہ چارج ہو رہا ہے"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"اپنا چارجر منسلک کریں۔"</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"غیر مقفل کرنے کیلئے مینو دبائیں۔"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"نیٹ ورک مقفل ہو گیا"</string> diff --git a/packages/SystemUI/res-keyguard/values-uz/strings.xml b/packages/SystemUI/res-keyguard/values-uz/strings.xml index 323fea5a608e..0b23e16ddd2f 100644 --- a/packages/SystemUI/res-keyguard/values-uz/strings.xml +++ b/packages/SystemUI/res-keyguard/values-uz/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Quvvat olmoqda"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Tezkor quvvat olmoqda"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Sekin quvvat olmoqda"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Quvvatlash moslamasini ulang."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Qulfdan chiqarish uchun Menyu tugmasini bosing."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Tarmoq qulflangan"</string> diff --git a/packages/SystemUI/res-keyguard/values-vi/strings.xml b/packages/SystemUI/res-keyguard/values-vi/strings.xml index 2ba5089c7ed9..9357eba66432 100644 --- a/packages/SystemUI/res-keyguard/values-vi/strings.xml +++ b/packages/SystemUI/res-keyguard/values-vi/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Đang sạc"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Đang sạc nhanh"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Đang sạc chậm"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Kết nối bộ sạc của bạn."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Nhấn vào Menu để mở khóa."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Mạng đã bị khóa"</string> diff --git a/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml index b4bff5fab6d6..96728ab6cead 100644 --- a/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 正在充电"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 正在快速充电"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 正在慢速充电"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"请连接充电器。"</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"按“菜单”即可解锁。"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"网络已锁定"</string> diff --git a/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml index b3d387706fa5..c683529d81ac 100644 --- a/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 正在充電"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 正在快速充電"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> •正在慢速充電"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"請連接充電器。"</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"按下 [選單] 即可解鎖。"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"網絡已鎖定"</string> diff --git a/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml index 03dec4852771..7a3b5c52acf7 100644 --- a/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 充電中"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 快速充電中"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 慢速充電中"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"請連接充電器。"</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"按選單鍵解鎖。"</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"網路已鎖定"</string> diff --git a/packages/SystemUI/res-keyguard/values-zu/strings.xml b/packages/SystemUI/res-keyguard/values-zu/strings.xml index 5ab567f706c3..ec38cbef456e 100644 --- a/packages/SystemUI/res-keyguard/values-zu/strings.xml +++ b/packages/SystemUI/res-keyguard/values-zu/strings.xml @@ -38,6 +38,8 @@ <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Iyashaja"</string> <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ishaja kaningi"</string> <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ishaja kancane"</string> + <!-- no translation found for keyguard_plugged_in_charging_limited (8190982388514496109) --> + <skip /> <string name="keyguard_low_battery" msgid="1868012396800230904">"Xhuma ishaja yakho."</string> <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Chofoza Menyu ukuvula."</string> <string name="keyguard_network_locked_message" msgid="407096292844868608">"Inethiwekhi ivaliwe"</string> diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml index f7e9fedd5f66..d95ab374a5cb 100644 --- a/packages/SystemUI/res-keyguard/values/strings.xml +++ b/packages/SystemUI/res-keyguard/values/strings.xml @@ -79,6 +79,9 @@ is not fully charged, and it's plugged into a slow charger, say that it's charging slowly. --> <string name="keyguard_plugged_in_charging_slowly"><xliff:g id="percentage">%s</xliff:g> • Charging slowly</string> + <!-- When the lock screen is showing and the phone plugged in, and the defend mode is triggered, say that it's limited temporarily. --> + <string name="keyguard_plugged_in_charging_limited"><xliff:g id="percentage">%s</xliff:g> • Battery limited temporarily</string> + <!-- When the lock screen is showing and the battery is low, warn user to plug in the phone soon. --> <string name="keyguard_low_battery">Connect your charger.</string> diff --git a/packages/SystemUI/res-product/values-de/strings.xml b/packages/SystemUI/res-product/values-de/strings.xml index 5c8f842fa2d3..a84413d825c2 100644 --- a/packages/SystemUI/res-product/values-de/strings.xml +++ b/packages/SystemUI/res-product/values-de/strings.xml @@ -21,7 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="dock_alignment_slow_charging" product="default" msgid="6997633396534416792">"Smartphone genau platzieren, um es schneller zu laden"</string> <string name="dock_alignment_not_charging" product="default" msgid="3980752926226749808">"Smartphone genau platzieren, um es kabellos zu laden"</string> - <string name="inattentive_sleep_warning_message" product="tv" msgid="6844464574089665063">"Das Android TV-Gerät wird gleich ausgeschaltet. Falls es eingeschaltet bleiben soll, drücke beispielsweise eine Taste oder berühre den Bildschirm."</string> + <string name="inattentive_sleep_warning_message" product="tv" msgid="6844464574089665063">"Das Android TV-Gerät wird gleich ausgeschaltet. Falls es eingeschaltet bleiben soll, drücke eine Taste."</string> <string name="inattentive_sleep_warning_message" product="default" msgid="5693904520452332224">"Das Gerät wird gleich ausgeschaltet. Falls es eingeschaltet bleiben soll, drücke beispielsweise eine Taste oder berühre den Bildschirm."</string> <string name="keyguard_missing_sim_message" product="tablet" msgid="5018086454277963787">"Keine SIM-Karte im Tablet."</string> <string name="keyguard_missing_sim_message" product="default" msgid="7053347843877341391">"Keine SIM-Karte im Smartphone."</string> diff --git a/packages/SystemUI/res-product/values-pl/strings.xml b/packages/SystemUI/res-product/values-pl/strings.xml index 9a9980a02782..5ac3d8420783 100644 --- a/packages/SystemUI/res-product/values-pl/strings.xml +++ b/packages/SystemUI/res-product/values-pl/strings.xml @@ -34,10 +34,10 @@ <string name="kg_failed_attempts_almost_at_erase_user" product="default" msgid="8110939900089863103">"Po raz <xliff:g id="NUMBER_0">%1$d</xliff:g> próbowano nieprawidłowo odblokować telefon. Po kolejnych <xliff:g id="NUMBER_1">%2$d</xliff:g> nieudanych próbach użytkownik zostanie usunięty, co spowoduje skasowanie wszystkich jego danych."</string> <string name="kg_failed_attempts_now_erasing_user" product="tablet" msgid="8509811676952707883">"Po raz <xliff:g id="NUMBER">%d</xliff:g> próbowano nieprawidłowo odblokować tablet. Użytkownik zostanie usunięty, co spowoduje skasowanie wszystkich jego danych."</string> <string name="kg_failed_attempts_now_erasing_user" product="default" msgid="3051962486994265014">"Po raz <xliff:g id="NUMBER">%d</xliff:g> próbowano nieprawidłowo odblokować telefon. Użytkownik zostanie usunięty, co spowoduje skasowanie wszystkich jego danych."</string> - <string name="kg_failed_attempts_almost_at_erase_profile" product="tablet" msgid="1049523640263353830">"Po raz <xliff:g id="NUMBER_0">%1$d</xliff:g> próbowano nieprawidłowo odblokować tablet. Po kolejnych <xliff:g id="NUMBER_1">%2$d</xliff:g> nieudanych próbach profil do pracy zostanie usunięty, co spowoduje skasowanie wszystkich jego danych."</string> - <string name="kg_failed_attempts_almost_at_erase_profile" product="default" msgid="3280816298678433681">"Po raz <xliff:g id="NUMBER_0">%1$d</xliff:g> próbowano nieprawidłowo odblokować telefon. Po kolejnych <xliff:g id="NUMBER_1">%2$d</xliff:g> nieudanych próbach profil do pracy zostanie usunięty, co spowoduje skasowanie wszystkich jego danych."</string> - <string name="kg_failed_attempts_now_erasing_profile" product="tablet" msgid="4417100487251371559">"Po raz <xliff:g id="NUMBER">%d</xliff:g> próbowano nieprawidłowo odblokować tablet. Profil do pracy zostanie usunięty, co spowoduje skasowanie wszystkich jego danych."</string> - <string name="kg_failed_attempts_now_erasing_profile" product="default" msgid="4682221342671290678">"Po raz <xliff:g id="NUMBER">%d</xliff:g> próbowano nieprawidłowo odblokować telefon. Profil do pracy zostanie usunięty, co spowoduje skasowanie wszystkich jego danych."</string> + <string name="kg_failed_attempts_almost_at_erase_profile" product="tablet" msgid="1049523640263353830">"Po raz <xliff:g id="NUMBER_0">%1$d</xliff:g> próbowano nieprawidłowo odblokować tablet. Po kolejnych <xliff:g id="NUMBER_1">%2$d</xliff:g> nieudanych próbach profil służbowy zostanie usunięty, co spowoduje skasowanie wszystkich jego danych."</string> + <string name="kg_failed_attempts_almost_at_erase_profile" product="default" msgid="3280816298678433681">"Po raz <xliff:g id="NUMBER_0">%1$d</xliff:g> próbowano nieprawidłowo odblokować telefon. Po kolejnych <xliff:g id="NUMBER_1">%2$d</xliff:g> nieudanych próbach profil służbowy zostanie usunięty, co spowoduje skasowanie wszystkich jego danych."</string> + <string name="kg_failed_attempts_now_erasing_profile" product="tablet" msgid="4417100487251371559">"Po raz <xliff:g id="NUMBER">%d</xliff:g> próbowano nieprawidłowo odblokować tablet. Profil służbowy zostanie usunięty, co spowoduje skasowanie wszystkich jego danych."</string> + <string name="kg_failed_attempts_now_erasing_profile" product="default" msgid="4682221342671290678">"Po raz <xliff:g id="NUMBER">%d</xliff:g> próbowano nieprawidłowo odblokować telefon. Profil służbowy zostanie usunięty, co spowoduje skasowanie wszystkich jego danych."</string> <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="1860049973474855672">"Po raz <xliff:g id="NUMBER_0">%1$d</xliff:g> nieprawidłowo narysowano wzór odblokowania. Po kolejnych <xliff:g id="NUMBER_1">%2$d</xliff:g> nieudanych próbach konieczne będzie odblokowanie tabletu przy użyciu konta e-mail.\n\n Spróbuj ponownie za <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string> <string name="kg_failed_attempts_almost_at_login" product="default" msgid="44112553371516141">"Po raz <xliff:g id="NUMBER_0">%1$d</xliff:g> nieprawidłowo narysowano wzór odblokowania. Po kolejnych <xliff:g id="NUMBER_1">%2$d</xliff:g> nieudanych próbach konieczne będzie odblokowanie telefonu przy użyciu konta e-mail.\n\n Spróbuj ponownie za <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string> <string name="global_action_lock_message" product="default" msgid="7092460751050168771">"Odblokuj telefon, by wyświetlić więcej opcji"</string> diff --git a/packages/SystemUI/res-product/values/strings.xml b/packages/SystemUI/res-product/values/strings.xml index f1c539e8eb4e..c1e81bacbec5 100644 --- a/packages/SystemUI/res-product/values/strings.xml +++ b/packages/SystemUI/res-product/values/strings.xml @@ -23,7 +23,7 @@ <!-- Indication when device is not charging due to bad placement on the dock. [CHAR LIMIT=60] --> <string name="dock_alignment_not_charging" product="default">Realign phone to charge wirelessly</string> - <!-- Message of the overlay warning the user to interact with the device or it will go to sleep. [CHAR LIMIT=NONE] --> + <!-- Message of the overlay warning the user that the TV is about to go to standby unless a TV remote button is pressed. [CHAR LIMIT=NONE] --> <string name="inattentive_sleep_warning_message" product="tv">The Android TV device will soon turn off; press a button to keep it on.</string> <!-- Message of the overlay warning the user to interact with the device or it will go to sleep. [CHAR LIMIT=NONE] --> <string name="inattentive_sleep_warning_message" product="default">The device will soon turn off; press to keep it on.</string> diff --git a/packages/SystemUI/res/layout/controls_dialog_pin.xml b/packages/SystemUI/res/layout/controls_dialog_pin.xml index 170b32b6c669..d0ef10b649bf 100644 --- a/packages/SystemUI/res/layout/controls_dialog_pin.xml +++ b/packages/SystemUI/res/layout/controls_dialog_pin.xml @@ -27,6 +27,7 @@ android:layout_height="wrap_content" android:minHeight="48dp" android:longClickable="false" + android:textAlignment="viewStart" android:inputType="numberPassword" /> <CheckBox android:id="@+id/controls_pin_use_alpha" diff --git a/packages/SystemUI/res/layout/global_actions_grid_v2.xml b/packages/SystemUI/res/layout/global_actions_grid_v2.xml index 7d45de3fa50d..30ffc32ce1f8 100644 --- a/packages/SystemUI/res/layout/global_actions_grid_v2.xml +++ b/packages/SystemUI/res/layout/global_actions_grid_v2.xml @@ -1,67 +1,15 @@ <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/global_actions_container" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > - <com.android.systemui.globalactions.GlobalActionsFlatLayout - android:id="@id/global_actions_view" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="horizontal" - android:theme="@style/qs_theme" - android:clipChildren="false" - android:clipToPadding="false" - android:layout_marginStart="@dimen/global_actions_side_margin" - > - <LinearLayout - android:id="@android:id/list" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:paddingTop="@dimen/global_actions_grid_vertical_padding" - android:paddingBottom="@dimen/global_actions_grid_vertical_padding" - android:orientation="horizontal" - android:gravity="left | center_vertical" - android:translationZ="@dimen/global_actions_translate" - > - <RelativeLayout - android:id="@+id/global_actions_overflow_button" - android:contentDescription="@string/accessibility_menu" - android:layout_width="48dp" - android:layout_height="48dp" - > - <ImageView - android:src="@drawable/ic_more_vert" - android:layout_centerInParent="true" - android:layout_width="24dp" - android:layout_height="24dp" - android:tint="@color/control_more_vert" - /> - </RelativeLayout> - </LinearLayout> - </com.android.systemui.globalactions.GlobalActionsFlatLayout> - <androidx.constraintlayout.widget.ConstraintLayout - android:id="@+id/global_actions_lock_message_container" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:visibility="gone"> - <TextView - android:id="@+id/global_actions_lock_message" - style="@style/TextAppearance.Control.Title" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginHorizontal="@dimen/global_actions_side_margin" - android:drawablePadding="12dp" - android:gravity="center" - android:text="@string/global_action_lock_message" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintTop_toTopOf="parent" - app:layout_constraintVertical_bias="0.35"/> - </androidx.constraintlayout.widget.ConstraintLayout> + <include layout="@layout/global_actions_view" /> + + <include layout="@layout/global_actions_lock_view" /> <com.android.systemui.globalactions.MinHeightScrollView android:layout_width="match_parent" diff --git a/packages/SystemUI/res/layout/global_actions_lock_view.xml b/packages/SystemUI/res/layout/global_actions_lock_view.xml new file mode 100644 index 000000000000..eccc63688065 --- /dev/null +++ b/packages/SystemUI/res/layout/global_actions_lock_view.xml @@ -0,0 +1,35 @@ +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<androidx.constraintlayout.widget.ConstraintLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/global_actions_lock_message_container" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:visibility="gone"> + <TextView + android:id="@+id/global_actions_lock_message" + style="@style/TextAppearance.Control.Title" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginHorizontal="@dimen/global_actions_side_margin" + android:drawablePadding="12dp" + android:gravity="center" + android:text="@string/global_action_lock_message" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintVertical_bias="0.35"/> +</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/global_actions_view.xml b/packages/SystemUI/res/layout/global_actions_view.xml new file mode 100644 index 000000000000..454707bc44e2 --- /dev/null +++ b/packages/SystemUI/res/layout/global_actions_view.xml @@ -0,0 +1,52 @@ +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<com.android.systemui.globalactions.GlobalActionsFlatLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@id/global_actions_view" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:theme="@style/qs_theme" + android:clipChildren="false" + android:clipToPadding="false" + android:layout_marginStart="@dimen/global_actions_side_margin" + > + <LinearLayout + android:id="@android:id/list" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingTop="@dimen/global_actions_grid_vertical_padding" + android:paddingBottom="@dimen/global_actions_grid_vertical_padding" + android:orientation="horizontal" + android:gravity="left | center_vertical" + android:translationZ="@dimen/global_actions_translate" + > + <RelativeLayout + android:id="@+id/global_actions_overflow_button" + android:contentDescription="@string/accessibility_menu" + android:layout_width="48dp" + android:layout_height="48dp" + > + <ImageView + android:src="@drawable/ic_more_vert" + android:layout_centerInParent="true" + android:layout_width="24dp" + android:layout_height="24dp" + android:tint="@color/control_more_vert" + /> + </RelativeLayout> + </LinearLayout> +</com.android.systemui.globalactions.GlobalActionsFlatLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml index 0ba546eff689..f9841005e8f7 100644 --- a/packages/SystemUI/res/layout/super_notification_shade.xml +++ b/packages/SystemUI/res/layout/super_notification_shade.xml @@ -78,7 +78,8 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="@dimen/status_bar_height" - android:layout_gravity="top|center_horizontal"> + android:layout_gravity="top|center_horizontal" + android:gravity="center_horizontal"> <com.android.systemui.statusbar.phone.LockIcon android:id="@+id/lock_icon" android:layout_width="@dimen/keyguard_lock_width" diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index 8d86478259fa..6b598da2826d 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -944,7 +944,7 @@ <string name="instant_apps_title" msgid="8942706782103036910">"<xliff:g id="APP">%1$s</xliff:g> en cours d\'exécution"</string> <string name="instant_apps_message" msgid="6112428971833011754">"Application ouverte sans avoir été installée."</string> <string name="instant_apps_message_with_help" msgid="1816952263531203932">"Application ouverte sans avoir été installée. Touchez ici pour en savoir plus."</string> - <string name="app_info" msgid="5153758994129963243">"Détails de l\'applic."</string> + <string name="app_info" msgid="5153758994129963243">"Détails de l\'appli"</string> <string name="go_to_web" msgid="636673528981366511">"Ouvrir le navigateur"</string> <string name="mobile_data" msgid="4564407557775397216">"Données cellulaires"</string> <string name="mobile_data_text_format" msgid="6806501540022589786">"<xliff:g id="ID_1">%1$s</xliff:g> : <xliff:g id="ID_2">%2$s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index 160e05d3dbc7..df86e8a4774c 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -33,8 +33,8 @@ <string name="invalid_charger_title" msgid="938685362320735167">"USB арқылы зарядтау мүмкін емес"</string> <string name="invalid_charger_text" msgid="2339310107232691577">"Құрылғымен бірге берілген зарядтау құралын пайдаланыңыз"</string> <string name="battery_low_why" msgid="2056750982959359863">"Параметрлер"</string> - <string name="battery_saver_confirmation_title" msgid="1234998463717398453">"Battery Saver функциясын қосу керек пе?"</string> - <string name="battery_saver_confirmation_title_generic" msgid="2299231884234959849">"Battery Saver туралы ақпарат"</string> + <string name="battery_saver_confirmation_title" msgid="1234998463717398453">"Батареяны үнемдеу режимін қосу керек пе?"</string> + <string name="battery_saver_confirmation_title_generic" msgid="2299231884234959849">"Батареяны үнемдеу режимі туралы ақпарат"</string> <string name="battery_saver_confirmation_ok" msgid="5042136476802816494">"Қосу"</string> <string name="battery_saver_start_action" msgid="4553256017945469937">"Battery saver функциясын қосу"</string> <string name="status_bar_settings_settings_button" msgid="534331565185171556">"Параметрлер"</string> @@ -501,7 +501,7 @@ <string name="user_remove_user_title" msgid="9124124694835811874">"Пайдаланушы жойылсын ба?"</string> <string name="user_remove_user_message" msgid="6702834122128031833">"Осы пайдаланушының барлық қолданбалары мен деректері жойылады."</string> <string name="user_remove_user_remove" msgid="8387386066949061256">"Жою"</string> - <string name="battery_saver_notification_title" msgid="8419266546034372562">"Battery saver қосулы"</string> + <string name="battery_saver_notification_title" msgid="8419266546034372562">"Батареяны үнемдеу режимі қосулы"</string> <string name="battery_saver_notification_text" msgid="2617841636449016951">"Өнімділікті және фондық деректерді азайтады"</string> <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Battery saver функциясын өшіру"</string> <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> жазу не трансляциялау кезінде экранда көрсетілетін немесе дыбысталатын барлық ақпаратты пайдалана алады. Бұған құпия сөздер, төлем туралы мәліметтер, суреттер, хабарлар және аудиоматериалдар кіреді."</string> @@ -777,7 +777,7 @@ <item quantity="one">%d минут</item> </plurals> <string name="battery_panel_title" msgid="5931157246673665963">"Батареяны пайдалану"</string> - <string name="battery_detail_charging_summary" msgid="8821202155297559706">"Зарядтау кезінде Батарея үнемдегіш қол жетімді емес"</string> + <string name="battery_detail_charging_summary" msgid="8821202155297559706">"Зарядтау кезінде Батареяны үнемдеу режимі істемейді"</string> <string name="battery_detail_switch_title" msgid="6940976502957380405">"Батареяны үнемдеу режимі"</string> <string name="battery_detail_switch_summary" msgid="3668748557848025990">"Өнімділікті және фондық деректерді азайтады"</string> <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> түймесі"</string> @@ -965,7 +965,7 @@ <string name="slice_permission_checkbox" msgid="4242888137592298523">"<xliff:g id="APP">%1$s</xliff:g> қолданбасына кез келген қолданбаның үзіндісін көрсетуге рұқсат беру"</string> <string name="slice_permission_allow" msgid="6340449521277951123">"Рұқсат беру"</string> <string name="slice_permission_deny" msgid="6870256451658176895">"Тыйым салу"</string> - <string name="auto_saver_title" msgid="6873691178754086596">"Түймені түртіп, Battery Saver функциясын реттеңіз"</string> + <string name="auto_saver_title" msgid="6873691178754086596">"Түймені түртіп, Батареяны үнемдеу режимін реттеңіз"</string> <string name="auto_saver_text" msgid="3214960308353838764">"Батареяның заряды бітуге жақындағанда қосыңыз."</string> <string name="no_auto_saver_action" msgid="7467924389609773835">"Жоқ, рақмет"</string> <string name="auto_saver_enabled_title" msgid="4294726198280286333">"Battery Saver кестесі қосылды"</string> diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml index b841fa95a57f..0d605c11cd1d 100644 --- a/packages/SystemUI/res/values-mn/strings.xml +++ b/packages/SystemUI/res/values-mn/strings.xml @@ -508,7 +508,7 @@ <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Энэ функцийг ажиллуулж байгаа үйлчилгээ нь бичлэг хийх эсвэл дамжуулах үед таны дэлгэц дээр харагдах эсвэл таны төхөөрөмжөөс тоглуулах бүх мэдээлэлд хандах боломжтой байна. Үүнд нууц үг, төлбөрийн дэлгэрэнгүй, зураг болон таны тоглуулдаг аудио зэрэг мэдээлэл багтана."</string> <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Бичлэг хийх эсвэл дамжуулахыг эхлүүлэх үү?"</string> <string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>-тай бичлэг хийж эсвэл дамжуулж эхлэх үү?"</string> - <string name="media_projection_remember_text" msgid="6896767327140422951">"Дахиж үл харуулах"</string> + <string name="media_projection_remember_text" msgid="6896767327140422951">"Дахиж бүү харуул"</string> <string name="clear_all_notifications_text" msgid="348312370303046130">"Бүгдийг арилгах"</string> <string name="manage_notifications_text" msgid="6885645344647733116">"Удирдах"</string> <string name="manage_notifications_history_text" msgid="57055985396576230">"Түүх"</string> @@ -874,12 +874,12 @@ <string-array name="clock_options"> <item msgid="3986445361435142273">"Цаг, минут, секундийг харуулах"</item> <item msgid="1271006222031257266">"Цаг, минутыг харуулах (өгөгдмөл)"</item> - <item msgid="6135970080453877218">"Энэ дүрс тэмдгийг бүү үзүүл"</item> + <item msgid="6135970080453877218">"Энэ дүрс тэмдгийг бүү харуул"</item> </string-array> <string-array name="battery_options"> <item msgid="7714004721411852551">"Хувийг тогтмол харуулах"</item> <item msgid="3805744470661798712">"Цэнэглэх үед хувийг тогтмол харуулах (өгөгдмөл)"</item> - <item msgid="8619482474544321778">"Энэ дүрс тэмдгийг бүү үзүүл"</item> + <item msgid="8619482474544321778">"Энэ дүрс тэмдгийг бүү харуул"</item> </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Бага ач холбогдолтой мэдэгдлийн дүрс тэмдгийг харуулах"</string> <string name="other" msgid="429768510980739978">"Бусад"</string> diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index 621c4977d674..881be66c00c2 100644 --- a/packages/SystemUI/res/values-ne/strings.xml +++ b/packages/SystemUI/res/values-ne/strings.xml @@ -692,7 +692,7 @@ <string name="tuner_full_importance_settings" msgid="1388025816553459059">"सशक्त सूचना नियन्त्रण"</string> <string name="tuner_full_importance_settings_on" msgid="917981436602311547">"अन छ"</string> <string name="tuner_full_importance_settings_off" msgid="5580102038749680829">"अफ"</string> - <string name="power_notification_controls_description" msgid="1334963837572708952">"सशक्त सूचना नियन्त्रणहरू मार्फत तपाईं अनुप्रयाेगका सूचनाहरूका लागि ० देखि ५ सम्मको महत्व सम्बन्धी स्तर सेट गर्न सक्नुहुन्छ। \n\n"<b>"स्तर ५"</b>" \n- सूचनाको सूचीको माथिल्लो भागमा देखाउने \n- पूर्ण स्क्रिनमा अवरोधका लागि अनुमति दिने \n- सधैँ चियाउने \n\n"<b>"स्तर ४"</b>" \n- पूर्ण स्क्रिनमा अवरोधलाई रोक्ने \n- सधैँ चियाउने \n\n"<b>"स्तर ३"</b>" \n- पूर्ण स्क्रिनमा अवरोधलाई रोक्ने \n- कहिल्यै नचियाउने \n\n"<b>"स्तर २"</b>" \n- पूर्ण स्क्रिनमा अवरोधलाई रोक्ने \n- कहिल्यै नचियाउने \n- कहिल्यै पनि आवाज ननिकाल्ने र कम्पन नगर्ने \n\n"<b>"स्तर १"</b>" \n- पूर्ण स्क्रिनमा अवरोध रोक्ने \n- कहिल्यै नचियाउने \n- कहिल्यै पनि आवाज ननिकाल्ने वा कम्पन नगर्ने \n- लक स्क्रिन र वस्तुस्थिति पट्टीबाट लुकाउने \n- सूचनाको सूचीको तल्लो भागमा देखाउने \n\n"<b>"स्तर ०"</b>" \n- अनुप्रयोगका सबै सूचनाहरूलाई रोक्ने"</string> + <string name="power_notification_controls_description" msgid="1334963837572708952">"सशक्त सूचना नियन्त्रणहरू मार्फत तपाईं अनुप्रयाेगका सूचनाहरूका लागि ० देखि ५ सम्मको महत्व सम्बन्धी स्तर सेट गर्न सक्नुहुन्छ। \n\n"<b>"स्तर ५"</b>" \n- सूचनाको सूचीको माथिल्लो भागमा देखाउने \n- पूर्ण स्क्रिनमा अवरोधका लागि अनुमति दिने \n- सधैँ चियाउने \n\n"<b>"स्तर ४"</b>" \n- पूर्ण स्क्रिनमा अवरोधलाई रोक्ने \n- सधैँ चियाउने \n\n"<b>"स्तर ३"</b>" \n- पूर्ण स्क्रिनमा अवरोधलाई रोक्ने \n- कहिल्यै नचियाउने \n\n"<b>"स्तर २"</b>" \n- पूर्ण स्क्रिनमा अवरोधलाई रोक्ने \n- कहिल्यै नचियाउने \n- कहिल्यै पनि आवाज ननिकाल्ने र कम्पन नगर्ने \n\n"<b>"स्तर १"</b>" \n- पूर्ण स्क्रिनमा अवरोध रोक्ने \n- कहिल्यै नचियाउने \n- कहिल्यै पनि आवाज ननिकाल्ने वा कम्पन नगर्ने \n- लक स्क्रिन र वस्तुस्थिति पट्टीबाट लुकाउने \n- सूचनाको सूचीको तल्लो भागमा देखाउने \n\n"<b>"स्तर ०"</b>" \n- एपका सबै सूचनाहरूलाई रोक्ने"</string> <string name="notification_header_default_channel" msgid="225454696914642444">"सूचनाहरू"</string> <string name="notification_channel_disabled" msgid="928065923928416337">"तपाईं अब उप्रान्त यी सूचनाहरू देख्नु हुने छैन"</string> <string name="notification_channel_minimized" msgid="6892672757877552959">"यी सूचनाहरू सानो बनाइने छ"</string> @@ -712,7 +712,7 @@ <string name="inline_silent_button_alert" msgid="5705343216858250354">"सतर्क गराउने"</string> <string name="inline_silent_button_keep_alerting" msgid="6577845442184724992">"सर्तक गराइरहनुहोस्"</string> <string name="inline_turn_off_notifications" msgid="8543989584403106071">"सूचनाहरू निष्क्रिय पार्नुहोस्"</string> - <string name="inline_keep_showing_app" msgid="4393429060390649757">"यो अनुप्रयोगका सूचनाहरू देखाउने क्रम जारी राख्ने हो?"</string> + <string name="inline_keep_showing_app" msgid="4393429060390649757">"यो एपका सूचनाहरू देखाउने क्रम जारी राख्ने हो?"</string> <string name="notification_silence_title" msgid="8608090968400832335">"मौन"</string> <string name="notification_alert_title" msgid="3656229781017543655">"पूर्वनिर्धारित"</string> <string name="notification_automatic_title" msgid="3745465364578762652">"स्वचालित"</string> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index 1c6b6a1d7961..3e944b5f7317 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -538,8 +538,8 @@ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Właściciel tego urządzenia: <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string> <string name="quick_settings_disclosure_management_vpns" msgid="371835422690053154">"To urządzenie należy do Twojej organizacji i jest połączone z sieciami VPN"</string> <string name="quick_settings_disclosure_named_management_vpns" msgid="4046375645500668555">"To urządzenie należy do organizacji <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> i jest połączone z sieciami VPN"</string> - <string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Twoja organizacja może monitorować ruch w sieci w Twoim profilu do pracy"</string> - <string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"Organizacja <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> może monitorować ruch w sieci w Twoim profilu do pracy"</string> + <string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Twoja organizacja może monitorować ruch w sieci w Twoim profilu służbowym"</string> + <string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"Organizacja <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> może monitorować ruch w sieci w Twoim profilu służbowym"</string> <string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Sieć może być monitorowana"</string> <string name="quick_settings_disclosure_vpns" msgid="7213546797022280246">"To urządzenie jest połączone z sieciami VPN"</string> <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="8117568745060010789">"Twój profil służbowy jest połączony z siecią <xliff:g id="VPN_APP">%1$s</xliff:g>"</string> @@ -558,7 +558,7 @@ <string name="monitoring_description_named_management" msgid="505833016545056036">"To urządzenie należy do organizacji <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nAdministrator IT może monitorować ustawienia, firmowe uprawnienia dostępu, aplikacje, dane dotyczące urządzenia i lokalizacji oraz nimi zarządzać.\n\nAby dowiedzieć się więcej, skontaktuj się z administratorem IT."</string> <string name="monitoring_description_management" msgid="4308879039175729014">"To urządzenie należy do Twojej organizacji.\n\nAdministrator IT może monitorować ustawienia, firmowe uprawnienia dostępu, aplikacje, dane dotyczące urządzenia i lokalizacji oraz nimi zarządzać.\n\nAby dowiedzieć się więcej, skontaktuj się z administratorem IT."</string> <string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Twoja organizacja zainstalowała urząd certyfikacji na tym urządzeniu. Zabezpieczony ruch w sieci może być monitorowany i zmieniany."</string> - <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Twoja organizacja zainstalowała urząd certyfikacji w Twoim profilu do pracy. Zabezpieczony ruch w sieci może być monitorowany i zmieniany."</string> + <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Twoja organizacja zainstalowała urząd certyfikacji w Twoim profilu służbowym. Zabezpieczony ruch w sieci może być monitorowany i zmieniany."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Urząd certyfikacji zainstalowany na tym urządzeniu. Twój zabezpieczony ruch w sieci może być monitorowany i zmieniany."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Administrator włączył rejestrowanie sieciowe, które pozwala monitorować ruch na Twoim urządzeniu."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Łączysz się z aplikacją <xliff:g id="VPN_APP">%1$s</xliff:g>, która może monitorować Twoją aktywność w sieci, w tym e-maile, aplikacje i strony internetowe."</string> @@ -577,14 +577,14 @@ <string name="monitoring_description_ca_cert_settings" msgid="8329781950135541003">"Otwórz zaufane certyfikaty"</string> <string name="monitoring_description_network_logging" msgid="577305979174002252">"Administrator włączył rejestrowanie sieciowe, które pozwala monitorować ruch na Twoim urządzeniu.\n\nAby dowiedzieć się więcej, skontaktuj się z administratorem."</string> <string name="monitoring_description_vpn" msgid="1685428000684586870">"Aplikacja otrzymała od Ciebie uprawnienia do konfigurowania połączenia VPN.\n\nMoże ona monitorować Twoją aktywność na urządzeniu i w sieci, w tym e-maile, aplikacje i strony internetowe."</string> - <string name="monitoring_description_vpn_profile_owned" msgid="4964237035412372751">"Twoim profilem do pracy zarządza <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nAdministrator może monitorować Twoją aktywność w sieci, w tym e-maile, aplikacje i strony internetowe.\n\nAby dowiedzieć się więcej, skontaktuj się z administratorem.\n\nŁączysz się też z siecią VPN, która może monitorować Twoją aktywność w sieci."</string> + <string name="monitoring_description_vpn_profile_owned" msgid="4964237035412372751">"Twoim profilem służbowym zarządza <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nAdministrator może monitorować Twoją aktywność w sieci, w tym e-maile, aplikacje i strony internetowe.\n\nAby dowiedzieć się więcej, skontaktuj się z administratorem.\n\nŁączysz się też z siecią VPN, która może monitorować Twoją aktywność w sieci."</string> <string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Tym urządzeniem zarządza Twój rodzic. Rodzic może zobaczyć różne informacje, np. o aplikacjach, których używasz, lokalizacji i czasie korzystania z urządzenia, a także zarządzać tymi danymi."</string> <string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string> <string name="monitoring_description_app" msgid="376868879287922929">"Łączysz się z aplikacją <xliff:g id="APPLICATION">%1$s</xliff:g>, która może monitorować Twoją aktywność w sieci, w tym e-maile, aplikacje i strony internetowe."</string> <string name="monitoring_description_app_personal" msgid="1970094872688265987">"Masz połączenie z aplikacją <xliff:g id="APPLICATION">%1$s</xliff:g>, która może monitorować Twoją prywatną aktywność w sieci, w tym e-maile, aplikacje i strony internetowe."</string> <string name="branded_monitoring_description_app_personal" msgid="1703511985892688885">"Masz połączenie z aplikacją <xliff:g id="APPLICATION">%1$s</xliff:g>, która może monitorować Twoją prywatną aktywność w sieci, w tym e-maile, aplikacje i strony internetowe."</string> - <string name="monitoring_description_app_work" msgid="3713084153786663662">"Organizacja <xliff:g id="ORGANIZATION">%1$s</xliff:g> zarządza Twoim profilem do pracy. Profil jest połączony z aplikacją <xliff:g id="APPLICATION">%2$s</xliff:g>, która może monitorować Twoją aktywność w sieci, w tym e-maile, aplikacje i strony internetowe.\n\nSkontaktuj się z administratorem, aby uzyskać więcej informacji."</string> - <string name="monitoring_description_app_personal_work" msgid="6175816356939166101">"Organizacja <xliff:g id="ORGANIZATION">%1$s</xliff:g> zarządza Twoim profilem do pracy. Profil jest połączony z aplikacją <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, która może monitorować Twoją aktywność w sieci, w tym e-maile, aplikacje i strony internetowe.\n\nMasz też połączenie z aplikacją <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, która może monitorować Twoją osobistą aktywność w sieci."</string> + <string name="monitoring_description_app_work" msgid="3713084153786663662">"Organizacja <xliff:g id="ORGANIZATION">%1$s</xliff:g> zarządza Twoim profilem służbowym. Profil jest połączony z aplikacją <xliff:g id="APPLICATION">%2$s</xliff:g>, która może monitorować Twoją aktywność w sieci, w tym e-maile, aplikacje i strony internetowe.\n\nSkontaktuj się z administratorem, aby uzyskać więcej informacji."</string> + <string name="monitoring_description_app_personal_work" msgid="6175816356939166101">"Organizacja <xliff:g id="ORGANIZATION">%1$s</xliff:g> zarządza Twoim profilem służbowym. Profil jest połączony z aplikacją <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, która może monitorować Twoją aktywność w sieci, w tym e-maile, aplikacje i strony internetowe.\n\nMasz też połączenie z aplikacją <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, która może monitorować Twoją osobistą aktywność w sieci."</string> <string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Blokada anulowana przez agenta zaufania"</string> <string name="keyguard_indication_trust_disabled" msgid="6820793704816727918">"Urządzenie pozostanie zablokowane, aż odblokujesz je ręcznie"</string> <string name="keyguard_indication_trust_unlocked_plugged_in" msgid="2323452175329362855">"<xliff:g id="KEYGUARD_INDICATION">%1$s</xliff:g>\n<xliff:g id="POWER_INDICATION">%2$s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-television/config.xml b/packages/SystemUI/res/values-television/config.xml index cebe1e83ceed..09ec439b183e 100644 --- a/packages/SystemUI/res/values-television/config.xml +++ b/packages/SystemUI/res/values-television/config.xml @@ -44,6 +44,9 @@ <item>com.android.systemui.wmshell.WMShell</item> </string-array> + <!-- Svelte specific logic, see RecentsConfiguration.SVELTE_* constants. --> + <integer name="recents_svelte_level">3</integer> + <!-- Show a separate icon for low and high volume on the volume dialog --> <bool name="config_showLowMediaVolumeIcon">true</bool> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 4407d8faeaa6..01b55b70d5ad 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -161,9 +161,6 @@ <!-- The number of milliseconds to extend ambient pulse by when prompted (e.g. on touch) --> <integer name="ambient_notification_extension_time">10000</integer> - <!-- Whether to enable KeyguardService or not --> - <bool name="config_enableKeyguardService">true</bool> - <!-- The maximum count of notifications on Keyguard. The rest will be collapsed in an overflow card. --> <integer name="keyguard_max_notification_count">3</integer> @@ -360,9 +357,6 @@ the notification is not swiped enough to dismiss it. --> <bool name="config_showNotificationGear">true</bool> - <!-- Whether or not a background should be drawn behind a notification. --> - <bool name="config_drawNotificationBackground">true</bool> - <!-- Whether or the notifications can be shown and dismissed with a drag. --> <bool name="config_enableNotificationShadeDrag">true</bool> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index ea1258f025f7..5b74687c3109 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -226,6 +226,8 @@ <string name="screenshot_saved_text">Tap to view your screenshot</string> <!-- Notification title displayed when we fail to take a screenshot. [CHAR LIMIT=50] --> <string name="screenshot_failed_title">Couldn\'t save screenshot</string> + <!-- Notification text displayed when we fail to save a screenshot due to locked storage. [CHAR LIMIT=100] --> + <string name="screenshot_failed_to_save_user_locked_text">Device must be unlocked before screenshot can be saved</string> <!-- Notification text displayed when we fail to save a screenshot for unknown reasons. [CHAR LIMIT=100] --> <string name="screenshot_failed_to_save_unknown_text">Try taking screenshot again</string> <!-- Notification text displayed when we fail to save a screenshot. [CHAR LIMIT=100] --> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java index d731840c38ae..68405a1fca68 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -76,6 +76,7 @@ public class KeyguardClockSwitch extends RelativeLayout { * Frame for clock when mode != KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL. */ private FrameLayout mNewLockscreenClockFrame; + private FrameLayout mNewLockscreenLargeClockFrame; /** * Frame for default and custom clock. @@ -154,14 +155,12 @@ public class KeyguardClockSwitch extends RelativeLayout { mNewLockscreenClockFrame.setVisibility(VISIBLE); statusAreaLP.removeRule(RelativeLayout.BELOW); - statusAreaLP.addRule(RelativeLayout.LEFT_OF, R.id.new_lockscreen_clock_view); statusAreaLP.addRule(RelativeLayout.ALIGN_PARENT_START); } else { setPaddingRelative(0, 0, 0, 0); mSmallClockFrame.setVisibility(VISIBLE); mNewLockscreenClockFrame.setVisibility(GONE); - statusAreaLP.removeRule(RelativeLayout.LEFT_OF); statusAreaLP.removeRule(RelativeLayout.ALIGN_PARENT_START); statusAreaLP.addRule(RelativeLayout.BELOW, R.id.clock_view); } @@ -175,6 +174,7 @@ public class KeyguardClockSwitch extends RelativeLayout { mClockView = findViewById(R.id.default_clock_view); mClockViewBold = findViewById(R.id.default_clock_view_bold); mNewLockscreenClockFrame = findViewById(R.id.new_lockscreen_clock_view); + mNewLockscreenLargeClockFrame = findViewById(R.id.new_lockscreen_clock_view_large); mSmallClockFrame = findViewById(R.id.clock_view); mKeyguardStatusArea = findViewById(R.id.keyguard_status_area); } @@ -292,6 +292,33 @@ public class KeyguardClockSwitch extends RelativeLayout { mClockViewBold.setFormat24Hour(format); } + private void updateClockLayout(boolean useLargeClock) { + if (mLockScreenMode != KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) return; + + Fade fadeIn = new Fade(); + fadeIn.setDuration(KeyguardSliceView.DEFAULT_ANIM_DURATION); + fadeIn.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); + + Fade fadeOut = new Fade(); + fadeOut.setDuration(KeyguardSliceView.DEFAULT_ANIM_DURATION / 2); + fadeOut.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN); + + if (useLargeClock) { + TransitionManager.beginDelayedTransition(mNewLockscreenClockFrame, fadeOut); + TransitionManager.beginDelayedTransition(mNewLockscreenLargeClockFrame, fadeIn); + + mNewLockscreenClockFrame.setVisibility(View.INVISIBLE); + addView(mNewLockscreenLargeClockFrame); + mNewLockscreenLargeClockFrame.setVisibility(View.VISIBLE); + } else { + TransitionManager.beginDelayedTransition(mNewLockscreenClockFrame, fadeIn); + TransitionManager.beginDelayedTransition(mNewLockscreenLargeClockFrame, fadeOut); + + removeView(mNewLockscreenLargeClockFrame); + mNewLockscreenClockFrame.setVisibility(View.VISIBLE); + } + } + /** * Set the amount (ratio) that the device has transitioned to doze. * @@ -312,6 +339,8 @@ public class KeyguardClockSwitch extends RelativeLayout { if (hasVisibleNotifications == mHasVisibleNotifications) { return; } + updateClockLayout(!hasVisibleNotifications); + mHasVisibleNotifications = hasVisibleNotifications; if (mDarkAmount == 0f && mBigClockContainer != null) { // Starting a fade transition since the visibility of the big clock will change. diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java index 6c695341680f..c5c36e920d45 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java @@ -61,6 +61,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS */ private AnimatableClockController mNewLockScreenClockViewController; private FrameLayout mNewLockScreenClockFrame; + private AnimatableClockController mNewLockScreenLargeClockViewController; private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL; @@ -189,6 +190,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS void refresh() { if (mNewLockScreenClockViewController != null) { mNewLockScreenClockViewController.refreshTime(); + mNewLockScreenLargeClockViewController.refreshTime(); } mView.refresh(); @@ -221,9 +223,15 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS mView.findViewById(R.id.animatable_clock_view), mStatusBarStateController); mNewLockScreenClockViewController.init(); + mNewLockScreenLargeClockViewController = + new AnimatableClockController( + mView.findViewById(R.id.animatable_clock_view_large), + mStatusBarStateController); + mNewLockScreenLargeClockViewController.init(); } } else { mNewLockScreenClockViewController = null; + mNewLockScreenLargeClockViewController = null; } mView.updateLockScreenMode(mLockScreenMode); updateAodIcons(); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java index a32cd1420fdc..3cbab8e66fdb 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java @@ -244,7 +244,7 @@ public class KeyguardSliceView extends LinearLayout { iconDrawable.setBounds(0, 0, Math.max(width, 1), iconSize); } } - button.setCompoundDrawables(iconDrawable, null, null, null); + button.setCompoundDrawablesRelative(iconDrawable, null, null, null); button.setOnClickListener(mOnClickListener); button.setClickable(pendingIntent != null); } @@ -536,9 +536,9 @@ public class KeyguardSliceView extends LinearLayout { } @Override - public void setCompoundDrawables(Drawable left, Drawable top, Drawable right, + public void setCompoundDrawablesRelative(Drawable start, Drawable top, Drawable end, Drawable bottom) { - super.setCompoundDrawables(left, top, right, bottom); + super.setCompoundDrawablesRelative(start, top, end, bottom); updateDrawableColors(); updatePadding(); } @@ -558,9 +558,9 @@ public class KeyguardSliceView extends LinearLayout { public void setLockScreenMode(int mode) { mLockScreenMode = mode; if (mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) { - setGravity(Gravity.START); + setTextAlignment(View.TEXT_ALIGNMENT_VIEW_START); } else { - setGravity(Gravity.CENTER); + setTextAlignment(View.TEXT_ALIGNMENT_CENTER); } updatePadding(); } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java index 2036b3321bda..97aa26fb7f68 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java @@ -178,6 +178,13 @@ public class KeyguardStatusView extends GridLayout { return mLogoutView.getVisibility() == VISIBLE ? mLogoutView.getHeight() : 0; } + int getOwnerInfoHeight() { + if (mOwnerInfo == null) { + return 0; + } + return mOwnerInfo.getVisibility() == VISIBLE ? mOwnerInfo.getHeight() : 0; + } + void updateLogoutView() { if (mLogoutView == null) { return; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java index dcb306e6110a..c0e06e87b753 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java @@ -130,6 +130,13 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV } /** + * Get the height of the logout button. + */ + public int getOwnerInfoHeight() { + return mView.getOwnerInfoHeight(); + } + + /** * Set keyguard status view alpha. */ public void setAlpha(float alpha) { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 767afa78b935..611131f6e216 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -1861,7 +1861,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab @Override public void onChange(boolean selfChange) { updateLockScreenMode(); - mHandler.sendEmptyMessage(MSG_LOCK_SCREEN_MODE); } }; mContext.getContentResolver().registerContentObserver( @@ -1870,14 +1869,22 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } private void updateLockScreenMode() { - mLockScreenMode = Settings.Global.getInt(mContext.getContentResolver(), + final int newMode = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.SHOW_NEW_LOCKSCREEN, isUdfpsEnrolled() ? 1 : 0); + if (newMode != mLockScreenMode) { + mLockScreenMode = newMode; + mHandler.sendEmptyMessage(MSG_LOCK_SCREEN_MODE); + } } private void updateUdfpsEnrolled(int userId) { mIsUdfpsEnrolled = mAuthController.isUdfpsEnrolled(userId); } + + /** + * @return true if there's at least one udfps enrolled + */ public boolean isUdfpsEnrolled() { return mIsUdfpsEnrolled; } @@ -1917,6 +1924,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab return; } + // TODO: Add support for multiple fingerprint sensors, b/173730729 boolean shouldListenForFingerprint = isUdfpsEnrolled() ? shouldListenForUdfps() : shouldListenForFingerprint(); boolean runningOrRestarting = mFingerprintRunningState == BIOMETRIC_STATE_RUNNING diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java index 8aa3493cc105..3852b24fe4b3 100644 --- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java @@ -24,6 +24,7 @@ import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.res.Resources; import android.graphics.RectF; import android.os.Handler; @@ -78,9 +79,9 @@ public class SwipeHelper implements Gefingerpoken { private float mInitialTouchPos; private float mPerpendicularInitialTouchPos; - private boolean mDragging; + private boolean mIsSwiping; private boolean mSnappingChild; - private View mCurrView; + private View mTouchedView; private boolean mCanCurrViewBeDimissed; private float mDensityScale; private float mTranslation = 0; @@ -95,14 +96,14 @@ public class SwipeHelper implements Gefingerpoken { @Override public void run() { - if (mCurrView != null && !mLongPressSent) { + if (mTouchedView != null && !mLongPressSent) { mLongPressSent = true; - if (mCurrView instanceof ExpandableNotificationRow) { - mCurrView.getLocationOnScreen(mViewOffset); + if (mTouchedView instanceof ExpandableNotificationRow) { + mTouchedView.getLocationOnScreen(mViewOffset); final int x = (int) mDownLocation[0] - mViewOffset[0]; final int y = (int) mDownLocation[1] - mViewOffset[1]; - mCurrView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED); - ((ExpandableNotificationRow) mCurrView).doLongClickCallback(x, y); + mTouchedView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED); + ((ExpandableNotificationRow) mTouchedView).doLongClickCallback(x, y); } } } @@ -281,10 +282,10 @@ public class SwipeHelper implements Gefingerpoken { @Override public boolean onInterceptTouchEvent(final MotionEvent ev) { - if (mCurrView instanceof ExpandableNotificationRow) { - NotificationMenuRowPlugin nmr = ((ExpandableNotificationRow) mCurrView).getProvider(); + if (mTouchedView instanceof ExpandableNotificationRow) { + NotificationMenuRowPlugin nmr = ((ExpandableNotificationRow) mTouchedView).getProvider(); if (nmr != null) { - mMenuRowIntercepting = nmr.onInterceptTouchEvent(mCurrView, ev); + mMenuRowIntercepting = nmr.onInterceptTouchEvent(mTouchedView, ev); } } final int action = ev.getAction(); @@ -292,19 +293,19 @@ public class SwipeHelper implements Gefingerpoken { switch (action) { case MotionEvent.ACTION_DOWN: mTouchAboveFalsingThreshold = false; - mDragging = false; + mIsSwiping = false; mSnappingChild = false; mLongPressSent = false; mVelocityTracker.clear(); - mCurrView = mCallback.getChildAtPosition(ev); + mTouchedView = mCallback.getChildAtPosition(ev); - if (mCurrView != null) { - onDownUpdate(mCurrView, ev); - mCanCurrViewBeDimissed = mCallback.canChildBeDismissed(mCurrView); + if (mTouchedView != null) { + onDownUpdate(mTouchedView, ev); + mCanCurrViewBeDimissed = mCallback.canChildBeDismissed(mTouchedView); mVelocityTracker.addMovement(ev); mInitialTouchPos = getPos(ev); mPerpendicularInitialTouchPos = getPerpendicularPos(ev); - mTranslation = getTranslation(mCurrView); + mTranslation = getTranslation(mTouchedView); mDownLocation[0] = ev.getRawX(); mDownLocation[1] = ev.getRawY(); mHandler.postDelayed(mPerformLongPress, mLongPressTimeout); @@ -312,7 +313,7 @@ public class SwipeHelper implements Gefingerpoken { break; case MotionEvent.ACTION_MOVE: - if (mCurrView != null && !mLongPressSent) { + if (mTouchedView != null && !mLongPressSent) { mVelocityTracker.addMovement(ev); float pos = getPos(ev); float perpendicularPos = getPerpendicularPos(ev); @@ -325,11 +326,11 @@ public class SwipeHelper implements Gefingerpoken { : mPagingTouchSlop; if (Math.abs(delta) > pagingTouchSlop && Math.abs(delta) > Math.abs(deltaPerpendicular)) { - if (mCallback.canChildBeDragged(mCurrView)) { - mCallback.onBeginDrag(mCurrView); - mDragging = true; + if (mCallback.canChildBeDragged(mTouchedView)) { + mIsSwiping = true; + mCallback.onBeginDrag(mTouchedView); mInitialTouchPos = getPos(ev); - mTranslation = getTranslation(mCurrView); + mTranslation = getTranslation(mTouchedView); } cancelLongPress(); } else if (ev.getClassification() == MotionEvent.CLASSIFICATION_DEEP_PRESS @@ -343,16 +344,16 @@ public class SwipeHelper implements Gefingerpoken { case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: - final boolean captured = (mDragging || mLongPressSent || mMenuRowIntercepting); - mDragging = false; - mCurrView = null; + final boolean captured = (mIsSwiping || mLongPressSent || mMenuRowIntercepting); + mIsSwiping = false; + mTouchedView = null; mLongPressSent = false; mMenuRowIntercepting = false; cancelLongPress(); if (captured) return true; break; } - return mDragging || mLongPressSent || mMenuRowIntercepting; + return mIsSwiping || mLongPressSent || mMenuRowIntercepting; } /** @@ -451,6 +452,7 @@ public class SwipeHelper implements Gefingerpoken { } if (!mCancelled || wasRemoved) { mCallback.onChildDismissed(animView); + resetSwipeState(); } if (endAction != null) { endAction.run(); @@ -501,6 +503,7 @@ public class SwipeHelper implements Gefingerpoken { updateSwipeProgressFromOffset(animView, canBeDismissed); onChildSnappedBack(animView, targetLeft); mCallback.onChildSnappedBack(animView, targetLeft); + resetSwipeState(); } } }); @@ -563,7 +566,7 @@ public class SwipeHelper implements Gefingerpoken { * @param targetLeft the target to snap to. */ public void snapChildIfNeeded(final View view, boolean animate, float targetLeft) { - if ((mDragging && mCurrView == view) || mSnappingChild) { + if ((mIsSwiping && mTouchedView == view) || mSnappingChild) { return; } boolean needToSnap = false; @@ -589,7 +592,7 @@ public class SwipeHelper implements Gefingerpoken { return true; } - if (!mDragging && !mMenuRowIntercepting) { + if (!mIsSwiping && !mMenuRowIntercepting) { if (mCallback.getChildAtPosition(ev) != null) { // We are dragging directly over a card, make sure that we also catch the gesture @@ -610,7 +613,7 @@ public class SwipeHelper implements Gefingerpoken { switch (action) { case MotionEvent.ACTION_OUTSIDE: case MotionEvent.ACTION_MOVE: - if (mCurrView != null) { + if (mTouchedView != null) { float delta = getPos(ev) - mInitialTouchPos; float absDelta = Math.abs(delta); if (absDelta >= getFalsingThreshold()) { @@ -618,9 +621,10 @@ public class SwipeHelper implements Gefingerpoken { } // don't let items that can't be dismissed be dragged more than // maxScrollDistance - if (CONSTRAIN_SWIPE && !mCallback.canChildBeDismissedInDirection(mCurrView, + if (CONSTRAIN_SWIPE && !mCallback.canChildBeDismissedInDirection( + mTouchedView, delta > 0)) { - float size = getSize(mCurrView); + float size = getSize(mTouchedView); float maxScrollDistance = MAX_SCROLL_SIZE_FRACTION * size; if (absDelta >= size) { delta = delta > 0 ? maxScrollDistance : -maxScrollDistance; @@ -636,32 +640,30 @@ public class SwipeHelper implements Gefingerpoken { } } - setTranslation(mCurrView, mTranslation + delta); - updateSwipeProgressFromOffset(mCurrView, mCanCurrViewBeDimissed); - onMoveUpdate(mCurrView, ev, mTranslation + delta, delta); + setTranslation(mTouchedView, mTranslation + delta); + updateSwipeProgressFromOffset(mTouchedView, mCanCurrViewBeDimissed); + onMoveUpdate(mTouchedView, ev, mTranslation + delta, delta); } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: - if (mCurrView == null) { + if (mTouchedView == null) { break; } mVelocityTracker.computeCurrentVelocity(1000 /* px/sec */, getMaxVelocity()); float velocity = getVelocity(mVelocityTracker); - if (!handleUpEvent(ev, mCurrView, velocity, getTranslation(mCurrView))) { + if (!handleUpEvent(ev, mTouchedView, velocity, getTranslation(mTouchedView))) { if (isDismissGesture(ev)) { - // flingadingy - dismissChild(mCurrView, velocity, + dismissChild(mTouchedView, velocity, !swipedFastEnough() /* useAccelerateInterpolator */); } else { - // snappity - mCallback.onDragCancelled(mCurrView); - snapChild(mCurrView, 0 /* leftTarget */, velocity); + mCallback.onDragCancelled(mTouchedView); + snapChild(mTouchedView, 0 /* leftTarget */, velocity); } - mCurrView = null; + mTouchedView = null; } - mDragging = false; + mIsSwiping = false; break; } return true; @@ -689,17 +691,18 @@ public class SwipeHelper implements Gefingerpoken { } protected boolean swipedFarEnough() { - float translation = getTranslation(mCurrView); + float translation = getTranslation(mTouchedView); return DISMISS_IF_SWIPED_FAR_ENOUGH - && Math.abs(translation) > SWIPED_FAR_ENOUGH_SIZE_FRACTION * getSize(mCurrView); + && Math.abs(translation) > SWIPED_FAR_ENOUGH_SIZE_FRACTION * getSize( + mTouchedView); } public boolean isDismissGesture(MotionEvent ev) { - float translation = getTranslation(mCurrView); + float translation = getTranslation(mTouchedView); return ev.getActionMasked() == MotionEvent.ACTION_UP && !mFalsingManager.isUnlockingDisabled() && !isFalseGesture() && (swipedFastEnough() || swipedFarEnough()) - && mCallback.canChildBeDismissedInDirection(mCurrView, translation > 0); + && mCallback.canChildBeDismissedInDirection(mTouchedView, translation > 0); } /** Returns true if the gesture should be rejected. */ @@ -715,7 +718,7 @@ public class SwipeHelper implements Gefingerpoken { protected boolean swipedFastEnough() { float velocity = getVelocity(mVelocityTracker); - float translation = getTranslation(mCurrView); + float translation = getTranslation(mTouchedView); boolean ret = (Math.abs(velocity) > getEscapeVelocity()) && (velocity > 0) == (translation > 0); return ret; @@ -726,6 +729,20 @@ public class SwipeHelper implements Gefingerpoken { return false; } + public boolean isSwiping() { + return mIsSwiping; + } + + @Nullable + public View getSwipedView() { + return mIsSwiping ? mTouchedView : null; + } + + public void resetSwipeState() { + mTouchedView = null; + mIsSwiping = false; + } + public interface Callback { View getChildAtPosition(MotionEvent ev); diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java index bb7906e7c3ae..714d267bb07d 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java @@ -23,6 +23,7 @@ import android.content.BroadcastReceiver; import android.content.ContentProvider; import android.content.Context; import android.content.Intent; +import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -94,7 +95,7 @@ public class SystemUIAppComponentFactory extends AppComponentFactory { } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { - // no-op + Log.w(TAG, "No injector for class: " + contentProvider.getClass(), e); } } ); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java index 2b33f8cd036e..935f89343754 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java @@ -101,7 +101,7 @@ public class AuthContainerView extends LinearLayout @VisibleForTesting final WakefulnessLifecycle mWakefulnessLifecycle; - private @ContainerState int mContainerState = STATE_UNKNOWN; + @VisibleForTesting @ContainerState int mContainerState = STATE_UNKNOWN; // Non-null only if the dialog is in the act of dismissing and has not sent the reason yet. @Nullable @AuthDialogCallback.DismissedReason Integer mPendingCallbackReason; @@ -281,7 +281,7 @@ public class AuthContainerView extends LinearLayout // Inflate biometric view only if necessary. if (Utils.isBiometricAllowed(mConfig.mPromptInfo)) { - if (config.mSensorIds.length == 1) { + if (config.mSensorIds.length == 1 || config.mSensorIds.length == 2) { final int singleSensorAuthId = config.mSensorIds[0]; if (Utils.containsSensorId(mFpProps, singleSensorAuthId)) { FingerprintSensorPropertiesInternal sensorProps = null; @@ -313,7 +313,6 @@ public class AuthContainerView extends LinearLayout return; } } else { - // The UI currently only supports authentication with a single sensor. Log.e(TAG, "Unsupported sensor array, length: " + config.mSensorIds.length); mBiometricView = null; mBackgroundView = null; @@ -632,10 +631,11 @@ public class AuthContainerView extends LinearLayout mWindowManager.removeView(this); } - private void onDialogAnimatedIn() { + @VisibleForTesting + void onDialogAnimatedIn() { if (mContainerState == STATE_PENDING_DISMISS) { Log.d(TAG, "onDialogAnimatedIn(): mPendingDismissDialog=true, dismissing now"); - animateAway(false /* sendReason */, 0); + animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED); return; } mContainerState = STATE_SHOWING; diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index 3fc9f4d2aa61..7bb8a8490160 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -187,12 +187,14 @@ class UdfpsController implements DozeReceiver { // TODO(b/152419866): Use the UDFPS window type when it becomes available. WindowManager.LayoutParams.TYPE_BOOT_PROGRESS, WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN - | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, PixelFormat.TRANSLUCENT); mCoreLayoutParams.setTitle(TAG); mCoreLayoutParams.setFitInsetsTypes(0); + mCoreLayoutParams.layoutInDisplayCutoutMode = + WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; + mCoreLayoutParams.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; mView = (UdfpsView) inflater.inflate(R.layout.udfps_view, null, false); mView.setSensorProperties(mSensorProps); diff --git a/packages/SystemUI/src/com/android/systemui/classifier/AccelerationClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/AccelerationClassifier.java deleted file mode 100644 index 4585fe9fb868..000000000000 --- a/packages/SystemUI/src/com/android/systemui/classifier/AccelerationClassifier.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.systemui.classifier; - -import android.view.MotionEvent; - -import java.util.HashMap; - -/** - * A classifier which looks at the speed and distance between successive points of a Stroke. - * It looks at two consecutive speeds between two points and calculates the ratio between them. - * The final result is the maximum of these values. It does the same for distances. If some speed - * or distance is equal to zero then the ratio between this and the next part is not calculated. To - * the duration of each part there is added one nanosecond so that it is always possible to - * calculate the speed of a part. - */ -public class AccelerationClassifier extends StrokeClassifier { - private final HashMap<Stroke, Data> mStrokeMap = new HashMap<>(); - - public AccelerationClassifier(ClassifierData classifierData) { - mClassifierData = classifierData; - } - - @Override - public String getTag() { - return "ACC"; - } - - @Override - public void onTouchEvent(MotionEvent event) { - int action = event.getActionMasked(); - - if (action == MotionEvent.ACTION_DOWN) { - mStrokeMap.clear(); - } - - for (int i = 0; i < event.getPointerCount(); i++) { - Stroke stroke = mClassifierData.getStroke(event.getPointerId(i)); - Point point = stroke.getPoints().get(stroke.getPoints().size() - 1); - if (mStrokeMap.get(stroke) == null) { - mStrokeMap.put(stroke, new Data(point)); - } else { - mStrokeMap.get(stroke).addPoint(point); - } - } - } - - @Override - public float getFalseTouchEvaluation(int type, Stroke stroke) { - Data data = mStrokeMap.get(stroke); - return 2 * SpeedRatioEvaluator.evaluate(data.maxSpeedRatio); - } - - private static class Data { - - static final float MILLIS_TO_NANOS = 1e6f; - - Point previousPoint; - float previousSpeed = 0; - float maxSpeedRatio = 0; - - public Data(Point point) { - previousPoint = point; - } - - public void addPoint(Point point) { - float distance = previousPoint.dist(point); - float duration = (float) (point.timeOffsetNano - previousPoint.timeOffsetNano + 1); - float speed = distance / duration; - - if (duration > 20 * MILLIS_TO_NANOS || duration < 5 * MILLIS_TO_NANOS) { - // reject this segment and ensure we won't use data about it in the next round. - previousSpeed = 0; - previousPoint = point; - return; - } - if (previousSpeed != 0.0f) { - maxSpeedRatio = Math.max(maxSpeedRatio, speed / previousSpeed); - } - - previousSpeed = speed; - previousPoint = point; - } - } -}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/classifier/AnglesClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/AnglesClassifier.java deleted file mode 100644 index 6d13973d23b5..000000000000 --- a/packages/SystemUI/src/com/android/systemui/classifier/AnglesClassifier.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.systemui.classifier; - -import android.os.Build; -import android.os.SystemProperties; -import android.view.MotionEvent; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - -/** - * A classifier which calculates the variance of differences between successive angles in a stroke. - * For each stroke it keeps its last three points. If some successive points are the same, it - * ignores the repetitions. If a new point is added, the classifier calculates the angle between - * the last three points. After that, it calculates the difference between this angle and the - * previously calculated angle. Then it calculates the variance of the differences from a stroke. - * To the differences there is artificially added value 0.0 and the difference between the first - * angle and PI (angles are in radians). It helps with strokes which have few points and punishes - * more strokes which are not smooth. - * - * This classifier also tries to split the stroke into two parts in the place in which the biggest - * angle is. It calculates the angle variance of the two parts and sums them up. The reason the - * classifier is doing this, is because some human swipes at the beginning go for a moment in one - * direction and then they rapidly change direction for the rest of the stroke (like a tick). The - * final result is the minimum of angle variance of the whole stroke and the sum of angle variances - * of the two parts split up. The classifier tries the tick option only if the first part is - * shorter than the second part. - * - * Additionally, the classifier classifies the angles as left angles (those angles which value is - * in [0.0, PI - ANGLE_DEVIATION) interval), straight angles - * ([PI - ANGLE_DEVIATION, PI + ANGLE_DEVIATION] interval) and right angles - * ((PI + ANGLE_DEVIATION, 2 * PI) interval) and then calculates the percentage of angles which are - * in the same direction (straight angles can be left angels or right angles) - */ -public class AnglesClassifier extends StrokeClassifier { - private HashMap<Stroke, Data> mStrokeMap = new HashMap<>(); - - public static final boolean VERBOSE = SystemProperties.getBoolean("debug.falsing_log.ang", - Build.IS_DEBUGGABLE); - - private static String TAG = "ANG"; - - public AnglesClassifier(ClassifierData classifierData) { - mClassifierData = classifierData; - } - - @Override - public String getTag() { - return TAG; - } - - @Override - public void onTouchEvent(MotionEvent event) { - int action = event.getActionMasked(); - - if (action == MotionEvent.ACTION_DOWN) { - mStrokeMap.clear(); - } - - for (int i = 0; i < event.getPointerCount(); i++) { - Stroke stroke = mClassifierData.getStroke(event.getPointerId(i)); - - if (mStrokeMap.get(stroke) == null) { - mStrokeMap.put(stroke, new Data()); - } - mStrokeMap.get(stroke).addPoint(stroke.getPoints().get(stroke.getPoints().size() - 1)); - } - } - - @Override - public float getFalseTouchEvaluation(int type, Stroke stroke) { - Data data = mStrokeMap.get(stroke); - return AnglesVarianceEvaluator.evaluate(data.getAnglesVariance(), type) - + AnglesPercentageEvaluator.evaluate(data.getAnglesPercentage(), type); - } - - private static class Data { - private final float ANGLE_DEVIATION = (float) Math.PI / 20.0f; - - private List<Point> mLastThreePoints = new ArrayList<>(); - private float mFirstAngleVariance; - private float mPreviousAngle; - private float mBiggestAngle; - private float mSumSquares; - private float mSecondSumSquares; - private float mSum; - private float mSecondSum; - private float mCount; - private float mSecondCount; - private float mFirstLength; - private float mLength; - private float mAnglesCount; - private float mLeftAngles; - private float mRightAngles; - private float mStraightAngles; - - public Data() { - mFirstAngleVariance = 0.0f; - mPreviousAngle = (float) Math.PI; - mBiggestAngle = 0.0f; - mSumSquares = mSecondSumSquares = 0.0f; - mSum = mSecondSum = 0.0f; - mCount = mSecondCount = 1.0f; - mLength = mFirstLength = 0.0f; - mAnglesCount = mLeftAngles = mRightAngles = mStraightAngles = 0.0f; - } - - public void addPoint(Point point) { - // Checking if the added point is different than the previously added point - // Repetitions are being ignored so that proper angles are calculated. - if (mLastThreePoints.isEmpty() - || !mLastThreePoints.get(mLastThreePoints.size() - 1).equals(point)) { - if (!mLastThreePoints.isEmpty()) { - mLength += mLastThreePoints.get(mLastThreePoints.size() - 1).dist(point); - } - mLastThreePoints.add(point); - if (mLastThreePoints.size() == 4) { - mLastThreePoints.remove(0); - - float angle = mLastThreePoints.get(1).getAngle(mLastThreePoints.get(0), - mLastThreePoints.get(2)); - - mAnglesCount++; - if (angle < Math.PI - ANGLE_DEVIATION) { - mLeftAngles++; - } else if (angle <= Math.PI + ANGLE_DEVIATION) { - mStraightAngles++; - } else { - mRightAngles++; - } - - float difference = angle - mPreviousAngle; - - // If this is the biggest angle of the stroke so then we save the value of - // the angle variance so far and start to count the values for the angle - // variance of the second part. - if (mBiggestAngle < angle) { - mBiggestAngle = angle; - mFirstLength = mLength; - mFirstAngleVariance = getAnglesVariance(mSumSquares, mSum, mCount); - mSecondSumSquares = 0.0f; - mSecondSum = 0.0f; - mSecondCount = 1.0f; - } else { - mSecondSum += difference; - mSecondSumSquares += difference * difference; - mSecondCount += 1.0; - } - - mSum += difference; - mSumSquares += difference * difference; - mCount += 1.0; - mPreviousAngle = angle; - } - } - } - - public float getAnglesVariance(float sumSquares, float sum, float count) { - return sumSquares / count - (sum / count) * (sum / count); - } - - public float getAnglesVariance() { - float anglesVariance = getAnglesVariance(mSumSquares, mSum, mCount); - if (VERBOSE) { - FalsingLog.i(TAG, "getAnglesVariance: (first pass) " + anglesVariance); - FalsingLog.i(TAG, " - mFirstLength=" + mFirstLength); - FalsingLog.i(TAG, " - mLength=" + mLength); - } - if (mFirstLength < mLength / 2f) { - anglesVariance = Math.min(anglesVariance, mFirstAngleVariance - + getAnglesVariance(mSecondSumSquares, mSecondSum, mSecondCount)); - if (VERBOSE) FalsingLog.i(TAG, "getAnglesVariance: (second pass) " + anglesVariance); - } - return anglesVariance; - } - - public float getAnglesPercentage() { - if (mAnglesCount == 0.0f) { - if (VERBOSE) FalsingLog.i(TAG, "getAnglesPercentage: count==0, result=1"); - return 1.0f; - } - final float result = (Math.max(mLeftAngles, mRightAngles) + mStraightAngles) / mAnglesCount; - if (VERBOSE) { - FalsingLog.i(TAG, "getAnglesPercentage: left=" + mLeftAngles + " right=" - + mRightAngles + " straight=" + mStraightAngles + " count=" + mAnglesCount - + " result=" + result); - } - return result; - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/classifier/AnglesPercentageEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/AnglesPercentageEvaluator.java deleted file mode 100644 index e6e42f2d0eef..000000000000 --- a/packages/SystemUI/src/com/android/systemui/classifier/AnglesPercentageEvaluator.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.systemui.classifier; - -public class AnglesPercentageEvaluator { - public static float evaluate(float value, int type) { - final boolean secureUnlock = type == Classifier.BOUNCER_UNLOCK; - float evaluation = 0.0f; - if (value < 1.00 && !secureUnlock) evaluation++; - if (value < 0.90 && !secureUnlock) evaluation++; - if (value < 0.70) evaluation++; - return evaluation; - } -} diff --git a/packages/SystemUI/src/com/android/systemui/classifier/AnglesVarianceEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/AnglesVarianceEvaluator.java deleted file mode 100644 index 9ffe783fbded..000000000000 --- a/packages/SystemUI/src/com/android/systemui/classifier/AnglesVarianceEvaluator.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.systemui.classifier; - -public class AnglesVarianceEvaluator { - public static float evaluate(float value, int type) { - float evaluation = 0.0f; - if (value > 0.20) evaluation++; - if (value > 0.40) evaluation++; - if (value > 0.80) evaluation++; - if (value > 1.50) evaluation++; - return evaluation; - } -} diff --git a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java index ae7d142a9e45..c7ad3e8f78eb 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java @@ -54,11 +54,6 @@ public abstract class Classifier { public @interface InteractionType {} /** - * Contains all the information about touch events from which the classifier can query - */ - protected ClassifierData mClassifierData; - - /** * Informs the classifier that a new touch event has occurred */ public void onTouchEvent(MotionEvent event) { diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ClassifierData.java b/packages/SystemUI/src/com/android/systemui/classifier/ClassifierData.java deleted file mode 100644 index 587abba47932..000000000000 --- a/packages/SystemUI/src/com/android/systemui/classifier/ClassifierData.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.systemui.classifier; - -import android.util.SparseArray; -import android.view.MotionEvent; - -import java.util.ArrayList; - -/** - * Contains data which is used to classify interaction sequences on the lockscreen. It does, for - * example, provide information on the current touch state. - */ -public class ClassifierData { - private static final long MINIMUM_DT_NANOS = 16666666; // 60Hz - private static final long MINIMUM_DT_SMEAR_NANOS = 2500000; // 2.5ms - - private SparseArray<Stroke> mCurrentStrokes = new SparseArray<>(); - private ArrayList<Stroke> mEndingStrokes = new ArrayList<>(); - private final float mDpi; - - public ClassifierData(float dpi) { - mDpi = dpi; - } - - /** Returns true if the event should be considered, false otherwise. */ - public boolean update(MotionEvent event) { - // We limit to 60hz sampling. Drop anything happening faster than that. - // Legacy code was created with an assumed sampling rate. As devices increase their - // sampling rate, this creates potentialy false positives. - if (event.getActionMasked() == MotionEvent.ACTION_MOVE - && mCurrentStrokes.size() != 0 - && event.getEventTimeNano() - mCurrentStrokes.valueAt(0).getLastEventTimeNano() - < MINIMUM_DT_NANOS - MINIMUM_DT_SMEAR_NANOS) { - return false; - } - - mEndingStrokes.clear(); - int action = event.getActionMasked(); - if (action == MotionEvent.ACTION_DOWN) { - mCurrentStrokes.clear(); - } - - for (int i = 0; i < event.getPointerCount(); i++) { - int id = event.getPointerId(i); - if (mCurrentStrokes.get(id) == null) { - mCurrentStrokes.put(id, new Stroke(event.getEventTimeNano(), mDpi)); - } - mCurrentStrokes.get(id).addPoint(event.getX(i), event.getY(i), - event.getEventTimeNano()); - - if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL - || (action == MotionEvent.ACTION_POINTER_UP && i == event.getActionIndex())) { - mEndingStrokes.add(getStroke(id)); - } - } - - return true; - } - - public void cleanUp(MotionEvent event) { - mEndingStrokes.clear(); - int action = event.getActionMasked(); - for (int i = 0; i < event.getPointerCount(); i++) { - int id = event.getPointerId(i); - if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL - || (action == MotionEvent.ACTION_POINTER_UP && i == event.getActionIndex())) { - mCurrentStrokes.remove(id); - } - } - } - - /** - * @return the list of Strokes which are ending in the recently added MotionEvent - */ - public ArrayList<Stroke> getEndingStrokes() { - return mEndingStrokes; - } - - /** - * @param id the id from MotionEvent - * @return the Stroke assigned to the id - */ - public Stroke getStroke(int id) { - return mCurrentStrokes.get(id); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DirectionClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DirectionClassifier.java deleted file mode 100644 index 610e21983b5d..000000000000 --- a/packages/SystemUI/src/com/android/systemui/classifier/DirectionClassifier.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.systemui.classifier; - -/** - * A classifier which looks at the general direction of a stroke and evaluates it depending on - * the type of action that takes place. - */ -public class DirectionClassifier extends StrokeClassifier { - public DirectionClassifier(ClassifierData classifierData) { - } - - @Override - public String getTag() { - return "DIR"; - } - - @Override - public float getFalseTouchEvaluation(int type, Stroke stroke) { - Point firstPoint = stroke.getPoints().get(0); - Point lastPoint = stroke.getPoints().get(stroke.getPoints().size() - 1); - return DirectionEvaluator.evaluate(lastPoint.x - firstPoint.x, lastPoint.y - firstPoint.y, - type); - } -}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DirectionEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/DirectionEvaluator.java deleted file mode 100644 index 78b41683a32b..000000000000 --- a/packages/SystemUI/src/com/android/systemui/classifier/DirectionEvaluator.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.systemui.classifier; - -public class DirectionEvaluator { - public static float evaluate(float xDiff, float yDiff, int type) { - float falsingEvaluation = 5.5f; - boolean vertical = Math.abs(yDiff) >= Math.abs(xDiff); - switch (type) { - case Classifier.QUICK_SETTINGS: - case Classifier.PULSE_EXPAND: - case Classifier.NOTIFICATION_DRAG_DOWN: - if (!vertical || yDiff <= 0.0) { - return falsingEvaluation; - } - break; - case Classifier.NOTIFICATION_DISMISS: - if (vertical) { - return falsingEvaluation; - } - break; - case Classifier.UNLOCK: - case Classifier.BOUNCER_UNLOCK: - if (!vertical || yDiff >= 0.0) { - return falsingEvaluation; - } - break; - case Classifier.LEFT_AFFORDANCE: - if (xDiff < 0.0 && yDiff > 0.0) { - return falsingEvaluation; - } - break; - case Classifier.RIGHT_AFFORDANCE: - if (xDiff > 0.0 && yDiff > 0.0) { - return falsingEvaluation; - } - default: - break; - } - return 0.0f; - } -} diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DurationCountClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DurationCountClassifier.java deleted file mode 100644 index 77fda2001f3c..000000000000 --- a/packages/SystemUI/src/com/android/systemui/classifier/DurationCountClassifier.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.systemui.classifier; - -/** - * A classifier which looks at the ratio between the duration of the stroke and its number of - * points. - */ -public class DurationCountClassifier extends StrokeClassifier { - public DurationCountClassifier(ClassifierData classifierData) { - } - - @Override - public String getTag() { - return "DUR"; - } - - @Override - public float getFalseTouchEvaluation(int type, Stroke stroke) { - return DurationCountEvaluator.evaluate(stroke.getDurationSeconds() / stroke.getCount()); - } -}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DurationCountEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/DurationCountEvaluator.java deleted file mode 100644 index 5395983968f7..000000000000 --- a/packages/SystemUI/src/com/android/systemui/classifier/DurationCountEvaluator.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.systemui.classifier; - - -public class DurationCountEvaluator { - public static float evaluate(float value) { - float evaluation = 0.0f; - if (value < 0.0105) evaluation++; - if (value < 0.00909) evaluation++; - if (value < 0.00667) evaluation++; - if (value > 0.0333) evaluation++; - if (value > 0.0500) evaluation++; - return evaluation; - } -} diff --git a/packages/SystemUI/src/com/android/systemui/classifier/EndPointLengthClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/EndPointLengthClassifier.java deleted file mode 100644 index de8a18860c38..000000000000 --- a/packages/SystemUI/src/com/android/systemui/classifier/EndPointLengthClassifier.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.systemui.classifier; - -/** - * A classifier which looks at the distance between the first and the last point from the stroke. - */ -public class EndPointLengthClassifier extends StrokeClassifier { - public EndPointLengthClassifier(ClassifierData classifierData) { - } - - @Override - public String getTag() { - return "END_LNGTH"; - } - - @Override - public float getFalseTouchEvaluation(int type, Stroke stroke) { - return EndPointLengthEvaluator.evaluate(stroke.getEndPointLength()); - } -}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/classifier/EndPointLengthEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/EndPointLengthEvaluator.java deleted file mode 100644 index bb2f1c4e355d..000000000000 --- a/packages/SystemUI/src/com/android/systemui/classifier/EndPointLengthEvaluator.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.systemui.classifier; - -public class EndPointLengthEvaluator { - public static float evaluate(float value) { - float evaluation = 0.0f; - if (value < 0.05) evaluation += 2.0; - if (value < 0.1) evaluation += 2.0; - if (value < 0.2) evaluation += 2.0; - if (value < 0.3) evaluation += 2.0; - if (value < 0.4) evaluation += 2.0; - if (value < 0.5) evaluation += 2.0; - return evaluation; - } -} diff --git a/packages/SystemUI/src/com/android/systemui/classifier/EndPointRatioClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/EndPointRatioClassifier.java deleted file mode 100644 index 9b6ddc8ffb2f..000000000000 --- a/packages/SystemUI/src/com/android/systemui/classifier/EndPointRatioClassifier.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.systemui.classifier; - -/** - * A classifier which looks at the ratio between the total length covered by the stroke and the - * distance between the first and last point from this stroke. - */ -public class EndPointRatioClassifier extends StrokeClassifier { - public EndPointRatioClassifier(ClassifierData classifierData) { - mClassifierData = classifierData; - } - - @Override - public String getTag() { - return "END_RTIO"; - } - - @Override - public float getFalseTouchEvaluation(int type, Stroke stroke) { - float ratio; - if (stroke.getTotalLength() == 0.0f) { - ratio = 1.0f; - } else { - ratio = stroke.getEndPointLength() / stroke.getTotalLength(); - } - return EndPointRatioEvaluator.evaluate(ratio); - } -}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/classifier/EndPointRatioEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/EndPointRatioEvaluator.java deleted file mode 100644 index 529fcec2710e..000000000000 --- a/packages/SystemUI/src/com/android/systemui/classifier/EndPointRatioEvaluator.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.systemui.classifier; - -public class EndPointRatioEvaluator { - public static float evaluate(float value) { - float evaluation = 0.0f; - if (value < 0.85) evaluation++; - if (value < 0.75) evaluation++; - if (value < 0.65) evaluation++; - if (value < 0.55) evaluation++; - if (value < 0.45) evaluation++; - if (value < 0.35) evaluation++; - return evaluation; - } -} diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java new file mode 100644 index 000000000000..c05ce93f0c13 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.classifier; + +import android.view.MotionEvent; + +/** + * Defines a class that can be used to ingest system events for later processing. + */ +public interface FalsingCollector { + /** */ + void onSuccessfulUnlock(); + + /** */ + void onNotificationActive(); + + /** */ + void setShowingAod(boolean showingAod); + + /** */ + void onNotificationStartDraggingDown(); + + /** */ + void onNotificationStopDraggingDown(); + + /** */ + void setNotificationExpanded(); + + /** */ + void onQsDown(); + + /** */ + void setQsExpanded(boolean expanded); + + /** */ + boolean shouldEnforceBouncer(); + + /** */ + void onTrackingStarted(boolean secure); + + /** */ + void onTrackingStopped(); + + /** */ + void onLeftAffordanceOn(); + + /** */ + void onCameraOn(); + + /** */ + void onAffordanceSwipingStarted(boolean rightCorner); + + /** */ + void onAffordanceSwipingAborted(); + + /** */ + void onStartExpandingFromPulse(); + + /** */ + void onExpansionFromPulseStopped(); + + /** */ + void onScreenOnFromTouch(); + + /** */ + boolean isReportingEnabled(); + + /** */ + void onUnlockHintStarted(); + + /** */ + void onCameraHintStarted(); + + /** */ + void onLeftAffordanceHintStarted(); + + /** */ + void onScreenTurningOn(); + + /** */ + void onScreenOff(); + + /** */ + void onNotificationStopDismissing(); + + /** */ + void onNotificationDismissed(); + + /** */ + void onNotificationStartDismissing(); + + /** */ + void onNotificationDoubleTap(boolean accepted, float dx, float dy); + + /** */ + void onBouncerShown(); + + /** */ + void onBouncerHidden(); + + /** */ + void onTouchEvent(MotionEvent ev, int width, int height); + + /** */ + void cleanup(); +} + diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java new file mode 100644 index 000000000000..a5691118261a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.classifier; + +import android.view.MotionEvent; + +/** */ +public class FalsingCollectorFake implements FalsingCollector { + @Override + public void onSuccessfulUnlock() { + } + + @Override + public void onNotificationActive() { + } + + @Override + public void setShowingAod(boolean showingAod) { + } + + @Override + public void onNotificationStartDraggingDown() { + } + + @Override + public void onNotificationStopDraggingDown() { + } + + @Override + public void setNotificationExpanded() { + } + + @Override + public void onQsDown() { + } + + @Override + public void setQsExpanded(boolean expanded) { + } + + @Override + public boolean shouldEnforceBouncer() { + return false; + } + + @Override + public void onTrackingStarted(boolean secure) { + } + + @Override + public void onTrackingStopped() { + } + + @Override + public void onLeftAffordanceOn() { + } + + @Override + public void onCameraOn() { + } + + @Override + public void onAffordanceSwipingStarted(boolean rightCorner) { + } + + @Override + public void onAffordanceSwipingAborted() { + } + + @Override + public void onStartExpandingFromPulse() { + } + + @Override + public void onExpansionFromPulseStopped() { + } + + @Override + public void onScreenOnFromTouch() { + } + + @Override + public boolean isReportingEnabled() { + return false; + } + + @Override + public void onUnlockHintStarted() { + } + + @Override + public void onCameraHintStarted() { + } + + @Override + public void onLeftAffordanceHintStarted() { + } + + @Override + public void onScreenTurningOn() { + } + + @Override + public void onScreenOff() { + } + + @Override + public void onNotificationStopDismissing() { + } + + @Override + public void onNotificationDismissed() { + } + + @Override + public void onNotificationStartDismissing() { + } + + @Override + public void onNotificationDoubleTap(boolean accepted, float dx, float dy) { + } + + @Override + public void onBouncerShown() { + } + + @Override + public void onBouncerHidden() { + } + + @Override + public void onTouchEvent(MotionEvent ev, int width, int height) { + } + + @Override + public void cleanup() { + } +} diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java new file mode 100644 index 000000000000..3547392512a7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java @@ -0,0 +1,321 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.classifier; + +import android.hardware.SensorManager; +import android.hardware.biometrics.BiometricSourceType; +import android.util.Log; +import android.view.MotionEvent; + +import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.keyguard.KeyguardUpdateMonitorCallback; +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.util.sensors.ProximitySensor; +import com.android.systemui.util.sensors.ThresholdSensor; + +import javax.inject.Inject; + +@SysUISingleton +class FalsingCollectorImpl implements FalsingCollector { + + private static final boolean DEBUG = false; + private static final String TAG = "FalsingManager"; + private static final String PROXIMITY_SENSOR_TAG = "FalsingManager"; + + private final FalsingDataProvider mFalsingDataProvider; + private final FalsingManager mFalsingManager; + private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; + private final ProximitySensor mProximitySensor; + private final StatusBarStateController mStatusBarStateController; + + private int mState; + private boolean mShowingAod; + private boolean mScreenOn; + private boolean mSessionStarted; + + private final ThresholdSensor.Listener mSensorEventListener = this::onProximityEvent; + + private final StatusBarStateController.StateListener mStatusBarStateListener = + new StatusBarStateController.StateListener() { + @Override + public void onStateChanged(int newState) { + logDebug("StatusBarState=" + StatusBarState.toShortString(newState)); + mState = newState; + updateSessionActive(); + } + }; + + + private final KeyguardUpdateMonitorCallback mKeyguardUpdateCallback = + new KeyguardUpdateMonitorCallback() { + @Override + public void onBiometricAuthenticated(int userId, + BiometricSourceType biometricSourceType, + boolean isStrongBiometric) { + if (userId == KeyguardUpdateMonitor.getCurrentUser() + && biometricSourceType == BiometricSourceType.FACE) { + mFalsingDataProvider.setJustUnlockedWithFace(true); + } + } + }; + + @Inject + FalsingCollectorImpl(FalsingDataProvider falsingDataProvider, FalsingManager falsingManager, + KeyguardUpdateMonitor keyguardUpdateMonitor, + ProximitySensor proximitySensor, StatusBarStateController statusBarStateController) { + mFalsingDataProvider = falsingDataProvider; + mFalsingManager = falsingManager; + mKeyguardUpdateMonitor = keyguardUpdateMonitor; + mProximitySensor = proximitySensor; + mStatusBarStateController = statusBarStateController; + + + mProximitySensor.setTag(PROXIMITY_SENSOR_TAG); + mProximitySensor.setDelay(SensorManager.SENSOR_DELAY_GAME); + + mStatusBarStateController.addCallback(mStatusBarStateListener); + mState = mStatusBarStateController.getState(); + + mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateCallback); + } + + @Override + public void onSuccessfulUnlock() { + mFalsingManager.onSuccessfulUnlock(); + sessionEnd(); + } + + @Override + public void onNotificationActive() { + } + + @Override + public void setShowingAod(boolean showingAod) { + mShowingAod = showingAod; + updateSessionActive(); + } + + @Override + public void onNotificationStartDraggingDown() { + updateInteractionType(Classifier.NOTIFICATION_DRAG_DOWN); + } + + @Override + public void onNotificationStopDraggingDown() { + } + + @Override + public void setNotificationExpanded() { + } + + @Override + public void onQsDown() { + updateInteractionType(Classifier.QUICK_SETTINGS); + } + + @Override + public void setQsExpanded(boolean expanded) { + if (expanded) { + unregisterSensors(); + } else if (mSessionStarted) { + registerSensors(); + } + } + + @Override + public boolean shouldEnforceBouncer() { + return false; + } + + @Override + public void onTrackingStarted(boolean secure) { + updateInteractionType(secure ? Classifier.BOUNCER_UNLOCK : Classifier.UNLOCK); + } + + @Override + public void onTrackingStopped() { + } + + @Override + public void onLeftAffordanceOn() { + } + + @Override + public void onCameraOn() { + } + + @Override + public void onAffordanceSwipingStarted(boolean rightCorner) { + updateInteractionType( + rightCorner ? Classifier.RIGHT_AFFORDANCE : Classifier.LEFT_AFFORDANCE); + } + + @Override + public void onAffordanceSwipingAborted() { + } + + @Override + public void onStartExpandingFromPulse() { + updateInteractionType(Classifier.PULSE_EXPAND); + } + + @Override + public void onExpansionFromPulseStopped() { + } + + @Override + public void onScreenOnFromTouch() { + onScreenTurningOn(); + } + + @Override + public boolean isReportingEnabled() { + return false; + } + + @Override + public void onUnlockHintStarted() { + } + + @Override + public void onCameraHintStarted() { + } + + @Override + public void onLeftAffordanceHintStarted() { + } + + @Override + public void onScreenTurningOn() { + mScreenOn = true; + updateSessionActive(); + } + + @Override + public void onScreenOff() { + mScreenOn = false; + updateSessionActive(); + } + + @Override + public void onNotificationStopDismissing() { + } + + @Override + public void onNotificationDismissed() { + } + + @Override + public void onNotificationStartDismissing() { + updateInteractionType(Classifier.NOTIFICATION_DISMISS); + } + + @Override + public void onNotificationDoubleTap(boolean accepted, float dx, float dy) { + } + + @Override + public void onBouncerShown() { + unregisterSensors(); + } + + @Override + public void onBouncerHidden() { + if (mSessionStarted) { + registerSensors(); + } + } + + @Override + public void onTouchEvent(MotionEvent ev, int width, int height) { + mFalsingDataProvider.onMotionEvent(ev); + mFalsingManager.onTouchEvent(ev, width, height); + } + + @Override + public void cleanup() { + unregisterSensors(); + mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateCallback); + mStatusBarStateController.removeCallback(mStatusBarStateListener); + } + + private void updateInteractionType(@Classifier.InteractionType int type) { + logDebug("InteractionType: " + type); + mFalsingDataProvider.setInteractionType(type); + } + + private boolean shouldSessionBeActive() { + return mScreenOn && (mState == StatusBarState.KEYGUARD) && !mShowingAod; + } + + private void updateSessionActive() { + if (shouldSessionBeActive()) { + sessionStart(); + } else { + sessionEnd(); + } + } + + private void sessionStart() { + if (!mSessionStarted && shouldSessionBeActive()) { + logDebug("Starting Session"); + mSessionStarted = true; + mFalsingDataProvider.setJustUnlockedWithFace(false); + registerSensors(); + mFalsingDataProvider.onSessionStarted(); + } + } + + private void sessionEnd() { + if (mSessionStarted) { + logDebug("Ending Session"); + mSessionStarted = false; + unregisterSensors(); + mFalsingDataProvider.onSessionEnd(); + } + } + + private void registerSensors() { + if (!mFalsingDataProvider.isWirelessCharging()) { + mProximitySensor.register(mSensorEventListener); + } + } + + private void unregisterSensors() { + mProximitySensor.unregister(mSensorEventListener); + } + + private void onProximityEvent(ThresholdSensor.ThresholdSensorEvent proximityEvent) { + // TODO: some of these classifiers might allow us to abort early, meaning we don't have to + // make these calls. + mFalsingManager.onProximityEvent(proximityEvent); + } + + + static void logDebug(String msg) { + logDebug(msg, null); + } + + static void logDebug(String msg, Throwable throwable) { + if (DEBUG) { + Log.d(TAG, msg, throwable); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java index 8d067489a8cc..b29871c1c3d0 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,63 +14,73 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; import android.util.DisplayMetrics; import android.view.MotionEvent; import android.view.MotionEvent.PointerCoords; import android.view.MotionEvent.PointerProperties; -import com.android.systemui.classifier.Classifier; +import com.android.systemui.classifier.brightline.BrightLineFalsingManager; +import com.android.systemui.classifier.brightline.FalsingClassifier; +import com.android.systemui.classifier.brightline.TimeLimitedMotionEventBuffer; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.policy.BatteryController; +import com.android.systemui.util.time.SystemClock; import java.util.ArrayList; +import java.util.Deque; +import java.util.LinkedList; import java.util.List; +import java.util.Queue; import javax.inject.Inject; /** * Acts as a cache and utility class for FalsingClassifiers. */ +@SysUISingleton public class FalsingDataProvider { private static final long MOTION_EVENT_AGE_MS = 1000; + private static final long EXTENDED_MOTION_EVENT_AGE_MS = 30 * 1000; private static final float THREE_HUNDRED_SIXTY_DEG = (float) (2 * Math.PI); private final int mWidthPixels; private final int mHeightPixels; private final BatteryController mBatteryController; + private final SystemClock mSystemClock; private final float mXdpi; private final float mYdpi; + private final List<SessionListener> mSessionListeners = new ArrayList<>(); private @Classifier.InteractionType int mInteractionType; - private final TimeLimitedMotionEventBuffer mRecentMotionEvents = - new TimeLimitedMotionEventBuffer(MOTION_EVENT_AGE_MS); + private final Deque<TimeLimitedMotionEventBuffer> mExtendedMotionEvents = new LinkedList<>(); + private TimeLimitedMotionEventBuffer mRecentMotionEvents = + new TimeLimitedMotionEventBuffer(MOTION_EVENT_AGE_MS); private boolean mDirty = true; private float mAngle = 0; - private MotionEvent mFirstActualMotionEvent; private MotionEvent mFirstRecentMotionEvent; private MotionEvent mLastMotionEvent; + private boolean mJustUnlockedWithFace; @Inject - public FalsingDataProvider(DisplayMetrics displayMetrics, BatteryController batteryController) { + public FalsingDataProvider(DisplayMetrics displayMetrics, BatteryController batteryController, + SystemClock systemClock) { mXdpi = displayMetrics.xdpi; mYdpi = displayMetrics.ydpi; mWidthPixels = displayMetrics.widthPixels; mHeightPixels = displayMetrics.heightPixels; mBatteryController = batteryController; + mSystemClock = systemClock; FalsingClassifier.logInfo("xdpi, ydpi: " + getXdpi() + ", " + getYdpi()); FalsingClassifier.logInfo("width, height: " + getWidthPixels() + ", " + getHeightPixels()); } void onMotionEvent(MotionEvent motionEvent) { - if (motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) { - mFirstActualMotionEvent = motionEvent; - } - List<MotionEvent> motionEvents = unpackMotionEvent(motionEvent); FalsingClassifier.logDebug("Unpacked into: " + motionEvents.size()); if (BrightLineFalsingManager.DEBUG) { @@ -81,7 +91,10 @@ public class FalsingDataProvider { } if (motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) { - mRecentMotionEvents.clear(); + if (!mRecentMotionEvents.isEmpty()) { + mExtendedMotionEvents.addFirst(mRecentMotionEvents); + mRecentMotionEvents = new TimeLimitedMotionEventBuffer(MOTION_EVENT_AGE_MS); + } } mRecentMotionEvents.addAll(motionEvents); @@ -91,55 +104,72 @@ public class FalsingDataProvider { } /** Returns screen width in pixels. */ - int getWidthPixels() { + public int getWidthPixels() { return mWidthPixels; } /** Returns screen height in pixels. */ - int getHeightPixels() { + public int getHeightPixels() { return mHeightPixels; } - float getXdpi() { + public float getXdpi() { return mXdpi; } - float getYdpi() { + public float getYdpi() { return mYdpi; } - List<MotionEvent> getRecentMotionEvents() { + public List<MotionEvent> getRecentMotionEvents() { return mRecentMotionEvents; } + /** Returns recent gestures, exclusive of the most recent gesture. Newer gestures come first. */ + public Queue<? extends List<MotionEvent>> getHistoricalMotionEvents() { + long nowMs = mSystemClock.uptimeMillis(); + + mExtendedMotionEvents.removeIf( + motionEvents -> motionEvents.isFullyExpired(nowMs - EXTENDED_MOTION_EVENT_AGE_MS)); + + return mExtendedMotionEvents; + } + /** * interactionType is defined by {@link com.android.systemui.classifier.Classifier}. */ - final void setInteractionType(@Classifier.InteractionType int interactionType) { + public final void setInteractionType(@Classifier.InteractionType int interactionType) { if (mInteractionType != interactionType) { mInteractionType = interactionType; mDirty = true; } } + /** + * Returns true if new data has been supplied since the last time this class has been accessed. + */ public boolean isDirty() { return mDirty; } - final int getInteractionType() { + /** Return the interaction type that is being compared against for falsing. */ + public final int getInteractionType() { return mInteractionType; } - MotionEvent getFirstActualMotionEvent() { - return mFirstActualMotionEvent; - } - - MotionEvent getFirstRecentMotionEvent() { + /** + * Get the first recorded {@link MotionEvent} of the most recent gesture. + * + * Note that MotionEvents are not kept forever. As a gesture gets longer in duration, older + * MotionEvents may expire and be ejected. + */ + public MotionEvent getFirstRecentMotionEvent() { recalculateData(); return mFirstRecentMotionEvent; } - MotionEvent getLastMotionEvent() { + /** Get the last recorded {@link MotionEvent}. */ + public MotionEvent getLastMotionEvent() { recalculateData(); return mLastMotionEvent; } @@ -149,12 +179,13 @@ public class FalsingDataProvider { * * The angle will be in radians, always be between 0 and 2*PI, inclusive. */ - float getAngle() { + public float getAngle() { recalculateData(); return mAngle; } - boolean isHorizontal() { + /** Returns if the most recent gesture is more horizontal than vertical. */ + public boolean isHorizontal() { recalculateData(); if (mRecentMotionEvents.isEmpty()) { return false; @@ -164,7 +195,13 @@ public class FalsingDataProvider { .abs(mFirstRecentMotionEvent.getY() - mLastMotionEvent.getY()); } - boolean isRight() { + /** + * Is the most recent gesture more right than left. + * + * This does not mean the gesture is mostly horizontal. Simply that it ended at least one pixel + * to the right of where it started. See also {@link #isHorizontal()}. + */ + public boolean isRight() { recalculateData(); if (mRecentMotionEvents.isEmpty()) { return false; @@ -173,11 +210,18 @@ public class FalsingDataProvider { return mLastMotionEvent.getX() > mFirstRecentMotionEvent.getX(); } - boolean isVertical() { + /** Returns if the most recent gesture is more vertical than horizontal. */ + public boolean isVertical() { return !isHorizontal(); } - boolean isUp() { + /** + * Is the most recent gesture more up than down. + * + * This does not mean the gesture is mostly vertical. Simply that it ended at least one pixel + * higher than it started. See also {@link #isVertical()}. + */ + public boolean isUp() { recalculateData(); if (mRecentMotionEvents.isEmpty()) { return false; @@ -187,7 +231,7 @@ public class FalsingDataProvider { } /** Returns true if phone is being charged without a cable. */ - boolean isWirelessCharging() { + public boolean isWirelessCharging() { return mBatteryController.isWirelessCharging(); } @@ -270,9 +314,21 @@ public class FalsingDataProvider { return motionEvents; } - void onSessionEnd() { - mFirstActualMotionEvent = null; + /** Register a {@link SessionListener}. */ + public void addSessionListener(SessionListener listener) { + mSessionListeners.add(listener); + } + + /** Unregister a {@link SessionListener}. */ + public void removeSessionListener(SessionListener listener) { + mSessionListeners.remove(listener); + } + + void onSessionStarted() { + mSessionListeners.forEach(SessionListener::onSessionStarted); + } + void onSessionEnd() { for (MotionEvent ev : mRecentMotionEvents) { ev.recycle(); } @@ -280,5 +336,24 @@ public class FalsingDataProvider { mRecentMotionEvents.clear(); mDirty = true; + + mSessionListeners.forEach(SessionListener::onSessionEnded); + } + + public boolean isJustUnlockedWithFace() { + return mJustUnlockedWithFace; + } + + public void setJustUnlockedWithFace(boolean justUnlockedWithFace) { + mJustUnlockedWithFace = justUnlockedWithFace; + } + + /** Implement to be alerted abotu the beginning and ending of falsing tracking. */ + public interface SessionListener { + /** Called when the lock screen is shown and falsing-tracking begins. */ + void onSessionStarted(); + + /** Called when the lock screen exits and falsing-tracking ends. */ + void onSessionEnded(); } } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingLog.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingLog.java deleted file mode 100644 index 8105c6a6e940..000000000000 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingLog.java +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.systemui.classifier; - -import android.app.ActivityThread; -import android.app.Application; -import android.os.Build; -import android.os.SystemProperties; -import android.util.Log; - -import java.io.File; -import java.io.IOException; -import java.io.PrintWriter; -import java.text.SimpleDateFormat; -import java.util.ArrayDeque; -import java.util.Date; -import java.util.Locale; - -/** - * Keeps track of interesting falsing data. - * - * By default the log only gets collected on userdebug builds. To turn it on on user: - * adb shell setprop debug.falsing_log true - * - * The log gets dumped as part of the SystemUI services. To dump on demand: - * adb shell dumpsys activity service com.android.systemui StatusBar | grep -A 999 FALSING | less - * - * To dump into logcat: - * adb shell setprop debug.falsing_logcat true - * - * To adjust the log buffer size: - * adb shell setprop debug.falsing_log_size 200 - */ -public class FalsingLog { - public static final boolean ENABLED = SystemProperties.getBoolean("debug.falsing_log", - Build.IS_DEBUGGABLE); - private static final boolean LOGCAT = SystemProperties.getBoolean("debug.falsing_logcat", - false); - - public static final boolean VERBOSE = false; - - private static final int MAX_SIZE = SystemProperties.getInt("debug.falsing_log_size", 100); - - private static final String TAG = "FalsingLog"; - - private final ArrayDeque<String> mLog = new ArrayDeque<>(MAX_SIZE); - private final SimpleDateFormat mFormat = new SimpleDateFormat("MM-dd HH:mm:ss", Locale.US); - - private static FalsingLog sInstance; - - private FalsingLog() { - } - - public static void v(String tag, String s) { - if (!VERBOSE) { - return; - } - if (LOGCAT) { - Log.v(TAG, tag + "\t" + s); - } - log("V", tag, s); - } - - public static void i(String tag, String s) { - if (LOGCAT) { - Log.i(TAG, tag + "\t" + s); - } - log("I", tag, s); - } - - public static void wLogcat(String tag, String s) { - Log.w(TAG, tag + "\t" + s); - log("W", tag, s); - } - - public static void w(String tag, String s) { - if (LOGCAT) { - Log.w(TAG, tag + "\t" + s); - } - log("W", tag, s); - } - - public static void e(String tag, String s) { - if (LOGCAT) { - Log.e(TAG, tag + "\t" + s); - } - log("E", tag, s); - } - - public static synchronized void log(String level, String tag, String s) { - if (!ENABLED) { - return; - } - if (sInstance == null) { - sInstance = new FalsingLog(); - } - - if (sInstance.mLog.size() >= MAX_SIZE) { - sInstance.mLog.removeFirst(); - } - String entry = new StringBuilder().append(sInstance.mFormat.format(new Date())) - .append(" ").append(level).append(" ") - .append(tag).append(" ").append(s).toString(); - sInstance.mLog.add(entry); - } - - public static synchronized void dump(PrintWriter pw) { - pw.println("FALSING LOG:"); - if (!ENABLED) { - pw.println("Disabled, to enable: setprop debug.falsing_log 1"); - pw.println(); - return; - } - if (sInstance == null || sInstance.mLog.isEmpty()) { - pw.println("<empty>"); - pw.println(); - return; - } - for (String s : sInstance.mLog) { - pw.println(s); - } - pw.println(); - } - - public static synchronized void wtf(String tag, String s, Throwable here) { - if (!ENABLED) { - return; - } - e(tag, s); - - Application application = ActivityThread.currentApplication(); - String fileMessage = ""; - if (Build.IS_DEBUGGABLE && application != null) { - File f = new File(application.getDataDir(), "falsing-" - + new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date()) + ".txt"); - PrintWriter pw = null; - try { - pw = new PrintWriter(f); - dump(pw); - pw.close(); - fileMessage = "Log written to " + f.getAbsolutePath(); - } catch (IOException e) { - Log.e(TAG, "Unable to write falsing log", e); - } finally { - if (pw != null) { - pw.close(); - } - } - } else { - Log.e(TAG, "Unable to write log, build must be debuggable."); - } - - Log.wtf(TAG, tag + " " + s + "; " + fileMessage, here); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java index 6961b45c3c37..32d27bcc01a6 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java @@ -21,7 +21,9 @@ import android.view.MotionEvent; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.util.sensors.ThresholdSensor; +import java.io.FileDescriptor; import java.io.PrintWriter; /** @@ -29,31 +31,19 @@ import java.io.PrintWriter; */ public class FalsingManagerFake implements FalsingManager { private boolean mIsFalseTouch; + private boolean mIsFalseTap; + private boolean mIsFalseDoubleTap; private boolean mIsUnlockingDisabled; private boolean mIsClassiferEnabled; private boolean mShouldEnforceBouncer; private boolean mIsReportingEnabled; + private boolean mIsFalseRobustTap; @Override public void onSuccessfulUnlock() { } - @Override - public void onNotificationActive() { - - } - - @Override - public void setShowingAod(boolean showingAod) { - - } - - @Override - public void onNotificatonStartDraggingDown() { - - } - @VisibleForTesting public void setIsUnlockingDisabled(boolean isUnlockingDisabled) { mIsUnlockingDisabled = isUnlockingDisabled; @@ -74,14 +64,26 @@ public class FalsingManagerFake implements FalsingManager { return mIsFalseTouch; } - @Override - public void onNotificatonStopDraggingDown() { + public void setFalseRobustTap(boolean falseRobustTap) { + mIsFalseRobustTap = falseRobustTap; + } + public void setFalseTap(boolean falseTap) { + mIsFalseTap = falseTap; + } + + public void setFalseDoubleTap(boolean falseDoubleTap) { + mIsFalseDoubleTap = falseDoubleTap; } @Override - public void setNotificationExpanded() { + public boolean isFalseTap(boolean robustCheck) { + return robustCheck ? mIsFalseRobustTap : mIsFalseTap; + } + @Override + public boolean isFalseDoubleTap() { + return mIsFalseDoubleTap; } @VisibleForTesting @@ -95,76 +97,15 @@ public class FalsingManagerFake implements FalsingManager { } @Override - public void onQsDown() { - - } - - @Override - public void setQsExpanded(boolean expanded) { - - } - - @VisibleForTesting - public void setShouldEnforceBouncer(boolean shouldEnforceBouncer) { - mShouldEnforceBouncer = shouldEnforceBouncer; - } - - @Override public boolean shouldEnforceBouncer() { return mShouldEnforceBouncer; } @Override - public void onTrackingStarted(boolean secure) { - - } - - @Override - public void onTrackingStopped() { - - } - - @Override - public void onLeftAffordanceOn() { - - } - - @Override - public void onCameraOn() { - - } - - @Override - public void onAffordanceSwipingStarted(boolean rightCorner) { - - } - - @Override - public void onAffordanceSwipingAborted() { - - } - - @Override - public void onStartExpandingFromPulse() { - - } - - @Override - public void onExpansionFromPulseStopped() { - - } - - @Override public Uri reportRejectedTouch() { return null; } - @Override - public void onScreenOnFromTouch() { - - } - - @VisibleForTesting public void setIsReportingEnabled(boolean isReportingEnabled) { mIsReportingEnabled = isReportingEnabled; @@ -176,71 +117,20 @@ public class FalsingManagerFake implements FalsingManager { } @Override - public void onUnlockHintStarted() { - - } - - @Override - public void onCameraHintStarted() { - - } - - @Override - public void onLeftAffordanceHintStarted() { - - } - - @Override - public void onScreenTurningOn() { - - } - - @Override - public void onScreenOff() { - - } - - @Override - public void onNotificationStopDismissing() { - - } - - @Override - public void onNotificationDismissed() { - - } - - @Override - public void onNotificationStartDismissing() { - - } - - @Override - public void onNotificationDoubleTap(boolean accepted, float dx, float dy) { - - } - - @Override - public void onBouncerShown() { + public void onTouchEvent(MotionEvent ev, int width, int height) { } @Override - public void onBouncerHidden() { - + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { } @Override - public void onTouchEvent(MotionEvent ev, int width, int height) { - + public void cleanup() { } @Override - public void dump(PrintWriter pw) { - - } + public void onProximityEvent(ThresholdSensor.ThresholdSensorEvent proximityEvent) { - @Override - public void cleanup() { } } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java deleted file mode 100644 index decaec10e572..000000000000 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java +++ /dev/null @@ -1,579 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.classifier; - -import android.app.ActivityManager; -import android.content.Context; -import android.database.ContentObserver; -import android.hardware.Sensor; -import android.hardware.SensorEvent; -import android.hardware.SensorEventListener; -import android.hardware.SensorManager; -import android.hardware.biometrics.BiometricSourceType; -import android.net.Uri; -import android.os.Handler; -import android.os.Looper; -import android.os.PowerManager; -import android.os.UserHandle; -import android.provider.Settings; -import android.view.InputDevice; -import android.view.MotionEvent; -import android.view.accessibility.AccessibilityManager; - -import com.android.internal.logging.MetricsLogger; -import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.keyguard.KeyguardUpdateMonitorCallback; -import com.android.systemui.Dependency; -import com.android.systemui.analytics.DataCollector; -import com.android.systemui.dagger.qualifiers.UiBackground; -import com.android.systemui.plugins.FalsingManager; -import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; -import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.util.sensors.AsyncSensorManager; - -import java.io.PrintWriter; -import java.util.concurrent.Executor; - -/** - * When the phone is locked, listens to touch, sensor and phone events and sends them to - * DataCollector and HumanInteractionClassifier. - * - * It does not collect touch events when the bouncer shows up. - */ -public class FalsingManagerImpl implements FalsingManager { - private static final String ENFORCE_BOUNCER = "falsing_manager_enforce_bouncer"; - - private static final int[] CLASSIFIER_SENSORS = new int[] { - Sensor.TYPE_PROXIMITY, - }; - - private static final int[] COLLECTOR_SENSORS = new int[] { - Sensor.TYPE_ACCELEROMETER, - Sensor.TYPE_GYROSCOPE, - Sensor.TYPE_PROXIMITY, - Sensor.TYPE_LIGHT, - Sensor.TYPE_ROTATION_VECTOR, - }; - public static final String FALSING_REMAIN_LOCKED = "falsing_failure_after_attempts"; - public static final String FALSING_SUCCESS = "falsing_success_after_attempts"; - - private final Handler mHandler = new Handler(Looper.getMainLooper()); - private final Context mContext; - - private final SensorManager mSensorManager; - private final DataCollector mDataCollector; - private final HumanInteractionClassifier mHumanInteractionClassifier; - private final AccessibilityManager mAccessibilityManager; - private final Executor mUiBgExecutor; - - private boolean mEnforceBouncer = false; - private boolean mBouncerOn = false; - private boolean mBouncerOffOnDown = false; - private boolean mSessionActive = false; - private boolean mIsTouchScreen = true; - private boolean mJustUnlockedWithFace = false; - private int mState = StatusBarState.SHADE; - private boolean mScreenOn; - private boolean mShowingAod; - private Runnable mPendingWtf; - private int mIsFalseTouchCalls; - private MetricsLogger mMetricsLogger; - - private SensorEventListener mSensorEventListener = new SensorEventListener() { - @Override - public synchronized void onSensorChanged(SensorEvent event) { - mDataCollector.onSensorChanged(event); - mHumanInteractionClassifier.onSensorChanged(event); - } - - @Override - public void onAccuracyChanged(Sensor sensor, int accuracy) { - mDataCollector.onAccuracyChanged(sensor, accuracy); - } - }; - - public StateListener mStatusBarStateListener = new StateListener() { - @Override - public void onStateChanged(int newState) { - if (FalsingLog.ENABLED) { - FalsingLog.i("setStatusBarState", new StringBuilder() - .append("from=").append(StatusBarState.toShortString(mState)) - .append(" to=").append(StatusBarState.toShortString(newState)) - .toString()); - } - mState = newState; - updateSessionActive(); - } - }; - - protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) { - @Override - public void onChange(boolean selfChange) { - updateConfiguration(); - } - }; - private final KeyguardUpdateMonitorCallback mKeyguardUpdateCallback = - new KeyguardUpdateMonitorCallback() { - @Override - public void onBiometricAuthenticated(int userId, - BiometricSourceType biometricSourceType, - boolean isStrongBiometric) { - if (userId == KeyguardUpdateMonitor.getCurrentUser() - && biometricSourceType == BiometricSourceType.FACE) { - mJustUnlockedWithFace = true; - } - } - }; - - FalsingManagerImpl(Context context, @UiBackground Executor uiBgExecutor) { - mContext = context; - mSensorManager = Dependency.get(AsyncSensorManager.class); - mAccessibilityManager = context.getSystemService(AccessibilityManager.class); - mDataCollector = DataCollector.getInstance(mContext); - mHumanInteractionClassifier = HumanInteractionClassifier.getInstance(mContext); - mUiBgExecutor = uiBgExecutor; - mScreenOn = context.getSystemService(PowerManager.class).isInteractive(); - mMetricsLogger = new MetricsLogger(); - - mContext.getContentResolver().registerContentObserver( - Settings.Secure.getUriFor(ENFORCE_BOUNCER), false, - mSettingsObserver, - UserHandle.USER_ALL); - - updateConfiguration(); - Dependency.get(StatusBarStateController.class).addCallback(mStatusBarStateListener); - Dependency.get(KeyguardUpdateMonitor.class).registerCallback(mKeyguardUpdateCallback); - } - - private void updateConfiguration() { - mEnforceBouncer = 0 != Settings.Secure.getInt(mContext.getContentResolver(), - ENFORCE_BOUNCER, 0); - } - - private boolean shouldSessionBeActive() { - if (FalsingLog.ENABLED && FalsingLog.VERBOSE) { - FalsingLog.v("shouldBeActive", new StringBuilder() - .append("enabled=").append(isEnabled() ? 1 : 0) - .append(" mScreenOn=").append(mScreenOn ? 1 : 0) - .append(" mState=").append(StatusBarState.toShortString(mState)) - .append(" mShowingAod=").append(mShowingAod ? 1 : 0) - .toString() - ); - } - return isEnabled() && mScreenOn && (mState == StatusBarState.KEYGUARD) && !mShowingAod; - } - - private boolean sessionEntrypoint() { - if (!mSessionActive && shouldSessionBeActive()) { - onSessionStart(); - return true; - } - return false; - } - - private void sessionExitpoint(boolean force) { - if (mSessionActive && (force || !shouldSessionBeActive())) { - mSessionActive = false; - if (mIsFalseTouchCalls != 0) { - if (FalsingLog.ENABLED) { - FalsingLog.i( - "isFalseTouchCalls", "Calls before failure: " + mIsFalseTouchCalls); - } - mMetricsLogger.histogram(FALSING_REMAIN_LOCKED, mIsFalseTouchCalls); - mIsFalseTouchCalls = 0; - } - - // This can be expensive, and doesn't need to happen on the main thread. - mUiBgExecutor.execute(() -> { - mSensorManager.unregisterListener(mSensorEventListener); - }); - } - } - - public void updateSessionActive() { - if (shouldSessionBeActive()) { - sessionEntrypoint(); - } else { - sessionExitpoint(false /* force */); - } - } - - private void onSessionStart() { - if (FalsingLog.ENABLED) { - FalsingLog.i("onSessionStart", "classifierEnabled=" + isClassifierEnabled()); - clearPendingWtf(); - } - mBouncerOn = false; - mSessionActive = true; - mJustUnlockedWithFace = false; - mIsFalseTouchCalls = 0; - - if (mHumanInteractionClassifier.isEnabled()) { - registerSensors(CLASSIFIER_SENSORS); - } - if (mDataCollector.isEnabledFull()) { - registerSensors(COLLECTOR_SENSORS); - } - if (mDataCollector.isEnabled()) { - mDataCollector.onFalsingSessionStarted(); - } - } - - private void registerSensors(int [] sensors) { - for (int sensorType : sensors) { - Sensor s = mSensorManager.getDefaultSensor(sensorType); - if (s != null) { - - // This can be expensive, and doesn't need to happen on the main thread. - mUiBgExecutor.execute(() -> { - mSensorManager.registerListener( - mSensorEventListener, s, SensorManager.SENSOR_DELAY_GAME); - }); - } - } - } - - public boolean isClassifierEnabled() { - return mHumanInteractionClassifier.isEnabled(); - } - - private boolean isEnabled() { - return mHumanInteractionClassifier.isEnabled() || mDataCollector.isEnabled(); - } - - public boolean isUnlockingDisabled() { - return mDataCollector.isUnlockingDisabled(); - } - /** - * @return true if the classifier determined that this is not a human interacting with the phone - */ - public boolean isFalseTouch(@Classifier.InteractionType int interactionType) { - if (FalsingLog.ENABLED) { - // We're getting some false wtfs from touches that happen after the device went - // to sleep. Only report missing sessions that happen when the device is interactive. - if (!mSessionActive && mContext.getSystemService(PowerManager.class).isInteractive() - && mPendingWtf == null) { - int enabled = isEnabled() ? 1 : 0; - int screenOn = mScreenOn ? 1 : 0; - String state = StatusBarState.toShortString(mState); - Throwable here = new Throwable("here"); - FalsingLog.wLogcat("isFalseTouch", new StringBuilder() - .append("Session is not active, yet there's a query for a false touch.") - .append(" enabled=").append(enabled) - .append(" mScreenOn=").append(screenOn) - .append(" mState=").append(state) - .append(". Escalating to WTF if screen does not turn on soon.") - .toString()); - - // Unfortunately we're also getting false positives for touches that happen right - // after the screen turns on, but before that notification has made it to us. - // Unfortunately there's no good way to catch that, except to wait and see if we get - // the screen on notification soon. - mPendingWtf = () -> FalsingLog.wtf("isFalseTouch", new StringBuilder() - .append("Session did not become active after query for a false touch.") - .append(" enabled=").append(enabled) - .append('/').append(isEnabled() ? 1 : 0) - .append(" mScreenOn=").append(screenOn) - .append('/').append(mScreenOn ? 1 : 0) - .append(" mState=").append(state) - .append('/').append(StatusBarState.toShortString(mState)) - .append(". Look for warnings ~1000ms earlier to see root cause.") - .toString(), here); - mHandler.postDelayed(mPendingWtf, 1000); - } - } - if (ActivityManager.isRunningInUserTestHarness()) { - // This is a test device running UiAutomator code. - return false; - } - if (mAccessibilityManager.isTouchExplorationEnabled()) { - // Touch exploration triggers false positives in the classifier and - // already sufficiently prevents false unlocks. - return false; - } - if (!mIsTouchScreen) { - // Unlocking with input devices besides the touchscreen should already be sufficiently - // anti-falsed. - return false; - } - if (mJustUnlockedWithFace) { - // Unlocking with face is a strong user presence signal, we can assume the user - // is present until the next session starts. - return false; - } - mIsFalseTouchCalls++; - boolean isFalse = mHumanInteractionClassifier.isFalseTouch(); - if (!isFalse) { - if (FalsingLog.ENABLED) { - FalsingLog.i("isFalseTouchCalls", "Calls before success: " + mIsFalseTouchCalls); - } - mMetricsLogger.histogram(FALSING_SUCCESS, mIsFalseTouchCalls); - mIsFalseTouchCalls = 0; - } - return isFalse; - } - - private void clearPendingWtf() { - if (mPendingWtf != null) { - mHandler.removeCallbacks(mPendingWtf); - mPendingWtf = null; - } - } - - - public boolean shouldEnforceBouncer() { - return mEnforceBouncer; - } - - public void setShowingAod(boolean showingAod) { - mShowingAod = showingAod; - updateSessionActive(); - } - - public void onScreenTurningOn() { - if (FalsingLog.ENABLED) { - FalsingLog.i("onScreenTurningOn", new StringBuilder() - .append("from=").append(mScreenOn ? 1 : 0) - .toString()); - clearPendingWtf(); - } - mScreenOn = true; - if (sessionEntrypoint()) { - mDataCollector.onScreenTurningOn(); - } - } - - public void onScreenOnFromTouch() { - if (FalsingLog.ENABLED) { - FalsingLog.i("onScreenOnFromTouch", new StringBuilder() - .append("from=").append(mScreenOn ? 1 : 0) - .toString()); - } - mScreenOn = true; - if (sessionEntrypoint()) { - mDataCollector.onScreenOnFromTouch(); - } - } - - public void onScreenOff() { - if (FalsingLog.ENABLED) { - FalsingLog.i("onScreenOff", new StringBuilder() - .append("from=").append(mScreenOn ? 1 : 0) - .toString()); - } - mDataCollector.onScreenOff(); - mScreenOn = false; - sessionExitpoint(false /* force */); - } - - public void onSuccessfulUnlock() { - if (FalsingLog.ENABLED) { - FalsingLog.i("onSucccessfulUnlock", ""); - } - mDataCollector.onSucccessfulUnlock(); - } - - public void onBouncerShown() { - if (FalsingLog.ENABLED) { - FalsingLog.i("onBouncerShown", new StringBuilder() - .append("from=").append(mBouncerOn ? 1 : 0) - .toString()); - } - if (!mBouncerOn) { - mBouncerOn = true; - mDataCollector.onBouncerShown(); - } - } - - public void onBouncerHidden() { - if (FalsingLog.ENABLED) { - FalsingLog.i("onBouncerHidden", new StringBuilder() - .append("from=").append(mBouncerOn ? 1 : 0) - .toString()); - } - if (mBouncerOn) { - mBouncerOn = false; - mDataCollector.onBouncerHidden(); - } - } - - public void onQsDown() { - if (FalsingLog.ENABLED) { - FalsingLog.i("onQsDown", ""); - } - mHumanInteractionClassifier.setType(Classifier.QUICK_SETTINGS); - mDataCollector.onQsDown(); - } - - public void setQsExpanded(boolean expanded) { - mDataCollector.setQsExpanded(expanded); - } - - public void onTrackingStarted(boolean secure) { - if (FalsingLog.ENABLED) { - FalsingLog.i("onTrackingStarted", ""); - } - mHumanInteractionClassifier.setType(secure - ? Classifier.BOUNCER_UNLOCK : Classifier.UNLOCK); - mDataCollector.onTrackingStarted(); - } - - public void onTrackingStopped() { - mDataCollector.onTrackingStopped(); - } - - public void onNotificationActive() { - mDataCollector.onNotificationActive(); - } - - public void onNotificationDoubleTap(boolean accepted, float dx, float dy) { - if (FalsingLog.ENABLED) { - FalsingLog.i("onNotificationDoubleTap", "accepted=" + accepted - + " dx=" + dx + " dy=" + dy + " (px)"); - } - mDataCollector.onNotificationDoubleTap(); - } - - public void setNotificationExpanded() { - mDataCollector.setNotificationExpanded(); - } - - public void onNotificatonStartDraggingDown() { - if (FalsingLog.ENABLED) { - FalsingLog.i("onNotificatonStartDraggingDown", ""); - } - mHumanInteractionClassifier.setType(Classifier.NOTIFICATION_DRAG_DOWN); - mDataCollector.onNotificatonStartDraggingDown(); - } - - public void onStartExpandingFromPulse() { - if (FalsingLog.ENABLED) { - FalsingLog.i("onStartExpandingFromPulse", ""); - } - mHumanInteractionClassifier.setType(Classifier.PULSE_EXPAND); - mDataCollector.onStartExpandingFromPulse(); - } - - public void onNotificatonStopDraggingDown() { - mDataCollector.onNotificatonStopDraggingDown(); - } - - public void onExpansionFromPulseStopped() { - mDataCollector.onExpansionFromPulseStopped(); - } - - public void onNotificationDismissed() { - mDataCollector.onNotificationDismissed(); - } - - public void onNotificationStartDismissing() { - if (FalsingLog.ENABLED) { - FalsingLog.i("onNotificationStartDismissing", ""); - } - mHumanInteractionClassifier.setType(Classifier.NOTIFICATION_DISMISS); - mDataCollector.onNotificatonStartDismissing(); - } - - public void onNotificationStopDismissing() { - mDataCollector.onNotificatonStopDismissing(); - } - - public void onCameraOn() { - mDataCollector.onCameraOn(); - } - - public void onLeftAffordanceOn() { - mDataCollector.onLeftAffordanceOn(); - } - - public void onAffordanceSwipingStarted(boolean rightCorner) { - if (FalsingLog.ENABLED) { - FalsingLog.i("onAffordanceSwipingStarted", ""); - } - if (rightCorner) { - mHumanInteractionClassifier.setType(Classifier.RIGHT_AFFORDANCE); - } else { - mHumanInteractionClassifier.setType(Classifier.LEFT_AFFORDANCE); - } - mDataCollector.onAffordanceSwipingStarted(rightCorner); - } - - public void onAffordanceSwipingAborted() { - mDataCollector.onAffordanceSwipingAborted(); - } - - public void onUnlockHintStarted() { - mDataCollector.onUnlockHintStarted(); - } - - public void onCameraHintStarted() { - mDataCollector.onCameraHintStarted(); - } - - public void onLeftAffordanceHintStarted() { - mDataCollector.onLeftAffordanceHintStarted(); - } - - public void onTouchEvent(MotionEvent event, int width, int height) { - if (event.getAction() == MotionEvent.ACTION_DOWN) { - mIsTouchScreen = event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN); - // If the bouncer was not shown during the down event, - // we want the entire gesture going to HumanInteractionClassifier - mBouncerOffOnDown = !mBouncerOn; - } - if (mSessionActive) { - if (!mBouncerOn) { - // In case bouncer is "visible", but onFullyShown has not yet been called, - // avoid adding the event to DataCollector - mDataCollector.onTouchEvent(event, width, height); - } - if (mBouncerOffOnDown) { - mHumanInteractionClassifier.onTouchEvent(event); - } - } - } - - public void dump(PrintWriter pw) { - pw.println("FALSING MANAGER"); - pw.print("classifierEnabled="); pw.println(isClassifierEnabled() ? 1 : 0); - pw.print("mSessionActive="); pw.println(mSessionActive ? 1 : 0); - pw.print("mBouncerOn="); pw.println(mSessionActive ? 1 : 0); - pw.print("mState="); pw.println(StatusBarState.toShortString(mState)); - pw.print("mScreenOn="); pw.println(mScreenOn ? 1 : 0); - pw.println(); - } - - @Override - public void cleanup() { - mSensorManager.unregisterListener(mSensorEventListener); - mContext.getContentResolver().unregisterContentObserver(mSettingsObserver); - Dependency.get(StatusBarStateController.class).removeCallback(mStatusBarStateListener); - Dependency.get(KeyguardUpdateMonitor.class).removeCallback(mKeyguardUpdateCallback); - } - - public Uri reportRejectedTouch() { - if (mDataCollector.isEnabled()) { - return mDataCollector.reportRejectedTouch(); - } - return null; - } - - public boolean isReportingEnabled() { - return mDataCollector.isReportingEnabled(); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java index 2c31862e9b79..74629411c13d 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java @@ -16,89 +16,69 @@ package com.android.systemui.classifier; -import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_MANAGER_ENABLED; - import android.content.Context; -import android.hardware.SensorManager; import android.net.Uri; import android.provider.DeviceConfig; import android.view.MotionEvent; import androidx.annotation.NonNull; -import com.android.internal.annotations.VisibleForTesting; -import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.Dumpable; import com.android.systemui.classifier.brightline.BrightLineFalsingManager; -import com.android.systemui.classifier.brightline.FalsingDataProvider; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.dagger.qualifiers.UiBackground; -import com.android.systemui.dock.DockManager; import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.FalsingPlugin; import com.android.systemui.plugins.PluginListener; -import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.util.DeviceConfigProxy; -import com.android.systemui.util.sensors.ProximitySensor; +import com.android.systemui.util.sensors.ThresholdSensor; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.concurrent.Executor; import javax.inject.Inject; +import javax.inject.Provider; /** * Simple passthrough implementation of {@link FalsingManager} allowing plugins to swap in. * - * {@link FalsingManagerImpl} is used when a Plugin is not loaded. + * {@link BrightLineFalsingManager} is used when a Plugin is not loaded. */ @SysUISingleton public class FalsingManagerProxy implements FalsingManager, Dumpable { - private static final String PROXIMITY_SENSOR_TAG = "FalsingManager"; + private static final String DUMPABLE_TAG = "FalsingManager"; + public static final String FALSING_REMAIN_LOCKED = "falsing_failure_after_attempts"; + public static final String FALSING_SUCCESS = "falsing_success_after_attempts"; - private final ProximitySensor mProximitySensor; - private final FalsingDataProvider mFalsingDataProvider; - private FalsingManager mInternalFalsingManager; - private DeviceConfig.OnPropertiesChangedListener mDeviceConfigListener; + private final PluginManager mPluginManager; private final DeviceConfigProxy mDeviceConfig; - private boolean mBrightlineEnabled; - private final DockManager mDockManager; - private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; - private Executor mUiBgExecutor; - private final StatusBarStateController mStatusBarStateController; + private final Provider<BrightLineFalsingManager> mBrightLineFalsingManagerProvider; + private final DumpManager mDumpManager; + final PluginListener<FalsingPlugin> mPluginListener; + + private FalsingManager mInternalFalsingManager; + + private final DeviceConfig.OnPropertiesChangedListener mDeviceConfigListener = + properties -> onDeviceConfigPropertiesChanged(properties.getNamespace()); @Inject - FalsingManagerProxy(Context context, PluginManager pluginManager, @Main Executor executor, - ProximitySensor proximitySensor, - DeviceConfigProxy deviceConfig, DockManager dockManager, - KeyguardUpdateMonitor keyguardUpdateMonitor, - DumpManager dumpManager, - @UiBackground Executor uiBgExecutor, - StatusBarStateController statusBarStateController, - FalsingDataProvider falsingDataProvider) { - mProximitySensor = proximitySensor; - mDockManager = dockManager; - mKeyguardUpdateMonitor = keyguardUpdateMonitor; - mUiBgExecutor = uiBgExecutor; - mStatusBarStateController = statusBarStateController; - mFalsingDataProvider = falsingDataProvider; - mProximitySensor.setTag(PROXIMITY_SENSOR_TAG); - mProximitySensor.setDelay(SensorManager.SENSOR_DELAY_GAME); + FalsingManagerProxy(PluginManager pluginManager, @Main Executor executor, + DeviceConfigProxy deviceConfig, DumpManager dumpManager, + Provider<BrightLineFalsingManager> brightLineFalsingManagerProvider) { + mPluginManager = pluginManager; + mDumpManager = dumpManager; mDeviceConfig = deviceConfig; - mDeviceConfigListener = - properties -> onDeviceConfigPropertiesChanged(context, properties.getNamespace()); - setupFalsingManager(context); + mBrightLineFalsingManagerProvider = brightLineFalsingManagerProvider; + setupFalsingManager(); mDeviceConfig.addOnPropertiesChangedListener( - DeviceConfig.NAMESPACE_SYSTEMUI, - executor, - mDeviceConfigListener + DeviceConfig.NAMESPACE_SYSTEMUI, executor, mDeviceConfigListener ); - final PluginListener<FalsingPlugin> mPluginListener = new PluginListener<FalsingPlugin>() { + mPluginListener = new PluginListener<FalsingPlugin>() { public void onPluginConnected(FalsingPlugin plugin, Context context) { FalsingManager pluginFalsingManager = plugin.getFalsingManager(context); if (pluginFalsingManager != null) { @@ -108,57 +88,33 @@ public class FalsingManagerProxy implements FalsingManager, Dumpable { } public void onPluginDisconnected(FalsingPlugin plugin) { - mInternalFalsingManager = new FalsingManagerImpl(context, mUiBgExecutor); + setupFalsingManager(); } }; - pluginManager.addPluginListener(mPluginListener, FalsingPlugin.class); + mPluginManager.addPluginListener(mPluginListener, FalsingPlugin.class); - dumpManager.registerDumpable("FalsingManager", this); + mDumpManager.registerDumpable(DUMPABLE_TAG, this); } - private void onDeviceConfigPropertiesChanged(Context context, String namespace) { + private void onDeviceConfigPropertiesChanged(String namespace) { if (!DeviceConfig.NAMESPACE_SYSTEMUI.equals(namespace)) { return; } - setupFalsingManager(context); + setupFalsingManager(); } /** - * Chooses the FalsingManager implementation. + * Setup the FalsingManager implementation. + * + * If multiple implementations are available, this is where the choice is made. */ - private void setupFalsingManager(Context context) { - boolean brightlineEnabled = mDeviceConfig.getBoolean( - DeviceConfig.NAMESPACE_SYSTEMUI, BRIGHTLINE_FALSING_MANAGER_ENABLED, true); - if (brightlineEnabled == mBrightlineEnabled && mInternalFalsingManager != null) { - return; - } - mBrightlineEnabled = brightlineEnabled; - + private void setupFalsingManager() { if (mInternalFalsingManager != null) { mInternalFalsingManager.cleanup(); } - if (!brightlineEnabled) { - mInternalFalsingManager = new FalsingManagerImpl(context, mUiBgExecutor); - } else { - mInternalFalsingManager = new BrightLineFalsingManager( - mFalsingDataProvider, - mKeyguardUpdateMonitor, - mProximitySensor, - mDeviceConfig, - mDockManager, - mStatusBarStateController - ); - } - } - - /** - * Returns the FalsingManager implementation in use. - */ - @VisibleForTesting - FalsingManager getInternalFalsingManager() { - return mInternalFalsingManager; + mInternalFalsingManager = mBrightLineFalsingManagerProvider.get(); } @Override @@ -167,21 +123,6 @@ public class FalsingManagerProxy implements FalsingManager, Dumpable { } @Override - public void onNotificationActive() { - mInternalFalsingManager.onNotificationActive(); - } - - @Override - public void setShowingAod(boolean showingAod) { - mInternalFalsingManager.setShowingAod(showingAod); - } - - @Override - public void onNotificatonStartDraggingDown() { - mInternalFalsingManager.onNotificatonStartDraggingDown(); - } - - @Override public boolean isUnlockingDisabled() { return mInternalFalsingManager.isUnlockingDisabled(); } @@ -192,13 +133,13 @@ public class FalsingManagerProxy implements FalsingManager, Dumpable { } @Override - public void onNotificatonStopDraggingDown() { - mInternalFalsingManager.onNotificatonStartDraggingDown(); + public boolean isFalseTap(boolean robustCheck) { + return mInternalFalsingManager.isFalseTap(robustCheck); } @Override - public void setNotificationExpanded() { - mInternalFalsingManager.setNotificationExpanded(); + public boolean isFalseDoubleTap() { + return mInternalFalsingManager.isFalseDoubleTap(); } @Override @@ -207,148 +148,40 @@ public class FalsingManagerProxy implements FalsingManager, Dumpable { } @Override - public void onQsDown() { - mInternalFalsingManager.onQsDown(); - } - - @Override - public void setQsExpanded(boolean expanded) { - mInternalFalsingManager.setQsExpanded(expanded); - } - - @Override public boolean shouldEnforceBouncer() { return mInternalFalsingManager.shouldEnforceBouncer(); } @Override - public void onTrackingStarted(boolean secure) { - mInternalFalsingManager.onTrackingStarted(secure); - } - - @Override - public void onTrackingStopped() { - mInternalFalsingManager.onTrackingStopped(); - } - - @Override - public void onLeftAffordanceOn() { - mInternalFalsingManager.onLeftAffordanceOn(); - } - - @Override - public void onCameraOn() { - mInternalFalsingManager.onCameraOn(); - } - - @Override - public void onAffordanceSwipingStarted(boolean rightCorner) { - mInternalFalsingManager.onAffordanceSwipingStarted(rightCorner); - } - - @Override - public void onAffordanceSwipingAborted() { - mInternalFalsingManager.onAffordanceSwipingAborted(); - } - - @Override - public void onStartExpandingFromPulse() { - mInternalFalsingManager.onStartExpandingFromPulse(); - } - - @Override - public void onExpansionFromPulseStopped() { - mInternalFalsingManager.onExpansionFromPulseStopped(); - } - - @Override public Uri reportRejectedTouch() { return mInternalFalsingManager.reportRejectedTouch(); } @Override - public void onScreenOnFromTouch() { - mInternalFalsingManager.onScreenOnFromTouch(); - } - - @Override public boolean isReportingEnabled() { return mInternalFalsingManager.isReportingEnabled(); } @Override - public void onUnlockHintStarted() { - mInternalFalsingManager.onUnlockHintStarted(); - } - - @Override - public void onCameraHintStarted() { - mInternalFalsingManager.onCameraHintStarted(); - } - - @Override - public void onLeftAffordanceHintStarted() { - mInternalFalsingManager.onLeftAffordanceHintStarted(); - } - - @Override - public void onScreenTurningOn() { - mInternalFalsingManager.onScreenTurningOn(); - } - - @Override - public void onScreenOff() { - mInternalFalsingManager.onScreenOff(); - } - - @Override - public void onNotificationStopDismissing() { - mInternalFalsingManager.onNotificationStopDismissing(); - } - - @Override - public void onNotificationDismissed() { - mInternalFalsingManager.onNotificationDismissed(); - } - - @Override - public void onNotificationStartDismissing() { - mInternalFalsingManager.onNotificationStartDismissing(); - } - - @Override - public void onNotificationDoubleTap(boolean accepted, float dx, float dy) { - mInternalFalsingManager.onNotificationDoubleTap(accepted, dx, dy); - } - - @Override - public void onBouncerShown() { - mInternalFalsingManager.onBouncerShown(); - } - - @Override - public void onBouncerHidden() { - mInternalFalsingManager.onBouncerHidden(); - } - - @Override public void onTouchEvent(MotionEvent ev, int width, int height) { mInternalFalsingManager.onTouchEvent(ev, width, height); } @Override - public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { - mInternalFalsingManager.dump(pw); + public void onProximityEvent(ThresholdSensor.ThresholdSensorEvent proximityEvent) { + mInternalFalsingManager.onProximityEvent(proximityEvent); } @Override - public void dump(PrintWriter pw) { - mInternalFalsingManager.dump(pw); + public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { + mInternalFalsingManager.dump(fd, pw, args); } @Override public void cleanup() { mDeviceConfig.removeOnPropertiesChangedListener(mDeviceConfigListener); + mPluginManager.removePluginListener(mPluginListener); + mDumpManager.unregisterDumpable(DUMPABLE_TAG); mInternalFalsingManager.cleanup(); } } diff --git a/core/proto/android/stats/accessibility/accessibility_enums.proto b/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java index 5118ad5a322c..937bcbaa6222 100644 --- a/core/proto/android/stats/accessibility/accessibility_enums.proto +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java @@ -14,22 +14,18 @@ * limitations under the License. */ -syntax = "proto2"; -package android.stats.accessibility; -option java_multiple_files = true; +package com.android.systemui.classifier; -// The entry point of the accessibility shortcut. -enum ShortcutType { - UNKNOWN_TYPE = 0; - A11Y_BUTTON = 1; - VOLUME_KEY = 2; - TRIPLE_TAP = 3; - A11Y_BUTTON_LONG_PRESS = 4; -} +import com.android.systemui.dagger.SysUISingleton; + +import dagger.Binds; +import dagger.Module; -// The service status code. -enum ServiceStatus { - UNKNOWN = 0; - ENABLED = 1; - DISABLED = 2; -}
\ No newline at end of file +/** Dagger Module for Falsing. */ +@Module +public interface FalsingModule { + /** */ + @Binds + @SysUISingleton + FalsingCollector bindsFalsingCollector(FalsingCollectorImpl impl); +} diff --git a/packages/SystemUI/src/com/android/systemui/classifier/GestureClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/GestureClassifier.java deleted file mode 100644 index 11388fc49594..000000000000 --- a/packages/SystemUI/src/com/android/systemui/classifier/GestureClassifier.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.systemui.classifier; - -/** - * An abstract class for classifiers which classify the whole gesture (all the strokes which - * occurred from DOWN event to UP/CANCEL event) - */ -public abstract class GestureClassifier extends Classifier { - - /** - * @param type the type of action for which this method is called - * @return a non-negative value which is used to determine whether the most recent gesture is a - * false interaction; the bigger the value the greater the chance that this a false - * interaction. - */ - public abstract float getFalseTouchEvaluation(int type); -} diff --git a/packages/SystemUI/src/com/android/systemui/classifier/HistoryEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/HistoryEvaluator.java deleted file mode 100644 index 4c647118a105..000000000000 --- a/packages/SystemUI/src/com/android/systemui/classifier/HistoryEvaluator.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.systemui.classifier; - -import android.os.SystemClock; - -import java.util.ArrayList; - -/** - * Holds the evaluations for ended strokes and gestures. These values are decreased through time. - */ -public class HistoryEvaluator { - private static final float INTERVAL = 50.0f; - private static final float HISTORY_FACTOR = 0.9f; - private static final float EPSILON = 1e-5f; - - private final ArrayList<Data> mStrokes = new ArrayList<>(); - private final ArrayList<Data> mGestureWeights = new ArrayList<>(); - private long mLastUpdate; - - public HistoryEvaluator() { - mLastUpdate = SystemClock.elapsedRealtime(); - } - - public void addStroke(float evaluation) { - decayValue(); - mStrokes.add(new Data(evaluation)); - } - - public void addGesture(float evaluation) { - decayValue(); - mGestureWeights.add(new Data(evaluation)); - } - - /** - * Calculates the weighted average of strokes and adds to it the weighted average of gestures - */ - public float getEvaluation() { - return weightedAverage(mStrokes) + weightedAverage(mGestureWeights); - } - - private float weightedAverage(ArrayList<Data> list) { - float sumValue = 0.0f; - float sumWeight = 0.0f; - int size = list.size(); - for (int i = 0; i < size; i++) { - Data data = list.get(i); - sumValue += data.evaluation * data.weight; - sumWeight += data.weight; - } - - if (sumWeight == 0.0f) { - return 0.0f; - } - - return sumValue / sumWeight; - } - - private void decayValue() { - long time = SystemClock.elapsedRealtime(); - - if (time <= mLastUpdate) { - return; - } - - // All weights are multiplied by HISTORY_FACTOR after each INTERVAL milliseconds. - float factor = (float) Math.pow(HISTORY_FACTOR, (time - mLastUpdate) / INTERVAL); - - decayValue(mStrokes, factor); - decayValue(mGestureWeights, factor); - mLastUpdate = time; - } - - private void decayValue(ArrayList<Data> list, float factor) { - int size = list.size(); - for (int i = 0; i < size; i++) { - list.get(i).weight *= factor; - } - - // Removing evaluations with such small weights that they do not matter anymore - while (!list.isEmpty() && isZero(list.get(0).weight)) { - list.remove(0); - } - } - - private boolean isZero(float x) { - return x <= EPSILON && x >= -EPSILON; - } - - /** - * For each stroke it holds its initial value and the current weight. Initially the - * weight is set to 1.0 - */ - private static class Data { - public float evaluation; - public float weight; - - public Data(float evaluation) { - this.evaluation = evaluation; - weight = 1.0f; - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java deleted file mode 100644 index 86dccb222875..000000000000 --- a/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java +++ /dev/null @@ -1,241 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.systemui.classifier; - -import android.content.Context; -import android.database.ContentObserver; -import android.hardware.SensorEvent; -import android.os.Handler; -import android.os.Looper; -import android.os.UserHandle; -import android.provider.Settings; -import android.util.DisplayMetrics; -import android.view.MotionEvent; - -import com.android.systemui.R; - -import java.util.ArrayDeque; - -/** - * An classifier trying to determine whether it is a human interacting with the phone or not. - */ -public class HumanInteractionClassifier extends Classifier { - private static final String HIC_ENABLE = "HIC_enable"; - private static final float FINGER_DISTANCE = 0.1f; - - private static HumanInteractionClassifier sInstance = null; - - private final Handler mHandler = new Handler(Looper.getMainLooper()); - private final Context mContext; - - private final StrokeClassifier[] mStrokeClassifiers; - private final GestureClassifier[] mGestureClassifiers; - private final ArrayDeque<MotionEvent> mBufferedEvents = new ArrayDeque<>(); - private final HistoryEvaluator mHistoryEvaluator; - private final float mDpi; - - private boolean mEnableClassifier = false; - private int mCurrentType = Classifier.GENERIC; - - protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) { - @Override - public void onChange(boolean selfChange) { - updateConfiguration(); - } - }; - - private HumanInteractionClassifier(Context context) { - mContext = context; - DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics(); - - // If the phone is rotated to landscape, the calculations would be wrong if xdpi and ydpi - // were to be used separately. Due negligible differences in xdpi and ydpi we can just - // take the average. - // Note that xdpi and ydpi are the physical pixels per inch and are not affected by scaling. - mDpi = (displayMetrics.xdpi + displayMetrics.ydpi) / 2.0f; - mClassifierData = new ClassifierData(mDpi); - mHistoryEvaluator = new HistoryEvaluator(); - - mStrokeClassifiers = new StrokeClassifier[]{ - new AnglesClassifier(mClassifierData), - new SpeedClassifier(mClassifierData), - new DurationCountClassifier(mClassifierData), - new EndPointRatioClassifier(mClassifierData), - new EndPointLengthClassifier(mClassifierData), - new AccelerationClassifier(mClassifierData), - new SpeedAnglesClassifier(mClassifierData), - new LengthCountClassifier(mClassifierData), - new DirectionClassifier(mClassifierData), - }; - - mGestureClassifiers = new GestureClassifier[] { - new PointerCountClassifier(mClassifierData), - new ProximityClassifier(mClassifierData) - }; - - mContext.getContentResolver().registerContentObserver( - Settings.Global.getUriFor(HIC_ENABLE), false, - mSettingsObserver, - UserHandle.USER_ALL); - - updateConfiguration(); - } - - public static HumanInteractionClassifier getInstance(Context context) { - if (sInstance == null) { - sInstance = new HumanInteractionClassifier(context); - } - return sInstance; - } - - private void updateConfiguration() { - boolean defaultValue = mContext.getResources().getBoolean( - R.bool.config_lockscreenAntiFalsingClassifierEnabled); - - mEnableClassifier = 0 != Settings.Global.getInt( - mContext.getContentResolver(), - HIC_ENABLE, defaultValue ? 1 : 0); - } - - public void setType(int type) { - mCurrentType = type; - } - - @Override - public void onTouchEvent(MotionEvent event) { - if (!mEnableClassifier) { - return; - } - - // If the user is dragging down the notification, they might want to drag it down - // enough to see the content, read it for a while and then lift the finger to open - // the notification. This kind of motion scores very bad in the Classifier so the - // MotionEvents which are close to the current position of the finger are not - // sent to the classifiers until the finger moves far enough. When the finger if lifted - // up, the last MotionEvent which was far enough from the finger is set as the final - // MotionEvent and sent to the Classifiers. - if (mCurrentType == Classifier.NOTIFICATION_DRAG_DOWN - || mCurrentType == Classifier.PULSE_EXPAND) { - mBufferedEvents.add(MotionEvent.obtain(event)); - Point pointEnd = new Point(event.getX() / mDpi, event.getY() / mDpi); - - while (pointEnd.dist(new Point(mBufferedEvents.getFirst().getX() / mDpi, - mBufferedEvents.getFirst().getY() / mDpi)) > FINGER_DISTANCE) { - addTouchEvent(mBufferedEvents.getFirst()); - mBufferedEvents.remove(); - } - - int action = event.getActionMasked(); - if (action == MotionEvent.ACTION_UP) { - mBufferedEvents.getFirst().setAction(MotionEvent.ACTION_UP); - addTouchEvent(mBufferedEvents.getFirst()); - mBufferedEvents.clear(); - } - } else { - addTouchEvent(event); - } - } - - private void addTouchEvent(MotionEvent event) { - if (!mClassifierData.update(event)) { - return; - } - - for (StrokeClassifier c : mStrokeClassifiers) { - c.onTouchEvent(event); - } - - for (GestureClassifier c : mGestureClassifiers) { - c.onTouchEvent(event); - } - - int size = mClassifierData.getEndingStrokes().size(); - for (int i = 0; i < size; i++) { - Stroke stroke = mClassifierData.getEndingStrokes().get(i); - float evaluation = 0.0f; - StringBuilder sb = FalsingLog.ENABLED ? new StringBuilder("stroke") : null; - for (StrokeClassifier c : mStrokeClassifiers) { - float e = c.getFalseTouchEvaluation(mCurrentType, stroke); - if (FalsingLog.ENABLED) { - String tag = c.getTag(); - sb.append(" ").append(e >= 1f ? tag : tag.toLowerCase()).append("=").append(e); - } - evaluation += e; - } - - if (FalsingLog.ENABLED) { - FalsingLog.i(" addTouchEvent", sb.toString()); - } - mHistoryEvaluator.addStroke(evaluation); - } - - int action = event.getActionMasked(); - if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { - float evaluation = 0.0f; - StringBuilder sb = FalsingLog.ENABLED ? new StringBuilder("gesture") : null; - for (GestureClassifier c : mGestureClassifiers) { - float e = c.getFalseTouchEvaluation(mCurrentType); - if (FalsingLog.ENABLED) { - String tag = c.getTag(); - sb.append(" ").append(e >= 1f ? tag : tag.toLowerCase()).append("=").append(e); - } - evaluation += e; - } - if (FalsingLog.ENABLED) { - FalsingLog.i(" addTouchEvent", sb.toString()); - } - mHistoryEvaluator.addGesture(evaluation); - setType(Classifier.GENERIC); - } - - mClassifierData.cleanUp(event); - } - - @Override - public void onSensorChanged(SensorEvent event) { - for (Classifier c : mStrokeClassifiers) { - c.onSensorChanged(event); - } - - for (Classifier c : mGestureClassifiers) { - c.onSensorChanged(event); - } - } - - public boolean isFalseTouch() { - if (mEnableClassifier) { - float evaluation = mHistoryEvaluator.getEvaluation(); - boolean result = evaluation >= 5.0f; - if (FalsingLog.ENABLED) { - FalsingLog.i("isFalseTouch", new StringBuilder() - .append("eval=").append(evaluation).append(" result=") - .append(result ? 1 : 0).toString()); - } - return result; - } - return false; - } - - public boolean isEnabled() { - return mEnableClassifier; - } - - @Override - public String getTag() { - return "HIC"; - } -} diff --git a/packages/SystemUI/src/com/android/systemui/classifier/LengthCountClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/LengthCountClassifier.java deleted file mode 100644 index 53678a6d1730..000000000000 --- a/packages/SystemUI/src/com/android/systemui/classifier/LengthCountClassifier.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.systemui.classifier; - -/** - * A classifier which looks at the ratio between the length of the stroke and its number of - * points. The number of points is subtracted by 2 because the UP event comes in with some delay - * and it should not influence the ratio and also strokes which are long and have a small number - * of points are punished more (these kind of strokes are usually bad ones and they tend to score - * well in other classifiers). - */ -public class LengthCountClassifier extends StrokeClassifier { - public LengthCountClassifier(ClassifierData classifierData) { - } - - @Override - public String getTag() { - return "LEN_CNT"; - } - - @Override - public float getFalseTouchEvaluation(int type, Stroke stroke) { - return LengthCountEvaluator.evaluate(stroke.getTotalLength() - / Math.max(1.0f, stroke.getCount() - 2)); - } -}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/classifier/LengthCountEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/LengthCountEvaluator.java deleted file mode 100644 index dac7a6f720a7..000000000000 --- a/packages/SystemUI/src/com/android/systemui/classifier/LengthCountEvaluator.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.systemui.classifier; - -/** - * A classifier which looks at the ratio between the length of the stroke and its number of - * points. - */ -public class LengthCountEvaluator { - public static float evaluate(float value) { - float evaluation = 0.0f; - if (value < 0.09) evaluation++; - if (value < 0.05) evaluation++; - if (value < 0.02) evaluation++; - if (value > 0.6) evaluation++; - if (value > 0.9) evaluation++; - if (value > 1.2) evaluation++; - return evaluation; - } -} diff --git a/packages/SystemUI/src/com/android/systemui/classifier/Point.java b/packages/SystemUI/src/com/android/systemui/classifier/Point.java deleted file mode 100644 index f3dc2be4144b..000000000000 --- a/packages/SystemUI/src/com/android/systemui/classifier/Point.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.systemui.classifier; - -public class Point { - public float x; - public float y; - public long timeOffsetNano; - - public Point(float x, float y) { - this.x = x; - this.y = y; - this.timeOffsetNano = 0; - } - - public Point(float x, float y, long timeOffsetNano) { - this.x = x; - this.y = y; - this.timeOffsetNano = timeOffsetNano; - } - - public boolean equals(Point p) { - return x == p.x && y == p.y; - } - - public float dist(Point a) { - return (float) Math.hypot(a.x - x, a.y - y); - } - - /** - * Calculates the cross product of vec(this, a) and vec(this, b) where vec(x,y) is the - * vector from point x to point y - */ - public float crossProduct(Point a, Point b) { - return (a.x - x) * (b.y - y) - (a.y - y) * (b.x - x); - } - - /** - * Calculates the dot product of vec(this, a) and vec(this, b) where vec(x,y) is the - * vector from point x to point y - */ - public float dotProduct(Point a, Point b) { - return (a.x - x) * (b.x - x) + (a.y - y) * (b.y - y); - } - - /** - * Calculates the angle in radians created by points (a, this, b). If any two of these points - * are the same, the method will return 0.0f - * - * @return the angle in radians - */ - public float getAngle(Point a, Point b) { - float dist1 = dist(a); - float dist2 = dist(b); - - if (dist1 == 0.0f || dist2 == 0.0f) { - return 0.0f; - } - - float crossProduct = crossProduct(a, b); - float dotProduct = dotProduct(a, b); - float cos = Math.min(1.0f, Math.max(-1.0f, dotProduct / dist1 / dist2)); - float angle = (float) Math.acos(cos); - if (crossProduct < 0.0) { - angle = 2.0f * (float) Math.PI - angle; - } - return angle; - } -} diff --git a/packages/SystemUI/src/com/android/systemui/classifier/PointerCountClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/PointerCountClassifier.java deleted file mode 100644 index 136c43345c9d..000000000000 --- a/packages/SystemUI/src/com/android/systemui/classifier/PointerCountClassifier.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.systemui.classifier; - -import android.view.MotionEvent; - -/** - * A classifier which looks at the total number of traces in the whole gesture. - */ -public class PointerCountClassifier extends GestureClassifier { - private int mCount; - - public PointerCountClassifier(ClassifierData classifierData) { - mCount = 0; - } - - @Override - public String getTag() { - return "PTR_CNT"; - } - - @Override - public void onTouchEvent(MotionEvent event) { - int action = event.getActionMasked(); - - if (action == MotionEvent.ACTION_DOWN) { - mCount = 1; - } - - if (action == MotionEvent.ACTION_POINTER_DOWN) { - ++mCount; - } - } - - @Override - public float getFalseTouchEvaluation(int type) { - return PointerCountEvaluator.evaluate(mCount); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java deleted file mode 100644 index 62adfc85621b..000000000000 --- a/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.systemui.classifier; - -import android.hardware.Sensor; -import android.hardware.SensorEvent; -import android.view.MotionEvent; - -/** - * A classifier which looks at the proximity sensor during the gesture. It calculates the percentage - * the proximity sensor showing the near state during the whole gesture - */ -public class ProximityClassifier extends GestureClassifier { - private long mGestureStartTimeNano; - private long mNearStartTimeNano; - private long mNearDuration; - private boolean mNear; - private float mAverageNear; - - public ProximityClassifier(ClassifierData classifierData) { - } - - @Override - public String getTag() { - return "PROX"; - } - - @Override - public void onSensorChanged(SensorEvent event) { - if (event.sensor.getType() == Sensor.TYPE_PROXIMITY) { - update(event.values[0] < event.sensor.getMaximumRange(), event.timestamp); - } - } - - @Override - public void onTouchEvent(MotionEvent event) { - int action = event.getActionMasked(); - - if (action == MotionEvent.ACTION_DOWN) { - mGestureStartTimeNano = event.getEventTimeNano(); - mNearStartTimeNano = event.getEventTimeNano(); - mNearDuration = 0; - } - - if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { - update(mNear, event.getEventTimeNano()); - long duration = event.getEventTimeNano() - mGestureStartTimeNano; - - if (duration == 0) { - mAverageNear = mNear ? 1.0f : 0.0f; - } else { - mAverageNear = (float) mNearDuration / (float) duration; - } - } - } - - - /** - * @param near is the sensor showing the near state right now - * @param timestampNano time of this event in nanoseconds - */ - private void update(boolean near, long timestampNano) { - // This if is necessary because MotionEvents and SensorEvents do not come in - // chronological order - if (timestampNano > mNearStartTimeNano) { - // if the state before was near then add the difference of the current time and - // mNearStartTimeNano to mNearDuration. - if (mNear) { - mNearDuration += timestampNano - mNearStartTimeNano; - } - - // if the new state is near, set mNearStartTimeNano equal to this moment. - if (near) { - mNearStartTimeNano = timestampNano; - } - } - mNear = near; - } - - @Override - public float getFalseTouchEvaluation(int type) { - return ProximityEvaluator.evaluate(mAverageNear, type); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ProximityEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/ProximityEvaluator.java deleted file mode 100644 index 91002bf1291c..000000000000 --- a/packages/SystemUI/src/com/android/systemui/classifier/ProximityEvaluator.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.systemui.classifier; - -public class ProximityEvaluator { - public static float evaluate(float value, int type) { - float evaluation = 0.0f; - float threshold = 0.1f; - if (type == Classifier.QUICK_SETTINGS) { - threshold = 1.0f; - } - if (value >= threshold) evaluation += 2.0; - return evaluation; - } -} diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesClassifier.java deleted file mode 100644 index 66f0cf68d8c1..000000000000 --- a/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesClassifier.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.systemui.classifier; - -import android.os.Build; -import android.os.SystemProperties; -import android.view.MotionEvent; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - -/** - * A classifier which for each point from a stroke, it creates a point on plane with coordinates - * (timeOffsetNano, distanceCoveredUpToThisPoint) (scaled by DURATION_SCALE and LENGTH_SCALE) - * and then it calculates the angle variance of these points like the class - * {@link AnglesClassifier} (without splitting it into two parts). The classifier ignores - * the last point of a stroke because the UP event comes in with some delay and this ruins the - * smoothness of this curve. Additionally, the classifier classifies calculates the percentage of - * angles which value is in [PI - ANGLE_DEVIATION, 2* PI) interval. The reason why the classifier - * does that is because the speed of a good stroke is most often increases, so most of these angels - * should be in this interval. - */ -public class SpeedAnglesClassifier extends StrokeClassifier { - public static final boolean VERBOSE = SystemProperties.getBoolean("debug.falsing_log.spd_ang", - Build.IS_DEBUGGABLE); - public static final String TAG = "SPD_ANG"; - - private HashMap<Stroke, Data> mStrokeMap = new HashMap<>(); - - public SpeedAnglesClassifier(ClassifierData classifierData) { - mClassifierData = classifierData; - } - - @Override - public String getTag() { - return TAG; - } - - @Override - public void onTouchEvent(MotionEvent event) { - int action = event.getActionMasked(); - - if (action == MotionEvent.ACTION_DOWN) { - mStrokeMap.clear(); - } - - for (int i = 0; i < event.getPointerCount(); i++) { - Stroke stroke = mClassifierData.getStroke(event.getPointerId(i)); - - if (mStrokeMap.get(stroke) == null) { - mStrokeMap.put(stroke, new Data()); - } - - if (action != MotionEvent.ACTION_UP && action != MotionEvent.ACTION_CANCEL - && !(action == MotionEvent.ACTION_POINTER_UP && i == event.getActionIndex())) { - mStrokeMap.get(stroke).addPoint( - stroke.getPoints().get(stroke.getPoints().size() - 1)); - } - } - } - - @Override - public float getFalseTouchEvaluation(int type, Stroke stroke) { - Data data = mStrokeMap.get(stroke); - return SpeedVarianceEvaluator.evaluate(data.getAnglesVariance()) - + SpeedAnglesPercentageEvaluator.evaluate(data.getAnglesPercentage()); - } - - private static class Data { - private final float DURATION_SCALE = 1e8f; - private final float LENGTH_SCALE = 1.0f; - private final float ANGLE_DEVIATION = (float) Math.PI / 10.0f; - - private List<Point> mLastThreePoints = new ArrayList<>(); - private Point mPreviousPoint; - private float mPreviousAngle; - private float mSumSquares; - private float mSum; - private float mCount; - private float mDist; - private float mAnglesCount; - private float mAcceleratingAngles; - - public Data() { - mPreviousPoint = null; - mPreviousAngle = (float) Math.PI; - mSumSquares = 0.0f; - mSum = 0.0f; - mCount = 1.0f; - mDist = 0.0f; - mAnglesCount = mAcceleratingAngles = 0.0f; - } - - public void addPoint(Point point) { - if (mPreviousPoint != null) { - mDist += mPreviousPoint.dist(point); - } - - mPreviousPoint = point; - Point speedPoint = new Point((float) point.timeOffsetNano / DURATION_SCALE, - mDist / LENGTH_SCALE); - - // Checking if the added point is different than the previously added point - // Repetitions are being ignored so that proper angles are calculated. - if (mLastThreePoints.isEmpty() - || !mLastThreePoints.get(mLastThreePoints.size() - 1).equals(speedPoint)) { - mLastThreePoints.add(speedPoint); - if (mLastThreePoints.size() == 4) { - mLastThreePoints.remove(0); - - float angle = mLastThreePoints.get(1).getAngle(mLastThreePoints.get(0), - mLastThreePoints.get(2)); - - mAnglesCount++; - if (angle >= (float) Math.PI - ANGLE_DEVIATION) { - mAcceleratingAngles++; - } - - float difference = angle - mPreviousAngle; - mSum += difference; - mSumSquares += difference * difference; - mCount += 1.0; - mPreviousAngle = angle; - } - } - } - - public float getAnglesVariance() { - final float v = mSumSquares / mCount - (mSum / mCount) * (mSum / mCount); - if (VERBOSE) { - FalsingLog.i(TAG, "getAnglesVariance: sum^2=" + mSumSquares - + " count=" + mCount + " result=" + v); - } - return v; - } - - public float getAnglesPercentage() { - if (mAnglesCount == 0.0f) { - return 1.0f; - } - final float v = (mAcceleratingAngles) / mAnglesCount; - if (VERBOSE) { - FalsingLog.i(TAG, "getAnglesPercentage: angles=" + mAcceleratingAngles - + " count=" + mAnglesCount + " result=" + v); - } - return v; - } - } -}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesPercentageEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesPercentageEvaluator.java deleted file mode 100644 index d50d406f2fef..000000000000 --- a/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesPercentageEvaluator.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.systemui.classifier; - -public class SpeedAnglesPercentageEvaluator { - public static float evaluate(float value) { - float evaluation = 0.0f; - if (value < 1.00) evaluation++; - if (value < 0.90) evaluation++; - if (value < 0.70) evaluation++; - return evaluation; - } -} diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SpeedClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/SpeedClassifier.java deleted file mode 100644 index 01fcc377ddbd..000000000000 --- a/packages/SystemUI/src/com/android/systemui/classifier/SpeedClassifier.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.systemui.classifier; - -/** - * A classifier that looks at the speed of the stroke. It calculates the speed of a stroke in - * inches per second. - */ -public class SpeedClassifier extends StrokeClassifier { - private final float NANOS_TO_SECONDS = 1e9f; - - public SpeedClassifier(ClassifierData classifierData) { - } - - @Override - public String getTag() { - return "SPD"; - } - - @Override - public float getFalseTouchEvaluation(int type, Stroke stroke) { - float duration = (float) stroke.getDurationNanos() / NANOS_TO_SECONDS; - if (duration == 0.0f) { - return SpeedEvaluator.evaluate(0.0f); - } - return SpeedEvaluator.evaluate(stroke.getTotalLength() / duration); - } -}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SpeedEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/SpeedEvaluator.java deleted file mode 100644 index afd8d0152172..000000000000 --- a/packages/SystemUI/src/com/android/systemui/classifier/SpeedEvaluator.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.systemui.classifier; - -public class SpeedEvaluator { - public static float evaluate(float value) { - float evaluation = 0.0f; - if (value < 4.0) evaluation++; - if (value < 2.2) evaluation++; - if (value > 35.0) evaluation++; - if (value > 50.0) evaluation++; - return evaluation; - } -} diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SpeedRatioEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/SpeedRatioEvaluator.java deleted file mode 100644 index e34f222d3799..000000000000 --- a/packages/SystemUI/src/com/android/systemui/classifier/SpeedRatioEvaluator.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.systemui.classifier; - -public class SpeedRatioEvaluator { - public static float evaluate(float value) { - float evaluation = 0.0f; - if (value == 0) return 0; - if (value <= 1.0) evaluation++; - if (value <= 0.5) evaluation++; - if (value > 9.0) evaluation++; - if (value > 18.0) evaluation++; - return evaluation; - } -} diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SpeedVarianceEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/SpeedVarianceEvaluator.java deleted file mode 100644 index 48b1b6e0dbf1..000000000000 --- a/packages/SystemUI/src/com/android/systemui/classifier/SpeedVarianceEvaluator.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.systemui.classifier; - -public class SpeedVarianceEvaluator { - public static float evaluate(float value) { - float evaluation = 0.0f; - if (value > 0.06) evaluation++; - if (value > 0.15) evaluation++; - if (value > 0.3) evaluation++; - if (value > 0.6) evaluation++; - return evaluation; - } -} diff --git a/packages/SystemUI/src/com/android/systemui/classifier/Stroke.java b/packages/SystemUI/src/com/android/systemui/classifier/Stroke.java deleted file mode 100644 index 977a2d0b528a..000000000000 --- a/packages/SystemUI/src/com/android/systemui/classifier/Stroke.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.systemui.classifier; - -import java.util.ArrayList; - -/** - * Contains data about a stroke (a single trace, all the events from a given id from the - * DOWN/POINTER_DOWN event till the UP/POINTER_UP/CANCEL event.) - */ -public class Stroke { - private final float NANOS_TO_SECONDS = 1e9f; - - private ArrayList<Point> mPoints = new ArrayList<>(); - private long mStartTimeNano; - private long mEndTimeNano; - private float mLength; - private final float mDpi; - - public Stroke(long eventTimeNano, float dpi) { - mDpi = dpi; - mStartTimeNano = mEndTimeNano = eventTimeNano; - } - - public void addPoint(float x, float y, long eventTimeNano) { - mEndTimeNano = eventTimeNano; - Point point = new Point(x / mDpi, y / mDpi, eventTimeNano - mStartTimeNano); - if (!mPoints.isEmpty()) { - mLength += mPoints.get(mPoints.size() - 1).dist(point); - } - mPoints.add(point); - } - - public int getCount() { - return mPoints.size(); - } - - public float getTotalLength() { - return mLength; - } - - public float getEndPointLength() { - return mPoints.get(0).dist(mPoints.get(mPoints.size() - 1)); - } - - public long getDurationNanos() { - return mEndTimeNano - mStartTimeNano; - } - - public float getDurationSeconds() { - return (float) getDurationNanos() / NANOS_TO_SECONDS; - } - - public ArrayList<Point> getPoints() { - return mPoints; - } - - public long getLastEventTimeNano() { - if (mPoints.isEmpty()) { - return mStartTimeNano; - } - - return mPoints.get(mPoints.size() - 1).timeOffsetNano; - } -} diff --git a/packages/SystemUI/src/com/android/systemui/classifier/StrokeClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/StrokeClassifier.java deleted file mode 100644 index 5da392f31d63..000000000000 --- a/packages/SystemUI/src/com/android/systemui/classifier/StrokeClassifier.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.systemui.classifier; - -/** - * An abstract class for classifiers which classify each stroke separately. - */ -public abstract class StrokeClassifier extends Classifier { - - /** - * @param type the type of action for which this method is called - * @param stroke the stroke for which the evaluation will be calculated - * @return a non-negative value which is used to determine whether this a false touch; the - * bigger the value the greater the chance that this a false touch - */ - public abstract float getFalseTouchEvaluation(int type, Stroke stroke); -} diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java index 9d847ca62465..70a57cc8bd2a 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java @@ -16,29 +16,32 @@ package com.android.systemui.classifier.brightline; -import static com.android.systemui.classifier.FalsingManagerImpl.FALSING_REMAIN_LOCKED; -import static com.android.systemui.classifier.FalsingManagerImpl.FALSING_SUCCESS; +import static com.android.systemui.classifier.FalsingManagerProxy.FALSING_SUCCESS; import android.app.ActivityManager; -import android.hardware.biometrics.BiometricSourceType; +import android.content.res.Resources; import android.net.Uri; import android.os.Build; +import android.util.IndentingPrintWriter; import android.util.Log; import android.view.MotionEvent; +import android.view.ViewConfiguration; + +import androidx.annotation.NonNull; import com.android.internal.logging.MetricsLogger; -import com.android.internal.util.IndentingPrintWriter; -import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.keyguard.KeyguardUpdateMonitorCallback; +import com.android.systemui.R; import com.android.systemui.classifier.Classifier; +import com.android.systemui.classifier.FalsingDataProvider; +import com.android.systemui.classifier.FalsingDataProvider.SessionListener; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dock.DockManager; import com.android.systemui.plugins.FalsingManager; -import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.phone.NotificationTapHelper; import com.android.systemui.util.DeviceConfigProxy; -import com.android.systemui.util.sensors.ProximitySensor; import com.android.systemui.util.sensors.ThresholdSensor; +import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayDeque; import java.util.ArrayList; @@ -48,28 +51,25 @@ import java.util.Queue; import java.util.StringJoiner; import java.util.stream.Collectors; +import javax.inject.Inject; + /** * FalsingManager designed to make clear why a touch was rejected. */ public class BrightLineFalsingManager implements FalsingManager { private static final String TAG = "FalsingManager"; - static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private static final int RECENT_INFO_LOG_SIZE = 40; private static final int RECENT_SWIPE_LOG_SIZE = 20; private final FalsingDataProvider mDataProvider; - private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; - private final ProximitySensor mProximitySensor; private final DockManager mDockManager; - private final StatusBarStateController mStatusBarStateController; - private boolean mSessionStarted; - private MetricsLogger mMetricsLogger; + private final SingleTapClassifier mSingleTapClassifier; + private final DoubleTapClassifier mDoubleTapClassifier; + private final MetricsLogger mMetricsLogger; private int mIsFalseTouchCalls; - private boolean mShowingAod; - private boolean mScreenOn; - private boolean mJustUnlockedWithFace; private static final Queue<String> RECENT_INFO_LOG = new ArrayDeque<>(RECENT_INFO_LOG_SIZE + 1); private static final Queue<DebugSwipeRecord> RECENT_SWIPES = @@ -77,45 +77,26 @@ public class BrightLineFalsingManager implements FalsingManager { private final List<FalsingClassifier> mClassifiers; - private ThresholdSensor.Listener mSensorEventListener = this::onProximityEvent; - - private final KeyguardUpdateMonitorCallback mKeyguardUpdateCallback = - new KeyguardUpdateMonitorCallback() { - @Override - public void onBiometricAuthenticated(int userId, - BiometricSourceType biometricSourceType, - boolean isStrongBiometric) { - if (userId == KeyguardUpdateMonitor.getCurrentUser() - && biometricSourceType == BiometricSourceType.FACE) { - mJustUnlockedWithFace = true; - } - } - }; - private boolean mPreviousResult = false; + private final SessionListener mSessionListener = new SessionListener() { + @Override + public void onSessionEnded() { + mClassifiers.forEach(FalsingClassifier::onSessionEnded); + } - private StatusBarStateController.StateListener mStatusBarStateListener = - new StatusBarStateController.StateListener() { @Override - public void onStateChanged(int newState) { - logDebug("StatusBarState=" + StatusBarState.toShortString(newState)); - mState = newState; - updateSessionActive(); + public void onSessionStarted() { + mClassifiers.forEach(FalsingClassifier::onSessionStarted); } }; - private int mState; + private boolean mPreviousResult = false; + + @Inject public BrightLineFalsingManager(FalsingDataProvider falsingDataProvider, - KeyguardUpdateMonitor keyguardUpdateMonitor, ProximitySensor proximitySensor, - DeviceConfigProxy deviceConfigProxy, - DockManager dockManager, StatusBarStateController statusBarStateController) { - mKeyguardUpdateMonitor = keyguardUpdateMonitor; + DeviceConfigProxy deviceConfigProxy, @Main Resources resources, + ViewConfiguration viewConfiguration, DockManager dockManager) { mDataProvider = falsingDataProvider; - mProximitySensor = proximitySensor; mDockManager = dockManager; - mStatusBarStateController = statusBarStateController; - mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateCallback); - mStatusBarStateController.addCallback(mStatusBarStateListener); - mState = mStatusBarStateController.getState(); mMetricsLogger = new MetricsLogger(); mClassifiers = new ArrayList<>(); @@ -129,58 +110,14 @@ public class BrightLineFalsingManager implements FalsingManager { mClassifiers.add(distanceClassifier); mClassifiers.add(proximityClassifier); mClassifiers.add(new ZigZagClassifier(mDataProvider, deviceConfigProxy)); - } - - private void registerSensors() { - if (!mDataProvider.isWirelessCharging()) { - mProximitySensor.register(mSensorEventListener); - } - } - private void unregisterSensors() { - mProximitySensor.unregister(mSensorEventListener); - } + mSingleTapClassifier = new SingleTapClassifier( + mDataProvider, viewConfiguration.getScaledTouchSlop()); + mDoubleTapClassifier = new DoubleTapClassifier(mDataProvider, mSingleTapClassifier, + resources.getDimension(R.dimen.double_tap_slop), + NotificationTapHelper.DOUBLE_TAP_TIMEOUT_MS); - private void sessionStart() { - if (!mSessionStarted && shouldSessionBeActive()) { - logDebug("Starting Session"); - mSessionStarted = true; - mJustUnlockedWithFace = false; - registerSensors(); - mClassifiers.forEach(FalsingClassifier::onSessionStarted); - } - } - - private void sessionEnd() { - if (mSessionStarted) { - logDebug("Ending Session"); - mSessionStarted = false; - unregisterSensors(); - mDataProvider.onSessionEnd(); - mClassifiers.forEach(FalsingClassifier::onSessionEnded); - if (mIsFalseTouchCalls != 0) { - mMetricsLogger.histogram(FALSING_REMAIN_LOCKED, mIsFalseTouchCalls); - mIsFalseTouchCalls = 0; - } - } - } - - - private void updateSessionActive() { - if (shouldSessionBeActive()) { - sessionStart(); - } else { - sessionEnd(); - } - } - - private boolean shouldSessionBeActive() { - return mScreenOn && (mState == StatusBarState.KEYGUARD) && !mShowingAod; - } - - private void updateInteractionType(@Classifier.InteractionType int type) { - logDebug("InteractionType: " + type); - mDataProvider.setInteractionType(type); + mDataProvider.addSessionListener(mSessionListener); } @Override @@ -195,8 +132,9 @@ public class BrightLineFalsingManager implements FalsingManager { return mPreviousResult; } - mPreviousResult = !ActivityManager.isRunningInUserTestHarness() && !mJustUnlockedWithFace - && !mDockManager.isDocked() && mClassifiers.stream().anyMatch(falsingClassifier -> { + mPreviousResult = !ActivityManager.isRunningInUserTestHarness() + && !mDataProvider.isJustUnlockedWithFace() && !mDockManager.isDocked() + && mClassifiers.stream().anyMatch(falsingClassifier -> { boolean result = falsingClassifier.isFalseTouch(); if (result) { logInfo(String.format( @@ -228,23 +166,56 @@ public class BrightLineFalsingManager implements FalsingManager { (int) (motionEvent.getEventTime() - motionEvent.getDownTime()))) .collect(Collectors.toList()))); while (RECENT_SWIPES.size() > RECENT_INFO_LOG_SIZE) { - DebugSwipeRecord record = RECENT_SWIPES.remove(); + RECENT_SWIPES.remove(); } - } return mPreviousResult; } @Override + public boolean isFalseTap(boolean robustCheck) { + if (!mSingleTapClassifier.isTap(mDataProvider.getRecentMotionEvents())) { + logInfo(String.format( + (Locale) null, "{classifier=%s}", mSingleTapClassifier.getClass().getName())); + String reason = mSingleTapClassifier.getReason(); + if (reason != null) { + logInfo(reason); + } + return true; + } + + // TODO(b/172655679): More heuristics to come. For now, allow touches through if face-authed + if (robustCheck) { + return !mDataProvider.isJustUnlockedWithFace(); + } + + return false; + } + + @Override + public boolean isFalseDoubleTap() { + boolean result = mDoubleTapClassifier.isFalseTouch(); + if (result) { + logInfo(String.format( + (Locale) null, "{classifier=%s}", mDoubleTapClassifier.getClass().getName())); + String reason = mDoubleTapClassifier.getReason(); + if (reason != null) { + logInfo(reason); + } + } + return result; + } + + @Override public void onTouchEvent(MotionEvent motionEvent, int width, int height) { // TODO: some of these classifiers might allow us to abort early, meaning we don't have to // make these calls. - mDataProvider.onMotionEvent(motionEvent); mClassifiers.forEach((classifier) -> classifier.onTouchEvent(motionEvent)); } - private void onProximityEvent(ThresholdSensor.ThresholdSensorEvent proximityEvent) { + @Override + public void onProximityEvent(ThresholdSensor.ThresholdSensorEvent proximityEvent) { // TODO: some of these classifiers might allow us to abort early, meaning we don't have to // make these calls. mClassifiers.forEach((classifier) -> classifier.onProximityEvent(proximityEvent)); @@ -256,22 +227,6 @@ public class BrightLineFalsingManager implements FalsingManager { mMetricsLogger.histogram(FALSING_SUCCESS, mIsFalseTouchCalls); mIsFalseTouchCalls = 0; } - sessionEnd(); - } - - @Override - public void onNotificationActive() { - } - - @Override - public void setShowingAod(boolean showingAod) { - mShowingAod = showingAod; - updateSessionActive(); - } - - @Override - public void onNotificatonStartDraggingDown() { - updateInteractionType(Classifier.NOTIFICATION_DRAG_DOWN); } @Override @@ -279,147 +234,29 @@ public class BrightLineFalsingManager implements FalsingManager { return false; } - - @Override - public void onNotificatonStopDraggingDown() { - } - - @Override - public void setNotificationExpanded() { - } - - @Override - public void onQsDown() { - updateInteractionType(Classifier.QUICK_SETTINGS); - } - - @Override - public void setQsExpanded(boolean expanded) { - if (expanded) { - unregisterSensors(); - } else if (mSessionStarted) { - registerSensors(); - } - } - @Override public boolean shouldEnforceBouncer() { return false; } @Override - public void onTrackingStarted(boolean secure) { - updateInteractionType(secure ? Classifier.BOUNCER_UNLOCK : Classifier.UNLOCK); - } - - @Override - public void onTrackingStopped() { - } - - @Override - public void onLeftAffordanceOn() { - } - - @Override - public void onCameraOn() { - } - - @Override - public void onAffordanceSwipingStarted(boolean rightCorner) { - updateInteractionType( - rightCorner ? Classifier.RIGHT_AFFORDANCE : Classifier.LEFT_AFFORDANCE); - } - - @Override - public void onAffordanceSwipingAborted() { - } - - @Override - public void onStartExpandingFromPulse() { - updateInteractionType(Classifier.PULSE_EXPAND); - } - - @Override - public void onExpansionFromPulseStopped() { - } - - @Override public Uri reportRejectedTouch() { return null; } @Override - public void onScreenOnFromTouch() { - onScreenTurningOn(); - } - - @Override public boolean isReportingEnabled() { return false; } @Override - public void onUnlockHintStarted() { - } - - @Override - public void onCameraHintStarted() { - } - - @Override - public void onLeftAffordanceHintStarted() { - } - - @Override - public void onScreenTurningOn() { - mScreenOn = true; - updateSessionActive(); - } - - @Override - public void onScreenOff() { - mScreenOn = false; - updateSessionActive(); - } - - - @Override - public void onNotificationStopDismissing() { - } - - @Override - public void onNotificationDismissed() { - } - - @Override - public void onNotificationStartDismissing() { - updateInteractionType(Classifier.NOTIFICATION_DISMISS); - } - - @Override - public void onNotificationDoubleTap(boolean b, float v, float v1) { - } - - @Override - public void onBouncerShown() { - unregisterSensors(); - } - - @Override - public void onBouncerHidden() { - if (mSessionStarted) { - registerSensors(); - } - } - - @Override - public void dump(PrintWriter pw) { + public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); ipw.println("BRIGHTLINE FALSING MANAGER"); ipw.print("classifierEnabled="); ipw.println(isClassifierEnabled() ? 1 : 0); ipw.print("mJustUnlockedWithFace="); - ipw.println(mJustUnlockedWithFace ? 1 : 0); + ipw.println(mDataProvider.isJustUnlockedWithFace() ? 1 : 0); ipw.print("isDocked="); ipw.println(mDockManager.isDocked() ? 1 : 0); ipw.print("width="); @@ -449,9 +286,7 @@ public class BrightLineFalsingManager implements FalsingManager { @Override public void cleanup() { - unregisterSensors(); - mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateCallback); - mStatusBarStateController.removeCallback(mStatusBarStateListener); + mDataProvider.removeSessionListener(mSessionListener); } static void logDebug(String msg) { diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DiagonalClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/DiagonalClassifier.java index 520e0a12dec4..a73ccf575249 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DiagonalClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/DiagonalClassifier.java @@ -23,6 +23,7 @@ import static com.android.systemui.classifier.Classifier.RIGHT_AFFORDANCE; import android.provider.DeviceConfig; +import com.android.systemui.classifier.FalsingDataProvider; import com.android.systemui.util.DeviceConfigProxy; import java.util.Locale; diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DistanceClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/DistanceClassifier.java index 0329183e6048..524d524f38b6 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DistanceClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/DistanceClassifier.java @@ -27,6 +27,7 @@ import android.provider.DeviceConfig; import android.view.MotionEvent; import android.view.VelocityTracker; +import com.android.systemui.classifier.FalsingDataProvider; import com.android.systemui.util.DeviceConfigProxy; import java.util.List; @@ -112,7 +113,6 @@ class DistanceClassifier extends FalsingClassifier { private DistanceVectors calculateDistances() { // This code assumes that there will be no missed DOWN or UP events. - VelocityTracker velocityTracker = VelocityTracker.obtain(); List<MotionEvent> motionEvents = getRecentMotionEvents(); if (motionEvents.size() < 3) { @@ -120,6 +120,8 @@ class DistanceClassifier extends FalsingClassifier { return new DistanceVectors(0, 0, 0, 0); } + VelocityTracker velocityTracker = VelocityTracker.obtain(); + for (MotionEvent motionEvent : motionEvents) { velocityTracker.addMovement(motionEvent); } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DoubleTapClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/DoubleTapClassifier.java new file mode 100644 index 000000000000..a27ea6172414 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/DoubleTapClassifier.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.classifier.brightline; + +import android.view.MotionEvent; + +import com.android.systemui.classifier.FalsingDataProvider; + +import java.util.List; +import java.util.Queue; + +/** + * Returns a false touch if the most two recent gestures are not taps or are too far apart. + */ +public class DoubleTapClassifier extends FalsingClassifier { + + private final SingleTapClassifier mSingleTapClassifier; + private final float mDoubleTapSlop; + private final long mDoubleTapTimeMs; + + private StringBuilder mReason = new StringBuilder(); + + DoubleTapClassifier(FalsingDataProvider dataProvider, SingleTapClassifier singleTapClassifier, + float doubleTapSlop, long doubleTapTimeMs) { + super(dataProvider); + mSingleTapClassifier = singleTapClassifier; + mDoubleTapSlop = doubleTapSlop; + mDoubleTapTimeMs = doubleTapTimeMs; + } + + @Override + boolean isFalseTouch() { + List<MotionEvent> secondTapEvents = getRecentMotionEvents(); + Queue<? extends List<MotionEvent>> historicalEvents = getHistoricalEvents(); + List<MotionEvent> firstTapEvents = historicalEvents.peek(); + + mReason = new StringBuilder(); + + if (firstTapEvents == null) { + mReason.append("Only one gesture recorded"); + return true; + } + + return !isDoubleTap(firstTapEvents, secondTapEvents, mReason); + } + + /** Returns true if the two supplied lists of {@link MotionEvent}s look like a double-tap. */ + public boolean isDoubleTap(List<MotionEvent> firstEvents, List<MotionEvent> secondEvents, + StringBuilder reason) { + + if (!mSingleTapClassifier.isTap(firstEvents)) { + reason.append("First gesture is not a tap. ").append(mSingleTapClassifier.getReason()); + return false; + } + + if (!mSingleTapClassifier.isTap(secondEvents)) { + reason.append("Second gesture is not a tap. ").append(mSingleTapClassifier.getReason()); + return false; + } + + MotionEvent firstFinalEvent = firstEvents.get(firstEvents.size() - 1); + MotionEvent secondFinalEvent = secondEvents.get(secondEvents.size() - 1); + + long dt = secondFinalEvent.getEventTime() - firstFinalEvent.getEventTime(); + + if (dt > mDoubleTapTimeMs) { + reason.append("Time between taps too large: ").append(dt).append("ms"); + return false; + } + + if (Math.abs(firstFinalEvent.getX() - secondFinalEvent.getX()) >= mDoubleTapSlop) { + reason.append("Delta X between taps too large:") + .append(Math.abs(firstFinalEvent.getX() - secondFinalEvent.getX())) + .append(" vs ") + .append(mDoubleTapSlop); + return false; + } + + if (Math.abs(firstFinalEvent.getY() - secondFinalEvent.getY()) >= mDoubleTapSlop) { + reason.append("Delta Y between taps too large:") + .append(Math.abs(firstFinalEvent.getY() - secondFinalEvent.getY())) + .append(" vs ") + .append(mDoubleTapSlop); + return false; + } + + return true; + } + + @Override + String getReason() { + return mReason.toString(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java index 85e95a66bfe3..568dc432729f 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java @@ -19,24 +19,30 @@ package com.android.systemui.classifier.brightline; import android.view.MotionEvent; import com.android.systemui.classifier.Classifier; +import com.android.systemui.classifier.FalsingDataProvider; import com.android.systemui.util.sensors.ProximitySensor; import java.util.List; +import java.util.Queue; /** * Base class for rules that determine False touches. */ -abstract class FalsingClassifier { +public abstract class FalsingClassifier { private final FalsingDataProvider mDataProvider; FalsingClassifier(FalsingDataProvider dataProvider) { - this.mDataProvider = dataProvider; + mDataProvider = dataProvider; } List<MotionEvent> getRecentMotionEvents() { return mDataProvider.getRecentMotionEvents(); } + Queue<? extends List<MotionEvent>> getHistoricalEvents() { + return mDataProvider.getHistoricalMotionEvents(); + } + MotionEvent getFirstMotionEvent() { return mDataProvider.getFirstRecentMotionEvent(); } @@ -121,15 +127,18 @@ abstract class FalsingClassifier { */ abstract String getReason(); - static void logDebug(String msg) { + /** */ + public static void logDebug(String msg) { BrightLineFalsingManager.logDebug(msg); } - static void logInfo(String msg) { + /** */ + public static void logInfo(String msg) { BrightLineFalsingManager.logInfo(msg); } - static void logError(String msg) { + /** */ + public static void logError(String msg) { BrightLineFalsingManager.logError(msg); } } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/PointerCountClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/PointerCountClassifier.java index b726c3e2783f..dd5d8a8f557b 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/PointerCountClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/PointerCountClassifier.java @@ -21,6 +21,8 @@ import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS; import android.view.MotionEvent; +import com.android.systemui.classifier.FalsingDataProvider; + import java.util.Locale; /** diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java index b128678af5db..3551c241e71e 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java @@ -22,6 +22,7 @@ import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS; import android.provider.DeviceConfig; import android.view.MotionEvent; +import com.android.systemui.classifier.FalsingDataProvider; import com.android.systemui.util.DeviceConfigProxy; import com.android.systemui.util.sensors.ProximitySensor; diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/SingleTapClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/SingleTapClassifier.java new file mode 100644 index 000000000000..8c7648149e44 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/SingleTapClassifier.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.classifier.brightline; + +import android.view.MotionEvent; + +import com.android.systemui.classifier.FalsingDataProvider; + +import java.util.List; + +/** + * Falsing classifier that accepts or rejects a single gesture as a tap. + */ +public class SingleTapClassifier extends FalsingClassifier { + private final float mTouchSlop; + private String mReason; + + SingleTapClassifier(FalsingDataProvider dataProvider, float touchSlop) { + super(dataProvider); + mTouchSlop = touchSlop; + } + + @Override + boolean isFalseTouch() { + return !isTap(getRecentMotionEvents()); + } + + /** Given a list of {@link android.view.MotionEvent}'s, returns true if the look like a tap. */ + public boolean isTap(List<MotionEvent> motionEvents) { + float downX = motionEvents.get(0).getX(); + float downY = motionEvents.get(0).getY(); + + for (MotionEvent event : motionEvents) { + if (Math.abs(event.getX() - downX) >= mTouchSlop) { + mReason = "dX too big for a tap: " + + Math.abs(event.getX() - downX) + + "vs " + + mTouchSlop; + return false; + } else if (Math.abs(event.getY() - downY) >= mTouchSlop) { + mReason = "dY too big for a tap: " + + Math.abs(event.getY() - downY) + + "vs " + + mTouchSlop; + return false; + } + } + mReason = ""; + return true; + } + + @Override + String getReason() { + return mReason; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/TimeLimitedMotionEventBuffer.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/TimeLimitedMotionEventBuffer.java index 9a83b5bd8328..7430a1eda920 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/TimeLimitedMotionEventBuffer.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/TimeLimitedMotionEventBuffer.java @@ -34,12 +34,24 @@ import java.util.ListIterator; public class TimeLimitedMotionEventBuffer implements List<MotionEvent> { private final LinkedList<MotionEvent> mMotionEvents; - private long mMaxAgeMs; + private final long mMaxAgeMs; - TimeLimitedMotionEventBuffer(long maxAgeMs) { + public TimeLimitedMotionEventBuffer(long maxAgeMs) { super(); - this.mMaxAgeMs = maxAgeMs; - this.mMotionEvents = new LinkedList<>(); + mMaxAgeMs = maxAgeMs; + mMotionEvents = new LinkedList<>(); + } + + /** + * Returns true if the most recent event in the buffer is past the expiration time. + * + * This method does not mutate the underlying data. This method does imply that, if the supplied + * expiration time is old enough and a new {@link MotionEvent} gets added to the buffer, all + * prior events would be removed. + */ + public boolean isFullyExpired(long expirationMs) { + return mMotionEvents.isEmpty() + || mMotionEvents.getLast().getEventTime() <= expirationMs; } private void ejectOldEvents() { diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/TypeClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/TypeClassifier.java index 5f1b37ae06d9..f62871f383b3 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/TypeClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/TypeClassifier.java @@ -26,6 +26,8 @@ import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS; import static com.android.systemui.classifier.Classifier.RIGHT_AFFORDANCE; import static com.android.systemui.classifier.Classifier.UNLOCK; +import com.android.systemui.classifier.FalsingDataProvider; + /** * Ensure that the swipe direction generally matches that of the interaction type. */ diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java index a796f3c6d547..9ca77d364bc4 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java @@ -25,6 +25,7 @@ import android.graphics.Point; import android.provider.DeviceConfig; import android.view.MotionEvent; +import com.android.systemui.classifier.FalsingDataProvider; import com.android.systemui.util.DeviceConfigProxy; import java.util.ArrayList; diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt index d3d24be0ad9d..96f207215f71 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt @@ -478,7 +478,7 @@ class ControlsControllerImpl @Inject constructor ( val pendingIntent = PendingIntent.getActivity(context, componentName.hashCode(), intent, - 0) + PendingIntent.FLAG_IMMUTABLE) val control = Control.StatelessBuilder(controlInfo.controlId, pendingIntent) .setTitle(controlInfo.controlTitle) .setSubtitle(controlInfo.controlSubtitle) diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index 780bb5b01103..4d697005be7b 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -27,6 +27,7 @@ import com.android.systemui.BootCompleteCache; import com.android.systemui.BootCompleteCacheImpl; import com.android.systemui.appops.dagger.AppOpsModule; import com.android.systemui.assist.AssistModule; +import com.android.systemui.classifier.FalsingModule; import com.android.systemui.controls.dagger.ControlsModule; import com.android.systemui.demomode.dagger.DemoModeModule; import com.android.systemui.doze.dagger.DozeComponent; @@ -89,6 +90,7 @@ import dagger.Provides; AssistModule.class, ControlsModule.class, DemoModeModule.class, + FalsingModule.class, LogModule.class, PeopleHubModule.class, PowerModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFalsingManagerAdapter.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFalsingManagerAdapter.java index 94b8ba305d0c..ef696a88548a 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeFalsingManagerAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFalsingManagerAdapter.java @@ -16,8 +16,8 @@ package com.android.systemui.doze; +import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.doze.dagger.DozeScope; -import com.android.systemui.plugins.FalsingManager; import javax.inject.Inject; @@ -27,16 +27,16 @@ import javax.inject.Inject; @DozeScope public class DozeFalsingManagerAdapter implements DozeMachine.Part { - private final FalsingManager mFalsingManager; + private final FalsingCollector mFalsingCollector; @Inject - public DozeFalsingManagerAdapter(FalsingManager falsingManager) { - mFalsingManager = falsingManager; + public DozeFalsingManagerAdapter(FalsingCollector falsingCollector) { + mFalsingCollector = falsingCollector; } @Override public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) { - mFalsingManager.setShowingAod(isAodMode(newState)); + mFalsingCollector.setShowingAod(isAodMode(newState)); } private boolean isAodMode(DozeMachine.State state) { diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java index 58e49f896931..6888d1ad6063 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java @@ -134,7 +134,7 @@ public class DozeTriggers implements DozeMachine.Part { DOZING_UPDATE_SENSOR_TAP(441), @UiEvent(doc = "Dozing updated because on display auth was triggered from AOD.") - DOZING_UPDATE_AUTH_TRIGGERED(442); + DOZING_UPDATE_AUTH_TRIGGERED(657); private final int mId; diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java index 37bcb163d6f3..bd3b899adee7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java @@ -329,7 +329,7 @@ public class KeyguardSliceProvider extends SliceProvider implements Method injectMethod = rootComponent.getClass() .getMethod("inject", getClass()); injectMethod.invoke(rootComponent, this); - Log.w("TAG", "mMediaManager is now: " + mMediaManager); + Log.w(TAG, "mMediaManager is now: " + mMediaManager); } catch (NoSuchMethodException ex) { Log.e(TAG, "Failed to find inject method for KeyguardSliceProvider", ex); } catch (IllegalAccessException | InvocationTargetException ex) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 4c68312b9378..c6bfcbaae188 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -34,10 +34,12 @@ import android.app.PendingIntent; import android.app.StatusBarManager; import android.app.trust.TrustManager; import android.content.BroadcastReceiver; +import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.UserInfo; import android.hardware.biometrics.BiometricSourceType; import android.media.AudioAttributes; @@ -86,11 +88,12 @@ import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.SystemUI; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.dagger.qualifiers.UiBackground; import com.android.systemui.dump.DumpManager; +import com.android.systemui.keyguard.KeyguardService; import com.android.systemui.keyguard.dagger.KeyguardModule; import com.android.systemui.navigationbar.NavigationModeController; -import com.android.systemui.plugins.FalsingManager; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.statusbar.phone.BiometricUnlockController; import com.android.systemui.statusbar.phone.KeyguardBypassController; @@ -220,7 +223,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable { private boolean mBootSendUserPresent; private boolean mShuttingDown; private boolean mDozing; - private final FalsingManager mFalsingManager; + private final FalsingCollector mFalsingCollector; /** High level access to the power manager for WakeLocks */ private final PowerManager mPM; @@ -712,7 +715,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable { */ public KeyguardViewMediator( Context context, - FalsingManager falsingManager, + FalsingCollector falsingCollector, LockPatternUtils lockPatternUtils, BroadcastDispatcher broadcastDispatcher, Lazy<KeyguardViewController> statusBarKeyguardViewManagerLazy, @@ -724,7 +727,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable { NavigationModeController navigationModeController, KeyguardDisplayManager keyguardDisplayManager) { super(context); - mFalsingManager = falsingManager; + mFalsingCollector = falsingCollector; mLockPatternUtils = lockPatternUtils; mBroadcastDispatcher = broadcastDispatcher; mKeyguardViewControllerLazy = statusBarKeyguardViewManagerLazy; @@ -779,7 +782,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable { // Assume keyguard is showing (unless it's disabled) until we know for sure, unless Keyguard // is disabled. - if (mContext.getResources().getBoolean(R.bool.config_enableKeyguardService)) { + if (isKeyguardServiceEnabled()) { setShowingLocked(!shouldWaitForProvisioning() && !mLockPatternUtils.isLockScreenDisabled( KeyguardUpdateMonitor.getCurrentUser()), true /* forceCallbacks */); @@ -957,6 +960,15 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable { mUpdateMonitor.dispatchFinishedGoingToSleep(why); } + private boolean isKeyguardServiceEnabled() { + try { + return mContext.getPackageManager().getServiceInfo( + new ComponentName(mContext, KeyguardService.class), 0).isEnabled(); + } catch (NameNotFoundException e) { + return true; + } + } + private long getLockTimeout(int userId) { // if the screen turned off because of timeout or the user hit the power button // and we don't need to lock immediately, set an alarm @@ -1670,7 +1682,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable { "KeyguardViewMediator#handleMessage START_KEYGUARD_EXIT_ANIM"); StartKeyguardExitAnimParams params = (StartKeyguardExitAnimParams) msg.obj; handleStartKeyguardExitAnimation(params.startTime, params.fadeoutDuration); - mFalsingManager.onSuccessfulUnlock(); + mFalsingCollector.onSuccessfulUnlock(); Trace.endSection(); break; case KEYGUARD_DONE_PENDING_TIMEOUT: diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java index e50fd6a96b5c..d7b5eea84c36 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java @@ -31,6 +31,8 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardViewController; import com.android.keyguard.dagger.KeyguardStatusViewComponent; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.classifier.FalsingCollector; +import com.android.systemui.classifier.FalsingModule; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.UiBackground; @@ -39,7 +41,6 @@ import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.keyguard.FaceAuthScreenBrightnessController; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.navigationbar.NavigationModeController; -import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.KeyguardLiftController; @@ -59,7 +60,8 @@ import dagger.Provides; /** * Dagger Module providing {@link StatusBar}. */ -@Module(subcomponents = {KeyguardStatusViewComponent.class}) +@Module(subcomponents = {KeyguardStatusViewComponent.class}, + includes = {FalsingModule.class}) public class KeyguardModule { /** * Provides our instance of KeyguardViewMediator which is considered optional. @@ -68,7 +70,7 @@ public class KeyguardModule { @SysUISingleton public static KeyguardViewMediator newKeyguardViewMediator( Context context, - FalsingManager falsingManager, + FalsingCollector falsingCollector, LockPatternUtils lockPatternUtils, BroadcastDispatcher broadcastDispatcher, Lazy<KeyguardViewController> statusBarKeyguardViewManagerLazy, @@ -83,7 +85,7 @@ public class KeyguardModule { KeyguardDisplayManager keyguardDisplayManager) { return new KeyguardViewMediator( context, - falsingManager, + falsingCollector, lockPatternUtils, broadcastDispatcher, statusBarKeyguardViewManagerLazy, diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt index 5eb6687f0b81..935352665314 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt @@ -12,6 +12,7 @@ import android.view.ViewGroup import android.widget.LinearLayout import androidx.annotation.VisibleForTesting import com.android.systemui.R +import com.android.systemui.classifier.FalsingCollector import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.plugins.ActivityStarter @@ -44,6 +45,7 @@ class MediaCarouselController @Inject constructor( @Main executor: DelayableExecutor, private val mediaManager: MediaDataManager, configurationController: ConfigurationController, + falsingCollector: FalsingCollector, falsingManager: FalsingManager ) { /** @@ -156,7 +158,7 @@ class MediaCarouselController @Inject constructor( pageIndicator = mediaFrame.requireViewById(R.id.media_page_indicator) mediaCarouselScrollHandler = MediaCarouselScrollHandler(mediaCarousel, pageIndicator, executor, mediaManager::onSwipeToDismiss, this::updatePageIndicatorLocation, - this::closeGuts, falsingManager) + this::closeGuts, falsingCollector, falsingManager) isRtl = context.resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL inflateSettingsButton() mediaContent = mediaCarousel.requireViewById(R.id.media_carousel) diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt index cb14f31abd16..bb6fbfab4c90 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt @@ -31,6 +31,7 @@ import com.android.systemui.Gefingerpoken import com.android.systemui.qs.PageIndicator import com.android.systemui.R import com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS +import com.android.systemui.classifier.FalsingCollector import com.android.systemui.plugins.FalsingManager import com.android.wm.shell.animation.PhysicsAnimator import com.android.systemui.util.concurrency.DelayableExecutor @@ -58,6 +59,7 @@ class MediaCarouselScrollHandler( private val dismissCallback: () -> Unit, private var translationChangedListener: () -> Unit, private val closeGuts: () -> Unit, + private val falsingCollector: FalsingCollector, private val falsingManager: FalsingManager ) { /** @@ -162,7 +164,7 @@ class MediaCarouselScrollHandler( override fun onDown(e: MotionEvent?): Boolean { if (falsingProtectionNeeded) { - falsingManager.onNotificationStartDismissing() + falsingCollector.onNotificationStartDismissing() } return false } @@ -258,7 +260,7 @@ class MediaCarouselScrollHandler( private fun onTouch(motionEvent: MotionEvent): Boolean { val isUp = motionEvent.action == MotionEvent.ACTION_UP if (isUp && falsingProtectionNeeded) { - falsingManager.onNotificationStopDismissing() + falsingCollector.onNotificationStopDismissing() } if (gestureDetector.onTouchEvent(motionEvent)) { if (isUp) { diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java index a27e9ac61848..8c66ba32ec79 100644 --- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java +++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java @@ -16,6 +16,8 @@ package com.android.systemui.power; +import static android.app.PendingIntent.FLAG_IMMUTABLE; + import android.app.KeyguardManager; import android.app.Notification; import android.app.NotificationManager; @@ -334,10 +336,14 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { } private PendingIntent pendingBroadcast(String action) { - return PendingIntent.getBroadcastAsUser(mContext, 0, - new Intent(action).setPackage(mContext.getPackageName()) - .setFlags(Intent.FLAG_RECEIVER_FOREGROUND), - 0, UserHandle.CURRENT); + return PendingIntent.getBroadcastAsUser( + mContext, + 0 /* request code */, + new Intent(action) + .setPackage(mContext.getPackageName()) + .setFlags(Intent.FLAG_RECEIVER_FOREGROUND), + FLAG_IMMUTABLE /* flags */, + UserHandle.CURRENT); } private static Intent settings(String action) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java index 19a8dabb31fc..16e95900052f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java @@ -61,7 +61,7 @@ public class QSContainerImpl extends FrameLayout { private float mQsExpansion; private QSCustomizer mQSCustomizer; private View mDragHandle; - private View mQSPanelContainer; + private NonInterceptingScrollView mQSPanelContainer; private View mBackground; @@ -126,18 +126,12 @@ public class QSContainerImpl extends FrameLayout { protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // QSPanel will show as many rows as it can (up to TileLayout.MAX_ROWS) such that the // bottom and footer are inside the screen. - Configuration config = getResources().getConfiguration(); - boolean navBelow = config.smallestScreenWidthDp >= 600 - || config.orientation != Configuration.ORIENTATION_LANDSCAPE; MarginLayoutParams layoutParams = (MarginLayoutParams) mQSPanelContainer.getLayoutParams(); // The footer is pinned to the bottom of QSPanel (same bottoms), therefore we don't need to // subtract its height. We do not care if the collapsed notifications fit in the screen. int maxQs = getDisplayHeight() - layoutParams.topMargin - layoutParams.bottomMargin - getPaddingBottom(); - if (navBelow) { - maxQs -= getResources().getDimensionPixelSize(R.dimen.navigation_bar_height); - } int padding = mPaddingLeft + mPaddingRight + layoutParams.leftMargin + layoutParams.rightMargin; @@ -217,12 +211,13 @@ public class QSContainerImpl extends FrameLayout { public void updateExpansion(boolean animate) { int height = calculateContainerHeight(); + int scrollBottom = calculateContainerBottom(); setBottom(getTop() + height); - mQSDetail.setBottom(getTop() + height); + mQSDetail.setBottom(getTop() + scrollBottom); // Pin the drag handle to the bottom of the panel. - mDragHandle.setTranslationY(height - mDragHandle.getHeight()); + mDragHandle.setTranslationY(scrollBottom - mDragHandle.getHeight()); mBackground.setTop(mQSPanelContainer.getTop()); - updateBackgroundBottom(height, animate); + updateBackgroundBottom(scrollBottom, animate); } private void updateBackgroundBottom(int height, boolean animated) { @@ -246,6 +241,15 @@ public class QSContainerImpl extends FrameLayout { + mHeader.getHeight(); } + int calculateContainerBottom() { + int heightOverride = mHeightOverride != -1 ? mHeightOverride : getMeasuredHeight(); + return mQSCustomizer.isCustomizing() ? mQSCustomizer.getHeight() + : Math.round(mQsExpansion + * (heightOverride + mQSPanelContainer.getScrollRange() + - mQSPanelContainer.getScrollY() - mHeader.getHeight())) + + mHeader.getHeight(); + } + public void setExpansion(float expansion) { mQsExpansion = expansion; mDragHandle.setAlpha(1.0f - expansion); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java index 043f5f1610a6..dbdd04a1e3ba 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java @@ -300,7 +300,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca ? View.VISIBLE : View.INVISIBLE); mHeader.setExpanded((keyguardShowing && !mHeaderAnimating && !mShowCollapsedOnKeyguard) - || (mQsExpanded && !mStackScrollerOverscrolling)); + || (mQsExpanded && !mStackScrollerOverscrolling), mQuickQSPanelController); mFooter.setVisibility( !mQsDisabled && (mQsExpanded || !keyguardShowing || mHeaderAnimating || mShowCollapsedOnKeyguard) diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index c080eccc1d8c..87a8da0cfa95 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -16,7 +16,6 @@ package com.android.systemui.qs; -import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT; import static com.android.systemui.util.Utils.useQsMediaPlayer; import android.annotation.NonNull; @@ -40,7 +39,6 @@ import com.android.internal.widget.RemeasuringLinearLayout; import com.android.systemui.R; import com.android.systemui.plugins.qs.DetailAdapter; import com.android.systemui.plugins.qs.QSTile; -import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.settings.brightness.BrightnessSlider; import com.android.systemui.statusbar.policy.BrightnessMirrorController; import com.android.systemui.tuner.TunerService; @@ -50,9 +48,6 @@ import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; -import javax.inject.Inject; -import javax.inject.Named; - /** View that represents the quick settings tile panel (when expanded/pulled down). **/ public class QSPanel extends LinearLayout implements Tunable { @@ -83,7 +78,6 @@ public class QSPanel extends LinearLayout implements Tunable { protected boolean mListening; private QSDetail.Callback mCallback; - private final QSLogger mQSLogger; protected QSTileHost mHost; private final List<OnConfigurationChangedListener> mOnConfigurationChangedListeners = new ArrayList<>(); @@ -119,19 +113,12 @@ public class QSPanel extends LinearLayout implements Tunable { private int mFooterMarginStartHorizontal; private Consumer<Boolean> mMediaVisibilityChangedListener; - - @Inject - public QSPanel( - @Named(VIEW_CONTEXT) Context context, - AttributeSet attrs, - QSLogger qsLogger - ) { + public QSPanel(Context context, AttributeSet attrs) { super(context, attrs); mUsingMediaPlayer = useQsMediaPlayer(context); mMediaTotalBottomMargin = getResources().getDimensionPixelSize( R.dimen.quick_settings_bottom_margin_media); mContext = context; - mQSLogger = qsLogger; setOrientation(VERTICAL); @@ -161,7 +148,6 @@ public class QSPanel extends LinearLayout implements Tunable { lp = new LayoutParams(LayoutParams.MATCH_PARENT, 0, 1); addView(mHorizontalLinearLayout, lp); } - mQSLogger.logAllTilesChangeListening(mListening, getDumpableTag(), ""); } protected void onMediaVisibilityChanged(Boolean visible) { @@ -439,7 +425,6 @@ public class QSPanel extends LinearLayout implements Tunable { public void setExpanded(boolean expanded) { if (mExpanded == expanded) return; - mQSLogger.logPanelExpanded(expanded, getDumpableTag()); mExpanded = expanded; if (!mExpanded && mTileLayout instanceof PagedTileLayout) { ((PagedTileLayout) mTileLayout).setCurrentItem(0, false); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java index 18c2925b7fd9..4418a7415c60 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java @@ -108,6 +108,11 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr } @Override + protected void onInit() { + mQSLogger.logAllTilesChangeListening(mView.isListening(), mView.getDumpableTag(), ""); + } + + @Override protected void onViewAttached() { mQsTileRevealController = createTileRevealController(); if (mQsTileRevealController != null) { @@ -221,6 +226,11 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr /** */ public void setExpanded(boolean expanded) { + if (mView.isExpanded() == expanded) { + return; + } + mQSLogger.logPanelExpanded(expanded, mView.getDumpableTag()); + mView.setExpanded(expanded); mMetricsLogger.visibility(MetricsEvent.QS_PANEL, expanded); if (!expanded) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java index bbd0c1abb239..c89f8e58f980 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java @@ -16,8 +16,6 @@ package com.android.systemui.qs; -import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT; - import android.content.Context; import android.content.res.Configuration; import android.graphics.Rect; @@ -31,10 +29,6 @@ import com.android.systemui.R; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.qs.QSTile.SignalState; import com.android.systemui.plugins.qs.QSTile.State; -import com.android.systemui.qs.logging.QSLogger; - -import javax.inject.Inject; -import javax.inject.Named; /** * Version of QSPanel that only shows N Quick Tiles in the QS Header. @@ -49,14 +43,8 @@ public class QuickQSPanel extends QSPanel { private boolean mDisabledByPolicy; private int mMaxTiles; - - @Inject - public QuickQSPanel( - @Named(VIEW_CONTEXT) Context context, - AttributeSet attrs, - QSLogger qsLogger, - UiEventLogger uiEventLogger) { - super(context, attrs, qsLogger); + public QuickQSPanel(Context context, AttributeSet attrs) { + super(context, attrs); mMaxTiles = Math.min(DEFAULT_MAX_TILES, getResources().getInteger(R.integer.quick_qs_panel_max_columns)); applyBottomMargin((View) mRegularTileLayout); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java index ac85ca4dda63..cca0e1b0c0f0 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java @@ -40,8 +40,6 @@ import javax.inject.Named; @QSScope public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel> { - private List<QSTile> mAllTiles = new ArrayList<>(); - private final QSPanel.OnConfigurationChangedListener mOnConfigurationChangedListener = newConfig -> { int newMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_columns); @@ -92,14 +90,14 @@ public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel> @Override public void setTiles() { - mAllTiles.clear(); + List<QSTile> tiles = new ArrayList(); for (QSTile tile : mHost.getTiles()) { - mAllTiles.add(tile); - if (mAllTiles.size() == QuickQSPanel.DEFAULT_MAX_TILES) { + tiles.add(tile); + if (tiles.size() == mView.getNumQuickTiles()) { break; } } - super.setTiles(mAllTiles.subList(0, mView.getNumQuickTiles()), true); + super.setTiles(tiles, true); } /** */ diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java index 6020e21035a8..b85ad8195edd 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java @@ -311,10 +311,11 @@ public class QuickStatusBarHeader extends RelativeLayout implements LifecycleOwn .build(); } - public void setExpanded(boolean expanded) { + /** */ + public void setExpanded(boolean expanded, QuickQSPanelController quickQSPanelController) { if (mExpanded == expanded) return; mExpanded = expanded; - mHeaderQsPanel.setExpanded(expanded); + quickQSPanelController.setExpanded(expanded); updateEverything(); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java index d7a2975a4b04..0f0a9a2766f1 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java @@ -134,7 +134,7 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState> getHost().collapsePanels(); Intent intent = mController.getPromptIntent(); ActivityStarter.OnDismissAction dismissAction = () -> { - mContext.startActivity(intent); + mHost.getUserContext().startActivity(intent); return false; }; mKeyguardDismissUtil.executeWhenUnlocked(dismissAction, false); diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java index 10a44ddb17f2..7ca82774f737 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java @@ -54,14 +54,31 @@ public class RecordingController private CountDownTimer mCountDownTimer = null; private BroadcastDispatcher mBroadcastDispatcher; + protected static final String INTENT_UPDATE_STATE = + "com.android.systemui.screenrecord.UPDATE_STATE"; + protected static final String EXTRA_STATE = "extra_state"; + private ArrayList<RecordingStateChangeCallback> mListeners = new ArrayList<>(); @VisibleForTesting protected final BroadcastReceiver mUserChangeReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - if (mStopIntent != null) { - stopRecording(); + stopRecording(); + } + }; + + @VisibleForTesting + protected final BroadcastReceiver mStateChangeReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (intent != null && INTENT_UPDATE_STATE.equals(intent.getAction())) { + if (intent.hasExtra(EXTRA_STATE)) { + boolean state = intent.getBooleanExtra(EXTRA_STATE, false); + updateState(state); + } else { + Log.e(TAG, "Received update intent with no state"); + } } } }; @@ -118,6 +135,10 @@ public class RecordingController IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_SWITCHED); mBroadcastDispatcher.registerReceiver(mUserChangeReceiver, userFilter, null, UserHandle.ALL); + + IntentFilter stateFilter = new IntentFilter(INTENT_UPDATE_STATE); + mBroadcastDispatcher.registerReceiver(mStateChangeReceiver, stateFilter, null, + UserHandle.ALL); Log.d(TAG, "sent start intent"); } catch (PendingIntent.CanceledException e) { Log.e(TAG, "Pending intent was cancelled: " + e.getMessage()); @@ -174,7 +195,6 @@ public class RecordingController } catch (PendingIntent.CanceledException e) { Log.e(TAG, "Error stopping: " + e.getMessage()); } - mBroadcastDispatcher.unregisterReceiver(mUserChangeReceiver); } /** @@ -182,6 +202,11 @@ public class RecordingController * @param isRecording */ public synchronized void updateState(boolean isRecording) { + if (!isRecording && mIsRecording) { + // Unregister receivers if we have stopped recording + mBroadcastDispatcher.unregisterReceiver(mUserChangeReceiver); + mBroadcastDispatcher.unregisterReceiver(mStateChangeReceiver); + } mIsRecording = isRecording; for (RecordingStateChangeCallback cb : mListeners) { if (isRecording) { diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java index 3bf118d1f39f..197582104f8e 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java @@ -69,6 +69,7 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis private static final String ACTION_STOP_NOTIF = "com.android.systemui.screenrecord.STOP_FROM_NOTIF"; private static final String ACTION_SHARE = "com.android.systemui.screenrecord.SHARE"; + private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF"; private final RecordingController mController; private final KeyguardDismissUtil mKeyguardDismissUtil; @@ -120,8 +121,8 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis String action = intent.getAction(); Log.d(TAG, "onStartCommand " + action); - int mCurrentUserId = mUserContextTracker.getUserContext().getUserId(); - UserHandle currentUser = new UserHandle(mCurrentUserId); + int currentUserId = mUserContextTracker.getUserContext().getUserId(); + UserHandle currentUser = new UserHandle(currentUserId); switch (action) { case ACTION_START: mAudioSource = ScreenRecordingAudioSource @@ -137,11 +138,22 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis mRecorder = new ScreenMediaRecorder( mUserContextTracker.getUserContext(), - mCurrentUserId, + currentUserId, mAudioSource, this ); - startRecording(); + + if (startRecording()) { + updateState(true); + createRecordingNotification(); + mUiEventLogger.log(Events.ScreenRecordEvent.SCREEN_RECORD_START); + } else { + updateState(false); + createErrorNotification(); + stopForeground(true); + stopSelf(); + return Service.START_NOT_STICKY; + } break; case ACTION_STOP_NOTIF: @@ -201,22 +213,63 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis return mRecorder; } + private void updateState(boolean state) { + int userId = mUserContextTracker.getUserContext().getUserId(); + if (userId == UserHandle.USER_SYSTEM) { + // Main user has a reference to the correct controller, so no need to use a broadcast + mController.updateState(state); + } else { + Intent intent = new Intent(RecordingController.INTENT_UPDATE_STATE); + intent.putExtra(RecordingController.EXTRA_STATE, state); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + sendBroadcast(intent, PERMISSION_SELF); + } + } + /** * Begin the recording session + * @return true if successful, false if something went wrong */ - private void startRecording() { + private boolean startRecording() { try { getRecorder().start(); - mController.updateState(true); - createRecordingNotification(); - mUiEventLogger.log(Events.ScreenRecordEvent.SCREEN_RECORD_START); - } catch (IOException | RemoteException | IllegalStateException e) { - Toast.makeText(this, - R.string.screenrecord_start_error, Toast.LENGTH_LONG) - .show(); + return true; + } catch (IOException | RemoteException | RuntimeException e) { + showErrorToast(R.string.screenrecord_start_error); e.printStackTrace(); - mController.updateState(false); } + return false; + } + + /** + * Simple error notification, needed since startForeground must be called to avoid errors + */ + @VisibleForTesting + protected void createErrorNotification() { + Resources res = getResources(); + NotificationChannel channel = new NotificationChannel( + CHANNEL_ID, + getString(R.string.screenrecord_name), + NotificationManager.IMPORTANCE_DEFAULT); + channel.setDescription(getString(R.string.screenrecord_channel_description)); + channel.enableVibration(true); + mNotificationManager.createNotificationChannel(channel); + + Bundle extras = new Bundle(); + extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, + res.getString(R.string.screenrecord_name)); + String notificationTitle = res.getString(R.string.screenrecord_start_error); + + Notification.Builder builder = new Notification.Builder(this, CHANNEL_ID) + .setSmallIcon(R.drawable.ic_screenrecord) + .setContentTitle(notificationTitle) + .addExtras(extras); + startForeground(NOTIFICATION_RECORDING_ID, builder.build()); + } + + @VisibleForTesting + protected void showErrorToast(int stringId) { + Toast.makeText(this, stringId, Toast.LENGTH_LONG).show(); } @VisibleForTesting @@ -247,6 +300,7 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis .setColorized(true) .setColor(getResources().getColor(R.color.GM2_red_700)) .setOngoing(true) + .setShowForegroundImmediately(true) .setContentIntent( PendingIntent.getService(this, REQUEST_CODE, stopIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE)) @@ -326,7 +380,7 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis } else { Log.e(TAG, "stopRecording called, but recorder was null"); } - mController.updateState(false); + updateState(false); } private void saveRecording(int userId) { @@ -344,8 +398,7 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis } } catch (IOException e) { Log.e(TAG, "Error saving screen recording: " + e.getMessage()); - Toast.makeText(this, R.string.screenrecord_delete_error, Toast.LENGTH_LONG) - .show(); + showErrorToast(R.string.screenrecord_delete_error); } finally { mNotificationManager.cancelAsUser(null, NOTIFICATION_PROCESSING_ID, currentUser); } diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java index 45564b0bfa6b..9037192e2ddc 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java @@ -94,7 +94,7 @@ public class ScreenMediaRecorder { mAudioSource = audioSource; } - private void prepare() throws IOException, RemoteException { + private void prepare() throws IOException, RemoteException, RuntimeException { //Setup media projection IBinder b = ServiceManager.getService(MEDIA_PROJECTION_SERVICE); IMediaProjectionManager mediaService = @@ -257,7 +257,7 @@ public class ScreenMediaRecorder { /** * Start screen recording */ - void start() throws IOException, RemoteException, IllegalStateException { + void start() throws IOException, RemoteException, RuntimeException { Log.d(TAG, "start recording"); prepare(); mMediaRecorder.start(); diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java index 672f82c2a583..2c82bcb24ad0 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java @@ -43,6 +43,7 @@ import android.view.WindowManager; import com.android.internal.logging.UiEventLogger; import com.android.internal.util.ScreenshotHelper; +import com.android.systemui.R; import com.android.systemui.shared.recents.utilities.BitmapUtil; import java.util.function.Consumer; @@ -55,6 +56,7 @@ public class TakeScreenshotService extends Service { private final ScreenshotController mScreenshot; private final UserManager mUserManager; private final UiEventLogger mUiEventLogger; + private final ScreenshotNotificationsController mNotificationsController; private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @@ -90,6 +92,8 @@ public class TakeScreenshotService extends Service { // animation and error notification. if (!mUserManager.isUserUnlocked()) { Log.w(TAG, "Skipping screenshot because storage is locked!"); + mNotificationsController.notifyScreenshotError( + R.string.screenshot_failed_to_save_user_locked_text); post(() -> uriConsumer.accept(null)); post(onComplete); return; @@ -125,11 +129,15 @@ public class TakeScreenshotService extends Service { }; @Inject - public TakeScreenshotService(ScreenshotController screenshotController, UserManager userManager, - UiEventLogger uiEventLogger) { + public TakeScreenshotService( + ScreenshotController screenshotController, + UserManager userManager, + UiEventLogger uiEventLogger, + ScreenshotNotificationsController notificationsController) { mScreenshot = screenshotController; mUserManager = userManager; mUiEventLogger = uiEventLogger; + mNotificationsController = notificationsController; } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java index e61e05a7dc2f..7ef88bb0c2c8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java @@ -31,6 +31,7 @@ import com.android.systemui.ExpandHelper; import com.android.systemui.Gefingerpoken; import com.android.systemui.Interpolators; import com.android.systemui.R; +import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.statusbar.notification.row.ExpandableView; @@ -46,6 +47,7 @@ public class DragDownHelper implements Gefingerpoken { private static final int SPRING_BACK_ANIMATION_LENGTH_MS = 375; private int mMinDragDistance; + private final FalsingManager mFalsingManager; private ExpandHelper.Callback mCallback; private float mInitialTouchX; private float mInitialTouchY; @@ -58,20 +60,21 @@ public class DragDownHelper implements Gefingerpoken { private boolean mDraggedFarEnough; private ExpandableView mStartingChild; private float mLastHeight; - private FalsingManager mFalsingManager; + private FalsingCollector mFalsingCollector; public DragDownHelper(Context context, View host, ExpandHelper.Callback callback, - DragDownCallback dragDownCallback, - FalsingManager falsingManager) { + DragDownCallback dragDownCallback, FalsingManager falsingManager, + FalsingCollector falsingCollector) { mMinDragDistance = context.getResources().getDimensionPixelSize( R.dimen.keyguard_drag_down_min_distance); + mFalsingManager = falsingManager; final ViewConfiguration configuration = ViewConfiguration.get(context); mTouchSlop = configuration.getScaledTouchSlop(); mSlopMultiplier = configuration.getScaledAmbiguousGestureMultiplier(); mCallback = callback; mDragDownCallback = dragDownCallback; mHost = host; - mFalsingManager = falsingManager; + mFalsingCollector = falsingCollector; } @Override @@ -96,7 +99,7 @@ public class DragDownHelper implements Gefingerpoken { ? mTouchSlop * mSlopMultiplier : mTouchSlop; if (h > touchSlop && h > Math.abs(x - mInitialTouchX)) { - mFalsingManager.onNotificatonStartDraggingDown(); + mFalsingCollector.onNotificationStartDraggingDown(); mDraggingDown = true; captureStartingChild(mInitialTouchX, mInitialTouchY); mInitialTouchY = y; @@ -229,7 +232,7 @@ public class DragDownHelper implements Gefingerpoken { } private void stopDragging() { - mFalsingManager.onNotificatonStopDraggingDown(); + mFalsingCollector.onNotificationStopDraggingDown(); if (mStartingChild != null) { cancelExpansion(mStartingChild); mStartingChild = null; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java index 216b83f2b4d5..84818eea8a23 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java @@ -41,6 +41,7 @@ import java.util.Objects; public class NotificationHeaderUtil { private static final TextViewComparator sTextViewComparator = new TextViewComparator(); + private static final TextViewComparator sAppNameComparator = new AppNameComparator(); private static final VisibilityApplicator sVisibilityApplicator = new VisibilityApplicator(); private static final VisibilityApplicator sAppNameApplicator = new AppNameApplicator(); private static final DataExtractor sIconExtractor = new DataExtractor() { @@ -119,7 +120,7 @@ public class NotificationHeaderUtil { mRow, com.android.internal.R.id.app_name_text, null, - sTextViewComparator, + sAppNameComparator, sAppNameApplicator)); mComparators.add(HeaderProcessor.forTextView(mRow, com.android.internal.R.id.header_text)); @@ -389,4 +390,17 @@ public class NotificationHeaderUtil { super.apply(parent, view, apply, reset); } } + + private static class AppNameComparator extends TextViewComparator { + @Override + public boolean compare(View parent, View child, Object parentData, Object childData) { + if (isEmpty(child)) { + // In headerless notifications the AppName view exists but is usually GONE (and not + // populated). We need to treat this case as equal to the header in order to + // deduplicate the view. + return true; + } + return super.compare(parent, child, parentData, childData); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt index 6fa3633acc43..b7343f67a86d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt @@ -31,6 +31,7 @@ import com.android.systemui.Gefingerpoken import com.android.systemui.Interpolators import com.android.systemui.R import com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN +import com.android.systemui.classifier.FalsingCollector import com.android.systemui.dagger.SysUISingleton import com.android.systemui.plugins.FalsingManager import com.android.systemui.plugins.statusbar.StatusBarStateController @@ -57,7 +58,8 @@ constructor( private val headsUpManager: HeadsUpManagerPhone, private val roundnessManager: NotificationRoundnessManager, private val statusBarStateController: StatusBarStateController, - private val falsingManager: FalsingManager + private val falsingManager: FalsingManager, + private val falsingCollector: FalsingCollector ) : Gefingerpoken { companion object { private val RUBBERBAND_FACTOR_STATIC = 0.25f @@ -148,7 +150,7 @@ constructor( MotionEvent.ACTION_MOVE -> { val h = y - mInitialTouchY if (h > mTouchSlop && h > Math.abs(x - mInitialTouchX)) { - falsingManager.onStartExpandingFromPulse() + falsingCollector.onStartExpandingFromPulse() isExpanding = true captureStartingChild(mInitialTouchX, mInitialTouchY) mInitialTouchY = y @@ -301,7 +303,7 @@ constructor( private fun cancelExpansion() { isExpanding = false - falsingManager.onExpansionFromPulseStopped() + falsingCollector.onExpansionFromPulseStopped() if (mStartingChild != null) { reset(mStartingChild!!) mStartingChild = null diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java index 41ce51c2762f..50bbc38eddf3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java @@ -22,37 +22,53 @@ import android.view.View; import android.view.accessibility.AccessibilityManager; import com.android.systemui.Gefingerpoken; +import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.plugins.FalsingManager; -import com.android.systemui.statusbar.phone.DoubleTapHelper; +import com.android.systemui.statusbar.phone.NotificationTapHelper; +import com.android.systemui.util.ViewController; import javax.inject.Inject; /** * Controller for {@link ActivatableNotificationView} */ -public class ActivatableNotificationViewController { - private final ActivatableNotificationView mView; +public class ActivatableNotificationViewController + extends ViewController<ActivatableNotificationView> { private final ExpandableOutlineViewController mExpandableOutlineViewController; private final AccessibilityManager mAccessibilityManager; private final FalsingManager mFalsingManager; - private DoubleTapHelper mDoubleTapHelper; - private boolean mNeedsDimming; + private final FalsingCollector mFalsingCollector; + private final NotificationTapHelper mNotificationTapHelper; + private final TouchHandler mTouchHandler = new TouchHandler(); - private TouchHandler mTouchHandler = new TouchHandler(); + private boolean mNeedsDimming; @Inject public ActivatableNotificationViewController(ActivatableNotificationView view, + NotificationTapHelper.Factory notificationTapHelpFactory, ExpandableOutlineViewController expandableOutlineViewController, - AccessibilityManager accessibilityManager, FalsingManager falsingManager) { - mView = view; + AccessibilityManager accessibilityManager, FalsingManager falsingManager, + FalsingCollector falsingCollector) { + super(view); mExpandableOutlineViewController = expandableOutlineViewController; mAccessibilityManager = accessibilityManager; mFalsingManager = falsingManager; + mFalsingCollector = falsingCollector; + + mNotificationTapHelper = notificationTapHelpFactory.create( + (active) -> { + if (active) { + mView.makeActive(); + mFalsingCollector.onNotificationActive(); + } else { + mView.makeInactive(true /* animate */); + } + }, mView::performClick, mView::handleSlideBack); mView.setOnActivatedListener(new ActivatableNotificationView.OnActivatedListener() { @Override public void onActivated(ActivatableNotificationView view) { - mFalsingManager.onNotificationActive(); + mFalsingCollector.onNotificationActive(); } @Override @@ -64,25 +80,25 @@ public class ActivatableNotificationViewController { /** * Initialize the controller, setting up handlers and other behavior. */ - public void init() { + @Override + public void onInit() { mExpandableOutlineViewController.init(); - mDoubleTapHelper = new DoubleTapHelper(mView, (active) -> { - if (active) { - mView.makeActive(); - mFalsingManager.onNotificationActive(); - } else { - mView.makeInactive(true /* animate */); - } - }, mView::performClick, mView::handleSlideBack, - mFalsingManager::onNotificationDoubleTap); mView.setOnTouchListener(mTouchHandler); mView.setTouchHandler(mTouchHandler); - mView.setOnDimmedListener(dimmed -> { - mNeedsDimming = dimmed; - }); + mView.setOnDimmedListener(dimmed -> mNeedsDimming = dimmed); mView.setAccessibilityManager(mAccessibilityManager); } + @Override + protected void onViewAttached() { + + } + + @Override + protected void onViewDetached() { + + } + class TouchHandler implements Gefingerpoken, View.OnTouchListener { private boolean mBlockNextTouch; @@ -103,7 +119,7 @@ public class ActivatableNotificationViewController { // let's ensure we have a ripple return false; } - result = mDoubleTapHelper.onTouchEvent(ev, mView.getActualHeight()); + result = mNotificationTapHelper.onTouchEvent(ev, mView.getActualHeight()); } else { return false; } @@ -117,7 +133,7 @@ public class ActivatableNotificationViewController { && !mAccessibilityManager.isTouchExplorationEnabled()) { if (!mView.isActive()) { return true; - } else if (!mDoubleTapHelper.isWithinDoubleTapSlop(ev)) { + } else if (mFalsingManager.isFalseDoubleTap()) { mBlockNextTouch = true; mView.makeInactive(true /* animate */); return true; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index ea3906418032..5ed17f1ee07f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -43,7 +43,6 @@ import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; -import android.provider.Settings; import android.service.notification.StatusBarNotification; import android.util.ArraySet; import android.util.AttributeSet; @@ -74,7 +73,7 @@ import com.android.internal.widget.MessagingLayout; import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; -import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.plugins.PluginListener; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem; @@ -213,7 +212,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private NotificationGuts mGuts; private NotificationEntry mEntry; private String mAppName; - private FalsingManager mFalsingManager; + private FalsingCollector mFalsingCollector; /** * Whether or not the notification is using the heads up view and should peek from the top. @@ -666,8 +665,12 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } else { smallHeight = mMaxSmallHeightBeforeS; } - } else if (isMediaLayout && showCompactMediaSeekbar) { - smallHeight = mMaxSmallHeightMedia; + } else if (isMediaLayout) { + // TODO(b/172652345): MediaStyle notifications currently look broken when we enforce + // the standard notification height, so we have to afford them more vertical space to + // make sure we don't crop them terribly. We actually need to revisit this and give + // them a headerless design, then remove this hack. + smallHeight = showCompactMediaSeekbar ? mMaxSmallHeightMedia : mMaxSmallHeightBeforeS; } else if (isMessagingLayout) { // TODO(b/173204301): MessagingStyle notifications currently look broken when we enforce // the standard notification height, so we have to afford them more vertical space to @@ -1580,7 +1583,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView OnExpandClickListener onExpandClickListener, NotificationMediaManager notificationMediaManager, CoordinateOnClickListener onFeedbackClickListener, - FalsingManager falsingManager, + FalsingCollector falsingCollector, StatusBarStateController statusBarStateController, PeopleNotificationIdentifier peopleNotificationIdentifier, OnUserInteractionCallback onUserInteractionCallback, @@ -1605,7 +1608,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mOnExpandClickListener = onExpandClickListener; mMediaManager = notificationMediaManager; setOnFeedbackClickListener(onFeedbackClickListener); - mFalsingManager = falsingManager; + mFalsingCollector = falsingCollector; mStatusBarStateController = statusBarStateController; mPeopleNotificationIdentifier = peopleNotificationIdentifier; @@ -2172,7 +2175,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView * @param allowChildExpansion whether a call to this method allows expanding children */ public void setUserExpanded(boolean userExpanded, boolean allowChildExpansion) { - mFalsingManager.setNotificationExpanded(); + mFalsingCollector.setNotificationExpanded(); if (mIsSummaryWithChildren && !shouldShowPublic() && allowChildExpansion && !mChildrenContainer.showingAsLowPriority()) { final boolean wasExpanded = mGroupExpansionManager.isGroupExpanded(mEntry); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java index cb2af54a25cb..0d0e97eae519 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java @@ -25,7 +25,7 @@ import android.view.ViewGroup; import androidx.annotation.NonNull; -import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shared.plugins.PluginManager; @@ -78,7 +78,7 @@ public class ExpandableNotificationRowController implements NodeController { private final ExpandableNotificationRow.CoordinateOnClickListener mOnFeedbackClickListener; private final NotificationGutsManager mNotificationGutsManager; private final OnUserInteractionCallback mOnUserInteractionCallback; - private final FalsingManager mFalsingManager; + private final FalsingCollector mFalsingCollector; private final boolean mAllowLongPress; private final PeopleNotificationIdentifier mPeopleNotificationIdentifier; private final Optional<BubblesManager> mBubblesManagerOptional; @@ -104,7 +104,7 @@ public class ExpandableNotificationRowController implements NodeController { NotificationGutsManager notificationGutsManager, @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress, OnUserInteractionCallback onUserInteractionCallback, - FalsingManager falsingManager, + FalsingCollector falsingCollector, PeopleNotificationIdentifier peopleNotificationIdentifier, Optional<BubblesManager> bubblesManagerOptional) { mView = view; @@ -127,7 +127,7 @@ public class ExpandableNotificationRowController implements NodeController { mOnUserInteractionCallback = onUserInteractionCallback; mOnFeedbackClickListener = mNotificationGutsManager::openGuts; mAllowLongPress = allowLongPress; - mFalsingManager = falsingManager; + mFalsingCollector = falsingCollector; mPeopleNotificationIdentifier = peopleNotificationIdentifier; mBubblesManagerOptional = bubblesManagerOptional; } @@ -150,7 +150,7 @@ public class ExpandableNotificationRowController implements NodeController { mOnExpandClickListener, mMediaManager, mOnFeedbackClickListener, - mFalsingManager, + mFalsingCollector, mStatusBarStateController, mPeopleNotificationIdentifier, mOnUserInteractionCallback, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java index 328774550f24..74e6c003041e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java @@ -1369,16 +1369,8 @@ public class NotificationContentView extends FrameLayout { bubbleButton.setOnClickListener(mContainingNotification.getBubbleClickListener()); bubbleButton.setVisibility(VISIBLE); actionContainer.setVisibility(VISIBLE); - - int paddingEnd = getResources().getDimensionPixelSize( - com.android.internal.R.dimen.bubble_visible_padding_end); - actionContainerLayout.setPaddingRelative(0, 0, paddingEnd, 0); } else { bubbleButton.setVisibility(GONE); - - int paddingEnd = getResources().getDimensionPixelSize( - com.android.internal.R.dimen.bubble_gone_padding_end); - actionContainerLayout.setPaddingRelative(0, 0, paddingEnd, 0); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java index 447fa43ef824..1034b1fd9031 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java @@ -295,7 +295,7 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl mMenuContainer = new FrameLayout(mContext); } final boolean newFlowHideShelf = Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.SHOW_NEW_NOTIF_DISMISS, 0) == 1; + Settings.Global.SHOW_NEW_NOTIF_DISMISS, 1 /* on by default */) == 1; if (newFlowHideShelf) { return; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java index d228ce159d48..05db67d706cf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java @@ -56,6 +56,7 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { private CachingIconView mIcon; private NotificationExpandButton mExpandButton; + private View mAltExpandTarget; protected NotificationHeaderView mNotificationHeader; protected NotificationTopLineView mNotificationTopLine; private TextView mHeaderText; @@ -106,6 +107,7 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { mHeaderText = mView.findViewById(com.android.internal.R.id.header_text); mAppNameText = mView.findViewById(com.android.internal.R.id.app_name_text); mExpandButton = mView.findViewById(com.android.internal.R.id.expand_button); + mAltExpandTarget = mView.findViewById(com.android.internal.R.id.alternate_expand_target); mRightIcon = mView.findViewById(com.android.internal.R.id.right_icon); mWorkProfileImage = mView.findViewById(com.android.internal.R.id.profile_badge); mNotificationHeader = mView.findViewById(com.android.internal.R.id.notification_header); @@ -260,6 +262,9 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { public void updateExpandability(boolean expandable, View.OnClickListener onClickListener) { mExpandButton.setVisibility(expandable ? View.VISIBLE : View.GONE); mExpandButton.setOnClickListener(expandable ? onClickListener : null); + if (mAltExpandTarget != null) { + mAltExpandTarget.setOnClickListener(expandable ? onClickListener : null); + } if (mNotificationHeader != null) { mNotificationHeader.setOnClickListener(expandable ? onClickListener : null); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java index 8050fea562a7..ac3b6d26fab6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java @@ -41,7 +41,6 @@ public class AmbientState { private static final float MAX_PULSE_HEIGHT = 100000f; private final SectionProvider mSectionProvider; - private ArrayList<View> mDraggedViews = new ArrayList<>(); private int mScrollY; private int mAnchorViewIndex; private int mAnchorViewY; @@ -83,9 +82,6 @@ public class AmbientState { private ExpandableNotificationRow mTrackedHeadsUpRow; private float mAppearFraction; - /** Tracks the state from AlertingNotificationManager#hasNotifications() */ - private boolean mHasAlertEntries; - public AmbientState( Context context, @NonNull SectionProvider sectionProvider) { @@ -161,19 +157,6 @@ public class AmbientState { mAnchorViewY = anchorViewY; } - /** Call when dragging begins. */ - public void onBeginDrag(View view) { - mDraggedViews.add(view); - } - - public void onDragFinished(View view) { - mDraggedViews.remove(view); - } - - public ArrayList<View> getDraggedViews() { - return mDraggedViews; - } - /** * @param dimmed Whether we are in a dimmed state (on the lockscreen), where the backgrounds are * translucent and everything is scaled back a bit. @@ -382,21 +365,10 @@ public class AmbientState { mPanelTracking = panelTracking; } - public boolean hasPulsingNotifications() { - return mPulsing && mHasAlertEntries; - } - public void setPulsing(boolean hasPulsing) { mPulsing = hasPulsing; } - /** - * @return if we're pulsing in general - */ - public boolean isPulsing() { - return mPulsing; - } - public boolean isPulsing(NotificationEntry entry) { return mPulsing && entry.isAlerting(); } @@ -555,8 +527,4 @@ public class AmbientState { public float getAppearFraction() { return mAppearFraction; } - - public void setHasAlertEntries(boolean hasAlertEntries) { - mHasAlertEntries = hasAlertEntries; - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSection.java index 1131a65abe93..ba03a50b0ba5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSection.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSection.java @@ -31,175 +31,22 @@ import com.android.systemui.statusbar.notification.ShadeViewRefactor; import com.android.systemui.statusbar.notification.row.ExpandableView; /** - * Represents the bounds of a section of the notification shade and handles animation when the - * bounds change. + * Represents the priority of a notification section and tracks first and last visible children. */ public class NotificationSection { private @PriorityBucket int mBucket; - private View mOwningView; - private Rect mBounds = new Rect(); - private Rect mCurrentBounds = new Rect(-1, -1, -1, -1); - private Rect mStartAnimationRect = new Rect(); - private Rect mEndAnimationRect = new Rect(); - private ObjectAnimator mTopAnimator = null; - private ObjectAnimator mBottomAnimator = null; private ExpandableView mFirstVisibleChild; private ExpandableView mLastVisibleChild; - NotificationSection(View owningView, @PriorityBucket int bucket) { - mOwningView = owningView; + NotificationSection(@PriorityBucket int bucket) { mBucket = bucket; } - public void cancelAnimators() { - if (mBottomAnimator != null) { - mBottomAnimator.cancel(); - } - if (mTopAnimator != null) { - mTopAnimator.cancel(); - } - } - - public Rect getCurrentBounds() { - return mCurrentBounds; - } - - public Rect getBounds() { - return mBounds; - } - - public boolean didBoundsChange() { - return !mCurrentBounds.equals(mBounds); - } - - public boolean areBoundsAnimating() { - return mBottomAnimator != null || mTopAnimator != null; - } - @PriorityBucket public int getBucket() { return mBucket; } - public void startBackgroundAnimation(boolean animateTop, boolean animateBottom) { - // Left and right bounds are always applied immediately. - mCurrentBounds.left = mBounds.left; - mCurrentBounds.right = mBounds.right; - startBottomAnimation(animateBottom); - startTopAnimation(animateTop); - } - - - @ShadeViewRefactor(ShadeViewRefactor.RefactorComponent.STATE_RESOLVER) - private void startTopAnimation(boolean animate) { - int previousEndValue = mEndAnimationRect.top; - int newEndValue = mBounds.top; - ObjectAnimator previousAnimator = mTopAnimator; - if (previousAnimator != null && previousEndValue == newEndValue) { - return; - } - if (!animate) { - // just a local update was performed - if (previousAnimator != null) { - // we need to increase all animation keyframes of the previous animator by the - // relative change to the end value - int previousStartValue = mStartAnimationRect.top; - PropertyValuesHolder[] values = previousAnimator.getValues(); - values[0].setIntValues(previousStartValue, newEndValue); - mStartAnimationRect.top = previousStartValue; - mEndAnimationRect.top = newEndValue; - previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); - return; - } else { - // no new animation needed, let's just apply the value - setBackgroundTop(newEndValue); - return; - } - } - if (previousAnimator != null) { - previousAnimator.cancel(); - } - ObjectAnimator animator = ObjectAnimator.ofInt(this, "backgroundTop", - mCurrentBounds.top, newEndValue); - Interpolator interpolator = Interpolators.FAST_OUT_SLOW_IN; - animator.setInterpolator(interpolator); - animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD); - // remove the tag when the animation is finished - animator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mStartAnimationRect.top = -1; - mEndAnimationRect.top = -1; - mTopAnimator = null; - } - }); - animator.start(); - mStartAnimationRect.top = mCurrentBounds.top; - mEndAnimationRect.top = newEndValue; - mTopAnimator = animator; - } - - @ShadeViewRefactor(ShadeViewRefactor.RefactorComponent.STATE_RESOLVER) - private void startBottomAnimation(boolean animate) { - int previousStartValue = mStartAnimationRect.bottom; - int previousEndValue = mEndAnimationRect.bottom; - int newEndValue = mBounds.bottom; - ObjectAnimator previousAnimator = mBottomAnimator; - if (previousAnimator != null && previousEndValue == newEndValue) { - return; - } - if (!animate) { - // just a local update was performed - if (previousAnimator != null) { - // we need to increase all animation keyframes of the previous animator by the - // relative change to the end value - PropertyValuesHolder[] values = previousAnimator.getValues(); - values[0].setIntValues(previousStartValue, newEndValue); - mStartAnimationRect.bottom = previousStartValue; - mEndAnimationRect.bottom = newEndValue; - previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); - return; - } else { - // no new animation needed, let's just apply the value - setBackgroundBottom(newEndValue); - return; - } - } - if (previousAnimator != null) { - previousAnimator.cancel(); - } - ObjectAnimator animator = ObjectAnimator.ofInt(this, "backgroundBottom", - mCurrentBounds.bottom, newEndValue); - Interpolator interpolator = Interpolators.FAST_OUT_SLOW_IN; - animator.setInterpolator(interpolator); - animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD); - // remove the tag when the animation is finished - animator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mStartAnimationRect.bottom = -1; - mEndAnimationRect.bottom = -1; - mBottomAnimator = null; - } - }); - animator.start(); - mStartAnimationRect.bottom = mCurrentBounds.bottom; - mEndAnimationRect.bottom = newEndValue; - mBottomAnimator = animator; - } - - @ShadeViewRefactor(ShadeViewRefactor.RefactorComponent.SHADE_VIEW) - private void setBackgroundTop(int top) { - mCurrentBounds.top = top; - mOwningView.invalidate(); - } - - @ShadeViewRefactor(ShadeViewRefactor.RefactorComponent.SHADE_VIEW) - private void setBackgroundBottom(int bottom) { - mCurrentBounds.bottom = bottom; - mOwningView.invalidate(); - } - public ExpandableView getFirstVisibleChild() { return mFirstVisibleChild; } @@ -219,93 +66,4 @@ public class NotificationSection { mLastVisibleChild = child; return changed; } - - public void resetCurrentBounds() { - mCurrentBounds.set(mBounds); - } - - /** - * Returns true if {@code top} is equal to the top of this section (if not currently animating) - * or where the top of this section will be when animation completes. - */ - public boolean isTargetTop(int top) { - return (mTopAnimator == null && mCurrentBounds.top == top) - || (mTopAnimator != null && mEndAnimationRect.top == top); - } - - /** - * Returns true if {@code bottom} is equal to the bottom of this section (if not currently - * animating) or where the bottom of this section will be when animation completes. - */ - public boolean isTargetBottom(int bottom) { - return (mBottomAnimator == null && mCurrentBounds.bottom == bottom) - || (mBottomAnimator != null && mEndAnimationRect.bottom == bottom); - } - - /** - * Update the bounds of this section based on it's views - * - * @param minTopPosition the minimum position that the top needs to have - * @param minBottomPosition the minimum position that the bottom needs to have - * @return the position of the new bottom - */ - public int updateBounds(int minTopPosition, int minBottomPosition, - boolean shiftBackgroundWithFirst) { - int top = minTopPosition; - int bottom = minTopPosition; - ExpandableView firstView = getFirstVisibleChild(); - if (firstView != null) { - // Round Y up to avoid seeing the background during animation - int finalTranslationY = (int) Math.ceil(ViewState.getFinalTranslationY(firstView)); - // TODO: look into the already animating part - int newTop; - if (isTargetTop(finalTranslationY)) { - // we're ending up at the same location as we are now, let's just skip the - // animation - newTop = finalTranslationY; - } else { - newTop = (int) Math.ceil(firstView.getTranslationY()); - } - top = Math.max(newTop, top); - if (firstView.showingPulsing()) { - // If we're pulsing, the notification can actually go below! - bottom = Math.max(bottom, finalTranslationY - + ExpandableViewState.getFinalActualHeight(firstView)); - if (shiftBackgroundWithFirst) { - mBounds.left += Math.max(firstView.getTranslation(), 0); - mBounds.right += Math.min(firstView.getTranslation(), 0); - } - } - } - top = Math.max(minTopPosition, top); - ExpandableView lastView = getLastVisibleChild(); - if (lastView != null) { - float finalTranslationY = ViewState.getFinalTranslationY(lastView); - int finalHeight = ExpandableViewState.getFinalActualHeight(lastView); - // Round Y down to avoid seeing the background during animation - int finalBottom = (int) Math.floor( - finalTranslationY + finalHeight - lastView.getClipBottomAmount()); - int newBottom; - if (isTargetBottom(finalBottom)) { - // we're ending up at the same location as we are now, lets just skip the animation - newBottom = finalBottom; - } else { - newBottom = (int) (lastView.getTranslationY() + lastView.getActualHeight() - - lastView.getClipBottomAmount()); - // The background can never be lower than the end of the last view - minBottomPosition = (int) Math.min( - lastView.getTranslationY() + lastView.getActualHeight(), - minBottomPosition); - } - bottom = Math.max(bottom, Math.max(newBottom, minBottomPosition)); - } - bottom = Math.max(top, bottom); - mBounds.top = top; - mBounds.bottom = bottom; - return bottom; - } - - public boolean needsBackground() { - return mFirstVisibleChild != null && mBucket != BUCKET_MEDIA_CONTROLS; - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt index 4f7e14ba4a4c..36c5419b7399 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt @@ -126,7 +126,7 @@ class NotificationSectionsManager @Inject internal constructor( fun createSectionsForBuckets(): Array<NotificationSection> = sectionsFeatureManager.getNotificationBuckets() - .map { NotificationSection(parent, it) } + .map { NotificationSection(it) } .toTypedArray() /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index fbcfef3964af..ba5f95e9c4d8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -38,12 +38,9 @@ import android.content.Intent; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Canvas; -import android.graphics.Color; import android.graphics.Outline; import android.graphics.Paint; import android.graphics.PointF; -import android.graphics.PorterDuff; -import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.os.Bundle; import android.os.UserHandle; @@ -73,7 +70,6 @@ import android.widget.OverScroller; import android.widget.ScrollView; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.graphics.ColorUtils; import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.keyguard.KeyguardSliceView; @@ -158,8 +154,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private ExpandHelper mExpandHelper; private NotificationSwipeHelper mSwipeHelper; private int mCurrentStackHeight = Integer.MAX_VALUE; - private final Paint mBackgroundPaint = new Paint(); - private final boolean mShouldDrawNotificationBackground; private boolean mHighPriorityBeforeSpeedBump; private boolean mDismissRtl; @@ -258,7 +252,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable protected FooterView mFooterView; protected EmptyShadeView mEmptyShadeView; private boolean mDismissAllInProgress; - private boolean mFadeNotificationsOnDismiss; private FooterDismissListener mFooterDismissListener; private boolean mFlingAfterUpEvent; @@ -328,10 +321,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } }; private NotificationSection[] mSections; - private boolean mAnimateNextBackgroundTop; - private boolean mAnimateNextBackgroundBottom; - private boolean mAnimateNextSectionBoundsChange; - private int mBgColor; private float mDimAmount; private ValueAnimator mDimAnimator; private ArrayList<ExpandableView> mTmpSortedChildren = new ArrayList<>(); @@ -342,42 +331,24 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } }; private ValueAnimator.AnimatorUpdateListener mDimUpdateListener - = new ValueAnimator.AnimatorUpdateListener() { - - @Override - public void onAnimationUpdate(ValueAnimator animation) { - setDimAmount((Float) animation.getAnimatedValue()); - } - }; + = animation -> setDimAmount((Float) animation.getAnimatedValue()); protected ViewGroup mQsContainer; private boolean mContinuousShadowUpdate; - private boolean mContinuousBackgroundUpdate; private ViewTreeObserver.OnPreDrawListener mShadowUpdater - = new ViewTreeObserver.OnPreDrawListener() { - - @Override - public boolean onPreDraw() { - updateViewShadows(); - return true; - } - }; - private ViewTreeObserver.OnPreDrawListener mBackgroundUpdater = () -> { - updateBackground(); + = () -> { + updateViewShadows(); return true; }; - private Comparator<ExpandableView> mViewPositionComparator = new Comparator<ExpandableView>() { - @Override - public int compare(ExpandableView view, ExpandableView otherView) { - float endY = view.getTranslationY() + view.getActualHeight(); - float otherEndY = otherView.getTranslationY() + otherView.getActualHeight(); - if (endY < otherEndY) { - return -1; - } else if (endY > otherEndY) { - return 1; - } else { - // The two notifications end at the same location - return 0; - } + private Comparator<ExpandableView> mViewPositionComparator = (view, otherView) -> { + float endY = view.getTranslationY() + view.getActualHeight(); + float otherEndY = otherView.getTranslationY() + otherView.getActualHeight(); + if (endY < otherEndY) { + return -1; + } else if (endY > otherEndY) { + return 1; + } else { + // The two notifications end at the same location + return 0; } }; private final ViewOutlineProvider mOutlineProvider = new ViewOutlineProvider() { @@ -386,7 +357,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable if (mAmbientState.isHiddenAtAll()) { float xProgress = mHideXInterpolator.getInterpolation( (1 - mLinearHideAmount) * mBackgroundXFactor); - outline.setRoundRect(mBackgroundAnimationRect, + outline.setRoundRect(mOutlineAnimationRect, MathUtils.lerp(mCornerRadius / 2.0f, mCornerRadius, xProgress)); outline.setAlpha(1.0f - mAmbientState.getHideAmount()); @@ -395,7 +366,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } } }; - private PorterDuffXfermode mSrcMode = new PorterDuffXfermode(PorterDuff.Mode.SRC); private boolean mPulsing; private boolean mScrollable; private View mForcedScroll; @@ -415,8 +385,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable */ private float mBackgroundXFactor = 1f; - private boolean mSwipingInProgress; - private boolean mUsingLightTheme; private boolean mQsExpanded; private boolean mForwardScrollable; @@ -431,7 +399,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private boolean mInHeadsUpPinnedMode; private boolean mHeadsUpAnimatingAway; private int mStatusBarState; - private int mCachedBackgroundColor; private boolean mHeadsUpGoingAwayAnimationsAllowed = true; private Runnable mReflingAndAnimateScroll = () -> { if (ANCHOR_SCROLLING) { @@ -441,7 +408,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable }; private int mCornerRadius; private int mSidePaddings; - private final Rect mBackgroundAnimationRect = new Rect(); + private final Rect mOutlineAnimationRect = new Rect(); private ArrayList<BiConsumer<Float, Float>> mExpandedHeightListeners = new ArrayList<>(); private int mHeadsUpInset; private HeadsUpAppearanceController mHeadsUpAppearanceController; @@ -461,7 +428,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private final NotificationSectionsManager mSectionsManager; private ForegroundServiceDungeonView mFgsSectionView; - private boolean mAnimateBottomOnLayout; private float mLastSentAppear; private float mLastSentExpandedHeight; private boolean mWillExpand; @@ -472,7 +438,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private boolean mKeyguardMediaControllorVisible; private NotificationEntry mTopHeadsUpEntry; - private long mNumHeadsUp; private NotificationStackScrollLayoutController.TouchHandler mTouchHandler; private final ExpandableView.OnHeightChangedListener mOnChildHeightChangedListener = @@ -539,7 +504,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mSections = mSectionsManager.createSectionsForBuckets(); mAmbientState = new AmbientState(context, mSectionsManager); - mBgColor = context.getColor(R.color.notification_shade_background_color); int minHeight = res.getDimensionPixelSize(R.dimen.notification_min_height); int maxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height); mExpandHelper = new ExpandHelper(getContext(), mExpandHelperCallback, @@ -548,13 +512,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mExpandHelper.setScrollAdapter(mScrollAdapter); mStackScrollAlgorithm = createStackScrollAlgorithm(context); - mShouldDrawNotificationBackground = - res.getBoolean(R.bool.config_drawNotificationBackground); setOutlineProvider(mOutlineProvider); - boolean willDraw = mShouldDrawNotificationBackground || DEBUG; - setWillNotDraw(!willDraw); - mBackgroundPaint.setAntiAlias(true); + setWillNotDraw(!DEBUG); if (DEBUG) { mDebugPaint = new Paint(); mDebugPaint.setColor(0xffff0000); @@ -599,7 +559,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable * @return the height at which we will wake up when pulsing */ public float getWakeUpHeight() { - ExpandableView firstChild = getFirstChildWithBackground(); + ExpandableView firstChild = getFirstExpandableView(); if (firstChild != null) { if (mKeyguardBypassEnabledProvider.getBypassEnabled()) { return firstChild.getHeadsUpHeightWithoutHeader(); @@ -650,22 +610,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } void updateBgColor() { - mBgColor = mContext.getColor(R.color.notification_shade_background_color); - updateBackgroundDimming(); mShelf.onUiModeChanged(); } @ShadeViewRefactor(RefactorComponent.DECORATOR) protected void onDraw(Canvas canvas) { - if (mShouldDrawNotificationBackground - && (mSections[0].getCurrentBounds().top - < mSections[mSections.length - 1].getCurrentBounds().bottom - || mAmbientState.isDozing())) { - drawBackground(canvas); - } else if (mInHeadsUpPinnedMode || mHeadsUpAnimatingAway) { - drawHeadsUpBackground(canvas); - } - if (DEBUG) { int y = mTopPadding; canvas.drawLine(0, y, getWidth(), y, mDebugPaint); @@ -700,160 +649,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } } - @ShadeViewRefactor(RefactorComponent.DECORATOR) - private void drawBackground(Canvas canvas) { - int lockScreenLeft = mSidePaddings; - int lockScreenRight = getWidth() - mSidePaddings; - int lockScreenTop = mSections[0].getCurrentBounds().top; - int lockScreenBottom = mSections[mSections.length - 1].getCurrentBounds().bottom; - int hiddenLeft = getWidth() / 2; - int hiddenTop = mTopPadding; - - float yProgress = 1 - mInterpolatedHideAmount; - float xProgress = mHideXInterpolator.getInterpolation( - (1 - mLinearHideAmount) * mBackgroundXFactor); - - int left = (int) MathUtils.lerp(hiddenLeft, lockScreenLeft, xProgress); - int right = (int) MathUtils.lerp(hiddenLeft, lockScreenRight, xProgress); - int top = (int) MathUtils.lerp(hiddenTop, lockScreenTop, yProgress); - int bottom = (int) MathUtils.lerp(hiddenTop, lockScreenBottom, yProgress); - mBackgroundAnimationRect.set( - left, - top, - right, - bottom); - - int backgroundTopAnimationOffset = top - lockScreenTop; - // TODO(kprevas): this may not be necessary any more since we don't display the shelf in AOD - boolean anySectionHasVisibleChild = false; - for (NotificationSection section : mSections) { - if (section.needsBackground()) { - anySectionHasVisibleChild = true; - break; - } - } - boolean shouldDrawBackground; - if (mKeyguardBypassEnabledProvider.getBypassEnabled() && onKeyguard()) { - shouldDrawBackground = isPulseExpanding(); - } else { - shouldDrawBackground = !mAmbientState.isDozing() || anySectionHasVisibleChild; - } - if (shouldDrawBackground) { - drawBackgroundRects(canvas, left, right, top, backgroundTopAnimationOffset); - } - - updateClipping(); - } - - /** - * Draws round rects for each background section. - * - * We want to draw a round rect for each background section as defined by {@link #mSections}. - * However, if two sections are directly adjacent with no gap between them (e.g. on the - * lockscreen where the shelf can appear directly below the high priority section, or while - * scrolling the shade so that the top of the shelf is right at the bottom of the high priority - * section), we don't want to round the adjacent corners. - * - * Since {@link Canvas} doesn't provide a way to draw a half-rounded rect, this means that we - * need to coalesce the backgrounds for adjacent sections and draw them as a single round rect. - * This method tracks the top of each rect we need to draw, then iterates through the visible - * sections. If a section is not adjacent to the previous section, we draw the previous rect - * behind the sections we've accumulated up to that point, then start a new rect at the top of - * the current section. When we're done iterating we will always have one rect left to draw. - */ - private void drawBackgroundRects(Canvas canvas, int left, int right, int top, - int animationYOffset) { - int backgroundRectTop = top; - int lastSectionBottom = - mSections[0].getCurrentBounds().bottom + animationYOffset; - int currentLeft = left; - int currentRight = right; - boolean first = true; - for (NotificationSection section : mSections) { - if (!section.needsBackground()) { - continue; - } - int sectionTop = section.getCurrentBounds().top + animationYOffset; - int ownLeft = Math.min(Math.max(left, section.getCurrentBounds().left), right); - int ownRight = Math.max(Math.min(right, section.getCurrentBounds().right), ownLeft); - // If sections are directly adjacent to each other, we don't want to draw them - // as separate roundrects, as the rounded corners right next to each other look - // bad. - if (sectionTop - lastSectionBottom > DISTANCE_BETWEEN_ADJACENT_SECTIONS_PX - || ((currentLeft != ownLeft || currentRight != ownRight) && !first)) { - canvas.drawRoundRect(currentLeft, - backgroundRectTop, - currentRight, - lastSectionBottom, - mCornerRadius, mCornerRadius, mBackgroundPaint); - backgroundRectTop = sectionTop; - } - currentLeft = ownLeft; - currentRight = ownRight; - lastSectionBottom = - section.getCurrentBounds().bottom + animationYOffset; - first = false; - } - canvas.drawRoundRect(currentLeft, - backgroundRectTop, - currentRight, - lastSectionBottom, - mCornerRadius, mCornerRadius, mBackgroundPaint); - } - - private void drawHeadsUpBackground(Canvas canvas) { - int left = mSidePaddings; - int right = getWidth() - mSidePaddings; - - float top = getHeight(); - float bottom = 0; - int childCount = getChildCount(); - for (int i = 0; i < childCount; i++) { - View child = getChildAt(i); - if (child.getVisibility() != View.GONE - && child instanceof ExpandableNotificationRow) { - ExpandableNotificationRow row = (ExpandableNotificationRow) child; - if ((row.isPinned() || row.isHeadsUpAnimatingAway()) && row.getTranslation() < 0 - && row.getProvider().shouldShowGutsOnSnapOpen()) { - top = Math.min(top, row.getTranslationY()); - bottom = Math.max(bottom, row.getTranslationY() + row.getActualHeight()); - } - } - } - - if (top < bottom) { - canvas.drawRoundRect( - left, top, right, bottom, - mCornerRadius, mCornerRadius, mBackgroundPaint); - } - } - - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - void updateBackgroundDimming() { - // No need to update the background color if it's not being drawn. - if (!mShouldDrawNotificationBackground) { - return; - } - final boolean clearUndershelf = Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.SHOW_NEW_NOTIF_DISMISS, 0 /* show background by default */) == 1; - if (clearUndershelf) { - mBackgroundPaint.setColor(Color.TRANSPARENT); - invalidate(); - return; - } - // Interpolate between semi-transparent notification panel background color - // and white AOD separator. - float colorInterpolation = MathUtils.smoothStep(0.4f /* start */, 1f /* end */, - mLinearHideAmount); - int color = ColorUtils.blendARGB(mBgColor, Color.WHITE, colorInterpolation); - - if (mCachedBackgroundColor != color) { - mCachedBackgroundColor = color; - mBackgroundPaint.setColor(color); - invalidate(); - } - } - private void reinitView() { initView(getContext(), mKeyguardBypassEnabledProvider, mSwipeHelper); } @@ -988,7 +783,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable updateContentHeight(); clampScrollPosition(); requestChildrenUpdate(); - updateFirstAndLastBackgroundViews(); + updateFirstAndLastExpandableView(); updateAlgorithmLayoutMinHeight(); updateOwnTranslationZ(); } @@ -1059,9 +854,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private void onPreDrawDuringAnimation() { mShelf.updateAppearance(); updateClippingToTopRoundedCorner(); - if (!mNeedsAnimation && !mChildrenUpdateRequested) { - updateBackground(); - } } @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) @@ -2378,131 +2170,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } } - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - private void updateBackground() { - // No need to update the background color if it's not being drawn. - if (!mShouldDrawNotificationBackground) { - return; - } - - updateBackgroundBounds(); - if (didSectionBoundsChange()) { - boolean animate = mAnimateNextSectionBoundsChange || mAnimateNextBackgroundTop - || mAnimateNextBackgroundBottom || areSectionBoundsAnimating(); - if (!isExpanded()) { - abortBackgroundAnimators(); - animate = false; - } - if (animate) { - startBackgroundAnimation(); - } else { - for (NotificationSection section : mSections) { - section.resetCurrentBounds(); - } - invalidate(); - } - } else { - abortBackgroundAnimators(); - } - mAnimateNextBackgroundTop = false; - mAnimateNextBackgroundBottom = false; - mAnimateNextSectionBoundsChange = false; - } - - @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) - private void abortBackgroundAnimators() { - for (NotificationSection section : mSections) { - section.cancelAnimators(); - } - } - - private boolean didSectionBoundsChange() { - for (NotificationSection section : mSections) { - if (section.didBoundsChange()) { - return true; - } - } - return false; - } - - @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) - private boolean areSectionBoundsAnimating() { - for (NotificationSection section : mSections) { - if (section.areBoundsAnimating()) { - return true; - } - } - return false; - } - - @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) - private void startBackgroundAnimation() { - // TODO(kprevas): do we still need separate fields for top/bottom? - // or can each section manage its own animation state? - NotificationSection firstVisibleSection = getFirstVisibleSection(); - NotificationSection lastVisibleSection = getLastVisibleSection(); - for (NotificationSection section : mSections) { - section.startBackgroundAnimation( - section == firstVisibleSection - ? mAnimateNextBackgroundTop - : mAnimateNextSectionBoundsChange, - section == lastVisibleSection - ? mAnimateNextBackgroundBottom - : mAnimateNextSectionBoundsChange); - } - } - - /** - * Update the background bounds to the new desired bounds - */ - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - private void updateBackgroundBounds() { - int left = mSidePaddings; - int right = getWidth() - mSidePaddings; - for (NotificationSection section : mSections) { - section.getBounds().left = left; - section.getBounds().right = right; - } - - if (!mIsExpanded) { - for (NotificationSection section : mSections) { - section.getBounds().top = 0; - section.getBounds().bottom = 0; - } - return; - } - int minTopPosition; - NotificationSection lastSection = getLastVisibleSection(); - boolean onKeyguard = mStatusBarState == StatusBarState.KEYGUARD; - if (!onKeyguard) { - minTopPosition = (int) (mTopPadding + mStackTranslation); - } else if (lastSection == null) { - minTopPosition = mTopPadding; - } else { - // The first sections could be empty while there could still be elements in later - // sections. The position of these first few sections is determined by the position of - // the first visible section. - NotificationSection firstVisibleSection = getFirstVisibleSection(); - firstVisibleSection.updateBounds(0 /* minTopPosition*/, 0 /* minBottomPosition */, - false /* shiftPulsingWithFirst */); - minTopPosition = firstVisibleSection.getBounds().top; - } - boolean shiftPulsingWithFirst = mNumHeadsUp <= 1 - && (mAmbientState.isDozing() - || (mKeyguardBypassEnabledProvider.getBypassEnabled() && onKeyguard)); - for (NotificationSection section : mSections) { - int minBottomPosition = minTopPosition; - if (section == lastSection) { - // We need to make sure the section goes all the way to the shelf - minBottomPosition = (int) (ViewState.getFinalTranslationY(mShelf) - + mShelf.getIntrinsicHeight()); - } - minTopPosition = section.updateBounds(minTopPosition, minBottomPosition, - shiftPulsingWithFirst); - shiftPulsingWithFirst = false; - } - } - private NotificationSection getFirstVisibleSection() { for (NotificationSection section : mSections) { if (section.getFirstVisibleChild() != null) { @@ -2523,7 +2190,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } @ShadeViewRefactor(RefactorComponent.COORDINATOR) - private ExpandableView getLastChildWithBackground() { + private ExpandableView getLastExpandableView() { int childCount = getChildCount(); for (int i = childCount - 1; i >= 0; i--) { ExpandableView child = (ExpandableView) getChildAt(i); @@ -2536,7 +2203,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } @ShadeViewRefactor(RefactorComponent.COORDINATOR) - private ExpandableView getFirstChildWithBackground() { + private ExpandableView getFirstExpandableView() { int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { ExpandableView child = (ExpandableView) getChildAt(i); @@ -2549,17 +2216,18 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } //TODO: We shouldn't have to generate this list every time - private List<ExpandableView> getChildrenWithBackground() { + private List<ExpandableView> getExpandableViewList() { ArrayList<ExpandableView> children = new ArrayList<>(); int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { ExpandableView child = (ExpandableView) getChildAt(i); - if (child.getVisibility() != View.GONE && !(child instanceof StackScrollerDecorView) - && child != mShelf) { + if (child.getVisibility() != View.GONE + && !(child instanceof StackScrollerDecorView) + && child != mShelf + && mSwipeHelper.getSwipedView() != child) { children.add(child); } } - return children; } @@ -3086,32 +2754,13 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) - private void updateFirstAndLastBackgroundViews() { - NotificationSection firstSection = getFirstVisibleSection(); - NotificationSection lastSection = getLastVisibleSection(); - ExpandableView previousFirstChild = - firstSection == null ? null : firstSection.getFirstVisibleChild(); - ExpandableView previousLastChild = - lastSection == null ? null : lastSection.getLastVisibleChild(); - - ExpandableView firstChild = getFirstChildWithBackground(); - ExpandableView lastChild = getLastChildWithBackground(); - boolean sectionViewsChanged = mSectionsManager.updateFirstAndLastViewsForAllSections( - mSections, getChildrenWithBackground()); - - if (mAnimationsEnabled && mIsExpanded) { - mAnimateNextBackgroundTop = firstChild != previousFirstChild; - mAnimateNextBackgroundBottom = lastChild != previousLastChild || mAnimateBottomOnLayout; - mAnimateNextSectionBoundsChange = sectionViewsChanged; - } else { - mAnimateNextBackgroundTop = false; - mAnimateNextBackgroundBottom = false; - mAnimateNextSectionBoundsChange = false; - } + private void updateFirstAndLastExpandableView() { + ExpandableView lastChild = getLastExpandableView(); + mSectionsManager.updateFirstAndLastViewsForAllSections( + mSections, getExpandableViewList()); mAmbientState.setLastVisibleBackgroundChild(lastChild); // TODO: Refactor SectionManager and put the RoundnessManager there. mController.getNoticationRoundessManager().updateRoundedChildren(mSections); - mAnimateBottomOnLayout = false; invalidate(); } @@ -3273,7 +2922,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable setAnimationRunning(true); mStateAnimator.startAnimationForEvents(mAnimationEvents, mGoToFullShadeDelay); mAnimationEvents.clear(); - updateBackground(); updateViewShadows(); updateClippingToTopRoundedCorner(); } else { @@ -3577,7 +3225,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable @Override @ShadeViewRefactor(RefactorComponent.INPUT) public boolean onGenericMotionEvent(MotionEvent event) { - if (!isScrollingEnabled() || !mIsExpanded || mSwipingInProgress || mExpandingNotification + if (!isScrollingEnabled() + || !mIsExpanded + || mSwipeHelper.isSwiping() + || mExpandingNotification || mDisallowScrollingInThisMotion) { return false; } @@ -4077,14 +3728,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable return false; } - @ShadeViewRefactor(RefactorComponent.INPUT) - void setSwipingInProgress(boolean swiping) { - mSwipingInProgress = swiping; - if (swiping) { - requestDisallowInterceptTouchEvent(true); - } - } - @Override @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public void onWindowFocusChanged(boolean hasWindowFocus) { @@ -4127,9 +3770,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mStatusBar.resetUserExpandedStates(); clearTemporaryViews(); clearUserLockedViews(); - ArrayList<View> draggedViews = mAmbientState.getDraggedViews(); - if (draggedViews.size() > 0) { - draggedViews.clear(); + if (mSwipeHelper.isSwiping()) { + mSwipeHelper.resetSwipeState(); updateContinuousShadowDrawing(); } } @@ -4364,7 +4006,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) private void setDimAmount(float dimAmount) { mDimAmount = dimAmount; - updateBackgroundDimming(); } @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) @@ -4433,7 +4074,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } runAnimationFinishedRunnables(); setAnimationRunning(false); - updateBackground(); updateViewShadows(); updateClippingToTopRoundedCorner(); } @@ -4568,7 +4208,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable invalidateOutline(); } updateAlgorithmHeightAndPadding(); - updateBackgroundDimming(); requestChildrenUpdate(); updateOwnTranslationZ(); } @@ -5196,15 +4835,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable ExpandableView child = (ExpandableView) getTransientView(i); child.dump(fd, pw, args); } - ArrayList<View> draggedViews = mAmbientState.getDraggedViews(); - int draggedCount = draggedViews.size(); - pw.println(" Dragged Views: " + draggedCount); - for (int i = 0; i < draggedCount; i++) { - View view = draggedViews.get(i); - if (view instanceof ExpandableView) { - ExpandableView expandableView = (ExpandableView) view; - expandableView.dump(fd, pw, args); - } + View swipedView = mSwipeHelper.getSwipedView(); + pw.println(" Swiped view: " + swipedView); + if (swipedView instanceof ExpandableView) { + ExpandableView expandableView = (ExpandableView) swipedView; + expandableView.dump(fd, pw, args); } } @@ -5453,7 +5088,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable */ public void setDozeAmount(float dozeAmount) { mAmbientState.setDozeAmount(dozeAmount); - updateContinuousBackgroundDrawing(); requestChildrenUpdate(); } @@ -5491,7 +5125,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } void setAnimateBottomOnLayout(boolean animateBottomOnLayout) { - mAnimateBottomOnLayout = animateBottomOnLayout; } public void setOnPulseHeightChangedListener(Runnable listener) { @@ -5522,27 +5155,16 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mSwipedOutViews.add(v); } - void addDraggedView(View view) { - mAmbientState.onBeginDrag(view); - } - - void removeDraggedView(View view) { - mAmbientState.onDragFinished(view); + void onSwipeBegin() { + requestDisallowInterceptTouchEvent(true); + updateContinuousShadowDrawing(); + requestChildrenUpdate(); } void setTopHeadsUpEntry(NotificationEntry topEntry) { mTopHeadsUpEntry = topEntry; } - void setNumHeadsUp(long numHeadsUp) { - mNumHeadsUp = numHeadsUp; - mAmbientState.setHasAlertEntries(numHeadsUp > 0); - } - - boolean getSwipingInProgress() { - return mSwipingInProgress; - } - public boolean getIsExpanded() { return mIsExpanded; } @@ -5583,10 +5205,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mTouchHandler = touchHandler; } - boolean isSwipingInProgress() { - return mSwipingInProgress; - } - boolean getCheckSnoozeLeaveBehind() { return mCheckForLeavebehind; } @@ -5661,23 +5279,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mSectionsManager.updateSectionBoundaries(reason); } - void updateContinuousBackgroundDrawing() { - boolean continuousBackground = !mAmbientState.isFullyAwake() - && !mAmbientState.getDraggedViews().isEmpty(); - if (continuousBackground != mContinuousBackgroundUpdate) { - mContinuousBackgroundUpdate = continuousBackground; - if (continuousBackground) { - getViewTreeObserver().addOnPreDrawListener(mBackgroundUpdater); - } else { - getViewTreeObserver().removeOnPreDrawListener(mBackgroundUpdater); - } - } - } - @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) void updateContinuousShadowDrawing() { boolean continuousShadowUpdate = mAnimationRunning - || !mAmbientState.getDraggedViews().isEmpty(); + || mSwipeHelper.isSwiping(); if (continuousShadowUpdate != mContinuousShadowUpdate) { if (continuousShadowUpdate) { getViewTreeObserver().addOnPreDrawListener(mShadowUpdater); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index 7cee365bc7d3..abbbbb8352f5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -60,10 +60,10 @@ import com.android.systemui.ExpandHelper; import com.android.systemui.Gefingerpoken; import com.android.systemui.R; import com.android.systemui.SwipeHelper; +import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.media.KeyguardMediaController; -import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; @@ -143,7 +143,7 @@ public class NotificationStackScrollLayoutController { private final ConfigurationController mConfigurationController; private final ZenModeController mZenModeController; private final MetricsLogger mMetricsLogger; - private final FalsingManager mFalsingManager; + private final FalsingCollector mFalsingCollector; private final Resources mResources; private final NotificationSwipeHelper.Builder mNotificationSwipeHelperBuilder; private final ScrimController mScrimController; @@ -361,8 +361,7 @@ public class NotificationStackScrollLayoutController { @Override public void onDragCancelled(View v) { - mView.setSwipingInProgress(false); - mFalsingManager.onNotificationStopDismissing(); + mFalsingCollector.onNotificationStopDismissing(); } /** @@ -392,14 +391,9 @@ public class NotificationStackScrollLayoutController { */ public void handleChildViewDismissed(View view) { - mView.setSwipingInProgress(false); if (mView.getDismissAllInProgress()) { return; } - - mView.removeDraggedView(view); - mView.updateContinuousShadowDrawing(); - if (view instanceof ExpandableNotificationRow) { ExpandableNotificationRow row = (ExpandableNotificationRow) view; if (row.isHeadsUp()) { @@ -410,8 +404,8 @@ public class NotificationStackScrollLayoutController { } mView.addSwipedOutView(view); - mFalsingManager.onNotificationDismissed(); - if (mFalsingManager.shouldEnforceBouncer()) { + mFalsingCollector.onNotificationDismissed(); + if (mFalsingCollector.shouldEnforceBouncer()) { mStatusBar.executeRunnableDismissingKeyguard( null, null /* cancelAction */, @@ -453,19 +447,12 @@ public class NotificationStackScrollLayoutController { @Override public void onBeginDrag(View v) { - mFalsingManager.onNotificationStartDismissing(); - mView.setSwipingInProgress(true); - mView.addDraggedView(v); - mView.updateContinuousShadowDrawing(); - mView.updateContinuousBackgroundDrawing(); - mView.requestChildrenUpdate(); + mFalsingCollector.onNotificationStartDismissing(); + mView.onSwipeBegin(); } @Override public void onChildSnappedBack(View animView, float targetLeft) { - mView.addDraggedView(animView); - mView.updateContinuousShadowDrawing(); - mView.updateContinuousBackgroundDrawing(); if (animView instanceof ExpandableNotificationRow) { ExpandableNotificationRow row = (ExpandableNotificationRow) animView; if (row.isPinned() && !canChildBeDismissed(row) @@ -529,9 +516,7 @@ public class NotificationStackScrollLayoutController { @Override public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) { - long numEntries = mHeadsUpManager.getAllEntries().count(); NotificationEntry topEntry = mHeadsUpManager.getTopEntry(); - mView.setNumHeadsUp(numEntries); mView.setTopHeadsUpEntry(topEntry); mNotificationRoundnessManager.updateView(entry.getRow(), false /* animate */); } @@ -561,7 +546,7 @@ public class NotificationStackScrollLayoutController { SysuiColorExtractor colorExtractor, NotificationLockscreenUserManager lockscreenUserManager, MetricsLogger metricsLogger, - FalsingManager falsingManager, + FalsingCollector falsingCollector, @Main Resources resources, NotificationSwipeHelper.Builder notificationSwipeHelperBuilder, StatusBar statusBar, @@ -595,7 +580,7 @@ public class NotificationStackScrollLayoutController { mColorExtractor = colorExtractor; mLockscreenUserManager = lockscreenUserManager; mMetricsLogger = metricsLogger; - mFalsingManager = falsingManager; + mFalsingCollector = falsingCollector; mResources = resources; mNotificationSwipeHelperBuilder = notificationSwipeHelperBuilder; mStatusBar = statusBar; @@ -676,8 +661,6 @@ public class NotificationStackScrollLayoutController { mHeadsUpManager.setAnimationStateHandler(mView::setHeadsUpGoingAwayAnimationsAllowed); mDynamicPrivacyController.addListener(mDynamicPrivacyControllerListener); - mScrimController.setScrimBehindChangeRunnable(mView::updateBackgroundDimming); - mLockscreenUserManager.addUserChangedListener(mLockscreenUserChangeListener); mFadeNotificationsOnDismiss = // TODO: this should probably be injected directly @@ -1536,12 +1519,12 @@ public class NotificationStackScrollLayoutController { NotificationGuts guts = mNotificationGutsManager.getExposedGuts(); boolean expandWantsIt = false; - boolean swipingInProgress = mView.isSwipingInProgress(); - if (!swipingInProgress && !mView.getOnlyScrollingInThisMotion() && guts == null) { + if (!mSwipeHelper.isSwiping() + && !mView.getOnlyScrollingInThisMotion() && guts == null) { expandWantsIt = mView.getExpandHelper().onInterceptTouchEvent(ev); } boolean scrollWantsIt = false; - if (!swipingInProgress && !mView.isExpandingNotification()) { + if (!mSwipeHelper.isSwiping() && !mView.isExpandingNotification()) { scrollWantsIt = mView.onInterceptTouchEventScroll(ev); } boolean swipeWantsIt = false; @@ -1582,10 +1565,9 @@ public class NotificationStackScrollLayoutController { || ev.getActionMasked() == MotionEvent.ACTION_UP; mView.handleEmptySpaceClick(ev); boolean expandWantsIt = false; - boolean swipingInProgress = mView.getSwipingInProgress(); boolean onlyScrollingInThisMotion = mView.getOnlyScrollingInThisMotion(); boolean expandingNotification = mView.isExpandingNotification(); - if (mView.getIsExpanded() && !swipingInProgress && !onlyScrollingInThisMotion + if (mView.getIsExpanded() && !mSwipeHelper.isSwiping() && !onlyScrollingInThisMotion && guts == null) { ExpandHelper expandHelper = mView.getExpandHelper(); if (isCancelOrUp) { @@ -1600,7 +1582,7 @@ public class NotificationStackScrollLayoutController { } } boolean scrollerWantsIt = false; - if (mView.isExpanded() && !swipingInProgress && !expandingNotification + if (mView.isExpanded() && !mSwipeHelper.isSwiping() && !expandingNotification && !mView.getDisallowScrollingInThisMotion()) { scrollerWantsIt = mView.onScrollTouch(ev); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DoubleTapHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DoubleTapHelper.java deleted file mode 100644 index 78ea5c03a7b5..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DoubleTapHelper.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.systemui.statusbar.phone; - -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewConfiguration; - -import com.android.systemui.R; - -/** - * Detects a double tap. - */ -public class DoubleTapHelper { - - private static final long DOUBLETAP_TIMEOUT_MS = 1200; - - private final View mView; - private final ActivationListener mActivationListener; - private final DoubleTapListener mDoubleTapListener; - private final SlideBackListener mSlideBackListener; - private final DoubleTapLogListener mDoubleTapLogListener; - - private float mTouchSlop; - private float mDoubleTapSlop; - - private boolean mActivated; - - private float mDownX; - private float mDownY; - private boolean mTrackTouch; - - private float mActivationX; - private float mActivationY; - private Runnable mTapTimeoutRunnable = this::makeInactive; - - public DoubleTapHelper(View view, ActivationListener activationListener, - DoubleTapListener doubleTapListener, SlideBackListener slideBackListener, - DoubleTapLogListener doubleTapLogListener) { - mTouchSlop = ViewConfiguration.get(view.getContext()).getScaledTouchSlop(); - mDoubleTapSlop = view.getResources().getDimension(R.dimen.double_tap_slop); - mView = view; - - mActivationListener = activationListener; - mDoubleTapListener = doubleTapListener; - mSlideBackListener = slideBackListener; - mDoubleTapLogListener = doubleTapLogListener; - } - - public boolean onTouchEvent(MotionEvent event) { - return onTouchEvent(event, Integer.MAX_VALUE); - } - - public boolean onTouchEvent(MotionEvent event, int maxTouchableHeight) { - int action = event.getActionMasked(); - switch (action) { - case MotionEvent.ACTION_DOWN: - mDownX = event.getX(); - mDownY = event.getY(); - mTrackTouch = true; - if (mDownY > maxTouchableHeight) { - mTrackTouch = false; - } - break; - case MotionEvent.ACTION_MOVE: - if (!isWithinTouchSlop(event)) { - makeInactive(); - mTrackTouch = false; - } - break; - case MotionEvent.ACTION_UP: - if (isWithinTouchSlop(event)) { - if (mSlideBackListener != null && mSlideBackListener.onSlideBack()) { - return true; - } - if (!mActivated) { - makeActive(); - mView.postDelayed(mTapTimeoutRunnable, DOUBLETAP_TIMEOUT_MS); - mActivationX = event.getX(); - mActivationY = event.getY(); - } else { - boolean withinDoubleTapSlop = isWithinDoubleTapSlop(event); - if (mDoubleTapLogListener != null) { - mDoubleTapLogListener.onDoubleTapLog(withinDoubleTapSlop, - event.getX() - mActivationX, - event.getY() - mActivationY); - } - if (withinDoubleTapSlop) { - makeInactive(); - if (!mDoubleTapListener.onDoubleTap()) { - return false; - } - } else { - makeInactive(); - mTrackTouch = false; - } - } - } else { - makeInactive(); - mTrackTouch = false; - } - break; - case MotionEvent.ACTION_CANCEL: - makeInactive(); - mTrackTouch = false; - break; - default: - break; - } - return mTrackTouch; - } - - private void makeActive() { - if (!mActivated) { - mActivated = true; - mActivationListener.onActiveChanged(true); - } - } - - private void makeInactive() { - if (mActivated) { - mActivated = false; - mActivationListener.onActiveChanged(false); - mView.removeCallbacks(mTapTimeoutRunnable); - } - } - - private boolean isWithinTouchSlop(MotionEvent event) { - return Math.abs(event.getX() - mDownX) < mTouchSlop - && Math.abs(event.getY() - mDownY) < mTouchSlop; - } - - public boolean isWithinDoubleTapSlop(MotionEvent event) { - if (!mActivated) { - // If we're not activated there's no double tap slop to satisfy. - return true; - } - - return Math.abs(event.getX() - mActivationX) < mDoubleTapSlop - && Math.abs(event.getY() - mActivationY) < mDoubleTapSlop; - } - - @FunctionalInterface - public interface ActivationListener { - void onActiveChanged(boolean active); - } - - @FunctionalInterface - public interface DoubleTapListener { - boolean onDoubleTap(); - } - - @FunctionalInterface - public interface SlideBackListener { - boolean onSlideBack(); - } - - @FunctionalInterface - public interface DoubleTapLogListener { - void onDoubleTapLog(boolean accepted, float dx, float dy); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java index 80cb289a3c0d..9854f5450df1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java @@ -39,9 +39,9 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.keyguard.ViewMediatorCallback; import com.android.keyguard.dagger.KeyguardBouncerComponent; import com.android.systemui.DejankUtils; +import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.dagger.qualifiers.RootView; import com.android.systemui.keyguard.DismissCallbackRegistry; -import com.android.systemui.plugins.FalsingManager; import com.android.systemui.shared.system.SysUiStatsLog; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -65,7 +65,7 @@ public class KeyguardBouncer { protected final Context mContext; protected final ViewMediatorCallback mCallback; protected final ViewGroup mContainer; - private final FalsingManager mFalsingManager; + private final FalsingCollector mFalsingCollector; private final DismissCallbackRegistry mDismissCallbackRegistry; private final Handler mHandler; private final List<BouncerExpansionCallback> mExpansionCallbacks = new ArrayList<>(); @@ -100,7 +100,7 @@ public class KeyguardBouncer { private KeyguardBouncer(Context context, ViewMediatorCallback callback, ViewGroup container, - DismissCallbackRegistry dismissCallbackRegistry, FalsingManager falsingManager, + DismissCallbackRegistry dismissCallbackRegistry, FalsingCollector falsingCollector, BouncerExpansionCallback expansionCallback, KeyguardStateController keyguardStateController, KeyguardUpdateMonitor keyguardUpdateMonitor, @@ -111,7 +111,7 @@ public class KeyguardBouncer { mCallback = callback; mContainer = container; mKeyguardUpdateMonitor = keyguardUpdateMonitor; - mFalsingManager = falsingManager; + mFalsingCollector = falsingCollector; mDismissCallbackRegistry = dismissCallbackRegistry; mHandler = handler; mKeyguardStateController = keyguardStateController; @@ -203,7 +203,7 @@ public class KeyguardBouncer { * will never be notified and its internal state will be out of sync. */ private void onFullyShown() { - mFalsingManager.onBouncerShown(); + mFalsingCollector.onBouncerShown(); if (mKeyguardViewController == null) { Log.wtf(TAG, "onFullyShown when view was null"); } else { @@ -223,7 +223,7 @@ public class KeyguardBouncer { if (mRoot != null) { mRoot.setVisibility(View.INVISIBLE); } - mFalsingManager.onBouncerHidden(); + mFalsingCollector.onBouncerHidden(); DejankUtils.postAfterTraversal(mResetRunnable); } @@ -290,7 +290,7 @@ public class KeyguardBouncer { mDismissCallbackRegistry.notifyDismissCancelled(); } mIsScrimmed = false; - mFalsingManager.onBouncerHidden(); + mFalsingCollector.onBouncerHidden(); mCallback.onBouncerVisiblityChanged(false /* shown */); cancelShowRunnable(); if (mKeyguardViewController != null) { @@ -327,7 +327,7 @@ public class KeyguardBouncer { public void reset() { cancelShowRunnable(); inflateView(); - mFalsingManager.onBouncerHidden(); + mFalsingCollector.onBouncerHidden(); } public void onScreenTurnedOff() { @@ -541,7 +541,7 @@ public class KeyguardBouncer { private final Context mContext; private final ViewMediatorCallback mCallback; private final DismissCallbackRegistry mDismissCallbackRegistry; - private final FalsingManager mFalsingManager; + private final FalsingCollector mFalsingCollector; private final KeyguardStateController mKeyguardStateController; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private final KeyguardBypassController mKeyguardBypassController; @@ -551,7 +551,7 @@ public class KeyguardBouncer { @Inject public Factory(Context context, ViewMediatorCallback callback, - DismissCallbackRegistry dismissCallbackRegistry, FalsingManager falsingManager, + DismissCallbackRegistry dismissCallbackRegistry, FalsingCollector falsingCollector, KeyguardStateController keyguardStateController, KeyguardUpdateMonitor keyguardUpdateMonitor, KeyguardBypassController keyguardBypassController, Handler handler, @@ -560,7 +560,7 @@ public class KeyguardBouncer { mContext = context; mCallback = callback; mDismissCallbackRegistry = dismissCallbackRegistry; - mFalsingManager = falsingManager; + mFalsingCollector = falsingCollector; mKeyguardStateController = keyguardStateController; mKeyguardUpdateMonitor = keyguardUpdateMonitor; mKeyguardBypassController = keyguardBypassController; @@ -572,7 +572,7 @@ public class KeyguardBouncer { public KeyguardBouncer create(@RootView ViewGroup container, BouncerExpansionCallback expansionCallback) { return new KeyguardBouncer(mContext, mCallback, container, - mDismissCallbackRegistry, mFalsingManager, expansionCallback, + mDismissCallbackRegistry, mFalsingCollector, expansionCallback, mKeyguardStateController, mKeyguardUpdateMonitor, mKeyguardBypassController, mHandler, mKeyguardSecurityModel, mKeyguardBouncerComponentFactory); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index c936e82c2a16..f7139aa2acce 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -79,6 +79,7 @@ import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.biometrics.AuthController; import com.android.systemui.classifier.Classifier; +import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.dagger.qualifiers.DisplayId; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.doze.DozeLog; @@ -364,7 +365,8 @@ public class NotificationPanelViewController extends PanelViewController { private boolean mHeadsUpAnimatingAway; private boolean mLaunchingAffordance; private boolean mAffordanceHasPreview; - private FalsingManager mFalsingManager; + private final FalsingManager mFalsingManager; + private final FalsingCollector mFalsingCollector; private String mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE; private Runnable mHeadsUpExistenceChangedRunnable = () -> { @@ -502,7 +504,7 @@ public class NotificationPanelViewController extends PanelViewController { NotificationWakeUpCoordinator coordinator, PulseExpansionHandler pulseExpansionHandler, DynamicPrivacyController dynamicPrivacyController, KeyguardBypassController bypassController, FalsingManager falsingManager, - ShadeController shadeController, + FalsingCollector falsingCollector, ShadeController shadeController, NotificationLockscreenUserManager notificationLockscreenUserManager, NotificationEntryManager notificationEntryManager, KeyguardStateController keyguardStateController, @@ -543,6 +545,7 @@ public class NotificationPanelViewController extends PanelViewController { mView.setWillNotDraw(!DEBUG); mInjectionInflationController = injectionInflationController; mFalsingManager = falsingManager; + mFalsingCollector = falsingCollector; mPowerManager = powerManager; mWakeUpCoordinator = coordinator; mAccessibilityManager = accessibilityManager; @@ -926,6 +929,8 @@ public class NotificationPanelViewController extends PanelViewController { if (mUpdateMonitor.isUdfpsEnrolled()) { availableSpace = mNotificationStackScrollLayoutController.getHeight() - minPadding - shelfSize + - mKeyguardStatusViewController.getOwnerInfoHeight() + - mKeyguardStatusViewController.getLogoutButtonHeight() - (mStatusBar.getDisplayHeight() - mAuthController.getUdfpsRegion().top); } @@ -1424,7 +1429,7 @@ public class NotificationPanelViewController extends PanelViewController { private void handleQsDown(MotionEvent event) { if (event.getActionMasked() == MotionEvent.ACTION_DOWN && shouldQuickSettingsIntercept( event.getX(), event.getY(), -1)) { - mFalsingManager.onQsDown(); + mFalsingCollector.onQsDown(); mQsTracking = true; onQsExpansionStarted(); mInitialHeightOnTouch = mQsExpansionHeight; @@ -1604,7 +1609,7 @@ public class NotificationPanelViewController extends PanelViewController { mQsExpanded = expanded; updateQsState(); requestPanelHeightUpdate(); - mFalsingManager.setQsExpanded(expanded); + mFalsingCollector.setQsExpanded(expanded); mStatusBar.setQsExpanded(expanded); mNotificationContainerParent.setQsExpanded(expanded); mPulseExpansionHandler.setQsExpanded(expanded); @@ -1741,7 +1746,7 @@ public class NotificationPanelViewController extends PanelViewController { } if (!mFalsingManager.isUnlockingDisabled() && mQsFullyExpanded - && mFalsingManager.shouldEnforceBouncer()) { + && mFalsingCollector.shouldEnforceBouncer()) { mStatusBar.executeRunnableDismissingKeyguard(null, null /* cancelAction */, false /* dismissShade */, true /* afterKeyguardGone */, false /* deferred */); } @@ -2402,7 +2407,7 @@ public class NotificationPanelViewController extends PanelViewController { @Override protected void onTrackingStarted() { - mFalsingManager.onTrackingStarted(!mKeyguardStateController.canDismissLockScreen()); + mFalsingCollector.onTrackingStarted(!mKeyguardStateController.canDismissLockScreen()); super.onTrackingStarted(); if (mQsFullyExpanded) { mQsExpandImmediate = true; @@ -2416,7 +2421,7 @@ public class NotificationPanelViewController extends PanelViewController { @Override protected void onTrackingStopped(boolean expand) { - mFalsingManager.onTrackingStopped(); + mFalsingCollector.onTrackingStopped(); super.onTrackingStopped(expand); if (expand) { mNotificationStackScrollLayoutController.setOverScrolledPixels(0.0f, true /* onTop */, @@ -3363,8 +3368,8 @@ public class NotificationPanelViewController extends PanelViewController { if (start) { mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_DIALER, lengthDp, velocityDp); mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_DIALER); - mFalsingManager.onLeftAffordanceOn(); - if (mFalsingManager.shouldEnforceBouncer()) { + mFalsingCollector.onLeftAffordanceOn(); + if (mFalsingCollector.shouldEnforceBouncer()) { mStatusBar.executeRunnableDismissingKeyguard( () -> mKeyguardBottomArea.launchLeftAffordance(), null, true /* dismissShade */, false /* afterKeyguardGone */, @@ -3379,8 +3384,8 @@ public class NotificationPanelViewController extends PanelViewController { MetricsEvent.ACTION_LS_CAMERA, lengthDp, velocityDp); mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_CAMERA); } - mFalsingManager.onCameraOn(); - if (mFalsingManager.shouldEnforceBouncer()) { + mFalsingCollector.onCameraOn(); + if (mFalsingCollector.shouldEnforceBouncer()) { mStatusBar.executeRunnableDismissingKeyguard( () -> mKeyguardBottomArea.launchCamera(mLastCameraLaunchSource), null, true /* dismissShade */, false /* afterKeyguardGone */, @@ -3411,7 +3416,7 @@ public class NotificationPanelViewController extends PanelViewController { @Override public void onSwipingStarted(boolean rightIcon) { - mFalsingManager.onAffordanceSwipingStarted(rightIcon); + mFalsingCollector.onAffordanceSwipingStarted(rightIcon); boolean camera = mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? !rightIcon @@ -3426,7 +3431,7 @@ public class NotificationPanelViewController extends PanelViewController { @Override public void onSwipingAborted() { - mFalsingManager.onAffordanceSwipingAborted(); + mFalsingCollector.onAffordanceSwipingAborted(); mKeyguardBottomArea.unbindCameraPrewarmService(false /* launched */); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java index 3db3ab5a56c8..ba4fbb871b97 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java @@ -36,6 +36,7 @@ import android.view.ViewGroup; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.ExpandHelper; import com.android.systemui.R; +import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.dock.DockManager; import com.android.systemui.doze.DozeLog; import com.android.systemui.plugins.FalsingManager; @@ -73,6 +74,7 @@ public class NotificationShadeWindowViewController { private final KeyguardBypassController mBypassController; private final PluginManager mPluginManager; private final FalsingManager mFalsingManager; + private final FalsingCollector mFalsingCollector; private final TunerService mTunerService; private final NotificationLockscreenUserManager mNotificationLockscreenUserManager; private final NotificationEntryManager mNotificationEntryManager; @@ -118,6 +120,7 @@ public class NotificationShadeWindowViewController { DynamicPrivacyController dynamicPrivacyController, KeyguardBypassController bypassController, FalsingManager falsingManager, + FalsingCollector falsingCollector, PluginManager pluginManager, TunerService tunerService, NotificationLockscreenUserManager notificationLockscreenUserManager, @@ -140,6 +143,7 @@ public class NotificationShadeWindowViewController { mDynamicPrivacyController = dynamicPrivacyController; mBypassController = bypassController; mFalsingManager = falsingManager; + mFalsingCollector = falsingCollector; mPluginManager = pluginManager; mTunerService = tunerService; mNotificationLockscreenUserManager = notificationLockscreenUserManager; @@ -234,7 +238,7 @@ public class NotificationShadeWindowViewController { if (mTouchCancelled || mExpandAnimationRunning || mExpandAnimationPending) { return false; } - mFalsingManager.onTouchEvent(ev, mView.getWidth(), mView.getHeight()); + mFalsingCollector.onTouchEvent(ev, mView.getWidth(), mView.getHeight()); mGestureDetector.onTouchEvent(ev); if (mBrightnessMirror != null && mBrightnessMirror.getVisibility() == View.VISIBLE) { @@ -394,7 +398,7 @@ public class NotificationShadeWindowViewController { setDragDownHelper( new DragDownHelper( mView.getContext(), mView, expandHelperCallback, - dragDownCallback, mFalsingManager)); + dragDownCallback, mFalsingManager, mFalsingCollector)); mDepthController.setRoot(mView); mNotificationPanelViewController.addExpansionListener(mDepthController); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationTapHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationTapHelper.java new file mode 100644 index 000000000000..50c8e2e0d710 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationTapHelper.java @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.phone; + +import android.view.MotionEvent; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.util.concurrency.DelayableExecutor; + +import javax.inject.Inject; + +/** + * Detects single and double taps on notifications. + */ +public class NotificationTapHelper { + + public static final long DOUBLE_TAP_TIMEOUT_MS = 1200; + + private final ActivationListener mActivationListener; + private final DoubleTapListener mDoubleTapListener; + private final FalsingManager mFalsingManager; + private final DelayableExecutor mExecutor; + private final SlideBackListener mSlideBackListener; + + private boolean mTrackTouch; + private Runnable mTimeoutCancel; + + private NotificationTapHelper(FalsingManager falsingManager, DelayableExecutor executor, + ActivationListener activationListener, DoubleTapListener doubleTapListener, + SlideBackListener slideBackListener) { + mFalsingManager = falsingManager; + mExecutor = executor; + mActivationListener = activationListener; + mDoubleTapListener = doubleTapListener; + mSlideBackListener = slideBackListener; + } + + @VisibleForTesting + boolean onTouchEvent(MotionEvent event) { + return onTouchEvent(event, Integer.MAX_VALUE); + } + + /** Call to have the helper process a touch event. */ + public boolean onTouchEvent(MotionEvent event, int maxTouchableHeight) { + int action = event.getActionMasked(); + switch (action) { + case MotionEvent.ACTION_DOWN: + mTrackTouch = event.getY() <= maxTouchableHeight; + break; + case MotionEvent.ACTION_MOVE: + if (mTrackTouch && mFalsingManager.isFalseTap(false)) { + makeInactive(); + mTrackTouch = false; + } + break; + case MotionEvent.ACTION_CANCEL: + makeInactive(); + mTrackTouch = false; + break; + case MotionEvent.ACTION_UP: + mTrackTouch = false; + + // 1) See if we have confidence that we can activate after a single tap. + // 2) Else, see if it looks like a tap at all and check for a double-tap. + if (!mFalsingManager.isFalseTap(true)) { + makeInactive(); + return mDoubleTapListener.onDoubleTap(); + } else if (!mFalsingManager.isFalseTap(false)) { + if (mSlideBackListener != null && mSlideBackListener.onSlideBack()) { + return true; + } + if (mTimeoutCancel == null) { + // first tap + makeActive(); + return true; + } else { + // second tap + makeInactive(); + if (!mFalsingManager.isFalseDoubleTap()) { + return mDoubleTapListener.onDoubleTap(); + } + } + } else { + makeInactive(); + } + break; + default: + break; + } + return mTrackTouch; + } + + private void makeActive() { + mTimeoutCancel = mExecutor.executeDelayed(this::makeInactive, DOUBLE_TAP_TIMEOUT_MS); + mActivationListener.onActiveChanged(true); + } + + private void makeInactive() { + mActivationListener.onActiveChanged(false); + if (mTimeoutCancel != null) { + mTimeoutCancel.run(); + mTimeoutCancel = null; + } + } + + /** */ + @FunctionalInterface + public interface ActivationListener { + /** */ + void onActiveChanged(boolean active); + } + + /** */ + @FunctionalInterface + public interface DoubleTapListener { + /** */ + boolean onDoubleTap(); + } + + /** */ + @FunctionalInterface + public interface SlideBackListener { + /** */ + boolean onSlideBack(); + } + + /** + * Injectable factory that creates a {@link NotificationTapHelper}. + */ + public static class Factory { + private final FalsingManager mFalsingManager; + private final DelayableExecutor mDelayableExecutor; + + @Inject + public Factory(FalsingManager falsingManager, @Main DelayableExecutor delayableExecutor) { + mFalsingManager = falsingManager; + mDelayableExecutor = delayableExecutor; + } + + /** Create a {@link NotificationTapHelper} */ + public NotificationTapHelper create(ActivationListener activationListener, + DoubleTapListener doubleTapListener, SlideBackListener slideBackListener) { + return new NotificationTapHelper(mFalsingManager, mDelayableExecutor, + activationListener, doubleTapListener, slideBackListener); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 5d8424592cd2..c8c5a6331a3b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -147,7 +147,7 @@ import com.android.systemui.SystemUI; import com.android.systemui.assist.AssistManager; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.charging.WirelessChargingAnimation; -import com.android.systemui.classifier.FalsingLog; +import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dagger.qualifiers.UiBackground; import com.android.systemui.demomode.DemoMode; @@ -385,6 +385,7 @@ public class StatusBar extends SystemUI implements DemoMode, private final StatusBarTouchableRegionManager mStatusBarTouchableRegionManager; private final DynamicPrivacyController mDynamicPrivacyController; private final BypassHeadsUpNotifier mBypassHeadsUpNotifier; + private final FalsingCollector mFalsingCollector; private final FalsingManager mFalsingManager; private final BroadcastDispatcher mBroadcastDispatcher; private final ConfigurationController mConfigurationController; @@ -693,6 +694,7 @@ public class StatusBar extends SystemUI implements DemoMode, DynamicPrivacyController dynamicPrivacyController, BypassHeadsUpNotifier bypassHeadsUpNotifier, FalsingManager falsingManager, + FalsingCollector falsingCollector, BroadcastDispatcher broadcastDispatcher, RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler, NotificationGutsManager notificationGutsManager, @@ -773,6 +775,7 @@ public class StatusBar extends SystemUI implements DemoMode, mStatusBarTouchableRegionManager = statusBarTouchableRegionManager; mDynamicPrivacyController = dynamicPrivacyController; mBypassHeadsUpNotifier = bypassHeadsUpNotifier; + mFalsingCollector = falsingCollector; mFalsingManager = falsingManager; mBroadcastDispatcher = broadcastDispatcher; mRemoteInputQuickSettingsDisabler = remoteInputQuickSettingsDisabler; @@ -1250,10 +1253,6 @@ public class StatusBar extends SystemUI implements DemoMode, message.write(SystemProperties.get("ro.serialno")); message.write("\n"); - PrintWriter falsingPw = new PrintWriter(message); - FalsingLog.dump(falsingPw); - falsingPw.flush(); - startActivityDismissingKeyguard(Intent.createChooser(new Intent(Intent.ACTION_SEND) .setType("*/*") .putExtra(Intent.EXTRA_SUBJECT, "Rejected touch report") @@ -1394,7 +1393,7 @@ public class StatusBar extends SystemUI implements DemoMode, where.getLocationInWindow(mTmpInt2); mWakeUpTouchLocation = new PointF(mTmpInt2[0] + where.getWidth() / 2, mTmpInt2[1] + where.getHeight() / 2); - mFalsingManager.onScreenOnFromTouch(); + mFalsingCollector.onScreenOnFromTouch(); } } @@ -1653,7 +1652,7 @@ public class StatusBar extends SystemUI implements DemoMode, return; } mReportRejectedTouch.setVisibility(mState == StatusBarState.KEYGUARD && !mDozing - && mFalsingManager.isReportingEnabled() ? View.VISIBLE : View.INVISIBLE); + && mFalsingCollector.isReportingEnabled() ? View.VISIBLE : View.INVISIBLE); } /** @@ -2676,9 +2675,6 @@ public class StatusBar extends SystemUI implements DemoMode, mLightBarController.dump(fd, pw, args); } - mFalsingManager.dump(pw); - FalsingLog.dump(pw); - pw.println("SharedPreferences:"); for (Map.Entry<String, ?> entry : Prefs.getAll(mContext).entrySet()) { pw.print(" "); pw.print(entry.getKey()); pw.print("="); pw.println(entry.getValue()); @@ -3693,7 +3689,7 @@ public class StatusBar extends SystemUI implements DemoMode, } public void onUnlockHintStarted() { - mFalsingManager.onUnlockHintStarted(); + mFalsingCollector.onUnlockHintStarted(); mKeyguardIndicationController.showTransientIndication(R.string.keyguard_unlock); } @@ -3703,17 +3699,17 @@ public class StatusBar extends SystemUI implements DemoMode, } public void onCameraHintStarted() { - mFalsingManager.onCameraHintStarted(); + mFalsingCollector.onCameraHintStarted(); mKeyguardIndicationController.showTransientIndication(R.string.camera_hint); } public void onVoiceAssistHintStarted() { - mFalsingManager.onLeftAffordanceHintStarted(); + mFalsingCollector.onLeftAffordanceHintStarted(); mKeyguardIndicationController.showTransientIndication(R.string.voice_hint); } public void onPhoneHintStarted() { - mFalsingManager.onLeftAffordanceHintStarted(); + mFalsingCollector.onLeftAffordanceHintStarted(); mKeyguardIndicationController.showTransientIndication(R.string.phone_hint); } @@ -3764,7 +3760,7 @@ public class StatusBar extends SystemUI implements DemoMode, boolean fullShadeNeedsBouncer = !mLockscreenUserManager. userAllowsPrivateNotificationsInPublic(mLockscreenUserManager.getCurrentUserId()) || !mLockscreenUserManager.shouldShowLockscreenNotifications() - || mFalsingManager.shouldEnforceBouncer(); + || mFalsingCollector.shouldEnforceBouncer(); if (mKeyguardBypassController.getBypassEnabled()) { fullShadeNeedsBouncer = false; } @@ -3896,7 +3892,7 @@ public class StatusBar extends SystemUI implements DemoMode, final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() { @Override public void onScreenTurningOn() { - mFalsingManager.onScreenTurningOn(); + mFalsingCollector.onScreenTurningOn(); mNotificationPanelViewController.onScreenTurningOn(); } @@ -3907,7 +3903,7 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void onScreenTurnedOff() { - mFalsingManager.onScreenOff(); + mFalsingCollector.onScreenOff(); mScrimController.onScreenTurnedOff(); updateIsKeyguard(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index f9dfd7a769a7..b912614ba3e8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -26,13 +26,17 @@ import static com.android.systemui.statusbar.phone.BiometricUnlockController.MOD import android.content.ComponentCallbacks2; import android.content.Context; import android.content.res.ColorStateList; +import android.content.res.Configuration; import android.os.Bundle; import android.os.SystemClock; +import android.util.TypedValue; +import android.view.Gravity; import android.view.KeyEvent; import android.view.View; import android.view.ViewGroup; import android.view.ViewRootImpl; import android.view.WindowManagerGlobal; +import android.widget.FrameLayout; import androidx.annotation.VisibleForTesting; @@ -160,6 +164,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb private boolean mPulsing; private boolean mGesturalNav; private boolean mIsDocked; + private boolean mIsPortraitMode; + private int mScreenWidthDp; protected boolean mFirstUpdate = true; protected boolean mLastShowing; @@ -174,6 +180,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb private boolean mLastPulsing; private int mLastBiometricMode; private boolean mLastLockVisible; + private boolean mLastLockOrientationIsPortrait; private OnDismissAction mAfterKeyguardGoneAction; private Runnable mKeyguardGoneCancelAction; @@ -263,6 +270,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mKeyguardUpdateManager.registerCallback(mUpdateMonitorCallback); mStatusBarStateController.addCallback(this); mConfigurationController.addCallback(this); + mIsPortraitMode = mContext.getResources().getConfiguration().orientation + == Configuration.ORIENTATION_PORTRAIT; + mScreenWidthDp = mContext.getResources().getConfiguration().screenWidthDp; mGesturalNav = QuickStepContract.isGesturalMode( mNavigationModeController.addListener(this)); if (mDockManager != null) { @@ -272,6 +282,18 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } @Override + public void onDensityOrFontScaleChanged() { + hideBouncer(true /* destroyView */); + } + + @Override + public void onConfigChanged(Configuration newConfig) { + mIsPortraitMode = newConfig.orientation == Configuration.ORIENTATION_PORTRAIT; + mScreenWidthDp = newConfig.screenWidthDp; + updateLockIcon(); + } + + @Override public void onPanelExpansionChanged(float expansion, boolean tracking) { // We don't want to translate the bounce when: // • Keyguard is occluded, because we're in a FLAG_SHOW_WHEN_LOCKED activity and need to @@ -317,14 +339,32 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb if (mLockIconContainer == null) { return; } + boolean keyguardWithoutQs = mStatusBarStateController.getState() == StatusBarState.KEYGUARD && !mNotificationPanelViewController.isQsExpanded(); boolean lockVisible = (mBouncer.isShowing() || keyguardWithoutQs) && !mBouncer.isAnimatingAway() && !mKeyguardStateController.isKeyguardFadingAway(); + boolean orientationChange = + lockVisible && (mLastLockOrientationIsPortrait != mIsPortraitMode); - if (mLastLockVisible != lockVisible) { + if (mLastLockVisible != lockVisible || orientationChange) { mLastLockVisible = lockVisible; + mLastLockOrientationIsPortrait = mIsPortraitMode; if (lockVisible) { + FrameLayout.LayoutParams lp = + (FrameLayout.LayoutParams) mLockIconContainer.getLayoutParams(); + if (mIsPortraitMode) { + lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL; + } else { + final int width = (int) TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, + mScreenWidthDp, + mContext.getResources().getDisplayMetrics()) / 3; + mLockIconContainer.setMinimumWidth(width); + lp.gravity = Gravity.TOP | Gravity.LEFT; + } + mLockIconContainer.setLayoutParams(lp); + CrossFadeHelper.fadeIn(mLockIconContainer, AppearAnimationUtils.DEFAULT_APPEAR_DURATION /* duration */, 0 /* delay */); @@ -685,11 +725,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } @Override - public void onDensityOrFontScaleChanged() { - hideBouncer(true /* destroyView */); - } - - @Override public void onNavigationModeChanged(int mode) { boolean gesturalNav = QuickStepContract.isGesturalMode(mode); if (gesturalNav != mGesturalNav) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java index f6631ce1414c..2aa3f37ccf11 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java @@ -31,6 +31,7 @@ import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.InitController; import com.android.systemui.assist.AssistManager; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.UiBackground; @@ -136,6 +137,7 @@ public interface StatusBarPhoneModule { DynamicPrivacyController dynamicPrivacyController, BypassHeadsUpNotifier bypassHeadsUpNotifier, FalsingManager falsingManager, + FalsingCollector falsingCollector, BroadcastDispatcher broadcastDispatcher, RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler, NotificationGutsManager notificationGutsManager, @@ -216,6 +218,7 @@ public interface StatusBarPhoneModule { dynamicPrivacyController, bypassHeadsUpNotifier, falsingManager, + falsingCollector, broadcastDispatcher, remoteInputQuickSettingsDisabler, notificationGutsManager, diff --git a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java index 4b4e1df21bd0..cdc5d8795b91 100644 --- a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java +++ b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java @@ -24,8 +24,6 @@ import android.view.LayoutInflater; import android.view.View; import com.android.systemui.dagger.SysUISingleton; -import com.android.systemui.qs.QSPanel; -import com.android.systemui.qs.QuickQSPanel; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import java.lang.reflect.InvocationTargetException; @@ -93,16 +91,6 @@ public class InjectionInflationController { * Creates the NotificationStackScrollLayout. */ NotificationStackScrollLayout createNotificationStackScrollLayout(); - - /** - * Creates the QSPanel. - */ - QSPanel createQSPanel(); - - /** - * Creates the QuickQSPanel. - */ - QuickQSPanel createQuickQSPanel(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java index 777db952591e..5088a53a2d5b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java @@ -211,6 +211,16 @@ public class AuthContainerViewTest extends SysuiTestCase { } @Test + public void testOnDialogAnimatedIn_sendsCancelReason_whenPendingDismiss() { + initializeContainer(Authenticators.BIOMETRIC_WEAK); + mAuthContainer.mContainerState = AuthContainerView.STATE_PENDING_DISMISS; + mAuthContainer.onDialogAnimatedIn(); + verify(mCallback).onDismissed( + eq(AuthDialogCallback.DISMISSED_USER_CANCELED), + eq(null) /* credentialAttestation */); + } + + @Test public void testLayoutParams_hasSecureWindowFlag() { final IBinder windowToken = mock(IBinder.class); final WindowManager.LayoutParams layoutParams = diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/ClassifierTest.java index a4d198a14541..472ed7a22fe3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/ClassifierTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,13 +14,14 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; import static com.android.systemui.classifier.Classifier.UNLOCK; import android.util.DisplayMetrics; import android.view.MotionEvent; +import com.android.systemui.util.time.FakeSystemClock; import com.android.systemui.utils.leaks.FakeBatteryController; import com.android.systemui.utils.leaks.LeakCheckedTest; @@ -44,7 +45,8 @@ public class ClassifierTest extends LeakCheckedTest { displayMetrics.widthPixels = 1000; displayMetrics.heightPixels = 1000; mFakeBatteryController = new FakeBatteryController(getLeakCheck()); - mDataProvider = new FalsingDataProvider(displayMetrics, mFakeBatteryController); + mDataProvider = new FalsingDataProvider(displayMetrics, mFakeBatteryController, + new FakeSystemClock()); mDataProvider.setInteractionType(UNLOCK); } @@ -53,7 +55,7 @@ public class ClassifierTest extends LeakCheckedTest { resetDataProvider(); } - FalsingDataProvider getDataProvider() { + protected FalsingDataProvider getDataProvider() { return mDataProvider; } @@ -61,15 +63,15 @@ public class ClassifierTest extends LeakCheckedTest { return mFakeBatteryController; } - void setOffsetX(float offsetX) { + protected void setOffsetX(float offsetX) { mOffsetX = offsetX; } - void setOffsetY(float offsetY) { + protected void setOffsetY(float offsetY) { mOffsetY = offsetY; } - void resetDataProvider() { + protected void resetDataProvider() { for (MotionEvent motionEvent : mMotionEvents) { motionEvent.recycle(); } @@ -79,28 +81,28 @@ public class ClassifierTest extends LeakCheckedTest { mDataProvider.onSessionEnd(); } - MotionEvent appendDownEvent(float x, float y) { + protected MotionEvent appendDownEvent(float x, float y) { return appendMotionEvent(MotionEvent.ACTION_DOWN, x, y); } - MotionEvent appendDownEvent(float x, float y, long eventTime) { + protected MotionEvent appendDownEvent(float x, float y, long eventTime) { return appendMotionEvent(MotionEvent.ACTION_DOWN, x, y, eventTime); } - MotionEvent appendMoveEvent(float x, float y) { + protected MotionEvent appendMoveEvent(float x, float y) { return appendMotionEvent(MotionEvent.ACTION_MOVE, x, y); } - MotionEvent appendMoveEvent(float x, float y, long eventTime) { + protected MotionEvent appendMoveEvent(float x, float y, long eventTime) { return appendMotionEvent(MotionEvent.ACTION_MOVE, x, y, eventTime); } - MotionEvent appendUpEvent(float x, float y) { + protected MotionEvent appendUpEvent(float x, float y) { return appendMotionEvent(MotionEvent.ACTION_UP, x, y); } - MotionEvent appendUpEvent(float x, float y, long eventTime) { + protected MotionEvent appendUpEvent(float x, float y, long eventTime) { return appendMotionEvent(MotionEvent.ACTION_UP, x, y, eventTime); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/BrightLineFalsingManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java index 061664b4f6d4..af5e789a0c41 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/BrightLineFalsingManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java @@ -14,119 +14,110 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; -import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper; -import android.util.DisplayMetrics; -import com.android.internal.logging.testing.UiEventLoggerFake; +import androidx.test.filters.SmallTest; + import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.systemui.dock.DockManager; -import com.android.systemui.dock.DockManagerFake; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.StatusBarStateControllerImpl; import com.android.systemui.statusbar.SysuiStatusBarStateController; -import com.android.systemui.util.DeviceConfigProxy; -import com.android.systemui.util.DeviceConfigProxyFake; import com.android.systemui.util.sensors.ProximitySensor; import com.android.systemui.util.sensors.ThresholdSensor; -import com.android.systemui.utils.leaks.FakeBatteryController; -import com.android.systemui.utils.leaks.LeakCheckedTest; 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; @SmallTest @RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper -public class BrightLineFalsingManagerTest extends LeakCheckedTest { - +public class FalsingCollectorImplTest extends SysuiTestCase { + private FalsingCollectorImpl mFalsingCollector; + @Mock + private FalsingDataProvider mFalsingDataProvider; + private final FalsingManagerFake mFalsingManager = new FalsingManagerFake(); @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor; @Mock private ProximitySensor mProximitySensor; + @Mock private SysuiStatusBarStateController mStatusBarStateController; - private FalsingDataProvider mFalsingDataProvider; - private FakeBatteryController mFakeBatteryController; - - private BrightLineFalsingManager mFalsingManager; @Before - public void setup() { + public void setUp() { MockitoAnnotations.initMocks(this); - mFakeBatteryController = new FakeBatteryController(getLeakCheck()); - DisplayMetrics dm = new DisplayMetrics(); - dm.xdpi = 100; - dm.ydpi = 100; - dm.widthPixels = 100; - dm.heightPixels = 100; - mFalsingDataProvider = new FalsingDataProvider(dm, mFakeBatteryController); - DeviceConfigProxy deviceConfigProxy = new DeviceConfigProxyFake(); - DockManager dockManager = new DockManagerFake(); - mStatusBarStateController = new StatusBarStateControllerImpl(new UiEventLoggerFake()); - mStatusBarStateController.setState(StatusBarState.KEYGUARD); - mFalsingManager = new BrightLineFalsingManager(mFalsingDataProvider, - mKeyguardUpdateMonitor, mProximitySensor, deviceConfigProxy, dockManager, - mStatusBarStateController); + + when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD); + + mFalsingCollector = new FalsingCollectorImpl(mFalsingDataProvider, mFalsingManager, + mKeyguardUpdateMonitor, mProximitySensor, mStatusBarStateController); } + @Test public void testRegisterSensor() { - mFalsingManager.onScreenTurningOn(); + mFalsingCollector.onScreenTurningOn(); verify(mProximitySensor).register(any(ThresholdSensor.Listener.class)); } @Test public void testNoProximityWhenWirelessCharging() { - mFakeBatteryController.setWirelessCharging(true); - mFalsingManager.onScreenTurningOn(); + when(mFalsingDataProvider.isWirelessCharging()).thenReturn(true); + mFalsingCollector.onScreenTurningOn(); verify(mProximitySensor, never()).register(any(ThresholdSensor.Listener.class)); } @Test public void testUnregisterSensor() { - mFalsingManager.onScreenTurningOn(); + mFalsingCollector.onScreenTurningOn(); reset(mProximitySensor); - mFalsingManager.onScreenOff(); + mFalsingCollector.onScreenOff(); verify(mProximitySensor).unregister(any(ThresholdSensor.Listener.class)); } @Test public void testUnregisterSensor_QS() { - mFalsingManager.onScreenTurningOn(); + mFalsingCollector.onScreenTurningOn(); reset(mProximitySensor); - mFalsingManager.setQsExpanded(true); + mFalsingCollector.setQsExpanded(true); verify(mProximitySensor).unregister(any(ThresholdSensor.Listener.class)); - mFalsingManager.setQsExpanded(false); + mFalsingCollector.setQsExpanded(false); verify(mProximitySensor).register(any(ThresholdSensor.Listener.class)); } @Test public void testUnregisterSensor_Bouncer() { - mFalsingManager.onScreenTurningOn(); + mFalsingCollector.onScreenTurningOn(); reset(mProximitySensor); - mFalsingManager.onBouncerShown(); + mFalsingCollector.onBouncerShown(); verify(mProximitySensor).unregister(any(ThresholdSensor.Listener.class)); - mFalsingManager.onBouncerHidden(); + mFalsingCollector.onBouncerHidden(); verify(mProximitySensor).register(any(ThresholdSensor.Listener.class)); } @Test public void testUnregisterSensor_StateTransition() { - mFalsingManager.onScreenTurningOn(); + + ArgumentCaptor<StatusBarStateController.StateListener> stateListenerArgumentCaptor = + ArgumentCaptor.forClass(StatusBarStateController.StateListener.class); + verify(mStatusBarStateController).addCallback(stateListenerArgumentCaptor.capture()); + + mFalsingCollector.onScreenTurningOn(); reset(mProximitySensor); - mStatusBarStateController.setState(StatusBarState.SHADE); + stateListenerArgumentCaptor.getValue().onStateChanged(StatusBarState.SHADE); verify(mProximitySensor).unregister(any(ThresholdSensor.Listener.class)); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/FalsingDataProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java index f13bc7379436..be0cc97a2f0f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/FalsingDataProviderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.Matchers.closeTo; @@ -26,6 +26,7 @@ import android.view.MotionEvent; import androidx.test.filters.SmallTest; +import com.android.systemui.util.time.FakeSystemClock; import com.android.systemui.utils.leaks.FakeBatteryController; import org.junit.After; @@ -51,7 +52,8 @@ public class FalsingDataProviderTest extends ClassifierTest { displayMetrics.ydpi = 100; displayMetrics.widthPixels = 1000; displayMetrics.heightPixels = 1000; - mDataProvider = new FalsingDataProvider(displayMetrics, mFakeBatteryController); + mDataProvider = new FalsingDataProvider(displayMetrics, mFakeBatteryController, + new FakeSystemClock()); } @After @@ -110,13 +112,6 @@ public class FalsingDataProviderTest extends ClassifierTest { assertThat(motionEventList.get(1).getX(), is(6f)); assertThat(motionEventList.get(0).getY(), is(7f)); assertThat(motionEventList.get(1).getY(), is(5f)); - - // The first, real event should still be a, however. - MotionEvent firstRealMotionEvent = mDataProvider.getFirstActualMotionEvent(); - assertThat(firstRealMotionEvent.getActionMasked(), is(MotionEvent.ACTION_DOWN)); - assertThat(firstRealMotionEvent.getEventTime(), is(1L)); - assertThat(firstRealMotionEvent.getX(), is(2f)); - assertThat(firstRealMotionEvent.getY(), is(9f)); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java deleted file mode 100644 index c3c9ecc23d59..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.classifier; - -import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_MANAGER_ENABLED; - -import static org.hamcrest.CoreMatchers.instanceOf; -import static org.junit.Assert.assertThat; - -import android.provider.DeviceConfig; -import android.testing.AndroidTestingRunner; -import android.util.DisplayMetrics; - -import androidx.test.filters.SmallTest; - -import com.android.internal.logging.testing.UiEventLoggerFake; -import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.systemui.classifier.brightline.BrightLineFalsingManager; -import com.android.systemui.classifier.brightline.FalsingDataProvider; -import com.android.systemui.dock.DockManager; -import com.android.systemui.dock.DockManagerFake; -import com.android.systemui.dump.DumpManager; -import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.shared.plugins.PluginManager; -import com.android.systemui.statusbar.StatusBarStateControllerImpl; -import com.android.systemui.util.DeviceConfigProxy; -import com.android.systemui.util.DeviceConfigProxyFake; -import com.android.systemui.util.concurrency.FakeExecutor; -import com.android.systemui.util.sensors.ProximitySensor; -import com.android.systemui.util.time.FakeSystemClock; -import com.android.systemui.utils.leaks.FakeBatteryController; -import com.android.systemui.utils.leaks.LeakCheckedTest; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -@SmallTest -@RunWith(AndroidTestingRunner.class) -public class FalsingManagerProxyTest extends LeakCheckedTest { - @Mock(stubOnly = true) - PluginManager mPluginManager; - @Mock(stubOnly = true) - ProximitySensor mProximitySensor; - @Mock(stubOnly = true) - private KeyguardUpdateMonitor mKeyguardUpdateMonitor; - @Mock DumpManager mDumpManager; - private FalsingManagerProxy mProxy; - private DeviceConfigProxy mDeviceConfig; - private FalsingDataProvider mFalsingDataProvider; - private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock()); - private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock()); - private DockManager mDockManager = new DockManagerFake(); - private StatusBarStateController mStatusBarStateController = - new StatusBarStateControllerImpl(new UiEventLoggerFake()); - - @Before - public void setup() { - MockitoAnnotations.initMocks(this); - mDeviceConfig = new DeviceConfigProxyFake(); - mDeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, - BRIGHTLINE_FALSING_MANAGER_ENABLED, "false", false); - mFalsingDataProvider = new FalsingDataProvider( - new DisplayMetrics(), new FakeBatteryController(getLeakCheck())); - } - - @After - public void tearDown() { - if (mProxy != null) { - mProxy.cleanup(); - } - } - - @Test - public void test_brightLineFalsingManagerDisabled() { - mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mExecutor, - mProximitySensor, mDeviceConfig, mDockManager, mKeyguardUpdateMonitor, - mDumpManager, mUiBgExecutor, mStatusBarStateController, mFalsingDataProvider); - assertThat(mProxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class)); - } - - @Test - public void test_brightLineFalsingManagerEnabled() throws InterruptedException { - mDeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, - BRIGHTLINE_FALSING_MANAGER_ENABLED, "true", false); - mExecutor.runAllReady(); - mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mExecutor, - mProximitySensor, mDeviceConfig, mDockManager, mKeyguardUpdateMonitor, - mDumpManager, mUiBgExecutor, mStatusBarStateController, mFalsingDataProvider); - assertThat(mProxy.getInternalFalsingManager(), instanceOf(BrightLineFalsingManager.class)); - } - - @Test - public void test_brightLineFalsingManagerToggled() throws InterruptedException { - mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mExecutor, - mProximitySensor, mDeviceConfig, mDockManager, mKeyguardUpdateMonitor, - mDumpManager, mUiBgExecutor, mStatusBarStateController, mFalsingDataProvider); - assertThat(mProxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class)); - - mDeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, - BRIGHTLINE_FALSING_MANAGER_ENABLED, "true", false); - mExecutor.runAllReady(); - assertThat(mProxy.getInternalFalsingManager(), - instanceOf(BrightLineFalsingManager.class)); - - mDeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, - BRIGHTLINE_FALSING_MANAGER_ENABLED, "false", false); - mExecutor.runAllReady(); - assertThat(mProxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class)); - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DiagonalClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DiagonalClassifierTest.java index e88ff2df60ca..714d6581ff00 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DiagonalClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DiagonalClassifierTest.java @@ -27,6 +27,8 @@ import android.testing.AndroidTestingRunner; import androidx.test.filters.SmallTest; +import com.android.systemui.classifier.ClassifierTest; +import com.android.systemui.classifier.FalsingDataProvider; import com.android.systemui.util.DeviceConfigProxyFake; import org.junit.After; diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DistanceClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DistanceClassifierTest.java index e5ab9be288ef..d66c7a9d43a5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DistanceClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DistanceClassifierTest.java @@ -23,6 +23,8 @@ import android.testing.AndroidTestingRunner; import androidx.test.filters.SmallTest; +import com.android.systemui.classifier.ClassifierTest; +import com.android.systemui.classifier.FalsingDataProvider; import com.android.systemui.util.DeviceConfigProxyFake; import org.junit.After; diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DoubleTapClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DoubleTapClassifierTest.java new file mode 100644 index 000000000000..288ab0ad6596 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DoubleTapClassifierTest.java @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.classifier.brightline; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.when; + +import android.testing.AndroidTestingRunner; +import android.view.MotionEvent; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.classifier.ClassifierTest; +import com.android.systemui.classifier.FalsingDataProvider; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; +import java.util.Deque; +import java.util.LinkedList; +import java.util.List; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class DoubleTapClassifierTest extends ClassifierTest { + + private static final int TOUCH_SLOP = 100; + private static final long DOUBLE_TAP_TIMEOUT_MS = 100; + + private List<MotionEvent> mMotionEvents = new ArrayList<>(); + private final Deque<List<MotionEvent>> mHistoricalMotionEvents = new LinkedList<>(); + + @Mock + private FalsingDataProvider mDataProvider; + @Mock + private SingleTapClassifier mSingleTapClassifier; + private DoubleTapClassifier mClassifier; + + @Before + public void setup() { + super.setup(); + MockitoAnnotations.initMocks(this); + mClassifier = new DoubleTapClassifier(mDataProvider, mSingleTapClassifier, TOUCH_SLOP, + DOUBLE_TAP_TIMEOUT_MS); + doReturn(mHistoricalMotionEvents).when(mDataProvider).getHistoricalMotionEvents(); + } + + @After + public void tearDown() { + for (MotionEvent motionEvent : mMotionEvents) { + motionEvent.recycle(); + } + + mMotionEvents.clear(); + super.tearDown(); + } + + + @Test + public void testSingleTap() { + when(mSingleTapClassifier.isTap(anyList())).thenReturn(true); + addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1); + addMotionEvent(0, 1, MotionEvent.ACTION_UP, TOUCH_SLOP, 1); + + boolean result = mClassifier.isFalseTouch(); + assertThat("Single tap recognized as a valid double tap", result, is(true)); + } + + @Test + public void testDoubleTap() { + when(mSingleTapClassifier.isTap(anyList())).thenReturn(true); + + addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1); + addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, 1); + + archiveMotionEvents(); + + addMotionEvent(2, 2, MotionEvent.ACTION_DOWN, TOUCH_SLOP, TOUCH_SLOP); + addMotionEvent(2, 3, MotionEvent.ACTION_UP, TOUCH_SLOP, TOUCH_SLOP); + + boolean result = mClassifier.isFalseTouch(); + assertThat(mClassifier.getReason(), result, is(false)); + } + + @Test + public void testBadFirstTap() { + when(mSingleTapClassifier.isTap(anyList())).thenReturn(false, true); + + addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1); + addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, 1); + + archiveMotionEvents(); + + addMotionEvent(2, 2, MotionEvent.ACTION_DOWN, 1, 1); + addMotionEvent(2, 3, MotionEvent.ACTION_UP, 1, 1); + + boolean result = mClassifier.isFalseTouch(); + assertThat("Bad first touch allowed", result, is(true)); + } + + @Test + public void testBadSecondTap() { + when(mSingleTapClassifier.isTap(anyList())).thenReturn(true, false); + + addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1); + addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, 1); + + archiveMotionEvents(); + + addMotionEvent(2, 2, MotionEvent.ACTION_DOWN, 1, 1); + addMotionEvent(2, 3, MotionEvent.ACTION_UP, 1, 1); + + boolean result = mClassifier.isFalseTouch(); + assertThat("Bad second touch allowed", result, is(true)); + } + + @Test + public void testBadTouchSlop() { + when(mSingleTapClassifier.isTap(anyList())).thenReturn(true); + + addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1); + addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, 1); + + archiveMotionEvents(); + + addMotionEvent(2, 2, MotionEvent.ACTION_DOWN, TOUCH_SLOP + 1, TOUCH_SLOP); + addMotionEvent(2, 3, MotionEvent.ACTION_UP, TOUCH_SLOP, TOUCH_SLOP + 1); + + boolean result = mClassifier.isFalseTouch(); + assertThat("Sloppy second touch allowed", result, is(true)); + } + + @Test + public void testBadTouchSlow() { + when(mSingleTapClassifier.isTap(anyList())).thenReturn(true); + + addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1); + addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, 1); + + archiveMotionEvents(); + + addMotionEvent(DOUBLE_TAP_TIMEOUT_MS + 1, DOUBLE_TAP_TIMEOUT_MS + 1, + MotionEvent.ACTION_DOWN, 1, 1); + addMotionEvent(DOUBLE_TAP_TIMEOUT_MS + 1, DOUBLE_TAP_TIMEOUT_MS + 2, + MotionEvent.ACTION_UP, 1, 1); + + boolean result = mClassifier.isFalseTouch(); + assertThat("Slow second tap allowed", result, is(true)); + } + + private void addMotionEvent(long downMs, long eventMs, int action, int x, int y) { + MotionEvent ev = MotionEvent.obtain(downMs, eventMs, action, x, y, 0); + mMotionEvents.add(ev); + when(mDataProvider.getRecentMotionEvents()).thenReturn(mMotionEvents); + } + + private void archiveMotionEvents() { + mHistoricalMotionEvents.addFirst(mMotionEvents); + doReturn(mHistoricalMotionEvents).when(mDataProvider).getHistoricalMotionEvents(); + mMotionEvents = new ArrayList<>(); + + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/PointerCountClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/PointerCountClassifierTest.java index 4f8e7c801d04..b512f0d6ef32 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/PointerCountClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/PointerCountClassifierTest.java @@ -26,6 +26,8 @@ import android.view.MotionEvent; import androidx.test.filters.SmallTest; +import com.android.systemui.classifier.ClassifierTest; + import org.junit.After; import org.junit.Before; import org.junit.Test; diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java index 3cebf0d6af57..c2e290f166a3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java @@ -28,6 +28,8 @@ import android.view.MotionEvent; import androidx.test.filters.SmallTest; +import com.android.systemui.classifier.ClassifierTest; +import com.android.systemui.classifier.FalsingDataProvider; import com.android.systemui.util.DeviceConfigProxyFake; import com.android.systemui.util.sensors.ProximitySensor; diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/SingleTapClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/SingleTapClassifierTest.java new file mode 100644 index 000000000000..d67f2b833deb --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/SingleTapClassifierTest.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.classifier.brightline; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.when; + +import android.testing.AndroidTestingRunner; +import android.view.MotionEvent; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.classifier.ClassifierTest; +import com.android.systemui.classifier.FalsingDataProvider; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; +import java.util.List; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class SingleTapClassifierTest extends ClassifierTest { + + private static final int TOUCH_SLOP = 100; + + private final List<MotionEvent> mMotionEvents = new ArrayList<>(); + + @Mock + private FalsingDataProvider mDataProvider; + private SingleTapClassifier mClassifier; + + @Before + public void setup() { + super.setup(); + MockitoAnnotations.initMocks(this); + mClassifier = new SingleTapClassifier(mDataProvider, TOUCH_SLOP); + } + + @After + public void tearDown() { + for (MotionEvent motionEvent : mMotionEvents) { + motionEvent.recycle(); + } + + mMotionEvents.clear(); + super.tearDown(); + } + + + @Test + public void testSimpleTap_XSlop() { + addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1); + addMotionEvent(0, 1, MotionEvent.ACTION_UP, TOUCH_SLOP, 1); + + assertThat(mClassifier.isFalseTouch(), is(false)); + + mMotionEvents.clear(); + + addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1); + addMotionEvent(0, 1, MotionEvent.ACTION_UP, -TOUCH_SLOP + 2, 1); + + assertThat(mClassifier.isFalseTouch(), is(false)); + + } + + @Test + public void testSimpleTap_YSlop() { + addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1); + addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, TOUCH_SLOP); + + assertThat(mClassifier.isFalseTouch(), is(false)); + + mMotionEvents.clear(); + + addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1); + addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, -TOUCH_SLOP + 2); + + assertThat(mClassifier.isFalseTouch(), is(false)); + } + + + @Test + public void testFalseTap_XSlop() { + addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1); + addMotionEvent(0, 1, MotionEvent.ACTION_UP, TOUCH_SLOP + 1, 1); + + assertThat(mClassifier.isFalseTouch(), is(true)); + + mMotionEvents.clear(); + + addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1); + addMotionEvent(0, 1, MotionEvent.ACTION_UP, -TOUCH_SLOP - 1, 1); + + assertThat(mClassifier.isFalseTouch(), is(true)); + + } + + @Test + public void testFalseTap_YSlop() { + addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1); + addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, TOUCH_SLOP + 1); + + assertThat(mClassifier.isFalseTouch(), is(true)); + + mMotionEvents.clear(); + + addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1); + addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, -TOUCH_SLOP - 1); + + assertThat(mClassifier.isFalseTouch(), is(true)); + } + + @Test + public void testLargeMovementFalses() { + addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1); + addMotionEvent(0, 1, MotionEvent.ACTION_MOVE, 1, TOUCH_SLOP + 1); + addMotionEvent(0, 2, MotionEvent.ACTION_UP, 1, 1); + + assertThat(mClassifier.isFalseTouch(), is(true)); + } + + @Test + public void testDirectlySuppliedMotionEvents() { + addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1); + addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, 1); + + assertThat(mClassifier.isTap(mMotionEvents), is(true)); + + addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1); + addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, TOUCH_SLOP + 1); + + assertThat(mClassifier.isTap(mMotionEvents), is(false)); + + } + + private void addMotionEvent(long downMs, long eventMs, int action, int x, int y) { + MotionEvent ev = MotionEvent.obtain(downMs, eventMs, action, x, y, 0); + mMotionEvents.add(ev); + when(mDataProvider.getRecentMotionEvents()).thenReturn(mMotionEvents); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/TimeLimitedMotionEventBufferTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/TimeLimitedMotionEventBufferTest.java new file mode 100644 index 000000000000..1dfffb271f02 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/TimeLimitedMotionEventBufferTest.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.classifier.brightline; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import android.testing.AndroidTestingRunner; +import android.view.MotionEvent; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class TimeLimitedMotionEventBufferTest extends SysuiTestCase { + + private static final long MAX_AGE_MS = 100; + + private TimeLimitedMotionEventBuffer mBuffer; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + mBuffer = new TimeLimitedMotionEventBuffer(MAX_AGE_MS); + } + + @After + public void tearDown() { + for (MotionEvent motionEvent : mBuffer) { + motionEvent.recycle(); + } + mBuffer.clear(); + } + + @Test + public void testAllEventsRetained() { + MotionEvent eventA = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0); + MotionEvent eventB = MotionEvent.obtain(0, 1, MotionEvent.ACTION_MOVE, 0, 0, 0); + MotionEvent eventC = MotionEvent.obtain(0, 2, MotionEvent.ACTION_MOVE, 0, 0, 0); + MotionEvent eventD = MotionEvent.obtain(0, 3, MotionEvent.ACTION_UP, 0, 0, 0); + + mBuffer.add(eventA); + mBuffer.add(eventB); + mBuffer.add(eventC); + mBuffer.add(eventD); + + assertThat(mBuffer.size(), is(4)); + } + + @Test + public void testOlderEventsRemoved() { + MotionEvent eventA = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0); + MotionEvent eventB = MotionEvent.obtain(0, 1, MotionEvent.ACTION_MOVE, 0, 0, 0); + MotionEvent eventC = MotionEvent.obtain( + 0, MAX_AGE_MS + 1, MotionEvent.ACTION_MOVE, 0, 0, 0); + MotionEvent eventD = MotionEvent.obtain( + 0, MAX_AGE_MS + 2, MotionEvent.ACTION_UP, 0, 0, 0); + + mBuffer.add(eventA); + mBuffer.add(eventB); + assertThat(mBuffer.size(), is(2)); + + mBuffer.add(eventC); + mBuffer.add(eventD); + assertThat(mBuffer.size(), is(2)); + + assertThat(mBuffer.get(0), is(eventC)); + assertThat(mBuffer.get(1), is(eventD)); + } + + @Test + public void testFullyExpired() { + MotionEvent eventA = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0); + MotionEvent eventB = MotionEvent.obtain(0, 1, MotionEvent.ACTION_MOVE, 0, 0, 0); + MotionEvent eventC = MotionEvent.obtain(0, 2, MotionEvent.ACTION_MOVE, 0, 0, 0); + MotionEvent eventD = MotionEvent.obtain(0, 3, MotionEvent.ACTION_UP, 0, 0, 0); + + mBuffer.add(eventA); + mBuffer.add(eventB); + mBuffer.add(eventC); + mBuffer.add(eventD); + + assertThat(mBuffer.isFullyExpired(2), is(false)); + assertThat(mBuffer.isFullyExpired(6), is(true)); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/TypeClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/TypeClassifierTest.java index 4346e7df088f..5f3b84c2f7ae 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/TypeClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/TypeClassifierTest.java @@ -33,6 +33,9 @@ import android.testing.AndroidTestingRunner; import androidx.test.filters.SmallTest; +import com.android.systemui.classifier.ClassifierTest; +import com.android.systemui.classifier.FalsingDataProvider; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ZigZagClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ZigZagClassifierTest.java index a8cce00ba996..e49262f5099f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ZigZagClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ZigZagClassifierTest.java @@ -23,6 +23,7 @@ import android.testing.AndroidTestingRunner; import androidx.test.filters.SmallTest; +import com.android.systemui.classifier.ClassifierTest; import com.android.systemui.util.DeviceConfigProxyFake; import org.junit.After; diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java index f6d6f562e3fa..67d0295c82d3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java @@ -41,7 +41,7 @@ import com.android.keyguard.KeyguardDisplayManager; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; -import com.android.systemui.classifier.FalsingManagerFake; +import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.dump.DumpManager; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; @@ -76,18 +76,18 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { private DeviceConfigProxy mDeviceConfig = new DeviceConfigProxyFake(); private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock()); - private FalsingManagerFake mFalsingManager; + private FalsingCollectorFake mFalsingCollector; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - mFalsingManager = new FalsingManagerFake(); + mFalsingCollector = new FalsingCollectorFake(); when(mLockPatternUtils.getDevicePolicyManager()).thenReturn(mDevicePolicyManager); when(mPowerManager.newWakeLock(anyInt(), any())).thenReturn(mock(WakeLock.class)); mViewMediator = new KeyguardViewMediator( - mContext, mFalsingManager, mLockPatternUtils, mBroadcastDispatcher, + mContext, mFalsingCollector, mLockPatternUtils, mBroadcastDispatcher, () -> mStatusBarKeyguardViewManager, mDismissCallbackRegistry, mUpdateMonitor, mDumpManager, mUiBgExecutor, mPowerManager, mTrustManager, mDeviceConfig, mNavigationModeController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java index 3e44fa4a9b2b..477fe6316399 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java @@ -16,6 +16,7 @@ package com.android.systemui.people.widget; +import static android.app.NotificationManager.IMPORTANCE_DEFAULT; import static android.app.NotificationManager.IMPORTANCE_HIGH; import static org.mockito.ArgumentMatchers.any; @@ -164,7 +165,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); NotificationChannel channel = - mNoMan.createNotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME); + new NotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, IMPORTANCE_DEFAULT); mNoMan.issueChannelModification(TEST_PACKAGE_A, UserHandle.getUserHandleForUid(0), channel, IMPORTANCE_HIGH); @@ -181,7 +182,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); NotificationChannel channel = - mNoMan.createNotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME); + new NotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, IMPORTANCE_DEFAULT); channel.setConversationId(TEST_PARENT_CHANNEL_ID, TEST_CONVERSATION_ID); mNoMan.issueChannelModification(TEST_PACKAGE_A, diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java index 3817703fe135..0dc268a3c186 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java @@ -168,14 +168,18 @@ public class QSPanelControllerBaseTest extends SysuiTestCase { @Test public void testSetExpanded_Metrics() { + when(mQSPanel.isExpanded()).thenReturn(false); mController.setExpanded(true); verify(mMetricsLogger).visibility(eq(MetricsEvent.QS_PANEL), eq(true)); + verify(mQSLogger).logPanelExpanded(true, mQSPanel.getDumpableTag()); assertEquals(1, mUiEventLogger.numLogs()); assertEquals(QSEvent.QS_PANEL_EXPANDED.getId(), mUiEventLogger.eventId(0)); mUiEventLogger.getLogs().clear(); + when(mQSPanel.isExpanded()).thenReturn(true); mController.setExpanded(false); verify(mMetricsLogger).visibility(eq(MetricsEvent.QS_PANEL), eq(false)); + verify(mQSLogger).logPanelExpanded(false, mQSPanel.getDumpableTag()); assertEquals(1, mUiEventLogger.numLogs()); assertEquals(QSEvent.QS_PANEL_COLLAPSED.getId(), mUiEventLogger.eventId(0)); mUiEventLogger.getLogs().clear(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java index 900f989c3bdf..a726181fc702 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java @@ -17,13 +17,10 @@ package com.android.systemui.qs; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.content.Context; -import android.os.UserManager; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; @@ -32,13 +29,11 @@ import android.widget.FrameLayout; import androidx.test.filters.SmallTest; -import com.android.systemui.Dependency; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.qs.QSTileView; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; -import com.android.systemui.statusbar.policy.SecurityController; import org.junit.Before; import org.junit.Test; @@ -76,16 +71,16 @@ public class QSPanelTest extends SysuiTestCase { MockitoAnnotations.initMocks(this); mTestableLooper = TestableLooper.get(this); - // Dependencies for QSSecurityFooter - mDependency.injectTestDependency(ActivityStarter.class, mActivityStarter); - mDependency.injectMockDependency(SecurityController.class); - mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper()); - mContext.addMockSystemService(Context.USER_SERVICE, mock(UserManager.class)); +// // Dependencies for QSSecurityFooter +// mDependency.injectTestDependency(ActivityStarter.class, mActivityStarter); +// mDependency.injectMockDependency(SecurityController.class); +// mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper()); +// mContext.addMockSystemService(Context.USER_SERVICE, mock(UserManager.class)); mDndTileRecord.tile = dndTile; mDndTileRecord.tileView = mQSTileView; mTestableLooper.runWithLooper(() -> { - mQsPanel = new QSPanel(mContext, null, mQSLogger); + mQsPanel = new QSPanel(mContext, null); mQsPanel.onFinishInflate(); // Provides a parent with non-zero size for QSPanel mParentView = new FrameLayout(mContext); @@ -100,15 +95,6 @@ public class QSPanelTest extends SysuiTestCase { } @Test - public void testSetExpanded_Metrics() { - mQsPanel.setExpanded(true); - verify(mQSLogger).logPanelExpanded(true, mQsPanel.getDumpableTag()); - - mQsPanel.setExpanded(false); - verify(mQSLogger).logPanelExpanded(false, mQsPanel.getDumpableTag()); - } - - @Test public void testOpenDetailsWithExistingTile_NoException() { mTestableLooper.processAllMessages(); mQsPanel.openDetails(dndTile); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt new file mode 100644 index 000000000000..3f2b4da764e3 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs + +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.internal.logging.MetricsLogger +import com.android.internal.logging.testing.UiEventLoggerFake +import com.android.systemui.SysuiTestCase +import com.android.systemui.dump.DumpManager +import com.android.systemui.media.MediaHost +import com.android.systemui.plugins.qs.QSTile +import com.android.systemui.qs.customize.QSCustomizerController +import com.android.systemui.qs.logging.QSLogger +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.`when` +import org.mockito.Mockito.any +import org.mockito.Mockito.times +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(AndroidTestingRunner::class) +class QuickQSPanelControllerTest : SysuiTestCase() { + + @Mock + private lateinit var quickQSPanel: QuickQSPanel + @Mock + private lateinit var qsTileHost: QSTileHost + @Mock + private lateinit var qsCustomizerController: QSCustomizerController + @Mock + private lateinit var mediaHost: MediaHost + @Mock + private lateinit var metricsLogger: MetricsLogger + private val uiEventLogger = UiEventLoggerFake() + @Mock + private lateinit var qsLogger: QSLogger + private val dumpManager = DumpManager() + @Mock + private lateinit var tile: QSTile + @Mock + private lateinit var tileLayout: TileLayout + + private lateinit var controller: QuickQSPanelController + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + `when`(quickQSPanel.tileLayout).thenReturn(tileLayout) + `when`(quickQSPanel.dumpableTag).thenReturn("") + + controller = QuickQSPanelController( + quickQSPanel, + qsTileHost, + qsCustomizerController, + false, + mediaHost, + metricsLogger, + uiEventLogger, + qsLogger, + dumpManager + ) + + controller.init() + } + + @After + fun tearDown() { + controller.onViewDetached() + } + + @Test + fun testTileSublistWithFewerTiles_noCrash() { + `when`(quickQSPanel.numQuickTiles).thenReturn(3) + + `when`(qsTileHost.tiles).thenReturn(listOf(tile, tile)) + + controller.setTiles() + } + + @Test + fun testTileSublistWithTooManyTiles() { + val limit = 3 + `when`(quickQSPanel.numQuickTiles).thenReturn(limit) + `when`(qsTileHost.tiles).thenReturn(listOf(tile, tile, tile, tile)) + + controller.setTiles() + + verify(quickQSPanel, times(limit)).addTile(any()) + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java index 11ef3e33f9d0..b7cc651dc24b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java @@ -19,6 +19,8 @@ package com.android.systemui.screenrecord; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import android.app.PendingIntent; @@ -128,6 +130,35 @@ public class RecordingControllerTest extends SysuiTestCase { verify(mCallback).onRecordingEnd(); } + // Test that broadcast will update state + @Test + public void testUpdateStateBroadcast() { + if (Looper.myLooper() == null) { + Looper.prepare(); + } + + // When a recording has started + PendingIntent startIntent = Mockito.mock(PendingIntent.class); + mController.startCountdown(0, 0, startIntent, null); + verify(mCallback).onCountdownEnd(); + + // then the receiver was registered + verify(mBroadcastDispatcher).registerReceiver(eq(mController.mStateChangeReceiver), + any(), any(), any()); + + // When the receiver gets an update + Intent intent = new Intent(RecordingController.INTENT_UPDATE_STATE); + intent.putExtra(RecordingController.EXTRA_STATE, false); + mController.mStateChangeReceiver.onReceive(mContext, intent); + + // then the state is updated + assertFalse(mController.isRecording()); + verify(mCallback).onRecordingEnd(); + + // and the receiver is unregistered + verify(mBroadcastDispatcher).unregisterReceiver(eq(mController.mStateChangeReceiver)); + } + // Test that switching users will stop an ongoing recording @Test public void testUserChange() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java index 3e37fde84544..91cafead596c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java @@ -17,15 +17,18 @@ package com.android.systemui.screenrecord; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.app.Notification; import android.app.NotificationManager; import android.content.Intent; +import android.os.RemoteException; import android.testing.AndroidTestingRunner; import androidx.test.filters.SmallTest; @@ -43,6 +46,7 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; +import java.io.IOException; import java.util.concurrent.Executor; @RunWith(AndroidTestingRunner.class) @@ -88,6 +92,9 @@ public class RecordingServiceTest extends SysuiTestCase { doNothing().when(mRecordingService).createRecordingNotification(); doReturn(mNotification).when(mRecordingService).createProcessingNotification(); doReturn(mNotification).when(mRecordingService).createSaveNotification(any()); + doNothing().when(mRecordingService).createErrorNotification(); + doNothing().when(mRecordingService).showErrorToast(anyInt()); + doNothing().when(mRecordingService).stopForeground(anyBoolean()); doNothing().when(mRecordingService).startForeground(anyInt(), any()); doReturn(mScreenMediaRecorder).when(mRecordingService).getRecorder(); @@ -124,4 +131,16 @@ public class RecordingServiceTest extends SysuiTestCase { .log(Events.ScreenRecordEvent.SCREEN_RECORD_END_NOTIFICATION); verify(mUiEventLogger, times(0)).log(Events.ScreenRecordEvent.SCREEN_RECORD_END_QS_TILE); } + + @Test + public void testErrorUpdatesState() throws IOException, RemoteException { + // When the screen recording does not start properly + doThrow(new RuntimeException("fail")).when(mScreenMediaRecorder).start(); + + Intent startIntent = RecordingService.getStartIntent(mContext, 0, 0, false); + mRecordingService.onStartCommand(startIntent, 0, 0); + + // Then the state is set to not recording + verify(mController).updateState(false); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NoManSimulator.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NoManSimulator.java index 1bfe10c5263b..4507366e3073 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NoManSimulator.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NoManSimulator.java @@ -16,8 +16,6 @@ package com.android.systemui.statusbar.notification.collection; -import static android.app.NotificationManager.IMPORTANCE_DEFAULT; - import static org.junit.Assert.assertNotNull; import android.app.NotificationChannel; @@ -76,10 +74,6 @@ public class NoManSimulator { } } - public NotificationChannel createNotificationChannel(String id, String name) { - return new NotificationChannel(id, name, IMPORTANCE_DEFAULT); - } - public void issueChannelModification( String pkg, UserHandle user, NotificationChannel channel, int modificationType) { for (NotificationHandler listener : mListeners) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java index dbaf5c467c45..ababebdd807c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java @@ -44,9 +44,9 @@ import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.NotificationMessagingUtil; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.media.MediaFeatureFlag; import com.android.systemui.media.dialog.MediaOutputDialogFactory; -import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.statusbar.FeatureFlags; @@ -129,7 +129,6 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase { @Mock(answer = Answers.RETURNS_SELF) private ExpandableNotificationRowComponent.Builder mExpandableNotificationRowComponentBuilder; @Mock private ExpandableNotificationRowComponent mExpandableNotificationRowComponent; - @Mock private FalsingManager mFalsingManager; @Mock private KeyguardBypassController mKeyguardBypassController; @Mock private StatusBarStateController mStatusBarStateController; @@ -244,7 +243,7 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase { mGutsManager, true, null, - mFalsingManager, + new FalsingCollectorFake(), mPeopleNotificationIdentifier, Optional.of(mock(BubblesManager.class)) )); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java index 37e82185d228..c426c87215d7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java @@ -15,6 +15,7 @@ package com.android.systemui.statusbar.notification.row; import static android.provider.Settings.Secure.SHOW_NOTIFICATION_SNOOZE; +import static android.provider.Settings.Global.SHOW_NEW_NOTIF_DISMISS; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; @@ -96,6 +97,7 @@ public class NotificationMenuRowTest extends LeakCheckedTest { @Test public void testNoAppOpsInSlowSwipe() { Settings.Secure.putInt(mContext.getContentResolver(), SHOW_NOTIFICATION_SNOOZE, 0); + Settings.Global.putInt(mContext.getContentResolver(), SHOW_NEW_NOTIF_DISMISS, 0); NotificationMenuRow row = new NotificationMenuRow(mContext, mPeopleNotificationIdentifier); row.createMenu(mRow, null); @@ -108,6 +110,7 @@ public class NotificationMenuRowTest extends LeakCheckedTest { @Test public void testNoSnoozeInSlowSwipe() { Settings.Secure.putInt(mContext.getContentResolver(), SHOW_NOTIFICATION_SNOOZE, 0); + Settings.Global.putInt(mContext.getContentResolver(), SHOW_NEW_NOTIF_DISMISS, 0); NotificationMenuRow row = new NotificationMenuRow(mContext, mPeopleNotificationIdentifier); row.createMenu(mRow, null); @@ -120,6 +123,7 @@ public class NotificationMenuRowTest extends LeakCheckedTest { @Test public void testSnoozeInSlowSwipe() { Settings.Secure.putInt(mContext.getContentResolver(), SHOW_NOTIFICATION_SNOOZE, 1); + Settings.Global.putInt(mContext.getContentResolver(), SHOW_NEW_NOTIF_DISMISS, 0); NotificationMenuRow row = new NotificationMenuRow(mContext, mPeopleNotificationIdentifier); row.createMenu(mRow, null); @@ -130,6 +134,19 @@ public class NotificationMenuRowTest extends LeakCheckedTest { } @Test + public void testSlowSwipe_newDismiss() { + Settings.Secure.putInt(mContext.getContentResolver(), SHOW_NOTIFICATION_SNOOZE, 1); + Settings.Global.putInt(mContext.getContentResolver(), SHOW_NEW_NOTIF_DISMISS, 1); + + NotificationMenuRow row = new NotificationMenuRow(mContext, mPeopleNotificationIdentifier); + row.createMenu(mRow, null); + + ViewGroup container = (ViewGroup) row.getMenuView(); + // Clear menu + assertEquals(0, container.getChildCount()); + } + + @Test public void testIsSnappedAndOnSameSide() { NotificationMenuRow row = Mockito.spy( new NotificationMenuRow(mContext, mPeopleNotificationIdentifier)); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java index baae8fda18f9..7470a13ae39e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java @@ -45,9 +45,9 @@ import android.widget.RemoteViews; import com.android.systemui.R; import com.android.systemui.TestableDependency; +import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.media.MediaFeatureFlag; import com.android.systemui.media.dialog.MediaOutputDialogFactory; -import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationRemoteInputManager; @@ -428,7 +428,7 @@ public class NotificationTestHelper { mock(OnExpandClickListener.class), mock(NotificationMediaManager.class), mock(ExpandableNotificationRow.CoordinateOnClickListener.class), - mock(FalsingManager.class), + new FalsingCollectorFake(), mStatusBarStateController, mPeopleNotificationIdentifier, mock(OnUserInteractionCallback.class), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java index 01d49c269fb2..3c4fde8f6106 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java @@ -42,9 +42,9 @@ import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.nano.MetricsProto; import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.SysuiTestCase; +import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.media.KeyguardMediaController; -import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -106,7 +106,6 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase { @Mock private SysuiColorExtractor mColorExtractor; @Mock private NotificationLockscreenUserManager mNotificationLockscreenUserManager; @Mock private MetricsLogger mMetricsLogger; - @Mock private FalsingManager mFalsingManager; @Mock private Resources mResources; @Mock(answer = Answers.RETURNS_SELF) private NotificationSwipeHelper.Builder mNotificationSwipeHelperBuilder; @@ -160,7 +159,7 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase { mColorExtractor, mNotificationLockscreenUserManager, mMetricsLogger, - mFalsingManager, + new FalsingCollectorFake(), mResources, mNotificationSwipeHelperBuilder, mStatusBar, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java index 1b05ad7f8b5b..bc014ec16103 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java @@ -50,9 +50,9 @@ import com.android.keyguard.ViewMediatorCallback; import com.android.keyguard.dagger.KeyguardBouncerComponent; import com.android.systemui.DejankUtils; import com.android.systemui.SysuiTestCase; +import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.plugins.ActivityStarter.OnDismissAction; -import com.android.systemui.plugins.FalsingManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import org.junit.Assert; @@ -72,7 +72,7 @@ import org.mockito.stubbing.Answer; public class KeyguardBouncerTest extends SysuiTestCase { @Mock - private FalsingManager mFalsingManager; + private FalsingCollector mFalsingCollector; @Mock private ViewMediatorCallback mViewMediatorCallback; @Mock @@ -128,7 +128,7 @@ public class KeyguardBouncerTest extends SysuiTestCase { final ViewGroup container = new FrameLayout(getContext()); mBouncer = new KeyguardBouncer.Factory(getContext(), mViewMediatorCallback, - mDismissCallbackRegistry, mFalsingManager, + mDismissCallbackRegistry, mFalsingCollector, mKeyguardStateController, mKeyguardUpdateMonitor, mKeyguardBypassController, mHandler, mKeyguardSecurityModel, mKeyguardBouncerComponentFactory) @@ -143,10 +143,10 @@ public class KeyguardBouncerTest extends SysuiTestCase { @Test public void testShow_notifiesFalsingManager() { mBouncer.show(true); - verify(mFalsingManager).onBouncerShown(); + verify(mFalsingCollector).onBouncerShown(); mBouncer.show(true, false); - verifyNoMoreInteractions(mFalsingManager); + verifyNoMoreInteractions(mFalsingCollector); } /** @@ -212,11 +212,11 @@ public class KeyguardBouncerTest extends SysuiTestCase { mBouncer.setExpansion(0.5f); mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN); - verify(mFalsingManager).onBouncerHidden(); + verify(mFalsingCollector).onBouncerHidden(); verify(mExpansionCallback).onFullyHidden(); mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE); - verify(mFalsingManager).onBouncerShown(); + verify(mFalsingCollector).onBouncerShown(); verify(mExpansionCallback).onFullyShown(); verify(mExpansionCallback, never()).onStartingToHide(); @@ -239,7 +239,7 @@ public class KeyguardBouncerTest extends SysuiTestCase { @Test public void testHide_notifiesFalsingManager() { mBouncer.hide(false); - verify(mFalsingManager).onBouncerHidden(); + verify(mFalsingCollector).onBouncerHidden(); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java index 4841b3bf951f..3d582e74ea4e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java @@ -60,10 +60,10 @@ import com.android.keyguard.dagger.KeyguardStatusViewComponent; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.biometrics.AuthController; +import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.doze.DozeLog; import com.android.systemui.media.MediaHierarchyManager; -import com.android.systemui.plugins.FalsingManager; import com.android.systemui.qs.QSDetailDisplayer; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.KeyguardAffordanceView; @@ -132,8 +132,6 @@ public class NotificationPanelViewTest extends SysuiTestCase { @Mock private KeyguardUpdateMonitor mUpdateMonitor; @Mock - private FalsingManager mFalsingManager; - @Mock private KeyguardBypassController mKeyguardBypassController; @Mock private DozeParameters mDozeParameters; @@ -252,7 +250,7 @@ public class NotificationPanelViewTest extends SysuiTestCase { mKeyguardBypassController, mHeadsUpManager, mock(NotificationRoundnessManager.class), mStatusBarStateController, - new FalsingManagerFake()); + new FalsingManagerFake(), new FalsingCollectorFake()); when(mKeyguardStatusViewComponentFactory.build(any())) .thenReturn(mKeyguardStatusViewComponent); when(mKeyguardStatusViewComponent.getKeyguardClockSwitchController()) @@ -263,7 +261,7 @@ public class NotificationPanelViewTest extends SysuiTestCase { mResources, mInjectionInflationController, coordinator, expansionHandler, mDynamicPrivacyController, mKeyguardBypassController, - mFalsingManager, mShadeController, + new FalsingManagerFake(), new FalsingCollectorFake(), mShadeController, mNotificationLockscreenUserManager, mNotificationEntryManager, mKeyguardStateController, mStatusBarStateController, mDozeLog, mDozeParameters, mCommandQueue, mVibratorHelper, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java index 25af584ed3f8..e0fa9babbb48 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java @@ -30,6 +30,7 @@ import androidx.test.filters.SmallTest; import com.android.systemui.R; import com.android.systemui.SystemUIFactory; import com.android.systemui.SysuiTestCase; +import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.dock.DockManager; import com.android.systemui.doze.DozeLog; @@ -111,6 +112,7 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase { mDynamicPrivacyController, mBypassController, new FalsingManagerFake(), + new FalsingCollectorFake(), mPluginManager, mTunerService, mNotificationLockScreenUserManager, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DoubleTapHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationTapHelperTest.java index df1233af6406..4ed27463eecd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DoubleTapHelperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationTapHelperTest.java @@ -16,17 +16,12 @@ package com.android.systemui.statusbar.phone; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyFloat; -import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.res.Resources; -import android.os.SystemClock; import android.testing.AndroidTestingRunner; import android.view.MotionEvent; import android.view.View; @@ -36,48 +31,47 @@ import androidx.test.filters.SmallTest; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.classifier.FalsingManagerFake; +import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.time.FakeSystemClock; 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; @SmallTest @RunWith(AndroidTestingRunner.class) -public class DoubleTapHelperTest extends SysuiTestCase { +public class NotificationTapHelperTest extends SysuiTestCase { - private DoubleTapHelper mDoubleTapHelper; - private int mTouchSlop; - private int mDoubleTouchSlop; + private NotificationTapHelper mNotificationTapHelper; + private final FakeSystemClock mFakeSystemClock = new FakeSystemClock(); + private final FalsingManagerFake mFalsingManager = new FalsingManagerFake(); + private final FakeExecutor mFakeExecutor = new FakeExecutor(mFakeSystemClock); @Mock private View mView; - @Mock private DoubleTapHelper.ActivationListener mActivationListener; - @Mock private DoubleTapHelper.DoubleTapListener mDoubleTapListener; - @Mock private DoubleTapHelper.SlideBackListener mSlideBackListener; - @Mock private DoubleTapHelper.DoubleTapLogListener mDoubleTapLogListener; + @Mock private NotificationTapHelper.ActivationListener mActivationListener; + @Mock private NotificationTapHelper.DoubleTapListener mDoubleTapListener; + @Mock private NotificationTapHelper.SlideBackListener mSlideBackListener; @Mock private Resources mResources; @Before public void setup() { MockitoAnnotations.initMocks(this); - mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop(); - // The double tap slop has to be less than the regular slop, otherwise it has no effect. - mDoubleTouchSlop = mTouchSlop - 1; when(mView.getContext()).thenReturn(mContext); when(mView.getResources()).thenReturn(mResources); when(mResources.getDimension(R.dimen.double_tap_slop)) - .thenReturn((float) mDoubleTouchSlop); + .thenReturn((float) ViewConfiguration.get(mContext).getScaledTouchSlop() - 1); - mDoubleTapHelper = new DoubleTapHelper(mView, - mActivationListener, - mDoubleTapListener, - mSlideBackListener, mDoubleTapLogListener); + mFalsingManager.setFalseRobustTap(true); // Test double tapping most of the time. + + mNotificationTapHelper = new NotificationTapHelper.Factory(mFalsingManager, mFakeExecutor) + .create(mActivationListener, mDoubleTapListener, mSlideBackListener); } @Test public void testDoubleTap_success() { - long downtimeA = SystemClock.uptimeMillis(); + long downtimeA = 100; long downtimeB = downtimeA + 100; MotionEvent evDownA = MotionEvent.obtain(downtimeA, @@ -105,16 +99,13 @@ public class DoubleTapHelperTest extends SysuiTestCase { 1, 0); - mDoubleTapHelper.onTouchEvent(evDownA); - mDoubleTapHelper.onTouchEvent(evUpA); + mNotificationTapHelper.onTouchEvent(evDownA); + mNotificationTapHelper.onTouchEvent(evUpA); verify(mActivationListener).onActiveChanged(true); - verify(mView).postDelayed(any(Runnable.class), anyLong()); - verify(mDoubleTapLogListener, never()).onDoubleTapLog(anyBoolean(), anyFloat(), anyFloat()); verify(mDoubleTapListener, never()).onDoubleTap(); - mDoubleTapHelper.onTouchEvent(evDownB); - mDoubleTapHelper.onTouchEvent(evUpB); - verify(mDoubleTapLogListener).onDoubleTapLog(true, 0, 0); + mNotificationTapHelper.onTouchEvent(evDownB); + mNotificationTapHelper.onTouchEvent(evUpB); verify(mDoubleTapListener).onDoubleTap(); evDownA.recycle(); @@ -125,7 +116,7 @@ public class DoubleTapHelperTest extends SysuiTestCase { @Test public void testSingleTap_timeout() { - long downtimeA = SystemClock.uptimeMillis(); + long downtimeA = 100; MotionEvent evDownA = MotionEvent.obtain(downtimeA, downtimeA, @@ -140,21 +131,19 @@ public class DoubleTapHelperTest extends SysuiTestCase { 1, 0); - ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class); - mDoubleTapHelper.onTouchEvent(evDownA); - mDoubleTapHelper.onTouchEvent(evUpA); - verify(mActivationListener).onActiveChanged(true); - verify(mView).postDelayed(runnableCaptor.capture(), anyLong()); - runnableCaptor.getValue().run(); + mNotificationTapHelper.onTouchEvent(evDownA); + mNotificationTapHelper.onTouchEvent(evUpA); verify(mActivationListener).onActiveChanged(true); + drainExecutor(); + verify(mActivationListener).onActiveChanged(false); evDownA.recycle(); evUpA.recycle(); } @Test - public void testSingleTap_slop() { - long downtimeA = SystemClock.uptimeMillis(); + public void testSingleTap_falsed() { + long downtimeA = 100; MotionEvent evDownA = MotionEvent.obtain(downtimeA, downtimeA, @@ -165,13 +154,13 @@ public class DoubleTapHelperTest extends SysuiTestCase { MotionEvent evUpA = MotionEvent.obtain(downtimeA, downtimeA + 1, MotionEvent.ACTION_UP, - 1 + mTouchSlop, + 1, 1, 0); - ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class); - mDoubleTapHelper.onTouchEvent(evDownA); - mDoubleTapHelper.onTouchEvent(evUpA); + mFalsingManager.setFalseTap(true); + mNotificationTapHelper.onTouchEvent(evDownA); + mNotificationTapHelper.onTouchEvent(evUpA); verify(mActivationListener, never()).onActiveChanged(true); verify(mDoubleTapListener, never()).onDoubleTap(); @@ -180,8 +169,8 @@ public class DoubleTapHelperTest extends SysuiTestCase { } @Test - public void testDoubleTap_slop() { - long downtimeA = SystemClock.uptimeMillis(); + public void testDoubleTap_falsed() { + long downtimeA = 100; long downtimeB = downtimeA + 100; MotionEvent evDownA = MotionEvent.obtain(downtimeA, @@ -206,17 +195,17 @@ public class DoubleTapHelperTest extends SysuiTestCase { downtimeB + 1, MotionEvent.ACTION_UP, 1, - 1 + mDoubleTouchSlop, + 1, 0); - mDoubleTapHelper.onTouchEvent(evDownA); - mDoubleTapHelper.onTouchEvent(evUpA); + mFalsingManager.setFalseDoubleTap(true); + + mNotificationTapHelper.onTouchEvent(evDownA); + mNotificationTapHelper.onTouchEvent(evUpA); verify(mActivationListener).onActiveChanged(true); - verify(mView).postDelayed(any(Runnable.class), anyLong()); - mDoubleTapHelper.onTouchEvent(evDownB); - mDoubleTapHelper.onTouchEvent(evUpB); - verify(mDoubleTapLogListener).onDoubleTapLog(false, 0, mDoubleTouchSlop); + mNotificationTapHelper.onTouchEvent(evDownB); + mNotificationTapHelper.onTouchEvent(evUpB); verify(mActivationListener).onActiveChanged(false); verify(mDoubleTapListener, never()).onDoubleTap(); @@ -228,8 +217,7 @@ public class DoubleTapHelperTest extends SysuiTestCase { @Test public void testSlideBack() { - long downtimeA = SystemClock.uptimeMillis(); - long downtimeB = downtimeA + 100; + long downtimeA = 100; MotionEvent evDownA = MotionEvent.obtain(downtimeA, downtimeA, @@ -243,42 +231,24 @@ public class DoubleTapHelperTest extends SysuiTestCase { 1, 1, 0); - MotionEvent evDownB = MotionEvent.obtain(downtimeB, - downtimeB, - MotionEvent.ACTION_DOWN, - 1, - 1, - 0); - MotionEvent evUpB = MotionEvent.obtain(downtimeB, - downtimeB + 1, - MotionEvent.ACTION_UP, - 1, - 1, - 0); when(mSlideBackListener.onSlideBack()).thenReturn(true); - mDoubleTapHelper.onTouchEvent(evDownA); - mDoubleTapHelper.onTouchEvent(evUpA); - verify(mActivationListener, never()).onActiveChanged(true); - verify(mActivationListener, never()).onActiveChanged(false); - verify(mDoubleTapListener, never()).onDoubleTap(); - mDoubleTapHelper.onTouchEvent(evDownB); - mDoubleTapHelper.onTouchEvent(evUpB); + mNotificationTapHelper.onTouchEvent(evDownA); + mNotificationTapHelper.onTouchEvent(evUpA); verify(mActivationListener, never()).onActiveChanged(true); verify(mActivationListener, never()).onActiveChanged(false); verify(mDoubleTapListener, never()).onDoubleTap(); + verify(mSlideBackListener).onSlideBack(); evDownA.recycle(); evUpA.recycle(); - evDownB.recycle(); - evUpB.recycle(); } @Test public void testMoreThanTwoTaps() { - long downtimeA = SystemClock.uptimeMillis(); + long downtimeA = 100; long downtimeB = downtimeA + 100; long downtimeC = downtimeB + 100; long downtimeD = downtimeC + 100; @@ -332,33 +302,26 @@ public class DoubleTapHelperTest extends SysuiTestCase { 1, 0); - mDoubleTapHelper.onTouchEvent(evDownA); - mDoubleTapHelper.onTouchEvent(evUpA); + mNotificationTapHelper.onTouchEvent(evDownA); + mNotificationTapHelper.onTouchEvent(evUpA); verify(mActivationListener).onActiveChanged(true); - verify(mView).postDelayed(any(Runnable.class), anyLong()); - verify(mDoubleTapLogListener, never()).onDoubleTapLog(anyBoolean(), anyFloat(), anyFloat()); verify(mDoubleTapListener, never()).onDoubleTap(); - mDoubleTapHelper.onTouchEvent(evDownB); - mDoubleTapHelper.onTouchEvent(evUpB); - verify(mDoubleTapLogListener).onDoubleTapLog(true, 0, 0); + mNotificationTapHelper.onTouchEvent(evDownB); + mNotificationTapHelper.onTouchEvent(evUpB); verify(mDoubleTapListener).onDoubleTap(); reset(mView); reset(mActivationListener); - reset(mDoubleTapLogListener); reset(mDoubleTapListener); - mDoubleTapHelper.onTouchEvent(evDownC); - mDoubleTapHelper.onTouchEvent(evUpC); + mNotificationTapHelper.onTouchEvent(evDownC); + mNotificationTapHelper.onTouchEvent(evUpC); verify(mActivationListener).onActiveChanged(true); - verify(mView).postDelayed(any(Runnable.class), anyLong()); - verify(mDoubleTapLogListener, never()).onDoubleTapLog(anyBoolean(), anyFloat(), anyFloat()); verify(mDoubleTapListener, never()).onDoubleTap(); - mDoubleTapHelper.onTouchEvent(evDownD); - mDoubleTapHelper.onTouchEvent(evUpD); - verify(mDoubleTapLogListener).onDoubleTapLog(true, 0, 0); + mNotificationTapHelper.onTouchEvent(evDownD); + mNotificationTapHelper.onTouchEvent(evUpD); verify(mDoubleTapListener).onDoubleTap(); evDownA.recycle(); @@ -366,4 +329,9 @@ public class DoubleTapHelperTest extends SysuiTestCase { evDownB.recycle(); evUpB.recycle(); } + + private void drainExecutor() { + mFakeExecutor.advanceClockToLast(); + mFakeExecutor.runAllReady(); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index 9832d31f7b05..83030eccedd1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -33,6 +33,7 @@ import android.testing.TestableLooper; import android.view.View; import android.view.ViewGroup; import android.view.ViewPropertyAnimator; +import android.widget.FrameLayout; import androidx.test.filters.SmallTest; @@ -103,6 +104,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { when(mLockIconContainer.getParent()).thenReturn(mock(ViewGroup.class)); when(mLockIconContainer.animate()).thenReturn(mock(ViewPropertyAnimator.class, RETURNS_DEEP_STUBS)); + when(mLockIconContainer.getLayoutParams()).thenReturn(mock(FrameLayout.LayoutParams.class)); when(mKeyguardBouncerFactory.create( any(ViewGroup.class), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index 710122d83b22..5416f75dffff 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -83,6 +83,7 @@ import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.assist.AssistManager; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.demomode.DemoModeController; @@ -356,6 +357,7 @@ public class StatusBarTest extends SysuiTestCase { mDynamicPrivacyController, mBypassHeadsUpNotifier, new FalsingManagerFake(), + new FalsingCollectorFake(), mBroadcastDispatcher, new RemoteInputQuickSettingsDisabler( mContext, diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/time/FakeSystemClock.java b/packages/SystemUI/tests/src/com/android/systemui/util/time/FakeSystemClock.java index ecfb357aa068..d8271e8292be 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/time/FakeSystemClock.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/time/FakeSystemClock.java @@ -28,17 +28,16 @@ import java.util.List; * backwards. uptimeMillis, elapsedRealtime, and currentThreadTimeMillis are all kept in sync. * * Unless otherwise specified, uptimeMillis and elapsedRealtime will advance the same amount with - * every call to {@link #advanceTime(long)}. Thread time always lags by 50% of the uptime + * every call to {@link #advanceTime}. Thread time always lags by 50% of the uptime * advancement to simulate time loss due to scheduling. */ public class FakeSystemClock implements SystemClock { private long mUptimeMillis = 10000; private long mElapsedRealtime = 10000; private long mCurrentThreadTimeMillis = 10000; - private long mCurrentTimeMillis = 1555555500000L; - private final List<ClockTickListener> mListeners = new ArrayList<>(); + @Override public long uptimeMillis() { return mUptimeMillis; @@ -72,21 +71,38 @@ public class FakeSystemClock implements SystemClock { mCurrentTimeMillis = millis; } - public void advanceTime(long uptime) { - advanceTime(uptime, 0); + /** + * Advances the time tracked by the fake clock and notifies any listeners that the time has + * changed (for example, an attached {@link FakeExecutor} may fire its pending runnables). + * + * All tracked times increment by [millis], with the exception of currentThreadTimeMillis, + * which advances by [millis] * 0.5 + */ + public void advanceTime(long millis) { + advanceTime(millis, 0); } - public void advanceTime(long uptime, long sleepTime) { - if (uptime < 0 || sleepTime < 0) { + /** + * Advances the time tracked by the fake clock and notifies any listeners that the time has + * changed (for example, an attached {@link FakeExecutor} may fire its pending runnables). + * + * The tracked times change as follows: + * - uptimeMillis increments by [awakeMillis] + * - currentThreadTimeMillis increments by [awakeMillis] * 0.5 + * - elapsedRealtime increments by [awakeMillis] + [sleepMillis] + * - currentTimeMillis increments by [awakeMillis] + [sleepMillis] + */ + public void advanceTime(long awakeMillis, long sleepMillis) { + if (awakeMillis < 0 || sleepMillis < 0) { throw new IllegalArgumentException("Time cannot go backwards"); } - if (uptime > 0 || sleepTime > 0) { - mUptimeMillis += uptime; - mElapsedRealtime += uptime + sleepTime; - mCurrentTimeMillis += uptime + sleepTime; + if (awakeMillis > 0 || sleepMillis > 0) { + mUptimeMillis += awakeMillis; + mElapsedRealtime += awakeMillis + sleepMillis; + mCurrentTimeMillis += awakeMillis + sleepMillis; - mCurrentThreadTimeMillis += Math.ceil(uptime * 0.5); + mCurrentThreadTimeMillis += Math.ceil(awakeMillis * 0.5); for (ClockTickListener listener : mListeners) { listener.onClockTick(); @@ -105,6 +121,4 @@ public class FakeSystemClock implements SystemClock { public interface ClockTickListener { void onClockTick(); } - - private static final long START_TIME = 10000; } diff --git a/packages/services/PacProcessor/AndroidManifest.xml b/packages/services/PacProcessor/AndroidManifest.xml index ad1326194a4d..533098cbdc75 100644 --- a/packages/services/PacProcessor/AndroidManifest.xml +++ b/packages/services/PacProcessor/AndroidManifest.xml @@ -3,6 +3,7 @@ package="com.android.pacprocessor"> <uses-permission android:name="android.permission.INTERNET" /> + <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <application android:label="@string/app_name" diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto index 15bd4dc66ccc..ae024ff6d043 100644 --- a/proto/src/system_messages.proto +++ b/proto/src/system_messages.proto @@ -332,5 +332,9 @@ message SystemMessage { // Notify the user that the admin suspended personal apps on the device. // Package: android NOTE_PERSONAL_APPS_SUSPENDED = 1003; + + // Notify the user that window magnification is available. + // package: android + NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE = 1004; } } diff --git a/services/Android.bp b/services/Android.bp index eb7b72ed2cfd..1101e2ad4234 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -9,6 +9,7 @@ filegroup { name: "services-all-sources", srcs: [ ":services.core-sources", + ":services.core-sources-am-wm", ":services.accessibility-sources", ":services.appprediction-sources", ":services.appwidget-sources", diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java index 857ac6ae62a9..be7643ecbd4e 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java @@ -37,6 +37,7 @@ import com.android.server.accessibility.gestures.TouchExplorer; import com.android.server.accessibility.magnification.FullScreenMagnificationGestureHandler; import com.android.server.accessibility.magnification.MagnificationGestureHandler; import com.android.server.accessibility.magnification.WindowMagnificationGestureHandler; +import com.android.server.accessibility.magnification.WindowMagnificationPromptController; import com.android.server.policy.WindowManagerPolicy; import java.util.ArrayList; @@ -561,8 +562,9 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo detectControlGestures, triggerable, displayId); } else { magnificationGestureHandler = new FullScreenMagnificationGestureHandler(displayContext, - mAms.getFullScreenMagnificationController(), mAms::onMagnificationScaleChanged, - detectControlGestures, triggerable, displayId); + mAms.getFullScreenMagnificationController(), + mAms::onMagnificationScaleChanged, detectControlGestures, triggerable, + new WindowMagnificationPromptController(displayContext, mUserId), displayId); } return magnificationGestureHandler; } diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java index d50e9d720861..efb9d87a0bfd 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java @@ -137,6 +137,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH @VisibleForTesting final ViewportDraggingState mViewportDraggingState; private final ScreenStateReceiver mScreenStateReceiver; + private final WindowMagnificationPromptController mPromptController; /** * {@code true} if this detector should detect and respond to triple-tap @@ -178,6 +179,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH ScaleChangedListener listener, boolean detectTripleTap, boolean detectShortcutTrigger, + @NonNull WindowMagnificationPromptController promptController, int displayId) { super(listener); if (DEBUG_ALL) { @@ -186,6 +188,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH + ", detectShortcutTrigger = " + detectShortcutTrigger + ")"); } mFullScreenMagnificationController = fullScreenMagnificationController; + mPromptController = promptController; mDisplayId = displayId; mDelegatingState = new DelegatingState(); @@ -195,7 +198,6 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH mDetectTripleTap = detectTripleTap; mDetectShortcutTrigger = detectShortcutTrigger; - if (mDetectShortcutTrigger) { mScreenStateReceiver = new ScreenStateReceiver(context, this); mScreenStateReceiver.register(); @@ -264,6 +266,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH if (mScreenStateReceiver != null) { mScreenStateReceiver.unregister(); } + mPromptController.onDestroy(); // Check if need to reset when MagnificationGestureHandler is the last magnifying service. mFullScreenMagnificationController.resetAllIfNeeded( AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); @@ -278,6 +281,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH if (wasMagnifying) { clearAndTransitionToStateDetecting(); } else { + mPromptController.showNotificationIfNeeded(); mDetectingState.toggleShortcutTriggered(); } } @@ -950,6 +954,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH if (mFullScreenMagnificationController.isMagnifying(mDisplayId)) { zoomOff(); } else { + mPromptController.showNotificationIfNeeded(); zoomOn(up.getX(), up.getY()); } } diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationPromptController.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationPromptController.java new file mode 100644 index 000000000000..721220729a33 --- /dev/null +++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationPromptController.java @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.accessibility.magnification; + +import static android.provider.Settings.Secure.ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT; + +import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_COMPONENT_NAME; +import static com.android.internal.messages.nano.SystemMessageProto.SystemMessage.NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE; + +import android.Manifest; +import android.annotation.MainThread; +import android.annotation.NonNull; +import android.app.ActivityOptions; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.StatusBarManager; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.database.ContentObserver; +import android.graphics.drawable.Icon; +import android.net.Uri; +import android.os.Bundle; +import android.os.UserHandle; +import android.provider.Settings; +import android.text.TextUtils; + +import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.notification.SystemNotificationChannels; + +/** + * A class to show notification to prompt the user that this feature is available. + */ +public class WindowMagnificationPromptController { + + private static final Uri MAGNIFICATION_WINDOW_MODE_PROMPT_URI = Settings.Secure.getUriFor( + ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT); + @VisibleForTesting + static final String ACTION_DISMISS = + "com.android.server.accessibility.magnification.action.DISMISS"; + @VisibleForTesting + static final String ACTION_TURN_ON_IN_SETTINGS = + "com.android.server.accessibility.magnification.action.TURN_ON_IN_SETTINGS"; + private final Context mContext; + private final NotificationManager mNotificationManager; + private final ContentObserver mContentObserver; + private final int mUserId; + @VisibleForTesting + BroadcastReceiver mNotificationActionReceiver; + + private boolean mNeedToShowNotification; + + @MainThread + public WindowMagnificationPromptController(@NonNull Context context, int userId) { + mContext = context; + mNotificationManager = context.getSystemService(NotificationManager.class); + mUserId = userId; + mContentObserver = new ContentObserver(null) { + @Override + public void onChange(boolean selfChange) { + super.onChange(selfChange); + onPromptSettingsValueChanged(); + } + }; + context.getContentResolver().registerContentObserver(MAGNIFICATION_WINDOW_MODE_PROMPT_URI, + false, mContentObserver, mUserId); + mNeedToShowNotification = isWindowMagnificationPromptEnabled(); + } + + @VisibleForTesting + protected void onPromptSettingsValueChanged() { + final boolean needToShowNotification = isWindowMagnificationPromptEnabled(); + if (mNeedToShowNotification == needToShowNotification) { + return; + } + mNeedToShowNotification = needToShowNotification; + if (!mNeedToShowNotification) { + unregisterReceiverIfNeeded(); + mNotificationManager.cancel(NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE); + } + } + + /** + * Shows the prompt notification that could bring users to magnification settings if necessary. + */ + @MainThread + void showNotificationIfNeeded() { + if (!mNeedToShowNotification) return; + + final Notification.Builder notificationBuilder = new Notification.Builder(mContext, + SystemNotificationChannels.ACCESSIBILITY_MAGNIFICATION); + notificationBuilder.setSmallIcon(R.drawable.ic_settings_24dp) + .setContentTitle(mContext.getString(R.string.window_magnification_prompt_title)) + .setContentText(mContext.getString(R.string.window_magnification_prompt_content)) + .setLargeIcon(Icon.createWithResource(mContext, + R.drawable.ic_accessibility_magnification)) + .setTicker(mContext.getString(R.string.window_magnification_prompt_title)) + .setOnlyAlertOnce(true) + .setDeleteIntent(createPendingIntent(ACTION_DISMISS)) + .setContentIntent(createPendingIntent(ACTION_TURN_ON_IN_SETTINGS)) + .setActions(buildTurnOnAction(), buildDismissAction()); + mNotificationManager.notify(NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE, + notificationBuilder.build()); + registerReceiverIfNeeded(); + } + + /** + * Called when this object is not used anymore to release resources if necessary. + */ + @VisibleForTesting + @MainThread + public void onDestroy() { + dismissNotification(); + mContext.getContentResolver().unregisterContentObserver(mContentObserver); + } + + private boolean isWindowMagnificationPromptEnabled() { + return Settings.Secure.getIntForUser(mContext.getContentResolver(), + ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT, 0, mUserId) == 1; + } + + private Notification.Action buildTurnOnAction() { + return new Notification.Action.Builder(null, + mContext.getString(R.string.turn_on_magnification_settings_action), + createPendingIntent(ACTION_TURN_ON_IN_SETTINGS)).build(); + } + + private Notification.Action buildDismissAction() { + return new Notification.Action.Builder(null, mContext.getString(R.string.dismiss_action), + createPendingIntent(ACTION_DISMISS)).build(); + } + + private PendingIntent createPendingIntent(String action) { + final Intent intent = new Intent(action); + intent.setPackage(mContext.getPackageName()); + return PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_IMMUTABLE); + } + + private void registerReceiverIfNeeded() { + if (mNotificationActionReceiver != null) { + return; + } + mNotificationActionReceiver = new NotificationActionReceiver(); + final IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(ACTION_DISMISS); + intentFilter.addAction(ACTION_TURN_ON_IN_SETTINGS); + mContext.registerReceiver(mNotificationActionReceiver, intentFilter, + Manifest.permission.MANAGE_ACCESSIBILITY, null); + } + + private void launchMagnificationSettings() { + final Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_DETAILS_SETTINGS); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + intent.putExtra(Intent.EXTRA_COMPONENT_NAME, + MAGNIFICATION_COMPONENT_NAME.flattenToShortString()); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + final Bundle bundle = ActivityOptions.makeBasic().setLaunchDisplayId( + mContext.getDisplayId()).toBundle(); + mContext.startActivityAsUser(intent, bundle, UserHandle.of(mUserId)); + mContext.getSystemService(StatusBarManager.class).collapsePanels(); + } + + private void dismissNotification() { + unregisterReceiverIfNeeded(); + mNotificationManager.cancel(NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE); + } + + private void unregisterReceiverIfNeeded() { + if (mNotificationActionReceiver == null) { + return; + } + mContext.unregisterReceiver(mNotificationActionReceiver); + mNotificationActionReceiver = null; + } + + private class NotificationActionReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + if (TextUtils.isEmpty(action)) return; + + mNeedToShowNotification = false; + Settings.Secure.putIntForUser(mContext.getContentResolver(), + ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT, 0, mUserId); + + if (ACTION_TURN_ON_IN_SETTINGS.equals(action)) { + launchMagnificationSettings(); + dismissNotification(); + } else if (ACTION_DISMISS.equals(action)) { + dismissNotification(); + } + } + } +} diff --git a/services/companion/java/com/android/server/companion/OWNERS b/services/companion/java/com/android/server/companion/OWNERS new file mode 100644 index 000000000000..da723b3b67da --- /dev/null +++ b/services/companion/java/com/android/server/companion/OWNERS @@ -0,0 +1 @@ +eugenesusla@google.com
\ No newline at end of file diff --git a/services/core/Android.bp b/services/core/Android.bp index 3a137263d182..fec7ac0dd5bd 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -1,7 +1,20 @@ filegroup { + name: "services.core-sources-am-wm", + srcs: [ + "java/com/android/server/am/**/*.java", + "java/com/android/server/wm/**/*.java", + ], + path: "java", + visibility: ["//frameworks/base/services"], +} + +filegroup { name: "services.core-sources", srcs: ["java/**/*.java"], - exclude_srcs: [":connectivity-service-srcs"], + exclude_srcs: [ + ":connectivity-service-srcs", + ":services.core-sources-am-wm" + ], path: "java", visibility: [ "//frameworks/base/services", @@ -13,7 +26,7 @@ genrule { name: "services.core.protologsrc", srcs: [ ":protolog-groups", - ":services.core-sources", + ":services.core-sources-am-wm", ], tools: ["protologtool"], cmd: "$(location protologtool) transform-protolog-calls " + @@ -23,7 +36,7 @@ genrule { "--loggroups-class com.android.internal.protolog.ProtoLogGroup " + "--loggroups-jar $(location :protolog-groups) " + "--output-srcjar $(out) " + - "$(locations :services.core-sources)", + "$(locations :services.core-sources-am-wm)", out: ["services.core.protolog.srcjar"], } @@ -31,7 +44,7 @@ genrule { name: "generate-protolog.json", srcs: [ ":protolog-groups", - ":services.core-sources", + ":services.core-sources-am-wm", ], tools: ["protologtool"], cmd: "$(location protologtool) generate-viewer-config " + @@ -39,7 +52,7 @@ genrule { "--loggroups-class com.android.internal.protolog.ProtoLogGroup " + "--loggroups-jar $(location :protolog-groups) " + "--viewer-conf $(out) " + - "$(locations :services.core-sources)", + "$(locations :services.core-sources-am-wm)", out: ["services.core.protolog.json"], } @@ -63,6 +76,7 @@ java_library_static { name: "services.core.unboosted", defaults: ["platform_service_defaults"], srcs: [ + ":services.core-sources", ":services.core.protologsrc", ":dumpstate_aidl", ":framework_native_aidl", @@ -75,6 +89,7 @@ java_library_static { ":platform-compat-config", ":display-device-config", ":cec-config", + ":device-state-config", "java/com/android/server/EventLogTags.logtags", "java/com/android/server/am/EventLogTags.logtags", "java/com/android/server/wm/EventLogTags.logtags", diff --git a/services/core/java/android/app/usage/UsageStatsManagerInternal.java b/services/core/java/android/app/usage/UsageStatsManagerInternal.java index fa84427ac064..b2226d1e0fa3 100644 --- a/services/core/java/android/app/usage/UsageStatsManagerInternal.java +++ b/services/core/java/android/app/usage/UsageStatsManagerInternal.java @@ -326,4 +326,18 @@ public abstract class UsageStatsManagerInternal { * @return {@code true} if the updating was successful, {@code false} otherwise */ public abstract boolean updatePackageMappingsData(); + + /** + * Listener interface for usage events. + */ + public interface UsageEventListener { + /** Callback to inform listeners of a new usage event. */ + void onUsageEvent(@UserIdInt int userId, @NonNull UsageEvents.Event event); + } + + /** Register a listener that will be notified of every new usage event. */ + public abstract void registerListener(@NonNull UsageEventListener listener); + + /** Unregister a listener from being notified of every new usage event. */ + public abstract void unregisterListener(@NonNull UsageEventListener listener); } diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java index f20566280bc8..6989e320f465 100644 --- a/services/core/java/android/content/pm/PackageManagerInternal.java +++ b/services/core/java/android/content/pm/PackageManagerInternal.java @@ -77,6 +77,7 @@ public abstract class PackageManagerInternal { PACKAGE_WIFI, PACKAGE_COMPANION, PACKAGE_RETAIL_DEMO, + PACKAGE_RECENTS, }) @Retention(RetentionPolicy.SOURCE) public @interface KnownPackage {} @@ -97,9 +98,10 @@ public abstract class PackageManagerInternal { public static final int PACKAGE_WIFI = 13; public static final int PACKAGE_COMPANION = 14; public static final int PACKAGE_RETAIL_DEMO = 15; + public static final int PACKAGE_RECENTS = 16; // Integer value of the last known package ID. Increases as new ID is added to KnownPackage. // Please note the numbers should be continuous. - public static final int LAST_KNOWN_PACKAGE = PACKAGE_RETAIL_DEMO; + public static final int LAST_KNOWN_PACKAGE = PACKAGE_RECENTS; @IntDef(flag = true, prefix = "RESOLVE_", value = { RESOLVE_NON_BROWSER_ONLY, @@ -1060,6 +1062,8 @@ public abstract class PackageManagerInternal { return "Retail Demo"; case PACKAGE_OVERLAY_CONFIG_SIGNATURE: return "Overlay Config Signature"; + case PACKAGE_RECENTS: + return "Recents"; } return "Unknown"; } diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java index 9455051ad740..bb4bbd5bc6d4 100644 --- a/services/core/java/com/android/server/BatteryService.java +++ b/services/core/java/com/android/server/BatteryService.java @@ -437,6 +437,10 @@ public final class BatteryService extends SystemService { info.legacy.legacy.batteryChargeCounter); Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryCurrent", info.legacy.legacy.batteryCurrent); + Trace.traceCounter(Trace.TRACE_TAG_POWER, "PlugType", + plugType(info.legacy.legacy)); + Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryStatus", + info.legacy.legacy.batteryStatus); synchronized (mLock) { if (!mUpdatesStopped) { @@ -471,6 +475,18 @@ public final class BatteryService extends SystemService { dst.batteryTechnology = src.batteryTechnology; } + private static int plugType(HealthInfo healthInfo) { + if (healthInfo.chargerAcOnline) { + return BatteryManager.BATTERY_PLUGGED_AC; + } else if (healthInfo.chargerUsbOnline) { + return BatteryManager.BATTERY_PLUGGED_USB; + } else if (healthInfo.chargerWirelessOnline) { + return BatteryManager.BATTERY_PLUGGED_WIRELESS; + } else { + return BATTERY_PLUGGED_NONE; + } + } + private void processValuesLocked(boolean force) { boolean logOutlier = false; long dischargeDuration = 0; @@ -478,15 +494,7 @@ public final class BatteryService extends SystemService { mBatteryLevelCritical = mHealthInfo.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN && mHealthInfo.batteryLevel <= mCriticalBatteryLevel; - if (mHealthInfo.chargerAcOnline) { - mPlugType = BatteryManager.BATTERY_PLUGGED_AC; - } else if (mHealthInfo.chargerUsbOnline) { - mPlugType = BatteryManager.BATTERY_PLUGGED_USB; - } else if (mHealthInfo.chargerWirelessOnline) { - mPlugType = BatteryManager.BATTERY_PLUGGED_WIRELESS; - } else { - mPlugType = BATTERY_PLUGGED_NONE; - } + mPlugType = plugType(mHealthInfo); if (DEBUG) { Slog.d(TAG, "Processing new values: " diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index d586f0017a1e..e8ee18c6f4d4 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -137,7 +137,6 @@ import android.net.netlink.InetDiagMessage; import android.net.shared.PrivateDnsConfig; import android.net.util.MultinetworkPolicyTracker; import android.net.util.NetdService; -import android.os.BasicShellCommandHandler; import android.os.Binder; import android.os.Build; import android.os.Bundle; @@ -190,6 +189,7 @@ import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.LocationPermissionChecker; import com.android.internal.util.MessageUtils; import com.android.internal.util.XmlUtils; +import com.android.modules.utils.BasicShellCommandHandler; import com.android.net.module.util.LinkPropertiesUtils.CompareOrUpdateResult; import com.android.net.module.util.LinkPropertiesUtils.CompareResult; import com.android.server.am.BatteryStatsService; @@ -2697,10 +2697,16 @@ public class ConnectivityService extends IConnectivityManager.Stub /** * Return an array of all current NetworkRequest sorted by request id. */ - private NetworkRequestInfo[] requestsSortedById() { + @VisibleForTesting + protected NetworkRequestInfo[] requestsSortedById() { NetworkRequestInfo[] requests = new NetworkRequestInfo[0]; requests = mNetworkRequests.values().toArray(requests); - Arrays.sort(requests, Comparator.comparingInt(nri -> nri.request.requestId)); + // Sort the array based off the NRI containing the min requestId in its requests. + Arrays.sort(requests, + Comparator.comparingInt(nri -> Collections.min(nri.mRequests, + Comparator.comparingInt(req -> req.requestId)).requestId + ) + ); return requests; } @@ -2765,6 +2771,7 @@ public class ConnectivityService extends IConnectivityManager.Stub networkCapabilities = new NetworkCapabilities(networkCapabilities); networkCapabilities.restrictCapabilitesForTestNetwork(nai.creatorUid); } + processCapabilitiesFromAgent(nai, networkCapabilities); updateCapabilities(nai.getCurrentScore(), nai, networkCapabilities); break; } @@ -2803,6 +2810,31 @@ public class ConnectivityService extends IConnectivityManager.Stub mKeepaliveTracker.handleEventSocketKeepalive(nai, msg); break; } + case NetworkAgent.EVENT_UNDERLYING_NETWORKS_CHANGED: { + if (!nai.supportsUnderlyingNetworks()) { + Log.wtf(TAG, "Non-virtual networks cannot have underlying networks"); + break; + } + final ArrayList<Network> underlying; + try { + underlying = ((Bundle) msg.obj).getParcelableArrayList( + NetworkAgent.UNDERLYING_NETWORKS_KEY); + } catch (NullPointerException | ClassCastException e) { + break; + } + final Network[] oldUnderlying = nai.declaredUnderlyingNetworks; + nai.declaredUnderlyingNetworks = (underlying != null) + ? underlying.toArray(new Network[0]) : null; + + if (!Arrays.equals(oldUnderlying, nai.declaredUnderlyingNetworks)) { + if (DBG) { + log(nai.toShortString() + " changed underlying networks to " + + Arrays.toString(nai.declaredUnderlyingNetworks)); + } + updateCapabilities(nai.getCurrentScore(), nai, nai.networkCapabilities); + notifyIfacesChangedForNetworkStats(); + } + } } } @@ -3388,7 +3420,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } mLegacyTypeTracker.remove(nai, wasDefault); if (!nai.networkCapabilities.hasTransport(TRANSPORT_VPN)) { - updateAllVpnsCapabilities(); + propagateUnderlyingNetworkCapabilities(); } rematchAllNetworksAndRequests(); mLingerMonitor.noteDisconnect(nai); @@ -4698,10 +4730,9 @@ public class ConnectivityService extends IConnectivityManager.Stub if (mLockdownEnabled) { return new VpnInfo[0]; } - List<VpnInfo> infoList = new ArrayList<>(); - for (int i = 0; i < mVpns.size(); i++) { - VpnInfo info = createVpnInfo(mVpns.valueAt(i)); + for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) { + VpnInfo info = createVpnInfo(nai); if (info != null) { infoList.add(info); } @@ -4714,13 +4745,10 @@ public class ConnectivityService extends IConnectivityManager.Stub * @return VPN information for accounting, or null if we can't retrieve all required * information, e.g underlying ifaces. */ - @Nullable - private VpnInfo createVpnInfo(Vpn vpn) { - VpnInfo info = vpn.getVpnInfo(); - if (info == null) { - return null; - } - Network[] underlyingNetworks = vpn.getUnderlyingNetworks(); + private VpnInfo createVpnInfo(NetworkAgentInfo nai) { + if (!nai.isVPN()) return null; + + Network[] underlyingNetworks = nai.declaredUnderlyingNetworks; // see VpnService.setUnderlyingNetworks()'s javadoc about how to interpret // the underlyingNetworks list. if (underlyingNetworks == null) { @@ -4729,23 +4757,33 @@ public class ConnectivityService extends IConnectivityManager.Stub underlyingNetworks = new Network[] { defaultNai.network }; } } - if (underlyingNetworks != null && underlyingNetworks.length > 0) { - List<String> interfaces = new ArrayList<>(); - for (Network network : underlyingNetworks) { - LinkProperties lp = getLinkProperties(network); - if (lp != null) { - for (String iface : lp.getAllInterfaceNames()) { - if (!TextUtils.isEmpty(iface)) { - interfaces.add(iface); - } - } + + if (ArrayUtils.isEmpty(underlyingNetworks)) return null; + + List<String> interfaces = new ArrayList<>(); + for (Network network : underlyingNetworks) { + NetworkAgentInfo underlyingNai = getNetworkAgentInfoForNetwork(network); + if (underlyingNai == null) continue; + LinkProperties lp = underlyingNai.linkProperties; + for (String iface : lp.getAllInterfaceNames()) { + if (!TextUtils.isEmpty(iface)) { + interfaces.add(iface); } } - if (!interfaces.isEmpty()) { - info.underlyingIfaces = interfaces.toArray(new String[interfaces.size()]); - } } - return info.underlyingIfaces == null ? null : info; + + if (interfaces.isEmpty()) return null; + + VpnInfo info = new VpnInfo(); + info.ownerUid = nai.networkCapabilities.getOwnerUid(); + info.vpnIface = nai.linkProperties.getInterfaceName(); + // Must be non-null or NetworkStatsService will crash. + // Cannot happen in production code because Vpn only registers the NetworkAgent after the + // tun or ipsec interface is created. + if (info.vpnIface == null) return null; + info.underlyingIfaces = interfaces.toArray(new String[0]); + + return info; } /** @@ -4768,33 +4806,19 @@ public class ConnectivityService extends IConnectivityManager.Stub } /** - * Ask all VPN objects to recompute and update their capabilities. + * Ask all networks with underlying networks to recompute and update their capabilities. * - * When underlying networks change, VPNs may have to update capabilities to reflect things - * like the metered bit, their transports, and so on. This asks the VPN objects to update - * their capabilities, and as this will cause them to send messages to the ConnectivityService - * handler thread through their agent, this is asynchronous. When the capabilities objects - * are computed they will be up-to-date as they are computed synchronously from here and - * this is running on the ConnectivityService thread. + * When underlying networks change, such networks may have to update capabilities to reflect + * things like the metered bit, their transports, and so on. The capabilities are calculated + * immediately. This method runs on the ConnectivityService thread. */ - private void updateAllVpnsCapabilities() { - Network defaultNetwork = getNetwork(getDefaultNetwork()); - synchronized (mVpns) { - for (int i = 0; i < mVpns.size(); i++) { - final Vpn vpn = mVpns.valueAt(i); - NetworkCapabilities nc = vpn.updateCapabilities(defaultNetwork); - updateVpnCapabilities(vpn, nc); - } - } - } - - private void updateVpnCapabilities(Vpn vpn, @Nullable NetworkCapabilities nc) { + private void propagateUnderlyingNetworkCapabilities() { ensureRunningOnConnectivityServiceThread(); - NetworkAgentInfo vpnNai = getNetworkAgentInfoForNetId(vpn.getNetId()); - if (vpnNai == null || nc == null) { - return; + for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) { + if (nai.supportsUnderlyingNetworks()) { + updateCapabilities(nai.getCurrentScore(), nai, nai.networkCapabilities); + } } - updateCapabilities(vpnNai.getCurrentScore(), vpnNai, nc); } @Override @@ -5107,7 +5131,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - private void onUserStart(int userId) { + private void onUserStarted(int userId) { synchronized (mVpns) { Vpn userVpn = mVpns.get(userId); if (userVpn != null) { @@ -5122,7 +5146,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - private void onUserStop(int userId) { + private void onUserStopped(int userId) { synchronized (mVpns) { Vpn userVpn = mVpns.get(userId); if (userVpn == null) { @@ -5136,28 +5160,22 @@ public class ConnectivityService extends IConnectivityManager.Stub private void onUserAdded(int userId) { mPermissionMonitor.onUserAdded(userId); - Network defaultNetwork = getNetwork(getDefaultNetwork()); synchronized (mVpns) { final int vpnsSize = mVpns.size(); for (int i = 0; i < vpnsSize; i++) { Vpn vpn = mVpns.valueAt(i); vpn.onUserAdded(userId); - NetworkCapabilities nc = vpn.updateCapabilities(defaultNetwork); - updateVpnCapabilities(vpn, nc); } } } private void onUserRemoved(int userId) { mPermissionMonitor.onUserRemoved(userId); - Network defaultNetwork = getNetwork(getDefaultNetwork()); synchronized (mVpns) { final int vpnsSize = mVpns.size(); for (int i = 0; i < vpnsSize; i++) { Vpn vpn = mVpns.valueAt(i); vpn.onUserRemoved(userId); - NetworkCapabilities nc = vpn.updateCapabilities(defaultNetwork); - updateVpnCapabilities(vpn, nc); } } } @@ -5239,9 +5257,9 @@ public class ConnectivityService extends IConnectivityManager.Stub if (userId == UserHandle.USER_NULL) return; if (Intent.ACTION_USER_STARTED.equals(action)) { - onUserStart(userId); + onUserStarted(userId); } else if (Intent.ACTION_USER_STOPPED.equals(action)) { - onUserStop(userId); + onUserStopped(userId); } else if (Intent.ACTION_USER_ADDED.equals(action)) { onUserAdded(userId); } else if (Intent.ACTION_USER_REMOVED.equals(action)) { @@ -5350,6 +5368,12 @@ public class ConnectivityService extends IConnectivityManager.Stub } } + private void ensureAllNetworkRequestsHaveType(List<NetworkRequest> requests) { + for (int i = 0; i < requests.size(); i++) { + ensureNetworkRequestHasType(requests.get(i)); + } + } + private void ensureNetworkRequestHasType(NetworkRequest request) { if (request.type == NetworkRequest.Type.NONE) { throw new IllegalArgumentException( @@ -5361,7 +5385,8 @@ public class ConnectivityService extends IConnectivityManager.Stub * Tracks info about the requester. * Also used to notice when the calling process dies so we can self-expire */ - private class NetworkRequestInfo implements IBinder.DeathRecipient { + @VisibleForTesting + protected class NetworkRequestInfo implements IBinder.DeathRecipient { final List<NetworkRequest> mRequests; final NetworkRequest request; @@ -5380,7 +5405,7 @@ public class ConnectivityService extends IConnectivityManager.Stub NetworkRequestInfo(NetworkRequest r, PendingIntent pi) { request = r; mRequests = initializeRequests(r); - ensureNetworkRequestHasType(request); + ensureAllNetworkRequestsHaveType(mRequests); mPendingIntent = pi; messenger = null; mBinder = null; @@ -5394,7 +5419,7 @@ public class ConnectivityService extends IConnectivityManager.Stub messenger = m; request = r; mRequests = initializeRequests(r); - ensureNetworkRequestHasType(request); + ensureAllNetworkRequestsHaveType(mRequests); mBinder = binder; mPid = getCallingPid(); mUid = getCallingUid(); @@ -5418,6 +5443,19 @@ public class ConnectivityService extends IConnectivityManager.Stub return Collections.unmodifiableList(tempRequests); } + private NetworkRequest getSatisfiedRequest() { + if (mSatisfier == null) { + return null; + } + + for (NetworkRequest req : mRequests) { + if (mSatisfier.isSatisfyingRequest(req.requestId)) { + return req; + } + } + + return null; + } private void enforceRequestCountLimit() { synchronized (mUidToNetworkRequestCount) { @@ -5436,14 +5474,16 @@ public class ConnectivityService extends IConnectivityManager.Stub } } + @Override public void binderDied() { log("ConnectivityService NetworkRequestInfo binderDied(" + - request + ", " + mBinder + ")"); - releaseNetworkRequest(request); + mRequests + ", " + mBinder + ")"); + releaseNetworkRequest(mRequests); } + @Override public String toString() { - return "uid/pid:" + mUid + "/" + mPid + " " + request + return "uid/pid:" + mUid + "/" + mPid + " " + mRequests + (mPendingIntent == null ? "" : " to trigger " + mPendingIntent); } } @@ -5763,6 +5803,12 @@ public class ConnectivityService extends IConnectivityManager.Stub return mNextNetworkProviderId.getAndIncrement(); } + private void releaseNetworkRequest(List<NetworkRequest> networkRequests) { + for (int i = 0; i < networkRequests.size(); i++) { + releaseNetworkRequest(networkRequests.get(i)); + } + } + @Override public void releaseNetworkRequest(NetworkRequest networkRequest) { ensureNetworkRequestHasType(networkRequest); @@ -5854,7 +5900,6 @@ public class ConnectivityService extends IConnectivityManager.Stub @GuardedBy("mBlockedAppUids") private final HashSet<Integer> mBlockedAppUids = new HashSet<>(); - // Note: if mDefaultRequest is changed, NetworkMonitor needs to be updated. @NonNull private final NetworkRequest mDefaultRequest; // The NetworkAgentInfo currently satisfying the default request, if any. @@ -5926,13 +5971,29 @@ public class ConnectivityService extends IConnectivityManager.Stub int currentScore, NetworkAgentConfig networkAgentConfig, int providerId) { if (networkCapabilities.hasTransport(TRANSPORT_TEST)) { enforceAnyPermissionOf(Manifest.permission.MANAGE_TEST_NETWORKS); + } else { + enforceNetworkFactoryPermission(); + } + + final int uid = Binder.getCallingUid(); + final long token = Binder.clearCallingIdentity(); + try { + return registerNetworkAgentInternal(messenger, networkInfo, linkProperties, + networkCapabilities, currentScore, networkAgentConfig, providerId, uid); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + private Network registerNetworkAgentInternal(Messenger messenger, NetworkInfo networkInfo, + LinkProperties linkProperties, NetworkCapabilities networkCapabilities, + int currentScore, NetworkAgentConfig networkAgentConfig, int providerId, int uid) { + if (networkCapabilities.hasTransport(TRANSPORT_TEST)) { // Strictly, sanitizing here is unnecessary as the capabilities will be sanitized in // the call to mixInCapabilities below anyway, but sanitizing here means the NAI never // sees capabilities that may be malicious, which might prevent mistakes in the future. networkCapabilities = new NetworkCapabilities(networkCapabilities); - networkCapabilities.restrictCapabilitesForTestNetwork(Binder.getCallingUid()); - } else { - enforceNetworkFactoryPermission(); + networkCapabilities.restrictCapabilitesForTestNetwork(uid); } LinkProperties lp = new LinkProperties(linkProperties); @@ -5943,9 +6004,10 @@ public class ConnectivityService extends IConnectivityManager.Stub final NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(), new Network(mNetIdManager.reserveNetId()), new NetworkInfo(networkInfo), lp, nc, currentScore, mContext, mTrackerHandler, new NetworkAgentConfig(networkAgentConfig), - this, mNetd, mDnsResolver, mNMS, providerId, Binder.getCallingUid()); + this, mNetd, mDnsResolver, mNMS, providerId, uid); // Make sure the LinkProperties and NetworkCapabilities reflect what the agent info says. + processCapabilitiesFromAgent(nai, nc); nai.getAndSetNetworkCapabilities(mixInCapabilities(nai, nc)); processLinkPropertiesFromAgent(nai, nai.linkProperties); @@ -5953,13 +6015,8 @@ public class ConnectivityService extends IConnectivityManager.Stub final String name = TextUtils.isEmpty(extraInfo) ? nai.networkCapabilities.getSsid() : extraInfo; if (DBG) log("registerNetworkAgent " + nai); - final long token = Binder.clearCallingIdentity(); - try { - mDeps.getNetworkStack().makeNetworkMonitor( - nai.network, name, new NetworkMonitorCallbacks(nai)); - } finally { - Binder.restoreCallingIdentity(token); - } + mDeps.getNetworkStack().makeNetworkMonitor( + nai.network, name, new NetworkMonitorCallbacks(nai)); // NetworkAgentInfo registration will finish when the NetworkMonitor is created. // If the network disconnects or sends any other event before that, messages are deferred by // NetworkAgent until nai.asyncChannel.connect(), which will be called when finalizing the @@ -5986,6 +6043,12 @@ public class ConnectivityService extends IConnectivityManager.Stub updateUids(nai, null, nai.networkCapabilities); } + /** + * Called when receiving LinkProperties directly from a NetworkAgent. + * Stores into |nai| any data coming from the agent that might also be written to the network's + * LinkProperties by ConnectivityService itself. This ensures that the data provided by the + * agent is not lost when updateLinkProperties is called. + */ private void processLinkPropertiesFromAgent(NetworkAgentInfo nai, LinkProperties lp) { lp.ensureDirectlyConnectedRoutes(); nai.clatd.setNat64PrefixFromRa(lp.getNat64Prefix()); @@ -6282,6 +6345,30 @@ public class ConnectivityService extends IConnectivityManager.Stub } /** + * Called when receiving NetworkCapabilities directly from a NetworkAgent. + * Stores into |nai| any data coming from the agent that might also be written to the network's + * NetworkCapabilities by ConnectivityService itself. This ensures that the data provided by the + * agent is not lost when updateCapabilities is called. + */ + private void processCapabilitiesFromAgent(NetworkAgentInfo nai, NetworkCapabilities nc) { + nai.declaredMetered = !nc.hasCapability(NET_CAPABILITY_NOT_METERED); + } + + /** Propagates to |nc| the capabilities declared by the underlying networks of |nai|. */ + private void mixInUnderlyingCapabilities(NetworkAgentInfo nai, NetworkCapabilities nc) { + Network[] underlyingNetworks = nai.declaredUnderlyingNetworks; + Network defaultNetwork = getNetwork(getDefaultNetwork()); + if (underlyingNetworks == null && defaultNetwork != null) { + // null underlying networks means to track the default. + underlyingNetworks = new Network[] { defaultNetwork }; + } + + // TODO(b/124469351): Get capabilities directly from ConnectivityService instead. + final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class); + Vpn.applyUnderlyingCapabilities(cm, underlyingNetworks, nc, nai.declaredMetered); + } + + /** * Augments the NetworkCapabilities passed in by a NetworkAgent with capabilities that are * maintained here that the NetworkAgent is not aware of (e.g., validated, captive portal, * and foreground status). @@ -6334,6 +6421,10 @@ public class ConnectivityService extends IConnectivityManager.Stub newNc.addCapability(NET_CAPABILITY_NOT_ROAMING); } + if (nai.supportsUnderlyingNetworks()) { + mixInUnderlyingCapabilities(nai, newNc); + } + return newNc; } @@ -6413,7 +6504,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (!newNc.hasTransport(TRANSPORT_VPN)) { // Tell VPNs about updated capabilities, since they may need to // bubble those changes through. - updateAllVpnsCapabilities(); + propagateUnderlyingNetworkCapabilities(); } if (!newNc.equalsTransportTypes(prevNc)) { @@ -6733,7 +6824,7 @@ public class ConnectivityService extends IConnectivityManager.Stub ? newNetwork.linkProperties.getTcpBufferSizes() : null); notifyIfacesChangedForNetworkStats(); // Fix up the NetworkCapabilities of any VPNs that don't specify underlying networks. - updateAllVpnsCapabilities(); + propagateUnderlyingNetworkCapabilities(); } private void processListenRequests(@NonNull final NetworkAgentInfo nai) { @@ -6780,7 +6871,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } public String toString() { - return mRequest.request.requestId + " : " + return mRequest.mRequests.get(0).requestId + " : " + (null != mOldNetwork ? mOldNetwork.network.netId : "null") + " → " + (null != mNewNetwork ? mNewNetwork.network.netId : "null"); } @@ -7195,7 +7286,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // onCapabilitiesUpdated being sent in updateAllVpnCapabilities below as // the VPN would switch from its default, blank capabilities to those // that reflect the capabilities of its underlying networks. - updateAllVpnsCapabilities(); + propagateUnderlyingNetworkCapabilities(); } networkAgent.created = true; } @@ -7237,8 +7328,8 @@ public class ConnectivityService extends IConnectivityManager.Stub // doing. updateSignalStrengthThresholds(networkAgent, "CONNECT", null); - if (networkAgent.isVPN()) { - updateAllVpnsCapabilities(); + if (networkAgent.supportsUnderlyingNetworks()) { + propagateUnderlyingNetworkCapabilities(); } // Consider network even though it is not yet validated. @@ -7495,13 +7586,6 @@ public class ConnectivityService extends IConnectivityManager.Stub throwIfLockdownEnabled(); success = mVpns.get(user).setUnderlyingNetworks(networks); } - if (success) { - mHandler.post(() -> { - // Update VPN's capabilities based on updated underlying network set. - updateAllVpnsCapabilities(); - notifyIfacesChangedForNetworkStats(); - }); - } return success; } @@ -8177,13 +8261,12 @@ public class ConnectivityService extends IConnectivityManager.Stub return false; } - final Network[] underlyingNetworks; - synchronized (mVpns) { - final Vpn vpn = getVpnIfOwner(callbackUid); - underlyingNetworks = (vpn == null) ? null : vpn.getUnderlyingNetworks(); - } - if (underlyingNetworks != null) { - if (Arrays.asList(underlyingNetworks).contains(nai.network)) return true; + for (NetworkAgentInfo virtual : mNetworkAgentInfos.values()) { + if (virtual.supportsUnderlyingNetworks() + && virtual.networkCapabilities.getOwnerUid() == callbackUid + && ArrayUtils.contains(virtual.declaredUnderlyingNetworks, nai.network)) { + return true; + } } // Administrator UIDs also contains the Owner UID diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java index d4e912b65e17..8ed23f900d73 100644 --- a/services/core/java/com/android/server/GestureLauncherService.java +++ b/services/core/java/com/android/server/GestureLauncherService.java @@ -44,6 +44,9 @@ import android.view.KeyEvent; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.UiEvent; +import com.android.internal.logging.UiEventLogger; +import com.android.internal.logging.UiEventLoggerImpl; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.wm.WindowManagerInternal; @@ -145,16 +148,44 @@ public class GestureLauncherService extends SystemService { private long mLastPowerDown; private int mPowerButtonConsecutiveTaps; private int mPowerButtonSlowConsecutiveTaps; + private final UiEventLogger mUiEventLogger; + @VisibleForTesting + public enum GestureLauncherEvent implements UiEventLogger.UiEventEnum { + @UiEvent(doc = "The user lifted the device just the right way to launch the camera.") + GESTURE_CAMERA_LIFT(658), + + @UiEvent(doc = "The user wiggled the device just the right way to launch the camera.") + GESTURE_CAMERA_WIGGLE(659), + + @UiEvent(doc = "The user double-tapped power quickly enough to launch the camera.") + GESTURE_CAMERA_DOUBLE_TAP_POWER(660), + + @UiEvent(doc = "The user multi-tapped power quickly enough to signal an emergency.") + GESTURE_PANIC_TAP_POWER(661); + + private final int mId; + + GestureLauncherEvent(int id) { + mId = id; + } + + @Override + public int getId() { + return mId; + } + } public GestureLauncherService(Context context) { - this(context, new MetricsLogger()); + this(context, new MetricsLogger(), new UiEventLoggerImpl()); } @VisibleForTesting - GestureLauncherService(Context context, MetricsLogger metricsLogger) { + GestureLauncherService(Context context, MetricsLogger metricsLogger, + UiEventLogger uiEventLogger) { super(context); mContext = context; mMetricsLogger = metricsLogger; + mUiEventLogger = uiEventLogger; } @Override @@ -460,11 +491,12 @@ public class GestureLauncherService extends SystemService { if (launchCamera) { mMetricsLogger.action(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE, (int) powerTapInterval); + mUiEventLogger.log(GestureLauncherEvent.GESTURE_CAMERA_DOUBLE_TAP_POWER); } } else if (launchEmergencyGesture) { Slog.i(TAG, "Emergency gesture detected, launching."); launchEmergencyGesture = handleEmergencyGesture(); - // TODO(b/160006048): Add logging + mUiEventLogger.log(GestureLauncherEvent.GESTURE_PANIC_TAP_POWER); } mMetricsLogger.histogram("power_consecutive_short_tap_count", mPowerButtonSlowConsecutiveTaps); @@ -587,6 +619,7 @@ public class GestureLauncherService extends SystemService { if (handleCameraGesture(true /* useWakelock */, StatusBarManager.CAMERA_LAUNCH_SOURCE_WIGGLE)) { mMetricsLogger.action(MetricsEvent.ACTION_WIGGLE_CAMERA_GESTURE); + mUiEventLogger.log(GestureLauncherEvent.GESTURE_CAMERA_WIGGLE); trackCameraLaunchEvent(event); } return; @@ -671,6 +704,7 @@ public class GestureLauncherService extends SystemService { if (handleCameraGesture(true /* useWakelock */, StatusBarManager.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER)) { MetricsLogger.action(mContext, MetricsEvent.ACTION_CAMERA_LIFT_TRIGGER); + mUiEventLogger.log(GestureLauncherEvent.GESTURE_CAMERA_LIFT); } } else { if (DBG_CAMERA_LIFT) Slog.d(TAG, "Ignoring lift event"); diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS index e67b9d8ccd91..8706cdf41df9 100644 --- a/services/core/java/com/android/server/OWNERS +++ b/services/core/java/com/android/server/OWNERS @@ -6,3 +6,6 @@ per-file VibratorManagerService.java, VibratorService.java, DisplayThread.java = # Zram writeback per-file ZramWriteback.java = minchan@google.com, rajekumar@google.com, srnvs@google.com + +# Userspace reboot +per-file UserspaceRebootLogger.java = ioffe@google.com, tomcherry@google.com diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java index 735d248b12e4..f3c5fd8d1064 100644 --- a/services/core/java/com/android/server/PackageWatchdog.java +++ b/services/core/java/com/android/server/PackageWatchdog.java @@ -47,7 +47,6 @@ import android.util.Xml; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.BackgroundThread; -import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.XmlUtils; @@ -64,7 +63,6 @@ import java.io.IOException; import java.io.InputStream; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; @@ -129,9 +127,17 @@ public class PackageWatchdog { @VisibleForTesting static final int DEFAULT_BOOT_LOOP_TRIGGER_COUNT = 5; static final long DEFAULT_BOOT_LOOP_TRIGGER_WINDOW_MS = TimeUnit.MINUTES.toMillis(10); + + // These properties track individual system server boot events, and are reset once the boot + // threshold is met, or the boot loop trigger window is exceeded between boot events. private static final String PROP_RESCUE_BOOT_COUNT = "sys.rescue_boot_count"; private static final String PROP_RESCUE_BOOT_START = "sys.rescue_boot_start"; + // These properties track multiple calls made to observers tracking boot loops. They are reset + // when the de-escalation window is exceeded between boot events. + private static final String PROP_BOOT_MITIGATION_WINDOW_START = "sys.boot_mitigation_start"; + private static final String PROP_BOOT_MITIGATION_COUNT = "sys.boot_mitigation_count"; + private long mNumberOfNativeCrashPollsRemaining; private static final int DB_VERSION = 1; @@ -191,7 +197,6 @@ public class PackageWatchdog { @FunctionalInterface @VisibleForTesting interface SystemClock { - // TODO: Add elapsedRealtime to this interface long uptimeMillis(); } @@ -471,13 +476,14 @@ public class PackageWatchdog { synchronized (mLock) { if (mBootThreshold.incrementAndTest()) { mBootThreshold.reset(); + int mitigationCount = mBootThreshold.getMitigationCount() + 1; PackageHealthObserver currentObserverToNotify = null; int currentObserverImpact = Integer.MAX_VALUE; for (int i = 0; i < mAllObservers.size(); i++) { final ObserverInternal observer = mAllObservers.valueAt(i); PackageHealthObserver registeredObserver = observer.registeredObserver; if (registeredObserver != null) { - int impact = registeredObserver.onBootLoop(); + int impact = registeredObserver.onBootLoop(mitigationCount); if (impact != PackageHealthObserverImpact.USER_IMPACT_NONE && impact < currentObserverImpact) { currentObserverToNotify = registeredObserver; @@ -486,7 +492,8 @@ public class PackageWatchdog { } } if (currentObserverToNotify != null) { - currentObserverToNotify.executeBootLoopMitigation(); + mBootThreshold.setMitigationCount(mitigationCount); + currentObserverToNotify.executeBootLoopMitigation(mitigationCount); } } } @@ -609,15 +616,20 @@ public class PackageWatchdog { /** * Called when the system server has booted several times within a window of time, defined * by {@link #mBootThreshold} + * + * @param mitigationCount the number of times mitigation has been attempted for this + * boot loop (including this time). */ - default @PackageHealthObserverImpact int onBootLoop() { + default @PackageHealthObserverImpact int onBootLoop(int mitigationCount) { return PackageHealthObserverImpact.USER_IMPACT_NONE; } /** * Executes mitigation for {@link #onBootLoop} + * @param mitigationCount the number of times mitigation has been attempted for this + * boot loop (including this time). */ - default boolean executeBootLoopMitigation() { + default boolean executeBootLoopMitigation(int mitigationCount) { return false; } @@ -1577,7 +1589,7 @@ public class PackageWatchdog { /** * Handles the thresholding logic for system server boots. */ - static class BootThreshold { + class BootThreshold { private final int mBootTriggerCount; private final long mTriggerWindow; @@ -1604,18 +1616,44 @@ public class PackageWatchdog { return SystemProperties.getLong(PROP_RESCUE_BOOT_START, 0); } + public int getMitigationCount() { + return SystemProperties.getInt(PROP_BOOT_MITIGATION_COUNT, 0); + } + public void setStart(long start) { - final long now = android.os.SystemClock.elapsedRealtime(); + setPropertyStart(PROP_RESCUE_BOOT_START, start); + } + + public void setMitigationStart(long start) { + setPropertyStart(PROP_BOOT_MITIGATION_WINDOW_START, start); + } + + public long getMitigationStart() { + return SystemProperties.getLong(PROP_BOOT_MITIGATION_WINDOW_START, 0); + } + + public void setMitigationCount(int count) { + SystemProperties.set(PROP_BOOT_MITIGATION_COUNT, Integer.toString(count)); + } + + public void setPropertyStart(String property, long start) { + final long now = mSystemClock.uptimeMillis(); final long newStart = MathUtils.constrain(start, 0, now); - SystemProperties.set(PROP_RESCUE_BOOT_START, Long.toString(newStart)); + SystemProperties.set(property, Long.toString(newStart)); } + /** Increments the boot counter, and returns whether the device is bootlooping. */ public boolean incrementAndTest() { - final long now = android.os.SystemClock.elapsedRealtime(); + final long now = mSystemClock.uptimeMillis(); if (now - getStart() < 0) { Slog.e(TAG, "Window was less than zero. Resetting start to current time."); setStart(now); + setMitigationStart(now); + } + if (now - getMitigationStart() > DEFAULT_DEESCALATION_WINDOW_MS) { + setMitigationCount(0); + setMitigationStart(now); } final long window = now - getStart(); if (window >= mTriggerWindow) { diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java index d04949ac54db..a1cf8162f0e9 100644 --- a/services/core/java/com/android/server/RescueParty.java +++ b/services/core/java/com/android/server/RescueParty.java @@ -16,6 +16,8 @@ package com.android.server; +import static android.provider.DeviceConfig.Properties; + import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo; import android.annotation.NonNull; @@ -29,7 +31,6 @@ import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.os.FileUtils; -import android.os.Process; import android.os.RecoverySystem; import android.os.RemoteCallback; import android.os.SystemClock; @@ -37,10 +38,10 @@ import android.os.SystemProperties; import android.os.UserHandle; import android.provider.DeviceConfig; import android.provider.Settings; +import android.text.TextUtils; import android.util.ArraySet; import android.util.ExceptionUtils; import android.util.Log; -import android.util.MathUtils; import android.util.Slog; import com.android.internal.annotations.GuardedBy; @@ -75,8 +76,6 @@ import java.util.concurrent.TimeUnit; public class RescueParty { @VisibleForTesting static final String PROP_ENABLE_RESCUE = "persist.sys.enable_rescue"; - @VisibleForTesting - static final String PROP_RESCUE_LEVEL = "sys.rescue_level"; static final String PROP_ATTEMPTING_FACTORY_RESET = "sys.attempting_factory_reset"; static final String PROP_MAX_RESCUE_LEVEL_ATTEMPTED = "sys.max_rescue_level_attempted"; @VisibleForTesting @@ -97,6 +96,12 @@ public class RescueParty { static final long DEFAULT_OBSERVING_DURATION_MS = TimeUnit.DAYS.toMillis(2); @VisibleForTesting static final int DEVICE_CONFIG_RESET_MODE = Settings.RESET_MODE_TRUSTED_DEFAULTS; + // The DeviceConfig namespace containing all RescueParty switches. + @VisibleForTesting + static final String NAMESPACE_CONFIGURATION = "configuration"; + @VisibleForTesting + static final String NAMESPACE_TO_PACKAGE_MAPPING_FLAG = + "namespace_to_package_mapping"; private static final String NAME = "rescue-party-observer"; @@ -107,8 +112,6 @@ public class RescueParty { "persist.device_config.configuration.disable_rescue_party"; private static final String PROP_DISABLE_FACTORY_RESET_FLAG = "persist.device_config.configuration.disable_rescue_party_factory_reset"; - // The DeviceConfig namespace containing all RescueParty switches. - private static final String NAMESPACE_CONFIGURATION = "configuration"; private static final int PERSISTENT_MASK = ApplicationInfo.FLAG_PERSISTENT | ApplicationInfo.FLAG_SYSTEM; @@ -168,13 +171,87 @@ public class RescueParty { */ public static void onSettingsProviderPublished(Context context) { handleNativeRescuePartyResets(); - executeRescueLevel(context, /*failedPackage=*/ null); ContentResolver contentResolver = context.getContentResolver(); Settings.Config.registerMonitorCallback(contentResolver, new RemoteCallback(result -> { handleMonitorCallback(context, result); })); } + + /** + * Called when {@code RollbackManager} performs Mainline module rollbacks, + * to avoid rolled back modules consuming flag values only expected to work + * on modules of newer versions. + */ + public static void resetDeviceConfigForPackages(List<String> packageNames) { + if (packageNames == null) { + return; + } + Set<String> namespacesToReset = new ArraySet<String>(); + Iterator<String> it = packageNames.iterator(); + RescuePartyObserver rescuePartyObserver = RescuePartyObserver.getInstanceIfCreated(); + // Get runtime package to namespace mapping if created. + if (rescuePartyObserver != null) { + while (it.hasNext()) { + String packageName = it.next(); + Set<String> runtimeAffectedNamespaces = + rescuePartyObserver.getAffectedNamespaceSet(packageName); + if (runtimeAffectedNamespaces != null) { + namespacesToReset.addAll(runtimeAffectedNamespaces); + } + } + } + // Get preset package to namespace mapping if created. + Set<String> presetAffectedNamespaces = getPresetNamespacesForPackages( + packageNames); + if (presetAffectedNamespaces != null) { + namespacesToReset.addAll(presetAffectedNamespaces); + } + + // Clear flags under the namespaces mapped to these packages. + // Using setProperties since DeviceConfig.resetToDefaults bans the current flag set. + Iterator<String> namespaceIt = namespacesToReset.iterator(); + while (namespaceIt.hasNext()) { + String namespaceToReset = namespaceIt.next(); + Properties properties = new Properties.Builder(namespaceToReset).build(); + try { + DeviceConfig.setProperties(properties); + } catch (DeviceConfig.BadConfigException exception) { + logCriticalInfo(Log.WARN, "namespace " + namespaceToReset + + " is already banned, skip reset."); + } + } + } + + private static Set<String> getPresetNamespacesForPackages(List<String> packageNames) { + Set<String> resultSet = new ArraySet<String>(); + try { + String flagVal = DeviceConfig.getString(NAMESPACE_CONFIGURATION, + NAMESPACE_TO_PACKAGE_MAPPING_FLAG, ""); + String[] mappingEntries = flagVal.split(","); + for (int i = 0; i < mappingEntries.length; i++) { + if (TextUtils.isEmpty(mappingEntries[i])) { + continue; + } + String[] splittedEntry = mappingEntries[i].split(":"); + if (splittedEntry.length != 2) { + throw new RuntimeException("Invalid mapping entry: " + mappingEntries[i]); + } + String namespace = splittedEntry[0]; + String packageName = splittedEntry[1]; + + if (packageNames.contains(packageName)) { + resultSet.add(namespace); + } + } + } catch (Exception e) { + resultSet.clear(); + Slog.e(TAG, "Failed to read preset package to namespaces mapping.", e); + } finally { + return resultSet; + } + } + @VisibleForTesting static long getElapsedRealtime() { return SystemClock.elapsedRealtime(); @@ -260,33 +337,6 @@ public class RescueParty { } } - /** - * Get the next rescue level. This indicates the next level of mitigation that may be taken. - */ - private static int getNextRescueLevel() { - return MathUtils.constrain(SystemProperties.getInt(PROP_RESCUE_LEVEL, LEVEL_NONE) + 1, - LEVEL_NONE, getMaxRescueLevel()); - } - - /** - * Escalate to the next rescue level. After incrementing the level you'll - * probably want to call {@link #executeRescueLevel(Context, String)}. - */ - private static void incrementRescueLevel(int triggerUid) { - final int level = getNextRescueLevel(); - SystemProperties.set(PROP_RESCUE_LEVEL, Integer.toString(level)); - - EventLogTags.writeRescueLevel(level, triggerUid); - logCriticalInfo(Log.WARN, "Incremented rescue level to " - + levelToString(level) + " triggered by UID " + triggerUid); - } - - private static void executeRescueLevel(Context context, @Nullable String failedPackage) { - final int level = SystemProperties.getInt(PROP_RESCUE_LEVEL, LEVEL_NONE); - if (level == LEVEL_NONE) return; - executeRescueLevel(context, failedPackage, level); - } - private static void executeRescueLevel(Context context, @Nullable String failedPackage, int level) { Slog.w(TAG, "Attempting rescue level " + levelToString(level)); @@ -501,6 +551,14 @@ public class RescueParty { } } + /** Gets singleton instance. It returns null if the instance is not created yet.*/ + @Nullable + public static RescuePartyObserver getInstanceIfCreated() { + synchronized (RescuePartyObserver.class) { + return sRescuePartyObserver; + } + } + @VisibleForTesting static void reset() { synchronized (RescuePartyObserver.class) { @@ -561,20 +619,19 @@ public class RescueParty { } @Override - public int onBootLoop() { + public int onBootLoop(int mitigationCount) { if (isDisabled()) { return PackageHealthObserverImpact.USER_IMPACT_NONE; } - return mapRescueLevelToUserImpact(getNextRescueLevel()); + return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount)); } @Override - public boolean executeBootLoopMitigation() { + public boolean executeBootLoopMitigation(int mitigationCount) { if (isDisabled()) { return false; } - incrementRescueLevel(Process.ROOT_UID); - executeRescueLevel(mContext, /*failedPackage=*/ null); + executeRescueLevel(mContext, /*failedPackage=*/ null, getRescueLevel(mitigationCount)); return true; } diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 8033ce787b5f..04a52e049474 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -3213,6 +3213,9 @@ class StorageManagerService extends IStorageManager.Stub try { mVold.unlockUserKey(userId, serialNumber, encodeBytes(token), encodeBytes(secret)); + } catch (ServiceSpecificException sse) { + Slog.d(TAG, "Expected if the user has not unlocked the device.", sse); + return; } catch (Exception e) { Slog.wtf(TAG, e); return; diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java index 5556e48a2018..9d60b8438153 100644 --- a/services/core/java/com/android/server/SystemServiceManager.java +++ b/services/core/java/com/android/server/SystemServiceManager.java @@ -21,6 +21,7 @@ import android.annotation.Nullable; import android.annotation.UserIdInt; import android.content.Context; import android.content.pm.UserInfo; +import android.os.Build; import android.os.Environment; import android.os.SystemClock; import android.os.Trace; @@ -31,6 +32,7 @@ import android.util.Slog; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; +import com.android.internal.os.ClassLoaderFactory; import com.android.internal.util.Preconditions; import com.android.server.SystemService.TargetUser; import com.android.server.am.EventLogTags; @@ -121,7 +123,10 @@ public final class SystemServiceManager implements Dumpable { if (pathClassLoader == null) { // NB: the parent class loader should always be the system server class loader. // Changing it has implications that require discussion with the mainline team. - pathClassLoader = new PathClassLoader(path, this.getClass().getClassLoader()); + pathClassLoader = (PathClassLoader) ClassLoaderFactory.createClassLoader( + path, null /* librarySearchPath */, null /* libraryPermittedPath */, + this.getClass().getClassLoader(), Build.VERSION.SDK_INT, + true /* isNamespaceShared */, null /* classLoaderName */); mLoadedPaths.put(path, pathClassLoader); } final Class<SystemService> serviceClass = loadClassFromLoader(className, pathClassLoader); diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING index ebeec39d6ae6..95af84293377 100644 --- a/services/core/java/com/android/server/TEST_MAPPING +++ b/services/core/java/com/android/server/TEST_MAPPING @@ -29,6 +29,10 @@ { "name": "CtsScopedStorageHostTest", "file_patterns": ["StorageManagerService\\.java"] + }, + { + "name": "CtsScopedStorageDeviceOnlyTest", + "file_patterns": ["StorageManagerService\\.java"] } ] } diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index ba52aee24dc2..eb55512b4d83 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -80,7 +80,6 @@ import android.telephony.emergency.EmergencyNumber; import android.telephony.ims.ImsReasonInfo; import android.text.TextUtils; import android.util.ArrayMap; -import android.util.ArraySet; import android.util.LocalLog; import android.util.Pair; @@ -102,13 +101,10 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Objects; -import java.util.Set; -import java.util.stream.Collectors; /** * Since phone process can be restarted, this class provides a centralized place @@ -148,14 +144,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { int callerUid; int callerPid; - Set<Integer> eventList; + long events; int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; int phoneId = SubscriptionManager.INVALID_SIM_SLOT_INDEX; - boolean matchPhoneStateListenerEvent(int event) { - return (callback != null) && (this.eventList.contains(event)); + boolean matchPhoneStateListenerEvent(long events) { + return (callback != null) && ((events & this.events) != 0); } boolean matchOnSubscriptionsChangedListener() { @@ -183,7 +179,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { + onSubscriptionsChangedListenerCallback + " onOpportunisticSubscriptionsChangedListenererCallback=" + onOpportunisticSubscriptionsChangedListenerCallback + " subId=" + subId - + " phoneId=" + phoneId + " events=" + eventList + "}"; + + " phoneId=" + phoneId + " events=" + Long.toHexString(events) + "}"; } } @@ -314,10 +310,6 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { private List<PhysicalChannelConfig> mPhysicalChannelConfigs; - private boolean mIsDataEnabled = false; - - private int mDataEnabledReason; - /** * Per-phone map of precise data connection state. The key of the map is the pair of transport * type and APN setting. This is the cache to prevent redundant callbacks to the listeners. @@ -327,62 +319,40 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { private List<Map<Pair<Integer, ApnSetting>, PreciseDataConnectionState>> mPreciseDataConnectionStates; - private static final Set<Integer> REQUIRE_PRECISE_PHONE_STATE_PERMISSION; - static { - REQUIRE_PRECISE_PHONE_STATE_PERMISSION = new HashSet<Integer>(); - REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add( - PhoneStateListener.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED); - REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add( - PhoneStateListener.EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED); - REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add( - PhoneStateListener.EVENT_PRECISE_CALL_STATE_CHANGED); - REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add( - PhoneStateListener.EVENT_CALL_DISCONNECT_CAUSE_CHANGED); - REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add( - PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED); - REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add( - PhoneStateListener.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED); - REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(PhoneStateListener.EVENT_REGISTRATION_FAILURE); - REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(PhoneStateListener.EVENT_BARRING_INFO_CHANGED); - REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add( - PhoneStateListener.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED); - REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add( - PhoneStateListener.EVENT_DATA_ENABLED_CHANGED); - } - - private boolean isLocationPermissionRequired(Set<Integer> events) { - return events.contains(PhoneStateListener.EVENT_CELL_LOCATION_CHANGED) - || events.contains(PhoneStateListener.EVENT_CELL_INFO_CHANGED) - || events.contains(PhoneStateListener.EVENT_REGISTRATION_FAILURE) - || events.contains(PhoneStateListener.EVENT_BARRING_INFO_CHANGED); - } - - private boolean isPhoneStatePermissionRequired(Set<Integer> events) { - return events.contains(PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED) - || events.contains(PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED) - || events.contains(PhoneStateListener.EVENT_EMERGENCY_NUMBER_LIST_CHANGED) - || events.contains(PhoneStateListener.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED); - } - - private boolean isPrecisePhoneStatePermissionRequired(Set<Integer> events) { - for (Integer requireEvent : REQUIRE_PRECISE_PHONE_STATE_PERMISSION) { - if (events.contains(requireEvent)) { - return true; - } - } - return false; - } - - private boolean isActiveEmergencySessionPermissionRequired(Set<Integer> events) { - return events.contains(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_CALL) - || events.contains(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_SMS); - } - - private boolean isPrivilegedPhoneStatePermissionRequired(Set<Integer> events) { - return events.contains(PhoneStateListener.EVENT_SRVCC_STATE_CHANGED) - || events.contains(PhoneStateListener.EVENT_VOICE_ACTIVATION_STATE_CHANGED) - || events.contains(PhoneStateListener.EVENT_RADIO_POWER_STATE_CHANGED); - } + // Starting in Q, almost all cellular location requires FINE location enforcement. + // Prior to Q, cellular was available with COARSE location enforcement. Bits in this + // list will be checked for COARSE on apps targeting P or earlier and FINE on Q or later. + static final long ENFORCE_LOCATION_PERMISSION_MASK = + PhoneStateListener.LISTEN_CELL_LOCATION + | PhoneStateListener.LISTEN_CELL_INFO + | PhoneStateListener.LISTEN_REGISTRATION_FAILURE + | PhoneStateListener.LISTEN_BARRING_INFO; + + static final long ENFORCE_PHONE_STATE_PERMISSION_MASK = + PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR + | PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR + | PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST + | PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED; + + static final long ENFORCE_PRECISE_PHONE_STATE_PERMISSION_MASK = + PhoneStateListener.LISTEN_PRECISE_CALL_STATE + | PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE + | PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES + | PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED + | PhoneStateListener.LISTEN_IMS_CALL_DISCONNECT_CAUSES + | PhoneStateListener.LISTEN_REGISTRATION_FAILURE + | PhoneStateListener.LISTEN_BARRING_INFO + | PhoneStateListener.LISTEN_PHYSICAL_CHANNEL_CONFIGURATION; + + static final long READ_ACTIVE_EMERGENCY_SESSION_PERMISSION_MASK = + PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_CALL + | PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_SMS; + + static final long READ_PRIVILEGED_PHONE_STATE_PERMISSION_MASK = + PhoneStateListener.LISTEN_OEM_HOOK_RAW_EVENT + | PhoneStateListener.LISTEN_SRVCC_STATE_CHANGED + | PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED + | PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE; private static final int MSG_USER_SWITCHED = 1; private static final int MSG_UPDATE_DEFAULT_SUB = 2; @@ -700,7 +670,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { r.callingFeatureId = callingFeatureId; r.callerUid = Binder.getCallingUid(); r.callerPid = Binder.getCallingPid(); - r.eventList = new ArraySet<>(); + r.events = 0; if (DBG) { log("listen oscl: Register r=" + r); } @@ -754,7 +724,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { r.callingFeatureId = callingFeatureId; r.callerUid = Binder.getCallingUid(); r.callerPid = Binder.getCallingPid(); - r.eventList = new ArraySet<>(); + r.events = 0; if (DBG) { log("listen ooscl: Register r=" + r); } @@ -827,338 +797,331 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } + @Deprecated + @Override + public void listen(String callingPackage, IPhoneStateListener callback, int events, + boolean notifyNow) { + listenWithFeature(callingPackage, null, callback, events, notifyNow); + } + + @Override + public void listenWithFeature(String callingPackage, String callingFeatureId, + IPhoneStateListener callback, long events, boolean notifyNow) { + listenForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, callingPackage, + callingFeatureId, callback, events, notifyNow); + } + @Override - public void listenWithEventList(int subId, String callingPackage, String callingFeatureId, - IPhoneStateListener callback, int[] events, boolean notifyNow) { - Set<Integer> eventList = Arrays.stream(events).boxed().collect(Collectors.toSet()); - listen(callingPackage, callingFeatureId, callback, eventList, notifyNow, subId); + public void listenForSubscriber(int subId, String callingPackage, String callingFeatureId, + IPhoneStateListener callback, long events, boolean notifyNow) { + listen(callingPackage, callingFeatureId, callback, events, notifyNow, subId); } private void listen(String callingPackage, @Nullable String callingFeatureId, - IPhoneStateListener callback, Set<Integer> events, boolean notifyNow, int subId) { + IPhoneStateListener callback, long events, boolean notifyNow, int subId) { int callerUserId = UserHandle.getCallingUserId(); - mAppOps.checkPackage(Binder.getCallingUid(), callingPackage); String str = "listen: E pkg=" + pii(callingPackage) + " uid=" + Binder.getCallingUid() - + " events=" + events + " notifyNow=" + notifyNow - + " subId=" + subId + " myUserId=" + UserHandle.myUserId() - + " callerUserId=" + callerUserId; + + " events=0x" + Long.toHexString(events) + " notifyNow=" + notifyNow + " subId=" + + subId + " myUserId=" + UserHandle.myUserId() + " callerUserId=" + callerUserId; mListenLog.log(str); if (VDBG) { log(str); } - if (events.isEmpty()) { - if (DBG) { - log("listen: Unregister"); + if (events != PhoneStateListener.LISTEN_NONE) { + // Checks permission and throws SecurityException for disallowed operations. For pre-M + // apps whose runtime permission has been revoked, we return immediately to skip sending + // events to the app without crashing it. + if (!checkListenerPermission(events, subId, callingPackage, callingFeatureId, + "listen")) { + return; } - events.clear(); - remove(callback.asBinder()); - return; - } - // Checks permission and throws SecurityException for disallowed operations. For pre-M - // apps whose runtime permission has been revoked, we return immediately to skip sending - // events to the app without crashing it. - if (!checkListenerPermission(events, subId, callingPackage, callingFeatureId, - "listen")) { - return; - } + int phoneId = getPhoneIdFromSubId(subId); + synchronized (mRecords) { + // register + IBinder b = callback.asBinder(); + boolean doesLimitApply = + Binder.getCallingUid() != Process.SYSTEM_UID + && Binder.getCallingUid() != Process.PHONE_UID + && Binder.getCallingUid() != Process.myUid(); + Record r = add(b, Binder.getCallingUid(), Binder.getCallingPid(), doesLimitApply); - int phoneId = getPhoneIdFromSubId(subId); - synchronized (mRecords) { - // register - IBinder b = callback.asBinder(); - boolean doesLimitApply = - Binder.getCallingUid() != Process.SYSTEM_UID - && Binder.getCallingUid() != Process.PHONE_UID - && Binder.getCallingUid() != Process.myUid(); - Record r = add(b, Binder.getCallingUid(), Binder.getCallingPid(), doesLimitApply); - - if (r == null) { - return; - } + if (r == null) { + return; + } - r.context = mContext; - r.callback = callback; - r.callingPackage = callingPackage; - r.callingFeatureId = callingFeatureId; - r.callerUid = Binder.getCallingUid(); - r.callerPid = Binder.getCallingPid(); - // Legacy applications pass SubscriptionManager.DEFAULT_SUB_ID, - // force all illegal subId to SubscriptionManager.DEFAULT_SUB_ID - if (!SubscriptionManager.isValidSubscriptionId(subId)) { - r.subId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID; - } else {//APP specify subID - r.subId = subId; - } - r.phoneId = phoneId; - r.eventList = events; - if (DBG) { - log("listen: Register r=" + r + " r.subId=" + r.subId + " phoneId=" + phoneId); - } - if (notifyNow && validatePhoneId(phoneId)) { - if (events.contains(PhoneStateListener.EVENT_SERVICE_STATE_CHANGED)) { - try { - if (VDBG) log("listen: call onSSC state=" + mServiceState[phoneId]); - ServiceState rawSs = new ServiceState(mServiceState[phoneId]); - if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { - r.callback.onServiceStateChanged(rawSs); - } else if (checkCoarseLocationAccess(r, Build.VERSION_CODES.Q)) { - r.callback.onServiceStateChanged( - rawSs.createLocationInfoSanitizedCopy(false)); - } else { - r.callback.onServiceStateChanged( - rawSs.createLocationInfoSanitizedCopy(true)); + r.context = mContext; + r.callback = callback; + r.callingPackage = callingPackage; + r.callingFeatureId = callingFeatureId; + r.callerUid = Binder.getCallingUid(); + r.callerPid = Binder.getCallingPid(); + // Legacy applications pass SubscriptionManager.DEFAULT_SUB_ID, + // force all illegal subId to SubscriptionManager.DEFAULT_SUB_ID + if (!SubscriptionManager.isValidSubscriptionId(subId)) { + r.subId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID; + } else {//APP specify subID + r.subId = subId; + } + r.phoneId = phoneId; + r.events = events; + if (DBG) { + log("listen: Register r=" + r + " r.subId=" + r.subId + " phoneId=" + phoneId); + } + if (notifyNow && validatePhoneId(phoneId)) { + if ((events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) { + try { + if (VDBG) log("listen: call onSSC state=" + mServiceState[phoneId]); + ServiceState rawSs = new ServiceState(mServiceState[phoneId]); + if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { + r.callback.onServiceStateChanged(rawSs); + } else if (checkCoarseLocationAccess(r, Build.VERSION_CODES.Q)) { + r.callback.onServiceStateChanged( + rawSs.createLocationInfoSanitizedCopy(false)); + } else { + r.callback.onServiceStateChanged( + rawSs.createLocationInfoSanitizedCopy(true)); + } + } catch (RemoteException ex) { + remove(r.binder); } - } catch (RemoteException ex) { - remove(r.binder); } - } - if (events.contains(PhoneStateListener.EVENT_SIGNAL_STRENGTH_CHANGED)) { - try { - if (mSignalStrength[phoneId] != null) { - int gsmSignalStrength = mSignalStrength[phoneId] - .getGsmSignalStrength(); - r.callback.onSignalStrengthChanged((gsmSignalStrength == 99 ? -1 - : gsmSignalStrength)); + if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) { + try { + if (mSignalStrength[phoneId] != null) { + int gsmSignalStrength = mSignalStrength[phoneId] + .getGsmSignalStrength(); + r.callback.onSignalStrengthChanged((gsmSignalStrength == 99 ? -1 + : gsmSignalStrength)); + } + } catch (RemoteException ex) { + remove(r.binder); } - } catch (RemoteException ex) { - remove(r.binder); } - } - if (events.contains( - PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED)) { - try { - r.callback.onMessageWaitingIndicatorChanged( - mMessageWaiting[phoneId]); - } catch (RemoteException ex) { - remove(r.binder); + if ((events & PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) != 0) { + try { + r.callback.onMessageWaitingIndicatorChanged( + mMessageWaiting[phoneId]); + } catch (RemoteException ex) { + remove(r.binder); + } } - } - if (events.contains( - PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED)) { - try { - r.callback.onCallForwardingIndicatorChanged( - mCallForwarding[phoneId]); - } catch (RemoteException ex) { - remove(r.binder); + if ((events & PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) != 0) { + try { + r.callback.onCallForwardingIndicatorChanged( + mCallForwarding[phoneId]); + } catch (RemoteException ex) { + remove(r.binder); + } } - } - if (validateEventAndUserLocked( - r, PhoneStateListener.EVENT_CELL_LOCATION_CHANGED)) { - try { - if (DBG_LOC) log("listen: mCellIdentity = " + mCellIdentity[phoneId]); - if (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE) - && checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { - // null will be translated to empty CellLocation object in client. - r.callback.onCellLocationChanged(mCellIdentity[phoneId]); + if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION)) { + try { + if (DBG_LOC) log("listen: mCellIdentity = " + mCellIdentity[phoneId]); + if (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE) + && checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { + // null will be translated to empty CellLocation object in client. + r.callback.onCellLocationChanged(mCellIdentity[phoneId]); + } + } catch (RemoteException ex) { + remove(r.binder); } - } catch (RemoteException ex) { - remove(r.binder); } - } - if (events.contains(PhoneStateListener.EVENT_CALL_STATE_CHANGED)) { - try { - r.callback.onCallStateChanged(mCallState[phoneId], - getCallIncomingNumber(r, phoneId)); - } catch (RemoteException ex) { - remove(r.binder); + if ((events & PhoneStateListener.LISTEN_CALL_STATE) != 0) { + try { + r.callback.onCallStateChanged(mCallState[phoneId], + getCallIncomingNumber(r, phoneId)); + } catch (RemoteException ex) { + remove(r.binder); + } } - } - if (events.contains(PhoneStateListener.EVENT_DATA_CONNECTION_STATE_CHANGED)) { - try { - r.callback.onDataConnectionStateChanged(mDataConnectionState[phoneId], + if ((events & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) { + try { + r.callback.onDataConnectionStateChanged(mDataConnectionState[phoneId], mDataConnectionNetworkType[phoneId]); - } catch (RemoteException ex) { - remove(r.binder); - } - } - if (events.contains(PhoneStateListener.EVENT_DATA_ACTIVITY_CHANGED)) { - try { - r.callback.onDataActivity(mDataActivity[phoneId]); - } catch (RemoteException ex) { - remove(r.binder); + } catch (RemoteException ex) { + remove(r.binder); + } } - } - if (events.contains(PhoneStateListener.EVENT_SIGNAL_STRENGTHS_CHANGED)) { - try { - if (mSignalStrength[phoneId] != null) { - r.callback.onSignalStrengthsChanged(mSignalStrength[phoneId]); + if ((events & PhoneStateListener.LISTEN_DATA_ACTIVITY) != 0) { + try { + r.callback.onDataActivity(mDataActivity[phoneId]); + } catch (RemoteException ex) { + remove(r.binder); } - } catch (RemoteException ex) { - remove(r.binder); } - } - if (events.contains( - PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) { - updateReportSignalStrengthDecision(r.subId); - try { - if (mSignalStrength[phoneId] != null) { - r.callback.onSignalStrengthsChanged(mSignalStrength[phoneId]); + if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0) { + try { + if (mSignalStrength[phoneId] != null) { + r.callback.onSignalStrengthsChanged(mSignalStrength[phoneId]); + } + } catch (RemoteException ex) { + remove(r.binder); } - } catch (RemoteException ex) { - remove(r.binder); } - } - if (validateEventAndUserLocked( - r, PhoneStateListener.EVENT_CELL_INFO_CHANGED)) { - try { - if (DBG_LOC) log("listen: mCellInfo[" + phoneId + "] = " - + mCellInfo.get(phoneId)); - if (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE) - && checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { - r.callback.onCellInfoChanged(mCellInfo.get(phoneId)); + if ((events & PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) + != 0) { + updateReportSignalStrengthDecision(r.subId); + try { + if (mSignalStrength[phoneId] != null) { + r.callback.onSignalStrengthsChanged(mSignalStrength[phoneId]); + } + } catch (RemoteException ex) { + remove(r.binder); } - } catch (RemoteException ex) { - remove(r.binder); } - } - if (events.contains(PhoneStateListener.EVENT_PRECISE_CALL_STATE_CHANGED)) { - try { - r.callback.onPreciseCallStateChanged(mPreciseCallState[phoneId]); - } catch (RemoteException ex) { - remove(r.binder); + if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO)) { + try { + if (DBG_LOC) log("listen: mCellInfo[" + phoneId + "] = " + + mCellInfo.get(phoneId)); + if (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE) + && checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { + r.callback.onCellInfoChanged(mCellInfo.get(phoneId)); + } + } catch (RemoteException ex) { + remove(r.binder); + } } - } - if (events.contains(PhoneStateListener.EVENT_CALL_DISCONNECT_CAUSE_CHANGED)) { - try { - r.callback.onCallDisconnectCauseChanged(mCallDisconnectCause[phoneId], - mCallPreciseDisconnectCause[phoneId]); - } catch (RemoteException ex) { - remove(r.binder); + if ((events & PhoneStateListener.LISTEN_PRECISE_CALL_STATE) != 0) { + try { + r.callback.onPreciseCallStateChanged(mPreciseCallState[phoneId]); + } catch (RemoteException ex) { + remove(r.binder); + } } - } - if (events.contains(PhoneStateListener.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED)) { - try { - r.callback.onImsCallDisconnectCauseChanged(mImsReasonInfo.get(phoneId)); - } catch (RemoteException ex) { - remove(r.binder); + if ((events & PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES) != 0) { + try { + r.callback.onCallDisconnectCauseChanged(mCallDisconnectCause[phoneId], + mCallPreciseDisconnectCause[phoneId]); + } catch (RemoteException ex) { + remove(r.binder); + } } - } - if (events.contains( - PhoneStateListener.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED)) { - try { - for (PreciseDataConnectionState pdcs - : mPreciseDataConnectionStates.get(phoneId).values()) { - r.callback.onPreciseDataConnectionStateChanged(pdcs); + if ((events & PhoneStateListener.LISTEN_IMS_CALL_DISCONNECT_CAUSES) != 0) { + try { + r.callback.onImsCallDisconnectCauseChanged(mImsReasonInfo.get(phoneId)); + } catch (RemoteException ex) { + remove(r.binder); } - } catch (RemoteException ex) { - remove(r.binder); } - } - if (events.contains(PhoneStateListener.EVENT_CARRIER_NETWORK_CHANGED)) { - try { - r.callback.onCarrierNetworkChange(mCarrierNetworkChangeState); - } catch (RemoteException ex) { - remove(r.binder); + if ((events & PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE) != 0) { + try { + for (PreciseDataConnectionState pdcs + : mPreciseDataConnectionStates.get(phoneId).values()) { + r.callback.onPreciseDataConnectionStateChanged(pdcs); + } + } catch (RemoteException ex) { + remove(r.binder); + } } - } - if (events.contains(PhoneStateListener.EVENT_VOICE_ACTIVATION_STATE_CHANGED)) { - try { - r.callback.onVoiceActivationStateChanged( - mVoiceActivationState[phoneId]); - } catch (RemoteException ex) { - remove(r.binder); + if ((events & PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE) != 0) { + try { + r.callback.onCarrierNetworkChange(mCarrierNetworkChangeState); + } catch (RemoteException ex) { + remove(r.binder); + } } - } - if (events.contains(PhoneStateListener.EVENT_DATA_ACTIVATION_STATE_CHANGED)) { - try { - r.callback.onDataActivationStateChanged(mDataActivationState[phoneId]); - } catch (RemoteException ex) { - remove(r.binder); + if ((events & PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE) !=0) { + try { + r.callback.onVoiceActivationStateChanged( + mVoiceActivationState[phoneId]); + } catch (RemoteException ex) { + remove(r.binder); + } } - } - if (events.contains(PhoneStateListener.EVENT_USER_MOBILE_DATA_STATE_CHANGED)) { - try { - r.callback.onUserMobileDataStateChanged(mUserMobileDataState[phoneId]); - } catch (RemoteException ex) { - remove(r.binder); + if ((events & PhoneStateListener.LISTEN_DATA_ACTIVATION_STATE) !=0) { + try { + r.callback.onDataActivationStateChanged(mDataActivationState[phoneId]); + } catch (RemoteException ex) { + remove(r.binder); + } } - } - if (events.contains(PhoneStateListener.EVENT_DISPLAY_INFO_CHANGED)) { - try { - if (mTelephonyDisplayInfos[phoneId] != null) { - r.callback.onDisplayInfoChanged(mTelephonyDisplayInfos[phoneId]); + if ((events & PhoneStateListener.LISTEN_USER_MOBILE_DATA_STATE) != 0) { + try { + r.callback.onUserMobileDataStateChanged(mUserMobileDataState[phoneId]); + } catch (RemoteException ex) { + remove(r.binder); } - } catch (RemoteException ex) { - remove(r.binder); } - } - if (events.contains(PhoneStateListener.EVENT_EMERGENCY_NUMBER_LIST_CHANGED)) { - try { - r.callback.onEmergencyNumberListChanged(mEmergencyNumberList); - } catch (RemoteException ex) { - remove(r.binder); + if ((events & PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED) != 0) { + try { + if (mTelephonyDisplayInfos[phoneId] != null) { + r.callback.onDisplayInfoChanged(mTelephonyDisplayInfos[phoneId]); + } + } catch (RemoteException ex) { + remove(r.binder); + } } - } - if (events.contains(PhoneStateListener.EVENT_PHONE_CAPABILITY_CHANGED)) { - try { - r.callback.onPhoneCapabilityChanged(mPhoneCapability); - } catch (RemoteException ex) { - remove(r.binder); + if ((events & PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST) != 0) { + try { + r.callback.onEmergencyNumberListChanged(mEmergencyNumberList); + } catch (RemoteException ex) { + remove(r.binder); + } } - } - if (events.contains( - PhoneStateListener.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED)) { - try { - r.callback.onActiveDataSubIdChanged(mActiveDataSubId); - } catch (RemoteException ex) { - remove(r.binder); + if ((events & PhoneStateListener.LISTEN_PHONE_CAPABILITY_CHANGE) != 0) { + try { + r.callback.onPhoneCapabilityChanged(mPhoneCapability); + } catch (RemoteException ex) { + remove(r.binder); + } } - } - if (events.contains(PhoneStateListener.EVENT_RADIO_POWER_STATE_CHANGED)) { - try { - r.callback.onRadioPowerStateChanged(mRadioPowerState); - } catch (RemoteException ex) { - remove(r.binder); + if ((events & PhoneStateListener + .LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE) != 0) { + try { + r.callback.onActiveDataSubIdChanged(mActiveDataSubId); + } catch (RemoteException ex) { + remove(r.binder); + } } - } - if (events.contains(PhoneStateListener.EVENT_SRVCC_STATE_CHANGED)) { - try { - r.callback.onSrvccStateChanged(mSrvccState[phoneId]); - } catch (RemoteException ex) { - remove(r.binder); + if ((events & PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED) != 0) { + try { + r.callback.onRadioPowerStateChanged(mRadioPowerState); + } catch (RemoteException ex) { + remove(r.binder); + } } - } - if (events.contains(PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED)) { - try { - r.callback.onCallAttributesChanged(mCallAttributes[phoneId]); - } catch (RemoteException ex) { - remove(r.binder); + if ((events & PhoneStateListener.LISTEN_SRVCC_STATE_CHANGED) != 0) { + try { + r.callback.onSrvccStateChanged(mSrvccState[phoneId]); + } catch (RemoteException ex) { + remove(r.binder); + } } - } - if (events.contains(PhoneStateListener.EVENT_BARRING_INFO_CHANGED)) { - BarringInfo barringInfo = mBarringInfo.get(phoneId); - BarringInfo biNoLocation = barringInfo != null - ? barringInfo.createLocationInfoSanitizedCopy() : null; - if (VDBG) log("listen: call onBarringInfoChanged=" + barringInfo); - try { - r.callback.onBarringInfoChanged( - checkFineLocationAccess(r, Build.VERSION_CODES.BASE) - ? barringInfo : biNoLocation); - } catch (RemoteException ex) { - remove(r.binder); + if ((events & PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED) != 0) { + try { + r.callback.onCallAttributesChanged(mCallAttributes[phoneId]); + } catch (RemoteException ex) { + remove(r.binder); + } } - } - if (events.contains( - PhoneStateListener.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED)) { - try { - r.callback.onPhysicalChannelConfigChanged( - mPhysicalChannelConfigs); - } catch (RemoteException ex) { - remove(r.binder); + if ((events & PhoneStateListener.LISTEN_BARRING_INFO) != 0) { + BarringInfo barringInfo = mBarringInfo.get(phoneId); + BarringInfo biNoLocation = barringInfo != null + ? barringInfo.createLocationInfoSanitizedCopy() : null; + if (VDBG) log("listen: call onBarringInfoChanged=" + barringInfo); + try { + r.callback.onBarringInfoChanged( + checkFineLocationAccess(r, Build.VERSION_CODES.BASE) + ? barringInfo : biNoLocation); + } catch (RemoteException ex) { + remove(r.binder); + } } - } - if (events.contains( - PhoneStateListener.EVENT_DATA_ENABLED_CHANGED)) { - try { - r.callback.onDataEnabledChanged(mIsDataEnabled, mDataEnabledReason); - } catch (RemoteException ex) { - remove(r.binder); + if ((events & PhoneStateListener.LISTEN_PHYSICAL_CHANNEL_CONFIGURATION) != 0) { + try { + r.callback.onPhysicalChannelConfigurationChanged( + mPhysicalChannelConfigs); + } catch (RemoteException ex) { + remove(r.binder); + } } } } + } else { + if(DBG) log("listen: Unregister"); + remove(callback.asBinder()); } } @@ -1170,7 +1133,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { // If any of the system clients wants to always listen to signal strength, // we need to set it on. if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) { + PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)) { telephonyManager.createForSubscriptionId(subscriptionId) .setAlwaysReportSignalStrength(true); return; @@ -1271,7 +1234,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { // strength is removed from registry records, we need to check if // the signal strength decision needs to update on its slot. if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) { + PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)) { updateReportSignalStrengthDecision(r.subId); } return; @@ -1291,8 +1254,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { synchronized (mRecords) { for (Record r : mRecords) { - if (r.matchPhoneStateListenerEvent(PhoneStateListener.EVENT_CALL_STATE_CHANGED) - && (r.subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)) { + if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_CALL_STATE) && + (r.subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)) { try { // Ensure the listener has read call log permission; if they do not return // an empty phone number. @@ -1326,9 +1289,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mCallState[phoneId] = state; mCallIncomingNumber[phoneId] = incomingNumber; for (Record r : mRecords) { - if (r.matchPhoneStateListenerEvent(PhoneStateListener.EVENT_CALL_STATE_CHANGED) - && (r.subId == subId) - && (r.subId != SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)) { + if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_CALL_STATE) && + (r.subId == subId) && + (r.subId != SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)) { try { String incomingNumberOrEmpty = getCallIncomingNumber(r, phoneId); r.callback.onCallStateChanged(state, incomingNumberOrEmpty); @@ -1368,9 +1331,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { log("notifyServiceStateForSubscriber: r=" + r + " subId=" + subId + " phoneId=" + phoneId + " state=" + state); } - if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_SERVICE_STATE_CHANGED) - && idMatch(r.subId, subId, phoneId)) { + if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_SERVICE_STATE) && + idMatch(r.subId, subId, phoneId)) { try { ServiceState stateToSend; @@ -1431,7 +1393,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { try { if ((activationType == SIM_ACTIVATION_TYPE_VOICE) && r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_VOICE_ACTIVATION_STATE_CHANGED) + PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE) && idMatch(r.subId, subId, phoneId)) { if (DBG) { log("notifyVoiceActivationStateForPhoneId: callback.onVASC r=" + r @@ -1442,7 +1404,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } if ((activationType == SIM_ACTIVATION_TYPE_DATA) && r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_DATA_ACTIVATION_STATE_CHANGED) + PhoneStateListener.LISTEN_DATA_ACTIVATION_STATE) && idMatch(r.subId, subId, phoneId)) { if (DBG) { log("notifyDataActivationStateForPhoneId: callback.onDASC r=" + r @@ -1481,11 +1443,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { log("notifySignalStrengthForPhoneId: r=" + r + " subId=" + subId + " phoneId=" + phoneId + " ss=" + signalStrength); } - if ((r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_SIGNAL_STRENGTHS_CHANGED) + if ((r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) || r.matchPhoneStateListenerEvent( - PhoneStateListener. - EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) + PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)) && idMatch(r.subId, subId, phoneId)) { try { if (DBG) { @@ -1498,9 +1458,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mRemoveList.add(r.binder); } } - if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_SIGNAL_STRENGTH_CHANGED) - && idMatch(r.subId, subId, phoneId)) { + if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_SIGNAL_STRENGTH) && + idMatch(r.subId, subId, phoneId)) { try { int gsmSignalStrength = signalStrength.getGsmSignalStrength(); int ss = (gsmSignalStrength == 99 ? -1 : gsmSignalStrength); @@ -1546,8 +1505,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_CARRIER_NETWORK_CHANGED) - && idMatch(r.subId, subId, phoneId)) { + PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE) && + idMatch(r.subId, subId, phoneId)) { try { r.callback.onCarrierNetworkChange(active); } catch (RemoteException ex) { @@ -1577,10 +1536,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if (validatePhoneId(phoneId)) { mCellInfo.set(phoneId, cellInfo); for (Record r : mRecords) { - if (validateEventAndUserLocked( - r, PhoneStateListener.EVENT_CELL_INFO_CHANGED) - && idMatch(r.subId, subId, phoneId) - && (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE) + if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO) && + idMatch(r.subId, subId, phoneId) && + (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE) && checkFineLocationAccess(r, Build.VERSION_CODES.Q))) { try { if (DBG_LOC) { @@ -1612,8 +1570,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mMessageWaiting[phoneId] = mwi; for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED) - && idMatch(r.subId, subId, phoneId)) { + PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) && + idMatch(r.subId, subId, phoneId)) { try { r.callback.onMessageWaitingIndicatorChanged(mwi); } catch (RemoteException ex) { @@ -1639,8 +1597,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mUserMobileDataState[phoneId] = state; for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_USER_MOBILE_DATA_STATE_CHANGED) - && idMatch(r.subId, subId, phoneId)) { + PhoneStateListener.LISTEN_USER_MOBILE_DATA_STATE) && + idMatch(r.subId, subId, phoneId)) { try { r.callback.onUserMobileDataStateChanged(state); } catch (RemoteException ex) { @@ -1678,7 +1636,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mTelephonyDisplayInfos[phoneId] = telephonyDisplayInfo; for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_DISPLAY_INFO_CHANGED) + PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED) && idMatchWithoutDefaultPhoneCheck(r.subId, subId)) { try { r.callback.onDisplayInfoChanged(telephonyDisplayInfo); @@ -1710,8 +1668,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mCallForwarding[phoneId] = cfi; for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED) - && idMatch(r.subId, subId, phoneId)) { + PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) && + idMatch(r.subId, subId, phoneId)) { try { r.callback.onCallForwardingIndicatorChanged(cfi); } catch (RemoteException ex) { @@ -1738,9 +1696,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mDataActivity[phoneId] = state; for (Record r : mRecords) { // Notify by correct subId. - if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_DATA_ACTIVITY_CHANGED) - && idMatch(r.subId, subId, phoneId)) { + if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_DATA_ACTIVITY) && + idMatch(r.subId, subId, phoneId)) { try { r.callback.onDataActivity(state); } catch (RemoteException ex) { @@ -1787,7 +1744,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mLocalLog.log(str); for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_DATA_CONNECTION_STATE_CHANGED) + PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) && idMatch(r.subId, subId, phoneId)) { try { if (DBG) { @@ -1812,7 +1769,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if (!Objects.equals(oldState, preciseState)) { for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED) + PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE) && idMatch(r.subId, subId, phoneId)) { try { r.callback.onPreciseDataConnectionStateChanged(preciseState); @@ -1857,10 +1814,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if (validatePhoneId(phoneId) && !Objects.equals(cellIdentity, mCellIdentity[phoneId])) { mCellIdentity[phoneId] = cellIdentity; for (Record r : mRecords) { - if (validateEventAndUserLocked( - r, PhoneStateListener.EVENT_CELL_LOCATION_CHANGED) - && idMatch(r.subId, subId, phoneId) - && (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE) + if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION) && + idMatch(r.subId, subId, phoneId) && + (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE) && checkFineLocationAccess(r, Build.VERSION_CODES.Q))) { try { if (DBG_LOC) { @@ -1911,8 +1867,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } for (Record r : mRecords) { - if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_PRECISE_CALL_STATE_CHANGED) + if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_PRECISE_CALL_STATE) && idMatch(r.subId, subId, phoneId)) { try { r.callback.onPreciseCallStateChanged(mPreciseCallState[phoneId]); @@ -1921,7 +1876,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } if (notifyCallAttributes && r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED) + PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED) && idMatch(r.subId, subId, phoneId)) { try { r.callback.onCallAttributesChanged(mCallAttributes[phoneId]); @@ -1970,7 +1925,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mImsReasonInfo.set(phoneId, imsReasonInfo); for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED) + PhoneStateListener.LISTEN_IMS_CALL_DISCONNECT_CAUSES) && idMatch(r.subId, subId, phoneId)) { try { if (DBG_LOC) { @@ -2002,8 +1957,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mSrvccState[phoneId] = state; for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_SRVCC_STATE_CHANGED) - && idMatch(r.subId, subId, phoneId)) { + PhoneStateListener.LISTEN_SRVCC_STATE_CHANGED) && + idMatch(r.subId, subId, phoneId)) { try { if (DBG_LOC) { log("notifySrvccStateChanged: mSrvccState=" + state + " r=" + r); @@ -2031,7 +1986,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { log("notifyOemHookRawEventForSubscriber: r=" + r + " subId=" + subId); } if ((r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_OEM_HOOK_RAW)) + PhoneStateListener.LISTEN_OEM_HOOK_RAW_EVENT)) && idMatch(r.subId, subId, phoneId)) { try { r.callback.onOemHookRawEvent(rawData); @@ -2059,7 +2014,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_PHONE_CAPABILITY_CHANGED)) { + PhoneStateListener.LISTEN_PHONE_CAPABILITY_CHANGE)) { try { r.callback.onPhoneCapabilityChanged(capability); } catch (RemoteException ex) { @@ -2084,7 +2039,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { synchronized (mRecords) { for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED)) { + PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE)) { try { r.callback.onActiveDataSubIdChanged(activeDataSubId); } catch (RemoteException ex) { @@ -2111,7 +2066,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_RADIO_POWER_STATE_CHANGED) + PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED) && idMatch(r.subId, subId, phoneId)) { try { r.callback.onRadioPowerStateChanged(state); @@ -2140,7 +2095,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_EMERGENCY_NUMBER_LIST_CHANGED) + PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST) && idMatch(r.subId, subId, phoneId)) { try { r.callback.onEmergencyNumberListChanged(mEmergencyNumberList); @@ -2172,7 +2127,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { for (Record r : mRecords) { // Send to all listeners regardless of subscription if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_OUTGOING_EMERGENCY_CALL)) { + PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_CALL)) { try { r.callback.onOutgoingEmergencyCall(emergencyNumber, subId); } catch (RemoteException ex) { @@ -2196,7 +2151,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { for (Record r : mRecords) { // Send to all listeners regardless of subscription if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_OUTGOING_EMERGENCY_SMS)) { + PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_SMS)) { try { r.callback.onOutgoingEmergencySms(emergencyNumber, subId); } catch (RemoteException ex) { @@ -2226,7 +2181,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED) + PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED) && idMatch(r.subId, subId, phoneId)) { try { r.callback.onCallAttributesChanged(mCallAttributes[phoneId]); @@ -2257,7 +2212,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if (validatePhoneId(phoneId)) { for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_REGISTRATION_FAILURE) + PhoneStateListener.LISTEN_REGISTRATION_FAILURE) && idMatch(r.subId, subId, phoneId)) { try { r.callback.onRegistrationFailed( @@ -2300,7 +2255,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if (VDBG) log("listen: call onBarringInfoChanged=" + barringInfo); for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_BARRING_INFO_CHANGED) + PhoneStateListener.LISTEN_BARRING_INFO) && idMatch(r.subId, subId, phoneId)) { try { if (DBG_LOC) { @@ -2327,14 +2282,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { * @param subId the subId * @param configs a list of {@link PhysicalChannelConfig}, the configs of physical channel. */ - public void notifyPhysicalChannelConfigForSubscriber( + public void notifyPhysicalChannelConfigurationForSubscriber( int subId, List<PhysicalChannelConfig> configs) { - if (!checkNotifyPermission("notifyPhysicalChannelConfig()")) { + if (!checkNotifyPermission("notifyPhysicalChannelConfiguration()")) { return; } if (VDBG) { - log("notifyPhysicalChannelConfig: subId=" + subId + " configs=" + configs); + log("notifyPhysicalChannelConfiguration: subId=" + subId + " configs=" + configs); } synchronized (mRecords) { @@ -2343,15 +2298,15 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mPhysicalChannelConfigs.set(phoneId, configs.get(phoneId)); for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED) + PhoneStateListener.LISTEN_PHYSICAL_CHANNEL_CONFIGURATION) && idMatch(r.subId, subId, phoneId)) { try { if (DBG_LOC) { - log("notifyPhysicalChannelConfig: " + log("notifyPhysicalChannelConfiguration: " + "mPhysicalChannelConfigs=" + configs + " r=" + r); } - r.callback.onPhysicalChannelConfigChanged(configs); + r.callback.onPhysicalChannelConfigurationChanged(configs); } catch (RemoteException ex) { mRemoveList.add(r.binder); } @@ -2661,18 +2616,18 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { == PackageManager.PERMISSION_GRANTED; } - private boolean checkListenerPermission(Set<Integer> events, int subId, String callingPackage, - @Nullable String callingFeatureId, String message) { + private boolean checkListenerPermission(long events, int subId, String callingPackage, + @Nullable String callingFeatureId, String message) { LocationAccessPolicy.LocationPermissionQuery.Builder locationQueryBuilder = new LocationAccessPolicy.LocationPermissionQuery.Builder() - .setCallingPackage(callingPackage) - .setMethod(message + " events: " + events) - .setCallingPid(Binder.getCallingPid()) - .setCallingUid(Binder.getCallingUid()); + .setCallingPackage(callingPackage) + .setMethod(message + " events: " + events) + .setCallingPid(Binder.getCallingPid()) + .setCallingUid(Binder.getCallingUid()); boolean shouldCheckLocationPermissions = false; - if (isLocationPermissionRequired(events)) { + if ((events & ENFORCE_LOCATION_PERMISSION_MASK) != 0) { // Everything that requires fine location started in Q. So far... locationQueryBuilder.setMinSdkVersionForFine(Build.VERSION_CODES.Q); // If we're enforcing fine starting in Q, we also want to enforce coarse even for @@ -2697,14 +2652,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - if (isPhoneStatePermissionRequired(events)) { + if ((events & ENFORCE_PHONE_STATE_PERMISSION_MASK) != 0) { if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState( mContext, subId, callingPackage, callingFeatureId, message)) { isPermissionCheckSuccessful = false; } } - if (isPrecisePhoneStatePermissionRequired(events)) { + if ((events & ENFORCE_PRECISE_PHONE_STATE_PERMISSION_MASK) != 0) { // check if calling app has either permission READ_PRECISE_PHONE_STATE // or with carrier privileges try { @@ -2715,20 +2670,21 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - if (isActiveEmergencySessionPermissionRequired(events)) { + if ((events & READ_ACTIVE_EMERGENCY_SESSION_PERMISSION_MASK) != 0) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION, null); } - if ((events.contains(PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED))) { + if ((events & PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) != 0) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH, null); } - if (isPrivilegedPhoneStatePermissionRequired(events)) { + if ((events & READ_PRIVILEGED_PHONE_STATE_PERMISSION_MASK) != 0) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null); } + return isPermissionCheckSuccessful; } @@ -2736,25 +2692,25 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { int size = mRemoveList.size(); if (VDBG) log("handleRemoveListLocked: mRemoveList.size()=" + size); if (size > 0) { - for (IBinder b : mRemoveList) { + for (IBinder b: mRemoveList) { remove(b); } mRemoveList.clear(); } } - private boolean validateEventAndUserLocked(Record r, int event) { + private boolean validateEventsAndUserLocked(Record r, int events) { int foregroundUser; final long callingIdentity = Binder.clearCallingIdentity(); boolean valid = false; try { foregroundUser = ActivityManager.getCurrentUser(); valid = UserHandle.getUserId(r.callerUid) == foregroundUser - && r.matchPhoneStateListenerEvent(event); + && r.matchPhoneStateListenerEvent(events); if (DBG | DBG_LOC) { - log("validateEventAndUserLocked: valid=" + valid + log("validateEventsAndUserLocked: valid=" + valid + " r.callerUid=" + r.callerUid + " foregroundUser=" + foregroundUser - + " r.eventList=" + r.eventList + " event=" + event); + + " r.events=" + r.events + " events=" + events); } } finally { Binder.restoreCallingIdentity(callingIdentity); @@ -2866,14 +2822,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } private void checkPossibleMissNotify(Record r, int phoneId) { - Set<Integer> events = r.eventList; - - if (events == null || events.isEmpty()) { - log("checkPossibleMissNotify: events = null."); - return; - } + long events = r.events; - if ((events.contains(PhoneStateListener.EVENT_SERVICE_STATE_CHANGED))) { + if ((events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) { try { if (VDBG) log("checkPossibleMissNotify: onServiceStateChanged state=" + mServiceState[phoneId]); @@ -2892,9 +2843,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - if (events.contains(PhoneStateListener.EVENT_SIGNAL_STRENGTHS_CHANGED) - || events.contains( - PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) { + if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0 + || (events & PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) != 0) { try { if (mSignalStrength[phoneId] != null) { SignalStrength signalStrength = mSignalStrength[phoneId]; @@ -2909,7 +2859,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - if (events.contains(PhoneStateListener.EVENT_SIGNAL_STRENGTH_CHANGED)) { + if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) { try { if (mSignalStrength[phoneId] != null) { int gsmSignalStrength = mSignalStrength[phoneId] @@ -2926,7 +2876,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - if (validateEventAndUserLocked(r, PhoneStateListener.EVENT_CELL_INFO_CHANGED)) { + if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO)) { try { if (DBG_LOC) { log("checkPossibleMissNotify: onCellInfoChanged[" + phoneId + "] = " @@ -2941,7 +2891,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - if (events.contains(PhoneStateListener.EVENT_USER_MOBILE_DATA_STATE_CHANGED)) { + if ((events & PhoneStateListener.LISTEN_USER_MOBILE_DATA_STATE) != 0) { try { if (VDBG) { log("checkPossibleMissNotify: onUserMobileDataStateChanged phoneId=" @@ -2953,7 +2903,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - if (events.contains(PhoneStateListener.EVENT_DISPLAY_INFO_CHANGED)) { + if ((events & PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED) != 0) { try { if (VDBG) { log("checkPossibleMissNotify: onDisplayInfoChanged phoneId=" @@ -2967,7 +2917,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - if (events.contains(PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED)) { + if ((events & PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) != 0) { try { if (VDBG) { log("checkPossibleMissNotify: onMessageWaitingIndicatorChanged phoneId=" @@ -2980,7 +2930,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - if (events.contains(PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED)) { + if ((events & PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) != 0) { try { if (VDBG) { log("checkPossibleMissNotify: onCallForwardingIndicatorChanged phoneId=" @@ -2993,7 +2943,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - if (validateEventAndUserLocked(r, PhoneStateListener.EVENT_CELL_LOCATION_CHANGED)) { + if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION)) { try { if (DBG_LOC) { log("checkPossibleMissNotify: onCellLocationChanged mCellIdentity = " @@ -3009,7 +2959,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - if (events.contains(PhoneStateListener.EVENT_DATA_CONNECTION_STATE_CHANGED)) { + if ((events & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) { try { if (DBG) { log("checkPossibleMissNotify: onDataConnectionStateChanged(mDataConnectionState" diff --git a/services/core/java/com/android/server/UserspaceRebootLogger.java b/services/core/java/com/android/server/UserspaceRebootLogger.java index 2403b637581f..89327b50883c 100644 --- a/services/core/java/com/android/server/UserspaceRebootLogger.java +++ b/services/core/java/com/android/server/UserspaceRebootLogger.java @@ -59,7 +59,7 @@ public final class UserspaceRebootLogger { */ public static void noteUserspaceRebootWasRequested() { if (!PowerManager.isRebootingUserspaceSupportedImpl()) { - Slog.wtf(TAG, "Userspace reboot is not supported."); + Slog.wtf(TAG, "noteUserspaceRebootWasRequested: Userspace reboot is not supported."); return; } @@ -77,7 +77,7 @@ public final class UserspaceRebootLogger { */ public static void noteUserspaceRebootSuccess() { if (!PowerManager.isRebootingUserspaceSupportedImpl()) { - Slog.wtf(TAG, "Userspace reboot is not supported."); + Slog.wtf(TAG, "noteUserspaceRebootSuccess: Userspace reboot is not supported."); return; } @@ -88,11 +88,11 @@ public final class UserspaceRebootLogger { /** * Returns {@code true} if {@code UserspaceRebootReported} atom should be logged. * - * <p>This call should only be made on devices supporting userspace reboot. + * <p>On devices that do not support userspace reboot this method will always return {@code + * false}. */ public static boolean shouldLogUserspaceRebootEvent() { if (!PowerManager.isRebootingUserspaceSupportedImpl()) { - Slog.wtf(TAG, "Userspace reboot is not supported."); return false; } @@ -110,7 +110,7 @@ public final class UserspaceRebootLogger { */ public static void logEventAsync(boolean userUnlocked, Executor executor) { if (!PowerManager.isRebootingUserspaceSupportedImpl()) { - Slog.wtf(TAG, "Userspace reboot is not supported."); + Slog.wtf(TAG, "logEventAsync: Userspace reboot is not supported."); return; } diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java index f376473bb2a1..165b6a1b08f1 100644 --- a/services/core/java/com/android/server/VcnManagementService.java +++ b/services/core/java/com/android/server/VcnManagementService.java @@ -165,9 +165,13 @@ public class VcnManagementService extends IVcnManagementService.Stub { // TODO: Clear VCN configuration, trigger teardown as necessary } - @VisibleForTesting(visibility = Visibility.PRIVATE) - class VcnNetworkProvider extends NetworkProvider { - VcnNetworkProvider(@NonNull Context context, @NonNull Looper looper) { + /** + * Network provider for VCN networks. + * + * @hide + */ + public class VcnNetworkProvider extends NetworkProvider { + VcnNetworkProvider(Context context, Looper looper) { super(context, looper, VcnNetworkProvider.class.getSimpleName()); } diff --git a/services/core/java/com/android/server/VibratorManagerService.java b/services/core/java/com/android/server/VibratorManagerService.java index 356cd0cd937b..f1f2815be8fa 100644 --- a/services/core/java/com/android/server/VibratorManagerService.java +++ b/services/core/java/com/android/server/VibratorManagerService.java @@ -159,7 +159,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { pw.println(" Prints this help text."); pw.println(""); pw.println(" list"); - pw.println(" Prints the id of device vibrators. This do not include any "); + pw.println(" Prints the id of device vibrators. This does not include any "); pw.println(" connected input device."); pw.println(""); } diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java index 63fba25a3785..db2b4e4edd2b 100644 --- a/services/core/java/com/android/server/VibratorService.java +++ b/services/core/java/com/android/server/VibratorService.java @@ -16,8 +16,6 @@ package com.android.server; -import static android.os.VibrationEffect.Composition.PrimitiveEffect; - import android.annotation.Nullable; import android.app.ActivityManager; import android.app.AppOpsManager; @@ -28,8 +26,6 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; -import android.content.res.Resources; -import android.hardware.input.InputManager; import android.hardware.vibrator.IVibrator; import android.os.BatteryStats; import android.os.Binder; @@ -45,7 +41,6 @@ import android.os.PowerManager.ServiceType; import android.os.PowerManagerInternal; import android.os.PowerSaveState; import android.os.Process; -import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ServiceManager; @@ -60,38 +55,33 @@ import android.os.WorkSource; import android.util.Slog; import android.util.SparseArray; import android.util.proto.ProtoOutputStream; -import android.view.InputDevice; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IBatteryStats; import com.android.internal.util.DumpUtils; import com.android.internal.util.FrameworkStatsLog; +import com.android.server.vibrator.InputDeviceDelegate; +import com.android.server.vibrator.Vibration; import com.android.server.vibrator.VibrationScaler; import com.android.server.vibrator.VibrationSettings; - -import libcore.util.NativeAllocationRegistry; +import com.android.server.vibrator.VibratorController; +import com.android.server.vibrator.VibratorController.OnVibrationCompleteListener; import java.io.FileDescriptor; import java.io.PrintWriter; -import java.text.SimpleDateFormat; +import java.lang.ref.WeakReference; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Date; import java.util.LinkedList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; -public class VibratorService extends IVibratorService.Stub - implements InputManager.InputDeviceListener { +/** System implementation of {@link IVibratorService}. */ +public class VibratorService extends IVibratorService.Stub { private static final String TAG = "VibratorService"; - private static final SimpleDateFormat DEBUG_DATE_FORMAT = - new SimpleDateFormat("MM-dd HH:mm:ss.SSS"); private static final boolean DEBUG = false; private static final String EXTERNAL_VIBRATOR_SERVICE = "external_vibrator_service"; - private static final long[] DOUBLE_CLICK_EFFECT_FALLBACK_TIMINGS = { 0, 30, 100, 30 }; - // Default vibration attributes. Used when vibration is requested without attributes private static final VibrationAttributes DEFAULT_ATTRIBUTES = new VibrationAttributes.Builder().build(); @@ -99,22 +89,17 @@ public class VibratorService extends IVibratorService.Stub // Used to generate globally unique vibration ids. private final AtomicInteger mNextVibrationId = new AtomicInteger(1); // 0 = no callback - private final LinkedList<VibrationInfo> mPreviousRingVibrations; - private final LinkedList<VibrationInfo> mPreviousNotificationVibrations; - private final LinkedList<VibrationInfo> mPreviousAlarmVibrations; - private final LinkedList<VibrationInfo> mPreviousExternalVibrations; - private final LinkedList<VibrationInfo> mPreviousVibrations; + private final LinkedList<Vibration.DebugInfo> mPreviousRingVibrations; + private final LinkedList<Vibration.DebugInfo> mPreviousNotificationVibrations; + private final LinkedList<Vibration.DebugInfo> mPreviousAlarmVibrations; + private final LinkedList<Vibration.DebugInfo> mPreviousExternalVibrations; + private final LinkedList<Vibration.DebugInfo> mPreviousVibrations; private final int mPreviousVibrationsLimit; - private final boolean mAllowPriorityVibrationsInLowPowerMode; - private final List<Integer> mSupportedEffects; - private final List<Integer> mSupportedPrimitives; - private final long mCapabilities; - private final int mDefaultVibrationAmplitude; - private final SparseArray<VibrationEffect> mFallbackEffects; private final SparseArray<Integer> mProcStatesCache = new SparseArray<>(); private final WorkSource mTmpWorkSource = new WorkSource(); private final Handler mH; private final Object mLock = new Object(); + private final VibratorController mVibratorController; private final Context mContext; private final PowerManager.WakeLock mWakeLock; @@ -122,61 +107,21 @@ public class VibratorService extends IVibratorService.Stub private final IBatteryStats mBatteryStatsService; private final String mSystemUiPackage; private PowerManagerInternal mPowerManagerInternal; - private InputManager mIm; private VibrationSettings mVibrationSettings; private VibrationScaler mVibrationScaler; + private InputDeviceDelegate mInputDeviceDelegate; - private final NativeWrapper mNativeWrapper; private volatile VibrateWaveformThread mThread; - // mInputDeviceVibrators lock should be acquired after mLock, if both are - // to be acquired - private final ArrayList<Vibrator> mInputDeviceVibrators = new ArrayList<>(); - private boolean mVibrateInputDevicesSetting; // guarded by mInputDeviceVibrators - private boolean mInputDeviceListenerRegistered; // guarded by mInputDeviceVibrators - @GuardedBy("mLock") private Vibration mCurrentVibration; + @GuardedBy("mLock") + private VibrationDeathRecipient mCurrentVibrationDeathRecipient; private int mCurVibUid = -1; private ExternalVibrationHolder mCurrentExternalVibration; - private boolean mVibratorUnderExternalControl; private boolean mLowPowerMode; - @GuardedBy("mLock") - private boolean mIsVibrating; - @GuardedBy("mLock") - private final RemoteCallbackList<IVibratorStateListener> mVibratorStateListeners = - new RemoteCallbackList<>(); private SparseArray<Vibration> mAlwaysOnEffects = new SparseArray<>(); - static native long vibratorInit(OnCompleteListener listener); - - static native long vibratorGetFinalizer(); - - static native boolean vibratorExists(long nativeServicePtr); - - static native void vibratorOn(long nativeServicePtr, long milliseconds, long vibrationId); - - static native void vibratorOff(long nativeServicePtr); - - static native void vibratorSetAmplitude(long nativeServicePtr, int amplitude); - - static native int[] vibratorGetSupportedEffects(long nativeServicePtr); - - static native int[] vibratorGetSupportedPrimitives(long nativeServicePtr); - - static native long vibratorPerformEffect( - long nativeServicePtr, long effect, long strength, long vibrationId); - - static native void vibratorPerformComposedEffect(long nativeServicePtr, - VibrationEffect.Composition.PrimitiveEffect[] effect, long vibrationId); - - static native void vibratorSetExternalControl(long nativeServicePtr, boolean enabled); - - static native long vibratorGetCapabilities(long nativeServicePtr); - static native void vibratorAlwaysOnEnable(long nativeServicePtr, long id, long effect, - long strength); - static native void vibratorAlwaysOnDisable(long nativeServicePtr, long id); - private final IUidObserver mUidObserver = new IUidObserver.Stub() { @Override public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) { @@ -187,118 +132,65 @@ public class VibratorService extends IVibratorService.Stub mProcStatesCache.delete(uid); } - @Override public void onUidActive(int uid) { + @Override + public void onUidActive(int uid) { } - @Override public void onUidIdle(int uid, boolean disabled) { + @Override + public void onUidIdle(int uid, boolean disabled) { } - @Override public void onUidCachedChanged(int uid, boolean cached) { + @Override + public void onUidCachedChanged(int uid, boolean cached) { } }; - /** Listener for vibration completion callbacks from native. */ - public interface OnCompleteListener { + /** + * Implementation of {@link OnVibrationCompleteListener} with a weak reference to this service. + */ + private static final class VibrationCompleteListener implements OnVibrationCompleteListener { + private WeakReference<VibratorService> mServiceRef; + + VibrationCompleteListener(VibratorService service) { + mServiceRef = new WeakReference<>(service); + } - /** Callback triggered when vibration is complete, identified by {@link Vibration#id}. */ - void onComplete(long vibrationId); + @Override + public void onComplete(int vibratorId, long vibrationId) { + VibratorService service = mServiceRef.get(); + if (service != null) { + service.onVibrationComplete(vibrationId); + } + } } - /** Holder for a {@link VibrationEffect}. */ - private final class Vibration implements IBinder.DeathRecipient { - - public final IBinder token; - // Start time in CLOCK_BOOTTIME base. - public final long startTime; - public final VibrationAttributes attrs; - public final long id; - public final int uid; - public final String opPkg; - public final String reason; - - // The actual effect to be played. - public VibrationEffect effect; - // The original effect that was requested. Typically these two things differ because - // the effect was scaled based on the users vibration intensity settings. - public VibrationEffect originalEffect; - // The scale applied to the original effect. - public float scale; - - // Start/end times in unix epoch time. Only to be used for debugging purposes and to - // correlate with other system events, any duration calculations should be done use - // startTime so as not to be affected by discontinuities created by RTC adjustments. - private final long mStartTimeDebug; - private long mEndTimeDebug; - private VibrationInfo.Status mStatus; - - private Vibration(IBinder token, VibrationEffect effect, - VibrationAttributes attrs, int uid, String opPkg, String reason) { - this.token = token; - this.effect = effect; - this.id = mNextVibrationId.getAndIncrement(); - this.startTime = SystemClock.elapsedRealtime(); - this.attrs = attrs; - this.uid = uid; - this.opPkg = opPkg; - this.reason = reason; - mStartTimeDebug = System.currentTimeMillis(); - mStatus = VibrationInfo.Status.RUNNING; + /** Death recipient to bind {@link Vibration}. */ + private final class VibrationDeathRecipient implements IBinder.DeathRecipient { + + private final Vibration mVibration; + + private VibrationDeathRecipient(Vibration vibration) { + mVibration = vibration; } @Override public void binderDied() { synchronized (mLock) { - if (this == mCurrentVibration) { + if (mVibration == mCurrentVibration) { if (DEBUG) { Slog.d(TAG, "Vibration finished because binder died, cleaning up"); } - doCancelVibrateLocked(VibrationInfo.Status.CANCELLED); + doCancelVibrateLocked(Vibration.Status.CANCELLED); } } } - public void end(VibrationInfo.Status status) { - if (hasEnded()) { - // Vibration already ended, keep first ending status set and ignore this one. - return; - } - mStatus = status; - mEndTimeDebug = System.currentTimeMillis(); - } - - public boolean hasEnded() { - return mStatus != VibrationInfo.Status.RUNNING; - } - - public boolean hasTimeoutLongerThan(long millis) { - final long duration = effect.getDuration(); - return duration >= 0 && duration > millis; - } - - public boolean isHapticFeedback() { - return VibratorService.this.isHapticFeedback(attrs.getUsage()); - } - - public boolean isNotification() { - return VibratorService.this.isNotification(attrs.getUsage()); + private void linkToDeath() throws RemoteException { + mVibration.token.linkToDeath(this, 0); } - public boolean isRingtone() { - return VibratorService.this.isRingtone(attrs.getUsage()); - } - - public boolean isAlarm() { - return VibratorService.this.isAlarm(attrs.getUsage()); - } - - public boolean isFromSystem() { - return uid == Process.SYSTEM_UID || uid == 0 || mSystemUiPackage.equals(opPkg); - } - - public VibrationInfo toInfo() { - return new VibrationInfo( - mStartTimeDebug, mEndTimeDebug, effect, originalEffect, scale, attrs, - uid, opPkg, reason, mStatus); + private void unlinkToDeath() { + mVibration.token.unlinkToDeath(this, 0); } } @@ -310,17 +202,17 @@ public class VibratorService extends IVibratorService.Stub private final long mStartTimeDebug; private long mEndTimeDebug; - private VibrationInfo.Status mStatus; + private Vibration.Status mStatus; private ExternalVibrationHolder(ExternalVibration externalVibration) { this.externalVibration = externalVibration; this.scale = IExternalVibratorService.SCALE_NONE; mStartTimeDebug = System.currentTimeMillis(); - mStatus = VibrationInfo.Status.RUNNING; + mStatus = Vibration.Status.RUNNING; } - public void end(VibrationInfo.Status status) { - if (mStatus != VibrationInfo.Status.RUNNING) { + public void end(Vibration.Status status) { + if (mStatus != Vibration.Status.RUNNING) { // Vibration already ended, keep first ending status set and ignore this one. return; } @@ -328,8 +220,8 @@ public class VibratorService extends IVibratorService.Stub mEndTimeDebug = System.currentTimeMillis(); } - public VibrationInfo toInfo() { - return new VibrationInfo( + public Vibration.DebugInfo getDebugInfo() { + return new Vibration.DebugInfo( mStartTimeDebug, mEndTimeDebug, /* effect= */ null, /* originalEffect= */ null, scale, externalVibration.getVibrationAttributes(), externalVibration.getUid(), externalVibration.getPackage(), @@ -337,176 +229,19 @@ public class VibratorService extends IVibratorService.Stub } } - /** Debug information about vibrations. */ - private static class VibrationInfo { - - public enum Status { - RUNNING, - FINISHED, - CANCELLED, - ERROR_APP_OPS, - IGNORED, - IGNORED_APP_OPS, - IGNORED_BACKGROUND, - IGNORED_RINGTONE, - IGNORED_UNKNOWN_VIBRATION, - IGNORED_UNSUPPORTED, - IGNORED_FOR_ALARM, - IGNORED_FOR_EXTERNAL, - IGNORED_FOR_ONGOING, - IGNORED_FOR_POWER, - IGNORED_FOR_SETTINGS, - } - - private final long mStartTimeDebug; - private final long mEndTimeDebug; - private final VibrationEffect mEffect; - private final VibrationEffect mOriginalEffect; - private final float mScale; - private final VibrationAttributes mAttrs; - private final int mUid; - private final String mOpPkg; - private final String mReason; - private final VibrationInfo.Status mStatus; - - VibrationInfo(long startTimeDebug, long endTimeDebug, VibrationEffect effect, - VibrationEffect originalEffect, float scale, VibrationAttributes attrs, - int uid, String opPkg, String reason, VibrationInfo.Status status) { - mStartTimeDebug = startTimeDebug; - mEndTimeDebug = endTimeDebug; - mEffect = effect; - mOriginalEffect = originalEffect; - mScale = scale; - mAttrs = attrs; - mUid = uid; - mOpPkg = opPkg; - mReason = reason; - mStatus = status; - } - - @Override - public String toString() { - return new StringBuilder() - .append("startTime: ") - .append(DEBUG_DATE_FORMAT.format(new Date(mStartTimeDebug))) - .append(", endTime: ") - .append(mEndTimeDebug == 0 ? null - : DEBUG_DATE_FORMAT.format(new Date(mEndTimeDebug))) - .append(", status: ") - .append(mStatus.name().toLowerCase()) - .append(", effect: ") - .append(mEffect) - .append(", originalEffect: ") - .append(mOriginalEffect) - .append(", scale: ") - .append(String.format("%.2f", mScale)) - .append(", attrs: ") - .append(mAttrs) - .append(", uid: ") - .append(mUid) - .append(", opPkg: ") - .append(mOpPkg) - .append(", reason: ") - .append(mReason) - .toString(); - } - - void dumpProto(ProtoOutputStream proto, long fieldId) { - final long token = proto.start(fieldId); - proto.write(VibrationProto.START_TIME, mStartTimeDebug); - proto.write(VibrationProto.END_TIME, mEndTimeDebug); - proto.write(VibrationProto.STATUS, mStatus.ordinal()); - - final long attrsToken = proto.start(VibrationProto.ATTRIBUTES); - proto.write(VibrationAttributesProto.USAGE, mAttrs.getUsage()); - proto.write(VibrationAttributesProto.AUDIO_USAGE, mAttrs.getAudioUsage()); - proto.write(VibrationAttributesProto.FLAGS, mAttrs.getFlags()); - proto.end(attrsToken); - - if (mEffect != null) { - dumpEffect(proto, VibrationProto.EFFECT, mEffect); - } - if (mOriginalEffect != null) { - dumpEffect(proto, VibrationProto.ORIGINAL_EFFECT, mOriginalEffect); - } - - proto.end(token); - } - - private void dumpEffect(ProtoOutputStream proto, long fieldId, VibrationEffect effect) { - final long token = proto.start(fieldId); - if (effect instanceof VibrationEffect.OneShot) { - dumpEffect(proto, VibrationEffectProto.ONESHOT, (VibrationEffect.OneShot) effect); - } else if (effect instanceof VibrationEffect.Waveform) { - dumpEffect(proto, VibrationEffectProto.WAVEFORM, (VibrationEffect.Waveform) effect); - } else if (effect instanceof VibrationEffect.Prebaked) { - dumpEffect(proto, VibrationEffectProto.PREBAKED, (VibrationEffect.Prebaked) effect); - } else if (effect instanceof VibrationEffect.Composed) { - dumpEffect(proto, VibrationEffectProto.COMPOSED, (VibrationEffect.Composed) effect); - } - proto.end(token); - } - - private void dumpEffect(ProtoOutputStream proto, long fieldId, - VibrationEffect.OneShot effect) { - final long token = proto.start(fieldId); - proto.write(OneShotProto.DURATION, (int) effect.getDuration()); - proto.write(OneShotProto.AMPLITUDE, effect.getAmplitude()); - proto.end(token); - } - - private void dumpEffect(ProtoOutputStream proto, long fieldId, - VibrationEffect.Waveform effect) { - final long token = proto.start(fieldId); - for (long timing : effect.getTimings()) { - proto.write(WaveformProto.TIMINGS, (int) timing); - } - for (int amplitude : effect.getAmplitudes()) { - proto.write(WaveformProto.AMPLITUDES, amplitude); - } - proto.write(WaveformProto.REPEAT, effect.getRepeatIndex() >= 0); - proto.end(token); - } - - private void dumpEffect(ProtoOutputStream proto, long fieldId, - VibrationEffect.Prebaked effect) { - final long token = proto.start(fieldId); - proto.write(PrebakedProto.EFFECT_ID, effect.getId()); - proto.write(PrebakedProto.EFFECT_STRENGTH, effect.getEffectStrength()); - proto.write(PrebakedProto.FALLBACK, effect.shouldFallback()); - proto.end(token); - } - - private void dumpEffect(ProtoOutputStream proto, long fieldId, - VibrationEffect.Composed effect) { - final long token = proto.start(fieldId); - for (PrimitiveEffect primitive : effect.getPrimitiveEffects()) { - proto.write(ComposedProto.EFFECT_IDS, primitive.id); - proto.write(ComposedProto.EFFECT_SCALES, primitive.scale); - proto.write(ComposedProto.DELAYS, primitive.delay); - } - proto.end(token); - } - } - VibratorService(Context context) { this(context, new Injector()); } @VisibleForTesting VibratorService(Context context, Injector injector) { - mNativeWrapper = injector.getNativeWrapper(); mH = injector.createHandler(Looper.myLooper()); - - mNativeWrapper.vibratorInit(this::onVibrationComplete); + mVibratorController = injector.createVibratorController( + new VibrationCompleteListener(this)); // Reset the hardware to a default state, in case this is a runtime // restart instead of a fresh boot. - mNativeWrapper.vibratorOff(); - - mSupportedEffects = asList(mNativeWrapper.vibratorGetSupportedEffects()); - mSupportedPrimitives = asList(mNativeWrapper.vibratorGetSupportedPrimitives()); - mCapabilities = mNativeWrapper.vibratorGetCapabilities(); + mVibratorController.off(); mContext = context; PowerManager pm = context.getSystemService(PowerManager.class); @@ -522,12 +257,6 @@ public class VibratorService extends IVibratorService.Stub mPreviousVibrationsLimit = mContext.getResources().getInteger( com.android.internal.R.integer.config_previousVibrationsDumpLimit); - mDefaultVibrationAmplitude = mContext.getResources().getInteger( - com.android.internal.R.integer.config_defaultVibrationAmplitude); - - mAllowPriorityVibrationsInLowPowerMode = mContext.getResources().getBoolean( - com.android.internal.R.bool.config_allowPriorityVibrationsInLowPowerMode); - mPreviousRingVibrations = new LinkedList<>(); mPreviousNotificationVibrations = new LinkedList<>(); mPreviousAlarmVibrations = new LinkedList<>(); @@ -538,48 +267,15 @@ public class VibratorService extends IVibratorService.Stub filter.addAction(Intent.ACTION_SCREEN_OFF); context.registerReceiver(mIntentReceiver, filter); - VibrationEffect clickEffect = createEffectFromResource( - com.android.internal.R.array.config_virtualKeyVibePattern); - VibrationEffect doubleClickEffect = VibrationEffect.createWaveform( - DOUBLE_CLICK_EFFECT_FALLBACK_TIMINGS, -1 /*repeatIndex*/); - VibrationEffect heavyClickEffect = createEffectFromResource( - com.android.internal.R.array.config_longPressVibePattern); - VibrationEffect tickEffect = createEffectFromResource( - com.android.internal.R.array.config_clockTickVibePattern); - - mFallbackEffects = new SparseArray<>(); - mFallbackEffects.put(VibrationEffect.EFFECT_CLICK, clickEffect); - mFallbackEffects.put(VibrationEffect.EFFECT_DOUBLE_CLICK, doubleClickEffect); - mFallbackEffects.put(VibrationEffect.EFFECT_TICK, tickEffect); - mFallbackEffects.put(VibrationEffect.EFFECT_HEAVY_CLICK, heavyClickEffect); - - mFallbackEffects.put(VibrationEffect.EFFECT_TEXTURE_TICK, - VibrationEffect.get(VibrationEffect.EFFECT_TICK, false)); - injector.addService(EXTERNAL_VIBRATOR_SERVICE, new ExternalVibratorService()); } - private VibrationEffect createEffectFromResource(int resId) { - long[] timings = getLongIntArray(mContext.getResources(), resId); - return createEffectFromTimings(timings); - } - - private static VibrationEffect createEffectFromTimings(long[] timings) { - if (timings == null || timings.length == 0) { - return null; - } else if (timings.length == 1) { - return VibrationEffect.createOneShot(timings[0], VibrationEffect.DEFAULT_AMPLITUDE); - } else { - return VibrationEffect.createWaveform(timings, -1); - } - } - public void systemReady() { Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "VibratorService#systemReady"); try { - mIm = mContext.getSystemService(InputManager.class); mVibrationSettings = new VibrationSettings(mContext, mH); mVibrationScaler = new VibrationScaler(mContext, mVibrationSettings); + mInputDeviceDelegate = new InputDeviceDelegate(mContext, mH); mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class); mPowerManagerInternal.registerLowPowerModeObserver( @@ -626,14 +322,19 @@ public class VibratorService extends IVibratorService.Stub if (DEBUG) { Slog.d(TAG, "Vibration finished by callback, cleaning up"); } - doCancelVibrateLocked(VibrationInfo.Status.FINISHED); + doCancelVibrateLocked(Vibration.Status.FINISHED); } } } @Override // Binder call public boolean hasVibrator() { - return doVibratorExists(); + // For now, we choose to ignore the presence of input devices that have vibrators + // when reporting whether the device has a vibrator. Applications often use this + // information to decide whether to enable certain features so they expect the + // result of hasVibrator() to be constant. For now, just report whether + // the device has a built-in vibrator. + return mVibratorController.isAvailable(); } @Override // Binder call @@ -641,32 +342,7 @@ public class VibratorService extends IVibratorService.Stub if (!hasPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE)) { throw new SecurityException("Requires ACCESS_VIBRATOR_STATE permission"); } - synchronized (mLock) { - return mIsVibrating; - } - } - - @GuardedBy("mLock") - private void notifyStateListenerLocked(IVibratorStateListener listener) { - try { - listener.onVibrating(mIsVibrating); - } catch (RemoteException | RuntimeException e) { - Slog.e(TAG, "Vibrator callback failed to call", e); - } - } - - @GuardedBy("mLock") - private void notifyStateListenersLocked() { - final int length = mVibratorStateListeners.beginBroadcast(); - try { - for (int i = 0; i < length; i++) { - final IVibratorStateListener listener = - mVibratorStateListeners.getBroadcastItem(i); - notifyStateListenerLocked(listener); - } - } finally { - mVibratorStateListeners.finishBroadcast(); - } + return mVibratorController.isVibrating(); } @Override // Binder call @@ -674,19 +350,7 @@ public class VibratorService extends IVibratorService.Stub if (!hasPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE)) { throw new SecurityException("Requires ACCESS_VIBRATOR_STATE permission"); } - synchronized (mLock) { - final long token = Binder.clearCallingIdentity(); - try { - if (!mVibratorStateListeners.register(listener)) { - return false; - } - // Notify its callback after new client registered. - notifyStateListenerLocked(listener); - return true; - } finally { - Binder.restoreCallingIdentity(token); - } - } + return mVibratorController.registerVibratorStateListener(listener); } @Override // Binder call @@ -695,54 +359,26 @@ public class VibratorService extends IVibratorService.Stub if (!hasPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE)) { throw new SecurityException("Requires ACCESS_VIBRATOR_STATE permission"); } - synchronized (mLock) { - final long token = Binder.clearCallingIdentity(); - try { - return mVibratorStateListeners.unregister(listener); - } finally { - Binder.restoreCallingIdentity(token); - } - } + return mVibratorController.unregisterVibratorStateListener(listener); } @Override // Binder call public boolean hasAmplitudeControl() { - synchronized (mInputDeviceVibrators) { - // Input device vibrators don't support amplitude controls yet, but are still used over - // the system vibrator when connected. - return hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL) - && mInputDeviceVibrators.isEmpty(); - } + // Input device vibrators always support amplitude controls. + return mInputDeviceDelegate.isAvailable() + || mVibratorController.hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL); } @Override // Binder call public int[] areEffectsSupported(int[] effectIds) { - int[] supported = new int[effectIds.length]; - if (mSupportedEffects == null) { - Arrays.fill(supported, Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN); - } else { - for (int i = 0; i < effectIds.length; i++) { - supported[i] = mSupportedEffects.contains(effectIds[i]) - ? Vibrator.VIBRATION_EFFECT_SUPPORT_YES - : Vibrator.VIBRATION_EFFECT_SUPPORT_NO; - } - } - return supported; + return mVibratorController.areEffectsSupported(effectIds); } @Override // Binder call public boolean[] arePrimitivesSupported(int[] primitiveIds) { - boolean[] supported = new boolean[primitiveIds.length]; - if (!hasCapability(IVibrator.CAP_COMPOSE_EFFECTS) || mSupportedPrimitives == null) { - return supported; - } - for (int i = 0; i < primitiveIds.length; i++) { - supported[i] = mSupportedPrimitives.contains(primitiveIds[i]); - } - return supported; + return mVibratorController.arePrimitivesSupported(primitiveIds); } - private static List<Integer> asList(int... vals) { if (vals == null) { return null; @@ -760,14 +396,14 @@ public class VibratorService extends IVibratorService.Stub if (!hasPermission(android.Manifest.permission.VIBRATE_ALWAYS_ON)) { throw new SecurityException("Requires VIBRATE_ALWAYS_ON permission"); } - if (!hasCapability(IVibrator.CAP_ALWAYS_ON_CONTROL)) { + if (!mVibratorController.hasCapability(IVibrator.CAP_ALWAYS_ON_CONTROL)) { Slog.e(TAG, "Always-on effects not supported."); return false; } if (effect == null) { synchronized (mLock) { mAlwaysOnEffects.delete(alwaysOnId); - mNativeWrapper.vibratorAlwaysOnDisable(alwaysOnId); + mVibratorController.updateAlwaysOn(alwaysOnId, /* effect= */ null); } } else { if (!verifyVibrationEffect(effect)) { @@ -779,7 +415,8 @@ public class VibratorService extends IVibratorService.Stub } attrs = fixupVibrationAttributes(attrs); synchronized (mLock) { - Vibration vib = new Vibration(null, effect, attrs, uid, opPkg, null); + Vibration vib = new Vibration(null, mNextVibrationId.getAndIncrement(), effect, + attrs, uid, opPkg, null); mAlwaysOnEffects.put(alwaysOnId, vib); updateAlwaysOnLocked(alwaysOnId, vib); } @@ -839,18 +476,6 @@ public class VibratorService extends IVibratorService.Stub return attrs; } - private static long[] getLongIntArray(Resources r, int resid) { - int[] ar = r.getIntArray(resid); - if (ar == null) { - return null; - } - long[] out = new long[ar.length]; - for (int i = 0; i < ar.length; i++) { - out[i] = ar[i]; - } - return out; - } - @Override // Binder call public void vibrate(int uid, String opPkg, VibrationEffect effect, @Nullable VibrationAttributes attrs, String reason, IBinder token) { @@ -869,24 +494,26 @@ public class VibratorService extends IVibratorService.Stub } attrs = fixupVibrationAttributes(attrs); - Vibration vib = new Vibration(token, effect, attrs, uid, opPkg, reason); + Vibration vib = new Vibration(token, mNextVibrationId.getAndIncrement(), effect, attrs, + uid, opPkg, reason); // If our current vibration is longer than the new vibration and is the same amplitude, // then just let the current one finish. synchronized (mLock) { + VibrationEffect currentEffect = + mCurrentVibration == null ? null : mCurrentVibration.getEffect(); if (effect instanceof VibrationEffect.OneShot - && mCurrentVibration != null - && mCurrentVibration.effect instanceof VibrationEffect.OneShot) { + && currentEffect instanceof VibrationEffect.OneShot) { VibrationEffect.OneShot newOneShot = (VibrationEffect.OneShot) effect; VibrationEffect.OneShot currentOneShot = - (VibrationEffect.OneShot) mCurrentVibration.effect; - if (mCurrentVibration.hasTimeoutLongerThan(newOneShot.getDuration()) + (VibrationEffect.OneShot) currentEffect; + if (currentOneShot.getDuration() > newOneShot.getDuration() && newOneShot.getAmplitude() == currentOneShot.getAmplitude()) { if (DEBUG) { Slog.d(TAG, "Ignoring incoming vibration in favor of current vibration"); } - endVibrationLocked(vib, VibrationInfo.Status.IGNORED_FOR_ONGOING); + endVibrationLocked(vib, Vibration.Status.IGNORED_FOR_ONGOING); return; } } @@ -898,7 +525,7 @@ public class VibratorService extends IVibratorService.Stub if (DEBUG) { Slog.d(TAG, "Ignoring incoming vibration for current external vibration"); } - endVibrationLocked(vib, VibrationInfo.Status.IGNORED_FOR_EXTERNAL); + endVibrationLocked(vib, Vibration.Status.IGNORED_FOR_EXTERNAL); return; } @@ -908,32 +535,32 @@ public class VibratorService extends IVibratorService.Stub // alarms, in favor of one-shot vibrations that are likely quite short. if (!isRepeatingVibration(effect) && mCurrentVibration != null - && isRepeatingVibration(mCurrentVibration.effect)) { + && isRepeatingVibration(currentEffect)) { if (DEBUG) { Slog.d(TAG, "Ignoring incoming vibration in favor of alarm vibration"); } - endVibrationLocked(vib, VibrationInfo.Status.IGNORED_FOR_ALARM); + endVibrationLocked(vib, Vibration.Status.IGNORED_FOR_ALARM); return; } if (mProcStatesCache.get(uid, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND - && !vib.isNotification() && !vib.isRingtone() && !vib.isAlarm()) { + && !isNotification(vib) && !isRingtone(vib) && !isAlarm(vib)) { Slog.e(TAG, "Ignoring incoming vibration as process with" + " uid= " + uid + " is background," + " attrs= " + vib.attrs); - endVibrationLocked(vib, VibrationInfo.Status.IGNORED_BACKGROUND); + endVibrationLocked(vib, Vibration.Status.IGNORED_BACKGROUND); return; } - linkVibration(vib); + linkVibrationLocked(vib); final long ident = Binder.clearCallingIdentity(); try { - doCancelVibrateLocked(VibrationInfo.Status.CANCELLED); + doCancelVibrateLocked(Vibration.Status.CANCELLED); startVibrationLocked(vib); if (!vib.hasEnded() && mCurrentVibration.id != vib.id) { // Vibration was unexpectedly ignored: add to list for debugging - endVibrationLocked(vib, VibrationInfo.Status.IGNORED); + endVibrationLocked(vib, Vibration.Status.IGNORED); } } finally { Binder.restoreCallingIdentity(ident); @@ -953,13 +580,13 @@ public class VibratorService extends IVibratorService.Stub return effect.getDuration() == Long.MAX_VALUE; } - private void endVibrationLocked(Vibration vib, VibrationInfo.Status status) { - final LinkedList<VibrationInfo> previousVibrations; - if (vib.isRingtone()) { + private void endVibrationLocked(Vibration vib, Vibration.Status status) { + final LinkedList<Vibration.DebugInfo> previousVibrations; + if (isRingtone(vib)) { previousVibrations = mPreviousRingVibrations; - } else if (vib.isNotification()) { + } else if (isNotification(vib)) { previousVibrations = mPreviousNotificationVibrations; - } else if (vib.isAlarm()) { + } else if (isAlarm(vib)) { previousVibrations = mPreviousAlarmVibrations; } else { previousVibrations = mPreviousVibrations; @@ -969,15 +596,15 @@ public class VibratorService extends IVibratorService.Stub previousVibrations.removeFirst(); } vib.end(status); - previousVibrations.addLast(vib.toInfo()); + previousVibrations.addLast(vib.getDebugInfo()); } - private void endVibrationLocked(ExternalVibrationHolder vib, VibrationInfo.Status status) { + private void endVibrationLocked(ExternalVibrationHolder vib, Vibration.Status status) { if (mPreviousExternalVibrations.size() > mPreviousVibrationsLimit) { mPreviousExternalVibrations.removeFirst(); } vib.end(status); - mPreviousExternalVibrations.addLast(vib.toInfo()); + mPreviousExternalVibrations.addLast(vib.getDebugInfo()); } @Override // Binder call @@ -993,7 +620,7 @@ public class VibratorService extends IVibratorService.Stub } final long ident = Binder.clearCallingIdentity(); try { - doCancelVibrateLocked(VibrationInfo.Status.CANCELLED); + doCancelVibrateLocked(Vibration.Status.CANCELLED); } finally { Binder.restoreCallingIdentity(ident); } @@ -1002,7 +629,7 @@ public class VibratorService extends IVibratorService.Stub } @GuardedBy("mLock") - private void doCancelVibrateLocked(VibrationInfo.Status status) { + private void doCancelVibrateLocked(Vibration.Status status) { Trace.asyncTraceEnd(Trace.TRACE_TAG_VIBRATOR, "vibration", 0); Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doCancelVibrateLocked"); try { @@ -1014,7 +641,7 @@ public class VibratorService extends IVibratorService.Stub endVibrationLocked(mCurrentExternalVibration, status); mCurrentExternalVibration.externalVibration.mute(); mCurrentExternalVibration = null; - setVibratorUnderExternalControl(false); + mVibratorController.setExternalControl(false); } doVibratorOff(); reportFinishVibrationLocked(status); @@ -1031,7 +658,7 @@ public class VibratorService extends IVibratorService.Stub synchronized (mLock) { // Make sure the vibration is really done. This also reports that the vibration is // finished. - doCancelVibrateLocked(VibrationInfo.Status.FINISHED); + doCancelVibrateLocked(Vibration.Status.FINISHED); } } @@ -1055,23 +682,22 @@ public class VibratorService extends IVibratorService.Stub try { // Set current vibration before starting it, so callback will work. mCurrentVibration = vib; - if (vib.effect instanceof VibrationEffect.OneShot) { + VibrationEffect effect = vib.getEffect(); + if (effect instanceof VibrationEffect.OneShot) { Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0); doVibratorOn(vib); - } else if (vib.effect instanceof VibrationEffect.Waveform) { - // mThread better be null here. doCancelVibrate should always be - // called before startNextVibrationLocked or startVibrationLocked. - mThread = new VibrateWaveformThread(vib); - mThread.start(); - } else if (vib.effect instanceof VibrationEffect.Prebaked) { + } else if (effect instanceof VibrationEffect.Waveform) { + Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0); + doVibratorWaveformEffectLocked(vib); + } else if (effect instanceof VibrationEffect.Prebaked) { Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0); doVibratorPrebakedEffectLocked(vib); - } else if (vib.effect instanceof VibrationEffect.Composed) { + } else if (effect instanceof VibrationEffect.Composed) { Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0); doVibratorComposedEffectLocked(vib); } else { Slog.e(TAG, "Unknown vibration type, ignoring"); - endVibrationLocked(vib, VibrationInfo.Status.IGNORED_UNKNOWN_VIBRATION); + endVibrationLocked(vib, Vibration.Status.IGNORED_UNKNOWN_VIBRATION); // The set current vibration is not actually playing, so drop it. mCurrentVibration = null; } @@ -1093,11 +719,7 @@ public class VibratorService extends IVibratorService.Stub /** Scale the vibration effect by the intensity as appropriate based its intent. */ private void applyVibrationIntensityScalingLocked(Vibration vib) { - VibrationEffect scaled = mVibrationScaler.scale(vib.effect, vib.attrs.getUsage()); - if (!scaled.equals(vib.effect)) { - vib.originalEffect = vib.effect; - vib.effect = scaled; - } + vib.updateEffect(mVibrationScaler.scale(vib.getEffect(), vib.attrs.getUsage())); } private static boolean shouldBypassDnd(VibrationAttributes attrs) { @@ -1123,21 +745,21 @@ public class VibratorService extends IVibratorService.Stub private boolean shouldVibrate(Vibration vib) { if (!shouldVibrateForPowerModeLocked(vib)) { - endVibrationLocked(vib, VibrationInfo.Status.IGNORED_FOR_POWER); + endVibrationLocked(vib, Vibration.Status.IGNORED_FOR_POWER); return false; } int intensity = mVibrationSettings.getCurrentIntensity(vib.attrs.getUsage()); if (intensity == Vibrator.VIBRATION_INTENSITY_OFF) { - endVibrationLocked(vib, VibrationInfo.Status.IGNORED_FOR_SETTINGS); + endVibrationLocked(vib, Vibration.Status.IGNORED_FOR_SETTINGS); return false; } - if (vib.isRingtone() && !mVibrationSettings.shouldVibrateForRingtone()) { + if (isRingtone(vib) && !mVibrationSettings.shouldVibrateForRingtone()) { if (DEBUG) { Slog.e(TAG, "Vibrate ignored, not vibrating for ringtones"); } - endVibrationLocked(vib, VibrationInfo.Status.IGNORED_RINGTONE); + endVibrationLocked(vib, Vibration.Status.IGNORED_RINGTONE); return false; } @@ -1147,9 +769,9 @@ public class VibratorService extends IVibratorService.Stub // We might be getting calls from within system_server, so we don't actually // want to throw a SecurityException here. Slog.w(TAG, "Would be an error: vibrate from uid " + vib.uid); - endVibrationLocked(vib, VibrationInfo.Status.ERROR_APP_OPS); + endVibrationLocked(vib, Vibration.Status.ERROR_APP_OPS); } else { - endVibrationLocked(vib, VibrationInfo.Status.IGNORED_APP_OPS); + endVibrationLocked(vib, Vibration.Status.IGNORED_APP_OPS); } return false; } @@ -1158,14 +780,14 @@ public class VibratorService extends IVibratorService.Stub } @GuardedBy("mLock") - private void reportFinishVibrationLocked(VibrationInfo.Status status) { + private void reportFinishVibrationLocked(Vibration.Status status) { Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "reportFinishVibrationLocked"); try { if (mCurrentVibration != null) { endVibrationLocked(mCurrentVibration, status); mAppOps.finishOp(AppOpsManager.OP_VIBRATE, mCurrentVibration.uid, mCurrentVibration.opPkg); - unlinkVibration(mCurrentVibration); + unlinkVibrationLocked(); mCurrentVibration = null; } } finally { @@ -1173,73 +795,45 @@ public class VibratorService extends IVibratorService.Stub } } - private void linkVibration(Vibration vib) { + @GuardedBy("mLock") + private void linkVibrationLocked(Vibration vib) { + // Unlink previously linked vibration, if any. + unlinkVibrationLocked(); // Only link against waveforms since they potentially don't have a finish if // they're repeating. Let other effects just play out until they're done. - if (vib.effect instanceof VibrationEffect.Waveform) { + if (vib.getEffect() instanceof VibrationEffect.Waveform) { try { - vib.token.linkToDeath(vib, 0); + mCurrentVibrationDeathRecipient = new VibrationDeathRecipient(vib); + mCurrentVibrationDeathRecipient.linkToDeath(); } catch (RemoteException e) { return; } } } - private void unlinkVibration(Vibration vib) { - if (vib.effect instanceof VibrationEffect.Waveform) { - vib.token.unlinkToDeath(vib, 0); + @GuardedBy("mLock") + private void unlinkVibrationLocked() { + if (mCurrentVibrationDeathRecipient != null) { + mCurrentVibrationDeathRecipient.unlinkToDeath(); + mCurrentVibrationDeathRecipient = null; } } private void updateVibrators() { synchronized (mLock) { - boolean devicesUpdated = updateInputDeviceVibratorsLocked(); + boolean devicesUpdated = mInputDeviceDelegate.updateInputDeviceVibrators( + mVibrationSettings.shouldVibrateInputDevices()); boolean lowPowerModeUpdated = updateLowPowerModeLocked(); if (devicesUpdated || lowPowerModeUpdated) { // If the state changes out from under us then just reset. - doCancelVibrateLocked(VibrationInfo.Status.CANCELLED); + doCancelVibrateLocked(Vibration.Status.CANCELLED); } updateAlwaysOnLocked(); } } - private boolean updateInputDeviceVibratorsLocked() { - boolean changed = false; - boolean vibrateInputDevices = mVibrationSettings.shouldVibrateInputDevices(); - if (vibrateInputDevices != mVibrateInputDevicesSetting) { - changed = true; - mVibrateInputDevicesSetting = vibrateInputDevices; - } - - if (mVibrateInputDevicesSetting) { - if (!mInputDeviceListenerRegistered) { - mInputDeviceListenerRegistered = true; - mIm.registerInputDeviceListener(this, mH); - } - } else { - if (mInputDeviceListenerRegistered) { - mInputDeviceListenerRegistered = false; - mIm.unregisterInputDeviceListener(this); - } - } - - mInputDeviceVibrators.clear(); - if (mVibrateInputDevicesSetting) { - int[] ids = mIm.getInputDeviceIds(); - for (int i = 0; i < ids.length; i++) { - InputDevice device = mIm.getInputDevice(ids[i]); - Vibrator vibrator = device.getVibrator(); - if (vibrator.hasVibrator()) { - mInputDeviceVibrators.add(vibrator); - } - } - return true; - } - return changed; - } - private boolean updateLowPowerModeLocked() { boolean lowPowerMode = mPowerManagerInternal .getLowPowerState(ServiceType.VIBRATION).batterySaverEnabled; @@ -1251,13 +845,13 @@ public class VibratorService extends IVibratorService.Stub } private void updateAlwaysOnLocked(int id, Vibration vib) { + VibrationEffect.Prebaked effect; if (!shouldVibrate(vib)) { - mNativeWrapper.vibratorAlwaysOnDisable(id); + effect = null; } else { - VibrationEffect.Prebaked scaled = mVibrationScaler.scale(vib.effect, - vib.attrs.getUsage()); - mNativeWrapper.vibratorAlwaysOnEnable(id, scaled.getId(), scaled.getEffectStrength()); + effect = mVibrationScaler.scale(vib.getEffect(), vib.attrs.getUsage()); } + mVibratorController.updateAlwaysOn(id, effect); } private void updateAlwaysOnLocked() { @@ -1268,85 +862,64 @@ public class VibratorService extends IVibratorService.Stub } } - @Override - public void onInputDeviceAdded(int deviceId) { - updateVibrators(); - } - - @Override - public void onInputDeviceChanged(int deviceId) { - updateVibrators(); - } - - @Override - public void onInputDeviceRemoved(int deviceId) { - updateVibrators(); - } - - private boolean doVibratorExists() { - // For now, we choose to ignore the presence of input devices that have vibrators - // when reporting whether the device has a vibrator. Applications often use this - // information to decide whether to enable certain features so they expect the - // result of hasVibrator() to be constant. For now, just report whether - // the device has a built-in vibrator. - //synchronized (mInputDeviceVibrators) { - // return !mInputDeviceVibrators.isEmpty() || vibratorExists(); - //} - return mNativeWrapper.vibratorExists(); - } - private void doVibratorOn(Vibration vib) { Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorOn"); try { - synchronized (mInputDeviceVibrators) { - final VibrationEffect.OneShot oneShot = vib.effect.resolve( - mDefaultVibrationAmplitude); - if (DEBUG) { - Slog.d(TAG, "Turning vibrator on for " + oneShot.getDuration() + " ms" - + " with amplitude " + oneShot.getAmplitude() + "."); - } + final VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) vib.getEffect(); + if (DEBUG) { + Slog.d(TAG, "Turning vibrator on for " + oneShot.getDuration() + " ms" + + " with amplitude " + oneShot.getAmplitude() + "."); + } + boolean inputDevicesAvailable = mInputDeviceDelegate.vibrateIfAvailable( + vib.uid, vib.opPkg, oneShot, vib.reason, vib.attrs); + if (inputDevicesAvailable) { + // The set current vibration is no longer being played by this service, so drop it. + mCurrentVibration = null; + endVibrationLocked(vib, Vibration.Status.FORWARDED_TO_INPUT_DEVICES); + } else { noteVibratorOnLocked(vib.uid, oneShot.getDuration()); - final int vibratorCount = mInputDeviceVibrators.size(); - if (vibratorCount != 0) { - for (int i = 0; i < vibratorCount; i++) { - mInputDeviceVibrators.get(i).vibrate(vib.uid, vib.opPkg, oneShot, - vib.reason, vib.attrs); - } - } else { - // Note: ordering is important here! Many haptic drivers will reset their - // amplitude when enabled, so we always have to enable first, then set the - // amplitude. - mNativeWrapper.vibratorOn(oneShot.getDuration(), vib.id); - doVibratorSetAmplitude(oneShot.getAmplitude()); - } + // Note: ordering is important here! Many haptic drivers will reset their + // amplitude when enabled, so we always have to enable first, then set the + // amplitude. + mVibratorController.on(oneShot.getDuration(), vib.id); + mVibratorController.setAmplitude(oneShot.getAmplitude()); } } finally { Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); } } - private void doVibratorSetAmplitude(int amplitude) { - if (hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL)) { - mNativeWrapper.vibratorSetAmplitude(amplitude); + private void doVibratorOff() { + Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorOff"); + try { + if (DEBUG) { + Slog.d(TAG, "Turning vibrator off."); + } + noteVibratorOffLocked(); + boolean inputDevicesAvailable = mInputDeviceDelegate.cancelVibrateIfAvailable(); + if (!inputDevicesAvailable) { + mVibratorController.off(); + } + } finally { + Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); } } - private void doVibratorOff() { - Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorOff"); + @GuardedBy("mLock") + private void doVibratorWaveformEffectLocked(Vibration vib) { + Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorWaveformEffectLocked"); try { - synchronized (mInputDeviceVibrators) { - if (DEBUG) { - Slog.d(TAG, "Turning vibrator off."); - } - noteVibratorOffLocked(); - final int vibratorCount = mInputDeviceVibrators.size(); - if (vibratorCount != 0) { - for (int i = 0; i < vibratorCount; i++) { - mInputDeviceVibrators.get(i).cancel(); - } - } else { - mNativeWrapper.vibratorOff(); - } + boolean inputDevicesAvailable = mInputDeviceDelegate.vibrateIfAvailable( + vib.uid, vib.opPkg, vib.getEffect(), vib.reason, vib.attrs); + if (inputDevicesAvailable) { + // The set current vibration is no longer being played by this service, so drop it. + mCurrentVibration = null; + endVibrationLocked(vib, Vibration.Status.FORWARDED_TO_INPUT_DEVICES); + } else { + // mThread better be null here. doCancelVibrate should always be + // called before startNextVibrationLocked or startVibrationLocked. + mThread = new VibrateWaveformThread(vib); + mThread.start(); } } finally { Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); @@ -1357,37 +930,33 @@ public class VibratorService extends IVibratorService.Stub private void doVibratorPrebakedEffectLocked(Vibration vib) { Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorPrebakedEffectLocked"); try { - final VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) vib.effect; - final boolean usingInputDeviceVibrators; - synchronized (mInputDeviceVibrators) { - usingInputDeviceVibrators = !mInputDeviceVibrators.isEmpty(); - } - // Input devices don't support prebaked effect, so skip trying it with them. - if (!usingInputDeviceVibrators) { - long duration = mNativeWrapper.vibratorPerformEffect( - prebaked.getId(), prebaked.getEffectStrength(), vib.id); + final VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) vib.getEffect(); + // Input devices don't support prebaked effect, so skip trying it with them and allow + // fallback to be attempted. + if (!mInputDeviceDelegate.isAvailable()) { + long duration = mVibratorController.on(prebaked, vib.id); if (duration > 0) { noteVibratorOnLocked(vib.uid, duration); return; } } - endVibrationLocked(vib, VibrationInfo.Status.IGNORED_UNSUPPORTED); + endVibrationLocked(vib, Vibration.Status.IGNORED_UNSUPPORTED); // The set current vibration is not actually playing, so drop it. mCurrentVibration = null; if (!prebaked.shouldFallback()) { return; } - VibrationEffect effect = getFallbackEffect(prebaked.getId()); + VibrationEffect effect = mVibrationSettings.getFallbackEffect(prebaked.getId()); if (effect == null) { Slog.w(TAG, "Failed to play prebaked effect, no fallback"); return; } - Vibration fallbackVib = new Vibration(vib.token, effect, vib.attrs, vib.uid, - vib.opPkg, vib.reason + " (fallback)"); + Vibration fallbackVib = new Vibration(vib.token, mNextVibrationId.getAndIncrement(), + effect, vib.attrs, vib.uid, vib.opPkg, vib.reason + " (fallback)"); // Set current vibration before starting it, so callback will work. mCurrentVibration = fallbackVib; - linkVibration(fallbackVib); + linkVibrationLocked(fallbackVib); applyVibrationIntensityScalingLocked(fallbackVib); startVibrationInnerLocked(fallbackVib); } finally { @@ -1400,53 +969,49 @@ public class VibratorService extends IVibratorService.Stub Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorComposedEffectLocked"); try { - final VibrationEffect.Composed composed = (VibrationEffect.Composed) vib.effect; - final boolean usingInputDeviceVibrators; - synchronized (mInputDeviceVibrators) { - usingInputDeviceVibrators = !mInputDeviceVibrators.isEmpty(); - } - // Input devices don't support composed effect, so skip trying it with them. - if (usingInputDeviceVibrators || !hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) { - endVibrationLocked(vib, VibrationInfo.Status.IGNORED_UNSUPPORTED); + final VibrationEffect.Composed composed = (VibrationEffect.Composed) vib.getEffect(); + boolean inputDevicesAvailable = mInputDeviceDelegate.vibrateIfAvailable( + vib.uid, vib.opPkg, composed, vib.reason, vib.attrs); + if (inputDevicesAvailable) { + // The set current vibration is no longer being played by this service, so drop it. + mCurrentVibration = null; + endVibrationLocked(vib, Vibration.Status.FORWARDED_TO_INPUT_DEVICES); + return; + } else if (!mVibratorController.hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) { // The set current vibration is not actually playing, so drop it. mCurrentVibration = null; + endVibrationLocked(vib, Vibration.Status.IGNORED_UNSUPPORTED); return; } - PrimitiveEffect[] primitiveEffects = - composed.getPrimitiveEffects().toArray(new PrimitiveEffect[0]); - mNativeWrapper.vibratorPerformComposedEffect(primitiveEffects, vib.id); + mVibratorController.on(composed, vib.id); // Composed effects don't actually give us an estimated duration, so we just guess here. - noteVibratorOnLocked(vib.uid, 10 * primitiveEffects.length); + noteVibratorOnLocked(vib.uid, 10 * composed.getPrimitiveEffects().size()); } finally { Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); } } - private boolean hasCapability(long capability) { - return (mCapabilities & capability) == capability; - } - - private VibrationEffect getFallbackEffect(int effectId) { - return mFallbackEffects.get(effectId); + private static boolean isNotification(Vibration vib) { + return vib.attrs.getUsage() == VibrationAttributes.USAGE_NOTIFICATION; } - private static boolean isNotification(int usageHint) { - return usageHint == VibrationAttributes.USAGE_NOTIFICATION; + private static boolean isRingtone(Vibration vib) { + return vib.attrs.getUsage() == VibrationAttributes.USAGE_RINGTONE; } - private static boolean isRingtone(int usageHint) { - return usageHint == VibrationAttributes.USAGE_RINGTONE; + private static boolean isHapticFeedback(Vibration vib) { + return vib.attrs.getUsage() == VibrationAttributes.USAGE_TOUCH; } - private static boolean isHapticFeedback(int usageHint) { - return usageHint == VibrationAttributes.USAGE_TOUCH; + private static boolean isAlarm(Vibration vib) { + return vib.attrs.getUsage() == VibrationAttributes.USAGE_ALARM; } - private static boolean isAlarm(int usageHint) { - return usageHint == VibrationAttributes.USAGE_ALARM; + private boolean isFromSystem(Vibration vib) { + return vib.uid == Process.SYSTEM_UID || vib.uid == 0 || mSystemUiPackage.equals(vib.opPkg); } private void noteVibratorOnLocked(int uid, long millis) { @@ -1455,10 +1020,6 @@ public class VibratorService extends IVibratorService.Stub FrameworkStatsLog.write_non_chained(FrameworkStatsLog.VIBRATOR_STATE_CHANGED, uid, null, FrameworkStatsLog.VIBRATOR_STATE_CHANGED__STATE__ON, millis); mCurVibUid = uid; - if (!mIsVibrating) { - mIsVibrating = true; - notifyStateListenersLocked(); - } } catch (RemoteException e) { } } @@ -1472,22 +1033,6 @@ public class VibratorService extends IVibratorService.Stub } catch (RemoteException e) { } mCurVibUid = -1; } - if (mIsVibrating) { - mIsVibrating = false; - notifyStateListenersLocked(); - } - } - - private void setVibratorUnderExternalControl(boolean externalControl) { - if (DEBUG) { - if (externalControl) { - Slog.d(TAG, "Vibrator going under external control."); - } else { - Slog.d(TAG, "Taking back control of vibrator."); - } - } - mVibratorUnderExternalControl = externalControl; - mNativeWrapper.vibratorSetExternalControl(externalControl); } private void dumpInternal(PrintWriter pw) { @@ -1495,48 +1040,43 @@ public class VibratorService extends IVibratorService.Stub synchronized (mLock) { pw.print(" mCurrentVibration="); if (mCurrentVibration != null) { - pw.println(mCurrentVibration.toInfo().toString()); + pw.println(mCurrentVibration.getDebugInfo().toString()); } else { pw.println("null"); } pw.print(" mCurrentExternalVibration="); if (mCurrentExternalVibration != null) { - pw.println(mCurrentExternalVibration.toInfo().toString()); + pw.println(mCurrentExternalVibration.getDebugInfo().toString()); } else { pw.println("null"); } - pw.println(" mVibratorUnderExternalControl=" + mVibratorUnderExternalControl); - pw.println(" mIsVibrating=" + mIsVibrating); - pw.println(" mVibratorStateListeners Count=" - + mVibratorStateListeners.getRegisteredCallbackCount()); pw.println(" mLowPowerMode=" + mLowPowerMode); + pw.println(" mVibratorController=" + mVibratorController); pw.println(" mVibrationSettings=" + mVibrationSettings); - pw.println(" mSupportedEffects=" + mSupportedEffects); - pw.println(" mSupportedPrimitives=" + mSupportedPrimitives); pw.println(); pw.println(" Previous ring vibrations:"); - for (VibrationInfo info : mPreviousRingVibrations) { + for (Vibration.DebugInfo info : mPreviousRingVibrations) { pw.print(" "); pw.println(info.toString()); } pw.println(" Previous notification vibrations:"); - for (VibrationInfo info : mPreviousNotificationVibrations) { + for (Vibration.DebugInfo info : mPreviousNotificationVibrations) { pw.println(" " + info); } pw.println(" Previous alarm vibrations:"); - for (VibrationInfo info : mPreviousAlarmVibrations) { + for (Vibration.DebugInfo info : mPreviousAlarmVibrations) { pw.println(" " + info); } pw.println(" Previous vibrations:"); - for (VibrationInfo info : mPreviousVibrations) { + for (Vibration.DebugInfo info : mPreviousVibrations) { pw.println(" " + info); } pw.println(" Previous external vibrations:"); - for (VibrationInfo info : mPreviousExternalVibrations) { + for (Vibration.DebugInfo info : mPreviousExternalVibrations) { pw.println(" " + info); } } @@ -1547,16 +1087,16 @@ public class VibratorService extends IVibratorService.Stub synchronized (mLock) { if (mCurrentVibration != null) { - mCurrentVibration.toInfo().dumpProto(proto, + mCurrentVibration.getDebugInfo().dumpProto(proto, VibratorServiceDumpProto.CURRENT_VIBRATION); } if (mCurrentExternalVibration != null) { - mCurrentExternalVibration.toInfo().dumpProto(proto, + mCurrentExternalVibration.getDebugInfo().dumpProto(proto, VibratorServiceDumpProto.CURRENT_EXTERNAL_VIBRATION); } - proto.write(VibratorServiceDumpProto.IS_VIBRATING, mIsVibrating); + proto.write(VibratorServiceDumpProto.IS_VIBRATING, mVibratorController.isVibrating()); proto.write(VibratorServiceDumpProto.VIBRATOR_UNDER_EXTERNAL_CONTROL, - mVibratorUnderExternalControl); + mVibratorController.isUnderExternalControl()); proto.write(VibratorServiceDumpProto.LOW_POWER_MODE, mLowPowerMode); proto.write(VibratorServiceDumpProto.HAPTIC_FEEDBACK_INTENSITY, mVibrationSettings.getCurrentIntensity(VibrationAttributes.USAGE_TOUCH)); @@ -1571,23 +1111,23 @@ public class VibratorService extends IVibratorService.Stub proto.write(VibratorServiceDumpProto.RING_DEFAULT_INTENSITY, mVibrationSettings.getDefaultIntensity(VibrationAttributes.USAGE_RINGTONE)); - for (VibrationInfo info : mPreviousRingVibrations) { + for (Vibration.DebugInfo info : mPreviousRingVibrations) { info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_RING_VIBRATIONS); } - for (VibrationInfo info : mPreviousNotificationVibrations) { + for (Vibration.DebugInfo info : mPreviousNotificationVibrations) { info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_NOTIFICATION_VIBRATIONS); } - for (VibrationInfo info : mPreviousAlarmVibrations) { + for (Vibration.DebugInfo info : mPreviousAlarmVibrations) { info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_ALARM_VIBRATIONS); } - for (VibrationInfo info : mPreviousVibrations) { + for (Vibration.DebugInfo info : mPreviousVibrations) { info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_VIBRATIONS); } - for (VibrationInfo info : mPreviousExternalVibrations) { + for (Vibration.DebugInfo info : mPreviousExternalVibrations) { info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_EXTERNAL_VIBRATIONS); } } @@ -1602,9 +1142,9 @@ public class VibratorService extends IVibratorService.Stub private boolean mForceStop; VibrateWaveformThread(Vibration vib) { - mWaveform = (VibrationEffect.Waveform) vib.effect; - mVibration = new Vibration(vib.token, /* effect= */ null, vib.attrs, vib.uid, - vib.opPkg, vib.reason); + mWaveform = (VibrationEffect.Waveform) vib.getEffect(); + mVibration = new Vibration(vib.token, /* id= */ 0, /* effect= */ null, vib.attrs, + vib.uid, vib.opPkg, vib.reason); mTmpWorkSource.set(vib.uid); mWakeLock.setWorkSource(mTmpWorkSource); } @@ -1676,14 +1216,18 @@ public class VibratorService extends IVibratorService.Stub // appropriate intervals. long onDuration = getTotalOnDuration( timings, amplitudes, index - 1, repeat); - mVibration.effect = VibrationEffect.createOneShot( - onDuration, amplitude); + mVibration.updateEffect( + VibrationEffect.createOneShot(onDuration, amplitude)); doVibratorOn(mVibration); nextVibratorStopTime = now + onDuration; } else { // Vibrator is already ON, so just change its amplitude. - doVibratorSetAmplitude(amplitude); + mVibratorController.setAmplitude(amplitude); } + } else { + // Previous vibration should have already finished, but we make sure + // the vibrator will be off for the next step when amplitude is 0. + doVibratorOff(); } // We wait until the time this waveform step was supposed to end, @@ -1740,95 +1284,12 @@ public class VibratorService extends IVibratorService.Stub } } - /** Wrapper around the static-native methods of {@link VibratorService} for tests. */ - @VisibleForTesting - public static class NativeWrapper { - - private long mNativeServicePtr = 0; - - /** Checks if vibrator exists on device. */ - public boolean vibratorExists() { - return VibratorService.vibratorExists(mNativeServicePtr); - } - - /** Initializes connection to vibrator HAL service. */ - public void vibratorInit(OnCompleteListener listener) { - mNativeServicePtr = VibratorService.vibratorInit(listener); - long finalizerPtr = VibratorService.vibratorGetFinalizer(); - - if (finalizerPtr != 0) { - NativeAllocationRegistry registry = - NativeAllocationRegistry.createMalloced( - VibratorService.class.getClassLoader(), finalizerPtr); - registry.registerNativeAllocation(this, mNativeServicePtr); - } - } - - /** Turns vibrator on for given time. */ - public void vibratorOn(long milliseconds, long vibrationId) { - VibratorService.vibratorOn(mNativeServicePtr, milliseconds, vibrationId); - } - - /** Turns vibrator off. */ - public void vibratorOff() { - VibratorService.vibratorOff(mNativeServicePtr); - } - - /** Sets the amplitude for the vibrator to run. */ - public void vibratorSetAmplitude(int amplitude) { - VibratorService.vibratorSetAmplitude(mNativeServicePtr, amplitude); - } - - /** Returns all predefined effects supported by the device vibrator. */ - public int[] vibratorGetSupportedEffects() { - return VibratorService.vibratorGetSupportedEffects(mNativeServicePtr); - } - - /** Returns all compose primitives supported by the device vibrator. */ - public int[] vibratorGetSupportedPrimitives() { - return VibratorService.vibratorGetSupportedPrimitives(mNativeServicePtr); - } - - /** Turns vibrator on to perform one of the supported effects. */ - public long vibratorPerformEffect(long effect, long strength, long vibrationId) { - return VibratorService.vibratorPerformEffect( - mNativeServicePtr, effect, strength, vibrationId); - } - - /** Turns vibrator on to perform one of the supported composed effects. */ - public void vibratorPerformComposedEffect( - VibrationEffect.Composition.PrimitiveEffect[] effect, long vibrationId) { - VibratorService.vibratorPerformComposedEffect(mNativeServicePtr, effect, - vibrationId); - } - - /** Enabled the device vibrator to be controlled by another service. */ - public void vibratorSetExternalControl(boolean enabled) { - VibratorService.vibratorSetExternalControl(mNativeServicePtr, enabled); - } - - /** Returns all capabilities of the device vibrator. */ - public long vibratorGetCapabilities() { - return VibratorService.vibratorGetCapabilities(mNativeServicePtr); - } - - /** Enable always-on vibration with given id and effect. */ - public void vibratorAlwaysOnEnable(long id, long effect, long strength) { - VibratorService.vibratorAlwaysOnEnable(mNativeServicePtr, id, effect, strength); - } - - /** Disable always-on vibration for given id. */ - public void vibratorAlwaysOnDisable(long id) { - VibratorService.vibratorAlwaysOnDisable(mNativeServicePtr, id); - } - } - /** Point of injection for test dependencies */ @VisibleForTesting static class Injector { - NativeWrapper getNativeWrapper() { - return new NativeWrapper(); + VibratorController createVibratorController(OnVibrationCompleteListener listener) { + return new VibratorController(/* vibratorId= */ 0, listener); } Handler createHandler(Looper looper) { @@ -1851,9 +1312,9 @@ public class VibratorService extends IVibratorService.Stub // haptic feedback as part of the transition. So we don't cancel // system vibrations. if (mCurrentVibration != null - && !(mCurrentVibration.isHapticFeedback() - && mCurrentVibration.isFromSystem())) { - doCancelVibrateLocked(VibrationInfo.Status.CANCELLED); + && !(isHapticFeedback(mCurrentVibration) + && isFromSystem(mCurrentVibration))) { + doCancelVibrateLocked(Vibration.Status.CANCELLED); } } } @@ -1894,7 +1355,7 @@ public class VibratorService extends IVibratorService.Stub @Override public int onExternalVibrationStart(ExternalVibration vib) { - if (!hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) { + if (!mVibratorController.hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) { return IExternalVibratorService.SCALE_MUTE; } if (ActivityManager.checkComponentPermission(android.Manifest.permission.VIBRATE, @@ -1912,9 +1373,9 @@ public class VibratorService extends IVibratorService.Stub vibHolder.scale = SCALE_MUTE; if (mode == AppOpsManager.MODE_ERRORED) { Slog.w(TAG, "Would be an error: external vibrate from uid " + vib.getUid()); - endVibrationLocked(vibHolder, VibrationInfo.Status.ERROR_APP_OPS); + endVibrationLocked(vibHolder, Vibration.Status.ERROR_APP_OPS); } else { - endVibrationLocked(vibHolder, VibrationInfo.Status.IGNORED_APP_OPS); + endVibrationLocked(vibHolder, Vibration.Status.IGNORED_APP_OPS); } return IExternalVibratorService.SCALE_MUTE; } @@ -1929,10 +1390,13 @@ public class VibratorService extends IVibratorService.Stub if (mCurrentExternalVibration == null) { // If we're not under external control right now, then cancel any normal // vibration that may be playing and ready the vibrator for external control. - doCancelVibrateLocked(VibrationInfo.Status.CANCELLED); - setVibratorUnderExternalControl(true); + if (DEBUG) { + Slog.d(TAG, "Vibrator going under external control."); + } + doCancelVibrateLocked(Vibration.Status.CANCELLED); + mVibratorController.setExternalControl(true); } else { - endVibrationLocked(mCurrentExternalVibration, VibrationInfo.Status.CANCELLED); + endVibrationLocked(mCurrentExternalVibration, Vibration.Status.CANCELLED); } // At this point we either have an externally controlled vibration playing, or // no vibration playing. Since the interface defines that only one externally @@ -1962,12 +1426,12 @@ public class VibratorService extends IVibratorService.Stub if (DEBUG) { Slog.e(TAG, "Stopping external vibration" + vib); } - doCancelExternalVibrateLocked(VibrationInfo.Status.FINISHED); + doCancelExternalVibrateLocked(Vibration.Status.FINISHED); } } } - private void doCancelExternalVibrateLocked(VibrationInfo.Status status) { + private void doCancelExternalVibrateLocked(Vibration.Status status) { Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doCancelExternalVibrateLocked"); try { if (mCurrentExternalVibration == null) { @@ -1978,7 +1442,7 @@ public class VibratorService extends IVibratorService.Stub mCurrentExternalDeathRecipient); mCurrentExternalDeathRecipient = null; mCurrentExternalVibration = null; - setVibratorUnderExternalControl(false); + mVibratorController.setExternalControl(false); } finally { Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); } @@ -1991,7 +1455,7 @@ public class VibratorService extends IVibratorService.Stub if (DEBUG) { Slog.d(TAG, "External vibration finished because binder died"); } - doCancelExternalVibrateLocked(VibrationInfo.Status.CANCELLED); + doCancelExternalVibrateLocked(Vibration.Status.CANCELLED); } } } @@ -2176,19 +1640,19 @@ public class VibratorService extends IVibratorService.Stub Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "runCapabilities"); try (PrintWriter pw = getOutPrintWriter();) { pw.println("Vibrator capabilities:"); - if (hasCapability(IVibrator.CAP_ALWAYS_ON_CONTROL)) { + if (mVibratorController.hasCapability(IVibrator.CAP_ALWAYS_ON_CONTROL)) { pw.println(" Always on effects"); } - if (hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) { + if (mVibratorController.hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) { pw.println(" Compose effects"); } - if (hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL)) { + if (mVibratorController.hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL)) { pw.println(" Amplitude control"); } - if (hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) { + if (mVibratorController.hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) { pw.println(" External control"); } - if (hasCapability(IVibrator.CAP_EXTERNAL_AMPLITUDE_CONTROL)) { + if (mVibratorController.hasCapability(IVibrator.CAP_EXTERNAL_AMPLITUDE_CONTROL)) { pw.println(" External amplitude control"); } pw.println(""); diff --git a/services/core/java/com/android/server/adb/AdbShellCommand.java b/services/core/java/com/android/server/adb/AdbShellCommand.java index 76918529d071..d7e95df332fa 100644 --- a/services/core/java/com/android/server/adb/AdbShellCommand.java +++ b/services/core/java/com/android/server/adb/AdbShellCommand.java @@ -16,7 +16,7 @@ package com.android.server.adb; -import android.os.BasicShellCommandHandler; +import com.android.modules.utils.BasicShellCommandHandler; import java.io.PrintWriter; import java.util.Objects; diff --git a/services/core/java/com/android/server/am/ActiveInstrumentation.java b/services/core/java/com/android/server/am/ActiveInstrumentation.java index 43474d5f22d4..61ccf11ff73e 100644 --- a/services/core/java/com/android/server/am/ActiveInstrumentation.java +++ b/services/core/java/com/android/server/am/ActiveInstrumentation.java @@ -52,6 +52,9 @@ class ActiveInstrumentation { // Whether the caller holds START_ACTIVITIES_FROM_BACKGROUND permission boolean mHasBackgroundActivityStartsPermission; + // Whether the caller holds START_FOREGROUND_SERVICES_FROM_BACKGROUND permission + boolean mHasBackgroundForegroundServiceStartsPermission; + // As given to us Bundle mArguments; @@ -128,6 +131,8 @@ class ActiveInstrumentation { } pw.print("mHasBackgroundActivityStartsPermission="); pw.println(mHasBackgroundActivityStartsPermission); + pw.print("mHasBackgroundForegroundServiceStartsPermission="); + pw.println(mHasBackgroundForegroundServiceStartsPermission); pw.print(prefix); pw.print("mArguments="); pw.println(mArguments); } diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index d6f72990e4ac..872902626fb4 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -17,6 +17,8 @@ package com.android.server.am; import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND; +import static android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND; +import static android.Manifest.permission.SYSTEM_ALERT_WINDOW; import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST; @@ -56,9 +58,11 @@ import android.app.PendingIntent; import android.app.Service; import android.app.ServiceStartArgs; import android.app.admin.DevicePolicyEventLogger; +import android.app.compat.CompatChanges; import android.appwidget.AppWidgetManagerInternal; import android.compat.annotation.ChangeId; import android.compat.annotation.Disabled; +import android.compat.annotation.EnabledSince; import android.content.ComponentName; import android.content.ComponentName.WithComponentName; import android.content.Context; @@ -104,7 +108,6 @@ import android.webkit.WebViewZygote; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.app.procstats.ServiceState; -import com.android.internal.compat.IPlatformCompat; import com.android.internal.messages.nano.SystemMessageProto; import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.os.BatteryStatsImpl; @@ -147,30 +150,40 @@ public final class ActiveServices { public static final int FGS_FEATURE_DENIED = 0; public static final int FGS_FEATURE_ALLOWED_BY_UID_STATE = 1; - public static final int FGS_FEATURE_ALLOWED_BY_UID_VISIBLE = 2; - public static final int FGS_FEATURE_ALLOWED_BY_FLAG = 3; - public static final int FGS_FEATURE_ALLOWED_BY_SYSTEM_UID = 4; - public static final int FGS_FEATURE_ALLOWED_BY_INSTR_PERMISSION = 5; - public static final int FGS_FEATURE_ALLOWED_BY_TOKEN = 6; - public static final int FGS_FEATURE_ALLOWED_BY_PERMISSION = 7; - public static final int FGS_FEATURE_ALLOWED_BY_WHITELIST = 8; - public static final int FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER = 9; - public static final int FGS_FEATURE_ALLOWED_BY_PROC_STATE = 10; - public static final int FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST = 11; + public static final int FGS_FEATURE_ALLOWED_BY_PROC_STATE = 2; + public static final int FGS_FEATURE_ALLOWED_BY_UID_VISIBLE = 3; + public static final int FGS_FEATURE_ALLOWED_BY_FLAG = 4; + public static final int FGS_FEATURE_ALLOWED_BY_SYSTEM_UID = 5; + public static final int FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_ACTIVITY_PERMISSION = 6; + public static final int FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_FGS_PERMISSION = 7; + public static final int FGS_FEATURE_ALLOWED_BY_ACTIVITY_TOKEN = 8; + public static final int FGS_FEATURE_ALLOWED_BY_FGS_TOKEN = 9; + public static final int FGS_FEATURE_ALLOWED_BY_BACKGROUND_ACTIVITY_PERMISSION = 10; + public static final int FGS_FEATURE_ALLOWED_BY_BACKGROUND_FGS_PERMISSION = 12; + public static final int FGS_FEATURE_ALLOWED_BY_ALLOWLIST = 13; + public static final int FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER = 14; + public static final int FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST = 15; + public static final int FGS_FEATURE_ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION = 16; + public static final int FGS_FEATURE_ALLOWED_BY_FGS_BINDING = 17; @IntDef(flag = true, prefix = { "FGS_FEATURE_" }, value = { FGS_FEATURE_DENIED, FGS_FEATURE_ALLOWED_BY_UID_STATE, + FGS_FEATURE_ALLOWED_BY_PROC_STATE, FGS_FEATURE_ALLOWED_BY_UID_VISIBLE, FGS_FEATURE_ALLOWED_BY_FLAG, FGS_FEATURE_ALLOWED_BY_SYSTEM_UID, - FGS_FEATURE_ALLOWED_BY_INSTR_PERMISSION, - FGS_FEATURE_ALLOWED_BY_TOKEN, - FGS_FEATURE_ALLOWED_BY_PERMISSION, - FGS_FEATURE_ALLOWED_BY_WHITELIST, + FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_ACTIVITY_PERMISSION, + FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_FGS_PERMISSION, + FGS_FEATURE_ALLOWED_BY_ACTIVITY_TOKEN, + FGS_FEATURE_ALLOWED_BY_FGS_TOKEN, + FGS_FEATURE_ALLOWED_BY_BACKGROUND_ACTIVITY_PERMISSION, + FGS_FEATURE_ALLOWED_BY_BACKGROUND_FGS_PERMISSION, + FGS_FEATURE_ALLOWED_BY_ALLOWLIST, FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER, - FGS_FEATURE_ALLOWED_BY_PROC_STATE, - FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST + FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST, + FGS_FEATURE_ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION, + FGS_FEATURE_ALLOWED_BY_FGS_BINDING }) @Retention(RetentionPolicy.SOURCE) public @interface FgsFeatureRetCode {} @@ -242,14 +255,12 @@ public final class ActiveServices { AppWidgetManagerInternal mAppWidgetManagerInternal; // white listed packageName. - ArraySet<String> mWhiteListAllowWhileInUsePermissionInFgs = new ArraySet<>(); + ArraySet<String> mAllowListWhileInUsePermissionInFgs = new ArraySet<>(); // TODO: remove this after feature development is done private static final SimpleDateFormat DATE_FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - private final IPlatformCompat mPlatformCompat; - /** * The BG-launch FGS restriction feature is going to be allowed only for apps targetSdkVersion * is higher than R. @@ -258,6 +269,14 @@ public final class ActiveServices { @Disabled static final long FGS_BG_START_RESTRICTION_CHANGE_ID = 170668199L; + /** + * If a service can not become foreground service due to BG-FGS-launch restriction or other + * reasons, throws an IllegalStateException. + */ + @ChangeId + @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.S) + static final long FGS_START_EXCEPTION_CHANGE_ID = 174041399L; + final Runnable mLastAnrDumpClearer = new Runnable() { @Override public void run() { synchronized (mAm) { @@ -456,26 +475,25 @@ public final class ActiveServices { ? maxBg : ActivityManager.isLowRamDeviceStatic() ? 1 : 8; final IBinder b = ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE); - mPlatformCompat = IPlatformCompat.Stub.asInterface(b); } void systemServicesReady() { AppStateTracker ast = LocalServices.getService(AppStateTracker.class); ast.addServiceStateListener(new ForcedStandbyListener()); mAppWidgetManagerInternal = LocalServices.getService(AppWidgetManagerInternal.class); - setWhiteListAllowWhileInUsePermissionInFgs(); + setAllowListWhileInUsePermissionInFgs(); } - private void setWhiteListAllowWhileInUsePermissionInFgs() { + private void setAllowListWhileInUsePermissionInFgs() { final String attentionServicePackageName = mAm.mContext.getPackageManager().getAttentionServicePackageName(); if (!TextUtils.isEmpty(attentionServicePackageName)) { - mWhiteListAllowWhileInUsePermissionInFgs.add(attentionServicePackageName); + mAllowListWhileInUsePermissionInFgs.add(attentionServicePackageName); } final String systemCaptionsServicePackageName = mAm.mContext.getPackageManager().getSystemCaptionsServicePackageName(); if (!TextUtils.isEmpty(systemCaptionsServicePackageName)) { - mWhiteListAllowWhileInUsePermissionInFgs.add(systemCaptionsServicePackageName); + mAllowListWhileInUsePermissionInFgs.add(systemCaptionsServicePackageName); } } @@ -583,13 +601,27 @@ public final class ActiveServices { Slog.wtf(TAG, "Background started FGS " + r.mInfoAllowStartForeground); r.mLoggedInfoAllowStartForeground = true; } - if (r.mAllowStartForeground == FGS_FEATURE_DENIED - && (mAm.mConstants.mFlagFgsStartRestrictionEnabled - || isChangeEnabled(FGS_BG_START_RESTRICTION_CHANGE_ID, r))) { - Slog.w(TAG, "startForegroundService() not allowed due to " + if (r.mAllowStartForeground == FGS_FEATURE_DENIED && isBgFgsRestrictionEnabled(r)) { + String msg = "startForegroundService() not allowed due to " + "mAllowStartForeground false: service " - + r.shortInstanceName); + + r.shortInstanceName; + Slog.w(TAG, msg); showFgsBgRestrictedNotificationLocked(r); + ApplicationInfo aInfo = null; + try { + aInfo = AppGlobals.getPackageManager().getApplicationInfo( + callingPackage, ActivityManagerService.STOCK_PM_FLAGS, + userId); + } catch (android.os.RemoteException e) { + // pm is in same process, this will never happen. + } + if (aInfo == null) { + throw new SecurityException("startServiceLocked failed, " + + "could not resolve client package " + callingPackage); + } + if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID, aInfo.uid)) { + throw new IllegalStateException(msg); + } return null; } } @@ -1449,7 +1481,7 @@ public final class ActiveServices { } try { - boolean ignoreForeground = false; + String ignoreForeground = null; final int mode = mAm.getAppOpsManager().checkOpNoThrow( AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName); switch (mode) { @@ -1459,9 +1491,9 @@ public final class ActiveServices { break; case AppOpsManager.MODE_IGNORED: // Whoops, silently ignore this. - Slog.w(TAG, "Service.startForeground() not allowed due to app op: service " - + r.shortInstanceName); - ignoreForeground = true; + ignoreForeground = "Service.startForeground() not allowed due to app op: " + + "service " + r.shortInstanceName; + Slog.w(TAG, ignoreForeground); break; default: throw new SecurityException("Foreground not allowed as per app op"); @@ -1469,19 +1501,18 @@ public final class ActiveServices { // Apps that are TOP or effectively similar may call startForeground() on // their services even if they are restricted from doing that while in bg. - if (!ignoreForeground + if (ignoreForeground == null && !appIsTopLocked(r.appInfo.uid) && appRestrictedAnyInBackground(r.appInfo.uid, r.packageName)) { - Slog.w(TAG, - "Service.startForeground() not allowed due to bg restriction: service " - + r.shortInstanceName); + ignoreForeground = "Service.startForeground() not allowed due to bg restriction" + + ":service " + r.shortInstanceName; + Slog.w(TAG, ignoreForeground); // Back off of any foreground expectations around this service, since we've // just turned down its fg request. updateServiceForegroundLocked(r.app, false); - ignoreForeground = true; } - if (!ignoreForeground) { + if (ignoreForeground == null) { if (isFgsBgStart(r.mAllowStartForeground)) { if (!r.mLoggedInfoAllowStartForeground) { Slog.wtf(TAG, "Background started FGS " @@ -1489,14 +1520,13 @@ public final class ActiveServices { r.mLoggedInfoAllowStartForeground = true; } if (r.mAllowStartForeground == FGS_FEATURE_DENIED - && (mAm.mConstants.mFlagFgsStartRestrictionEnabled - || isChangeEnabled(FGS_BG_START_RESTRICTION_CHANGE_ID, r))) { - Slog.w(TAG, "Service.startForeground() not allowed due to " - + "mAllowStartForeground false: service " - + r.shortInstanceName); + && isBgFgsRestrictionEnabled(r)) { + ignoreForeground = "Service.startForeground() not allowed due to " + + "mAllowStartForeground false: service " + + r.shortInstanceName; + Slog.w(TAG, ignoreForeground); showFgsBgRestrictedNotificationLocked(r); updateServiceForegroundLocked(r.app, true); - ignoreForeground = true; } } } @@ -1505,7 +1535,7 @@ public final class ActiveServices { // services, so now that we've enforced the startForegroundService() contract // we only do the machinery of making the service foreground when the app // is not restricted. - if (!ignoreForeground) { + if (ignoreForeground == null) { if (r.foregroundId != id) { cancelForegroundNotificationLocked(r); r.foregroundId = id; @@ -1567,6 +1597,10 @@ public final class ActiveServices { if (DEBUG_FOREGROUND_SERVICE) { Slog.d(TAG, "Suppressing startForeground() for FAS " + r); } + if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID, r.appInfo.uid) + && isBgFgsRestrictionEnabled(r)) { + throw new IllegalStateException(ignoreForeground); + } } } finally { if (stopProcStatsOp) { @@ -2094,6 +2128,12 @@ public final class ActiveServices { "BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS"); } + if ((flags & Context.BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND) != 0) { + mAm.enforceCallingPermission( + android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND, + "BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND"); + } + final boolean callerFg = callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND; final boolean isBindExternal = (flags & Context.BIND_EXTERNAL_SERVICE) != 0; final boolean allowInstant = (flags & Context.BIND_ALLOW_INSTANT) != 0; @@ -2239,6 +2279,11 @@ public final class ActiveServices { if ((flags & Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS) != 0) { s.setAllowedBgActivityStartsByBinding(true); } + + if ((flags & Context.BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND) != 0) { + s.setAllowedBgFgsStartsByBinding(true); + } + if (s.app != null) { updateServiceClientActivitiesLocked(s.app, c, true); } @@ -2256,7 +2301,6 @@ public final class ActiveServices { return 0; } } - setFgsRestrictionLocked(callingPackage, callingPid, callingUid, service, s, false); if (s.app != null) { @@ -3647,6 +3691,9 @@ public final class ActiveServices { if ((c.flags & Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS) != 0) { s.updateIsAllowedBgActivityStartsByBinding(); } + if ((c.flags & Context.BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND) != 0) { + s.updateIsAllowedBgFgsStartsByBinding(); + } if (s.app != null) { updateServiceClientActivitiesLocked(s.app, c, true); } @@ -5113,13 +5160,22 @@ public final class ActiveServices { } if (ret == FGS_FEATURE_DENIED) { + for (int i = mAm.mProcessList.mLruProcesses.size() - 1; i >= 0; i--) { + final ProcessRecord pr = mAm.mProcessList.mLruProcesses.get(i); + if (pr.uid == callingUid) { + if (pr.areBackgroundActivityStartsAllowedByToken()) { + ret = FGS_FEATURE_ALLOWED_BY_ACTIVITY_TOKEN; + break; + } + } + } + } + + if (ret == FGS_FEATURE_DENIED) { if (r.app != null) { ActiveInstrumentation instr = r.app.getActiveInstrumentation(); if (instr != null && instr.mHasBackgroundActivityStartsPermission) { - ret = FGS_FEATURE_ALLOWED_BY_INSTR_PERMISSION; - } - if (r.app.areBackgroundActivityStartsAllowedByToken()) { - ret = FGS_FEATURE_ALLOWED_BY_TOKEN; + ret = FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_ACTIVITY_PERMISSION; } } } @@ -5127,15 +5183,15 @@ public final class ActiveServices { if (ret == FGS_FEATURE_DENIED) { if (mAm.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid) == PERMISSION_GRANTED) { - ret = FGS_FEATURE_ALLOWED_BY_PERMISSION; + ret = FGS_FEATURE_ALLOWED_BY_BACKGROUND_ACTIVITY_PERMISSION; } } if (ret == FGS_FEATURE_DENIED) { - final boolean isWhiteListedPackage = - mWhiteListAllowWhileInUsePermissionInFgs.contains(callingPackage); - if (isWhiteListedPackage) { - ret = FGS_FEATURE_ALLOWED_BY_WHITELIST; + final boolean isAllowedPackage = + mAllowListWhileInUsePermissionInFgs.contains(callingPackage); + if (isAllowedPackage) { + ret = FGS_FEATURE_ALLOWED_BY_ALLOWLIST; } } @@ -5187,6 +5243,39 @@ public final class ActiveServices { } if (ret == FGS_FEATURE_DENIED) { + for (int i = mAm.mProcessList.mLruProcesses.size() - 1; i >= 0; i--) { + final ProcessRecord pr = mAm.mProcessList.mLruProcesses.get(i); + if (pr.uid == callingUid) { + if (pr.areBackgroundFgsStartsAllowedByToken()) { + ret = FGS_FEATURE_ALLOWED_BY_FGS_BINDING; + break; + } else { + final ActiveInstrumentation instr = pr.getActiveInstrumentation(); + if (instr != null + && instr.mHasBackgroundForegroundServiceStartsPermission) { + ret = FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_FGS_PERMISSION; + break; + } + } + } + } + } + + if (ret == FGS_FEATURE_DENIED) { + if (mAm.checkPermission(START_FOREGROUND_SERVICES_FROM_BACKGROUND, callingPid, + callingUid) == PERMISSION_GRANTED) { + ret = FGS_FEATURE_ALLOWED_BY_BACKGROUND_FGS_PERMISSION; + } + } + + if (ret == FGS_FEATURE_DENIED) { + if (mAm.checkPermission(SYSTEM_ALERT_WINDOW, callingPid, + callingUid) == PERMISSION_GRANTED) { + ret = FGS_FEATURE_ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION; + } + } + + if (ret == FGS_FEATURE_DENIED) { if (mAm.mConstants.mFlagFgsStartTempAllowListEnabled && mAm.isOnDeviceIdleWhitelistLocked(r.appInfo.uid, false)) { // uid is on DeviceIdleController's allowlist. @@ -5217,26 +5306,36 @@ public final class ActiveServices { return "DENIED"; case FGS_FEATURE_ALLOWED_BY_UID_STATE: return "ALLOWED_BY_UID_STATE"; + case FGS_FEATURE_ALLOWED_BY_PROC_STATE: + return "ALLOWED_BY_PROC_STATE"; case FGS_FEATURE_ALLOWED_BY_UID_VISIBLE: return "ALLOWED_BY_UID_VISIBLE"; case FGS_FEATURE_ALLOWED_BY_FLAG: return "ALLOWED_BY_FLAG"; case FGS_FEATURE_ALLOWED_BY_SYSTEM_UID: return "ALLOWED_BY_SYSTEM_UID"; - case FGS_FEATURE_ALLOWED_BY_INSTR_PERMISSION: - return "ALLOWED_BY_INSTR_PERMISSION"; - case FGS_FEATURE_ALLOWED_BY_TOKEN: - return "ALLOWED_BY_TOKEN"; - case FGS_FEATURE_ALLOWED_BY_PERMISSION: - return "ALLOWED_BY_PERMISSION"; - case FGS_FEATURE_ALLOWED_BY_WHITELIST: + case FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_ACTIVITY_PERMISSION: + return "ALLOWED_BY_INSTR_BACKGROUND_ACTIVITY_PERMISSION"; + case FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_FGS_PERMISSION: + return "ALLOWED_BY_INSTR_BACKGROUND_FGS_PERMISSION"; + case FGS_FEATURE_ALLOWED_BY_ACTIVITY_TOKEN: + return "ALLOWED_BY_ACTIVITY_TOKEN"; + case FGS_FEATURE_ALLOWED_BY_FGS_TOKEN: + return "ALLOWED_BY_FGS_TOKEN"; + case FGS_FEATURE_ALLOWED_BY_BACKGROUND_ACTIVITY_PERMISSION: + return "ALLOWED_BY_BACKGROUND_ACTIVITY_PERMISSION"; + case FGS_FEATURE_ALLOWED_BY_BACKGROUND_FGS_PERMISSION: + return "ALLOWED_BY_BACKGROUND_FGS_PERMISSION"; + case FGS_FEATURE_ALLOWED_BY_ALLOWLIST: return "ALLOWED_BY_WHITELIST"; case FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER: return "ALLOWED_BY_DEVICE_OWNER"; - case FGS_FEATURE_ALLOWED_BY_PROC_STATE: - return "ALLOWED_BY_PROC_STATE"; case FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST: return "ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST"; + case FGS_FEATURE_ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION: + return "ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION"; + case FGS_FEATURE_ALLOWED_BY_FGS_BINDING: + return "ALLOWED_BY_FGS_BINDING"; default: return ""; } @@ -5271,11 +5370,10 @@ public final class ActiveServices { NOTE_FOREGROUND_SERVICE_BG_LAUNCH, n.build(), UserHandle.ALL); } - private boolean isChangeEnabled(long changeId, ServiceRecord r) { - boolean enabled = false; - try { - enabled = mPlatformCompat.isChangeEnabled(changeId, r.appInfo); - } catch (RemoteException e) { } - return enabled; + private boolean isBgFgsRestrictionEnabled(ServiceRecord r) { + if (mAm.mConstants.mFlagFgsStartRestrictionEnabled) { + return true; + } + return CompatChanges.isChangeEnabled(FGS_BG_START_RESTRICTION_CHANGE_ID, r.appInfo.uid); } } diff --git a/services/core/java/com/android/server/am/ActiveUids.java b/services/core/java/com/android/server/am/ActiveUids.java index c86c29f8ac2a..27be53a5faf4 100644 --- a/services/core/java/com/android/server/am/ActiveUids.java +++ b/services/core/java/com/android/server/am/ActiveUids.java @@ -52,9 +52,8 @@ final class ActiveUids { void clear() { mActiveUids.clear(); - if (mPostChangesToAtm) { - mService.mAtmInternal.onActiveUidsCleared(); - } + // It is only called for a temporal container with mPostChangesToAtm == false or test case. + // So there is no need to notify activity task manager. } UidRecord get(int uid) { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 9eca15b241f4..75e8b13ccc0d 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -22,6 +22,7 @@ import static android.Manifest.permission.FILTER_EVENTS; import static android.Manifest.permission.INTERACT_ACROSS_USERS; import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND; +import static android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND; import static android.app.ActivityManager.INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS; import static android.app.ActivityManager.INSTR_FLAG_DISABLE_ISOLATED_STORAGE; import static android.app.ActivityManager.INSTR_FLAG_DISABLE_TEST_API_CHECKS; @@ -13321,15 +13322,22 @@ public class ActivityManagerService extends IActivityManager.Stub // See if the caller is allowed to do this. Note we are checking against // the actual real caller (not whoever provided the operation as say a // PendingIntent), because that who is actually supplied the arguments. - if (checkComponentPermission( - android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, + if (checkComponentPermission(CHANGE_DEVICE_IDLE_TEMP_WHITELIST, + realCallingPid, realCallingUid, -1, true) + != PackageManager.PERMISSION_GRANTED + && checkComponentPermission(START_ACTIVITIES_FROM_BACKGROUND, + realCallingPid, realCallingUid, -1, true) + != PackageManager.PERMISSION_GRANTED + && checkComponentPermission(START_FOREGROUND_SERVICES_FROM_BACKGROUND, realCallingPid, realCallingUid, -1, true) != PackageManager.PERMISSION_GRANTED) { String msg = "Permission Denial: " + intent.getAction() + " broadcast from " + callerPackage + " (pid=" + callingPid + ", uid=" + callingUid + ")" + " requires " - + android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST; + + CHANGE_DEVICE_IDLE_TEMP_WHITELIST + " or " + + START_ACTIVITIES_FROM_BACKGROUND + " or " + + START_FOREGROUND_SERVICES_FROM_BACKGROUND; Slog.w(TAG, msg); throw new SecurityException(msg); } @@ -14289,8 +14297,10 @@ public class ActivityManagerService extends IActivityManager.Stub activeInstr.mHasBackgroundActivityStartsPermission = checkPermission( START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid) == PackageManager.PERMISSION_GRANTED; + activeInstr.mHasBackgroundForegroundServiceStartsPermission = checkPermission( + START_FOREGROUND_SERVICES_FROM_BACKGROUND, callingPid, callingUid) + == PackageManager.PERMISSION_GRANTED; activeInstr.mNoRestart = noRestart; - boolean disableHiddenApiChecks = ai.usesNonSdkApi() || (flags & INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS) != 0; boolean disableTestApiChecks = disableHiddenApiChecks diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java index 93dd1aa37a11..fdc0f5949da8 100644 --- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java +++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java @@ -37,13 +37,14 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.os.BatteryStatsImpl; +import com.android.internal.power.MeasuredEnergyArray; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.function.pooled.PooledLambda; -import java.util.concurrent.ExecutionException; import libcore.util.EmptyArray; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.Future; @@ -103,6 +104,9 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { private boolean mOnBatteryScreenOff; @GuardedBy("this") + private int mScreenState; + + @GuardedBy("this") private boolean mUseLatestStates = true; @GuardedBy("this") @@ -194,15 +198,17 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { } @Override - public Future<?> scheduleCpuSyncDueToScreenStateChange( - boolean onBattery, boolean onBatteryScreenOff) { + public Future<?> scheduleSyncDueToScreenStateChange( + int flags, boolean onBattery, boolean onBatteryScreenOff, int screenState) { synchronized (BatteryExternalStatsWorker.this) { if (mCurrentFuture == null || (mUpdateFlags & UPDATE_CPU) == 0) { mOnBattery = onBattery; mOnBatteryScreenOff = onBatteryScreenOff; mUseLatestStates = false; } - return scheduleSyncLocked("screen-state", UPDATE_CPU); + // always update screen state + mScreenState = screenState; + return scheduleSyncLocked("screen-state", flags); } } @@ -332,6 +338,7 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { final int[] uidsToRemove; final boolean onBattery; final boolean onBatteryScreenOff; + final int screenState; final boolean useLatestStates; synchronized (BatteryExternalStatsWorker.this) { updateFlags = mUpdateFlags; @@ -339,6 +346,7 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { uidsToRemove = mUidsToRemove.size() > 0 ? mUidsToRemove.toArray() : EmptyArray.INT; onBattery = mOnBattery; onBatteryScreenOff = mOnBatteryScreenOff; + screenState = mScreenState; useLatestStates = mUseLatestStates; mUpdateFlags = 0; mCurrentReason = null; @@ -360,7 +368,7 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { } try { updateExternalStatsLocked(reason, updateFlags, onBattery, - onBatteryScreenOff, useLatestStates); + onBatteryScreenOff, screenState, useLatestStates); } finally { if (DEBUG) { Slog.d(TAG, "end updateExternalStatsSync"); @@ -402,13 +410,14 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { }; @GuardedBy("mWorkerLock") - private void updateExternalStatsLocked(final String reason, int updateFlags, - boolean onBattery, boolean onBatteryScreenOff, boolean useLatestStates) { + private void updateExternalStatsLocked(final String reason, int updateFlags, boolean onBattery, + boolean onBatteryScreenOff, int screenState, boolean useLatestStates) { // We will request data from external processes asynchronously, and wait on a timeout. SynchronousResultReceiver wifiReceiver = null; SynchronousResultReceiver bluetoothReceiver = null; CompletableFuture<ModemActivityInfo> modemFuture = CompletableFuture.completedFuture(null); boolean railUpdated = false; + MeasuredEnergyArray energyArray = null; if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_WIFI) != 0) { // We were asked to fetch WiFi data. @@ -486,6 +495,13 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { } } + if ((updateFlags & UPDATE_ENERGY) != 0) { + synchronized (mStats) { + // TODO(b/172934873) evaluate a safe way to query the HAL without holding mStats + energyArray = mStats.getEnergyConsumptionDataLocked(); + } + } + final WifiActivityEnergyInfo wifiInfo = awaitControllerInfo(wifiReceiver); final BluetoothActivityEnergyInfo bluetoothInfo = awaitControllerInfo(bluetoothReceiver); ModemActivityInfo modemInfo = null; @@ -533,6 +549,11 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { Slog.w(TAG, "bluetooth info is invalid: " + bluetoothInfo); } } + + if ((updateFlags & UPDATE_ENERGY) != 0 && energyArray != null) { + // Always use what BatteryExternalStatsWorker thinks screenState is. + mStats.updateMeasuredEnergyStatsLocked(energyArray, screenState); + } } // WiFi and Modem state are updated without the mStats lock held, because they diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index c3ba15024a78..46e16bcbb1b6 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -21,6 +21,8 @@ import android.content.ContentResolver; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.hardware.power.stats.EnergyConsumerId; +import android.hardware.power.stats.EnergyConsumerResult; import android.os.BatteryStats; import android.os.BatteryStatsInternal; import android.os.Binder; @@ -59,6 +61,7 @@ import com.android.internal.os.BinderCallsStats; import com.android.internal.os.PowerProfile; import com.android.internal.os.RailStats; import com.android.internal.os.RpmStats; +import com.android.internal.power.MeasuredEnergyArray; import com.android.internal.util.DumpUtils; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.ParseUtils; @@ -66,6 +69,7 @@ import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.LocalServices; import com.android.server.Watchdog; import com.android.server.pm.UserManagerInternal; +import com.android.server.powerstats.PowerStatsHALWrapper; import java.io.File; import java.io.FileDescriptor; @@ -90,7 +94,7 @@ import java.util.concurrent.Future; public final class BatteryStatsService extends IBatteryStats.Stub implements PowerManagerInternal.LowPowerModeListener, BatteryStatsImpl.PlatformIdleStateCallback, - BatteryStatsImpl.RailEnergyDataCallback, + BatteryStatsImpl.MeasuredEnergyRetriever, Watchdog.Monitor { static final String TAG = "BatteryStatsService"; static final boolean DBG = false; @@ -115,6 +119,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub private CharBuffer mUtf16BufferStat = CharBuffer.allocate(MAX_LOW_POWER_STATS_SIZE); private static final int MAX_LOW_POWER_STATS_SIZE = 4096; + private final PowerStatsHALWrapper.IPowerStatsHALWrapper mPowerStatsHALWrapper; + private final HandlerThread mHandlerThread; private final Handler mHandler; private final Object mLock = new Object(); @@ -186,6 +192,43 @@ public final class BatteryStatsService extends IBatteryStats.Stub } } + @Override + public MeasuredEnergyArray getEnergyConsumptionData() { + final EnergyConsumerResult[] results = mPowerStatsHALWrapper.getEnergyConsumed(new int[0]); + if (results == null) return null; + final int size = results.length; + final int[] subsystems = new int[size]; + final long[] energyUJ = new long[size]; + + for (int i = 0; i < size; i++) { + final EnergyConsumerResult consumer = results[i]; + final int subsystem; + switch (consumer.energyConsumerId) { + case EnergyConsumerId.DISPLAY: + subsystem = MeasuredEnergyArray.SUBSYSTEM_DISPLAY; + break; + default: + continue; + } + subsystems[i] = subsystem; + energyUJ[i] = consumer.energyUWs; + } + return new MeasuredEnergyArray() { + @Override + public int getSubsystem(int index) { + return subsystems[index]; + } + @Override + public long getEnergy(int index) { + return energyUJ[index]; + } + @Override + public int size() { + return size; + } + }; + } + BatteryStatsService(Context context, File systemDir, Handler handler) { // BatteryStatsImpl expects the ActivityManagerService handler, so pass that one through. mContext = context; @@ -202,6 +245,12 @@ public final class BatteryStatsService extends IBatteryStats.Stub mHandlerThread = new HandlerThread("batterystats-handler"); mHandlerThread.start(); mHandler = new Handler(mHandlerThread.getLooper()); + + // TODO(b/173077356): Replace directly calling the HAL with PowerStatsService queries + // Make sure to init Hal Wrapper before creating BatteryStatsImpl. + mPowerStatsHALWrapper = new PowerStatsHALWrapper.PowerStatsHALWrapperImpl(); + mPowerStatsHALWrapper.initialize(); + mStats = new BatteryStatsImpl(systemDir, handler, this, this, mUserManagerUserInfoProvider); mWorker = new BatteryExternalStatsWorker(context, mStats); @@ -519,16 +568,24 @@ public final class BatteryStatsService extends IBatteryStats.Stub return data; } - public ParcelFileDescriptor getStatisticsStream() { + /** + * Returns parceled BatteryStats as a MemoryFile. + * + * @param forceUpdate If true, runs a sync to get fresh battery stats. Otherwise, + * returns the current values. + */ + public ParcelFileDescriptor getStatisticsStream(boolean forceUpdate) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.BATTERY_STATS, null); //Slog.i("foo", "SENDING BATTERY INFO:"); //mStats.dumpLocked(new LogPrinter(Log.INFO, "foo", Log.LOG_ID_SYSTEM)); Parcel out = Parcel.obtain(); - // Drain the handler queue to make sure we've handled all pending works, so we'll get - // an accurate stats. - awaitCompletion(); - syncStats("get-stats", BatteryExternalStatsWorker.UPDATE_ALL); + if (forceUpdate) { + // Drain the handler queue to make sure we've handled all pending works, so we'll get + // an accurate stats. + awaitCompletion(); + syncStats("get-stats", BatteryExternalStatsWorker.UPDATE_ALL); + } synchronized (mStats) { mStats.writeToParcel(out, 0); } @@ -1955,6 +2012,15 @@ public final class BatteryStatsService extends IBatteryStats.Stub } } + private void dumpMeasuredEnergyStats(PrintWriter pw) { + // Wait for the completion of pending works if there is any + awaitCompletion(); + syncStats("dump", BatteryExternalStatsWorker.UPDATE_ENERGY); + synchronized (mStats) { + mStats.dumpMeasuredEnergyStatsLocked(pw); + } + } + private int doEnableOrDisable(PrintWriter pw, int i, String[] args, boolean enable) { i++; if (i >= args.length) { @@ -2095,6 +2161,9 @@ public final class BatteryStatsService extends IBatteryStats.Stub } else if ("--cpu".equals(arg)) { dumpCpuStats(pw); return; + } else if ("--measured-energy".equals(arg)) { + dumpMeasuredEnergyStats(pw); + return; } else if ("-a".equals(arg)) { flags |= BatteryStats.DUMP_VERBOSE; } else if (arg.length() > 0 && arg.charAt(0) == '-'){ diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index 1b06dd90df83..cf4adc65a7fc 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -359,6 +359,8 @@ class ProcessRecord implements WindowProcessListener { // It must obtain the proc state from a persistent/top process or FGS, not transitive. int mAllowStartFgsState = PROCESS_STATE_NONEXISTENT; + private final ArraySet<Binder> mBackgroundFgsStartTokens = new ArraySet<>(); + void setStartParams(int startUid, HostingRecord hostingRecord, String seInfo, long startTime) { this.startUid = startUid; @@ -1965,6 +1967,18 @@ class ProcessRecord implements WindowProcessListener { } } + public void addAllowBackgroundFgsStartsToken(Binder entity) { + mBackgroundFgsStartTokens.add(entity); + } + + public void removeAllowBackgroundFgsStartsToken(Binder entity) { + mBackgroundFgsStartTokens.remove(entity); + } + + public boolean areBackgroundFgsStartsAllowedByToken() { + return !mBackgroundFgsStartTokens.isEmpty(); + } + ErrorDialogController getDialogController() { return mDialogController; } diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index 364ad21b93cf..e129561b8bc5 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -147,6 +147,10 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN @GuardedBy("ams") private List<IBinder> mBgActivityStartsByStartOriginatingTokens = new ArrayList<>(); + // any current binding to this service has BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND + // flag? if true, the process can start FGS from background. + boolean mIsAllowedBgFgsStartsByBinding; + // allow while-in-use permissions in foreground service or not. // while-in-use permissions in FGS started from background might be restricted. boolean mAllowWhileInUsePermissionInFgs; @@ -418,6 +422,10 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN pw.print(prefix); pw.print("mIsAllowedBgActivityStartsByStart="); pw.println(mIsAllowedBgActivityStartsByStart); } + if (mIsAllowedBgFgsStartsByBinding) { + pw.print(prefix); pw.print("mIsAllowedBgFgsStartsByBinding="); + pw.println(mIsAllowedBgFgsStartsByBinding); + } pw.print(prefix); pw.print("allowWhileInUsePermissionInFgs="); pw.println(mAllowWhileInUsePermissionInFgs); pw.print(prefix); pw.print("recentCallingPackage="); @@ -600,6 +608,11 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN } else { _proc.removeAllowBackgroundActivityStartsToken(this); } + if (mIsAllowedBgFgsStartsByBinding) { + _proc.addAllowBackgroundFgsStartsToken(this); + } else { + _proc.removeAllowBackgroundFgsStartsToken(this); + } } if (app != null && app != _proc) { // If the old app is allowed to start bg activities because of a service start, leave it @@ -686,11 +699,34 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN setAllowedBgActivityStartsByBinding(isAllowedByBinding); } + void updateIsAllowedBgFgsStartsByBinding() { + boolean isAllowedByBinding = false; + for (int conni = connections.size() - 1; conni >= 0; conni--) { + ArrayList<ConnectionRecord> cr = connections.valueAt(conni); + for (int i = 0; i < cr.size(); i++) { + if ((cr.get(i).flags + & Context.BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND) != 0) { + isAllowedByBinding = true; + break; + } + } + if (isAllowedByBinding) { + break; + } + } + setAllowedBgFgsStartsByBinding(isAllowedByBinding); + } + void setAllowedBgActivityStartsByBinding(boolean newValue) { mIsAllowedBgActivityStartsByBinding = newValue; updateParentProcessBgActivityStartsToken(); } + void setAllowedBgFgsStartsByBinding(boolean newValue) { + mIsAllowedBgFgsStartsByBinding = newValue; + updateParentProcessBgFgsStartsToken(); + } + /** * Called when the service is started with allowBackgroundActivityStarts set. We allow * it for background activity starts, setting up a callback to remove this ability after a @@ -777,6 +813,17 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN } } + private void updateParentProcessBgFgsStartsToken() { + if (app == null) { + return; + } + if (mIsAllowedBgFgsStartsByBinding) { + app.addAllowBackgroundFgsStartsToken(this); + } else { + app.removeAllowBackgroundFgsStartsToken(this); + } + } + /** * Returns the originating token if that's the only reason background activity starts are * allowed. In order for that to happen the service has to be allowed only due to starts, since diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index 09937e3a74c1..da4704097ad0 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -94,7 +94,6 @@ import android.app.AsyncNotedAppOp; import android.app.RuntimeAppOpAccessMessage; import android.app.SyncNotedAppOp; import android.content.BroadcastReceiver; -import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; @@ -3018,28 +3017,6 @@ public class AppOpsService extends IAppOpsService.Stub { } } - private boolean isTrustedVoiceServiceProxy(String packageName, int code) { - if (code != OP_RECORD_AUDIO) { - return false; - } - final String voiceRecognitionComponent = Settings.Secure.getString( - mContext.getContentResolver(), Settings.Secure.VOICE_RECOGNITION_SERVICE); - final String voiceInteractionComponent = Settings.Secure.getString( - mContext.getContentResolver(), Settings.Secure.VOICE_INTERACTION_SERVICE); - - final String voiceRecognitionServicePackageName = - getComponentPackageNameFromString(voiceRecognitionComponent); - final String voiceInteractionServicePackageName = - getComponentPackageNameFromString(voiceInteractionComponent); - return Objects.equals(packageName, voiceRecognitionServicePackageName) && Objects.equals( - voiceRecognitionServicePackageName, voiceInteractionServicePackageName); - } - - private String getComponentPackageNameFromString(String from) { - ComponentName componentName = from != null ? ComponentName.unflattenFromString(from) : null; - return componentName != null ? componentName.getPackageName() : ""; - } - @Override public int noteProxyOperation(int code, int proxiedUid, String proxiedPackageName, String proxiedAttributionTag, int proxyUid, String proxyPackageName, @@ -3057,10 +3034,12 @@ public class AppOpsService extends IAppOpsService.Stub { // This is a workaround for R QPR, new API change is not allowed. We only allow the current // voice recognizer is also the voice interactor to noteproxy op. - final boolean isTrustVoiceServiceProxy = isTrustedVoiceServiceProxy(proxyPackageName, code); + final boolean isTrustVoiceServiceProxy = + AppOpsManager.isTrustedVoiceServiceProxy(mContext, proxyPackageName, code); + final boolean isSelfBlame = Binder.getCallingUid() == proxiedUid; final boolean isProxyTrusted = mContext.checkPermission( Manifest.permission.UPDATE_APP_OPS_STATS, -1, proxyUid) - == PackageManager.PERMISSION_GRANTED || isTrustVoiceServiceProxy; + == PackageManager.PERMISSION_GRANTED || isTrustVoiceServiceProxy || isSelfBlame; final int proxyFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXY : AppOpsManager.OP_FLAG_UNTRUSTED_PROXY; @@ -3524,10 +3503,12 @@ public class AppOpsService extends IAppOpsService.Stub { // This is a workaround for R QPR, new API change is not allowed. We only allow the current // voice recognizer is also the voice interactor to noteproxy op. - final boolean isTrustVoiceServiceProxy = isTrustedVoiceServiceProxy(proxyPackageName, code); + final boolean isTrustVoiceServiceProxy = + AppOpsManager.isTrustedVoiceServiceProxy(mContext, proxyPackageName, code); + final boolean isSelfBlame = Binder.getCallingUid() == proxiedUid; final boolean isProxyTrusted = mContext.checkPermission( Manifest.permission.UPDATE_APP_OPS_STATS, -1, proxyUid) - == PackageManager.PERMISSION_GRANTED || isTrustVoiceServiceProxy; + == PackageManager.PERMISSION_GRANTED || isTrustVoiceServiceProxy || isSelfBlame; final int proxyFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXY : AppOpsManager.OP_FLAG_UNTRUSTED_PROXY; diff --git a/services/core/java/com/android/server/appop/TEST_MAPPING b/services/core/java/com/android/server/appop/TEST_MAPPING index 84de25c06ebf..72d3835efc26 100644 --- a/services/core/java/com/android/server/appop/TEST_MAPPING +++ b/services/core/java/com/android/server/appop/TEST_MAPPING @@ -48,10 +48,10 @@ ] }, { - "name": "CtsStatsdHostTestCases", + "name": "CtsStatsdAtomHostTestCases", "options": [ { - "include-filter": "android.cts.statsd.atom.UidAtomTests#testAppOps" + "include-filter": "android.cts.statsdatom.appops.AppOpsTests#testAppOps" } ] } diff --git a/services/core/java/com/android/server/biometrics/AuthSession.java b/services/core/java/com/android/server/biometrics/AuthSession.java index efc025dd7b0c..e4d90525fc68 100644 --- a/services/core/java/com/android/server/biometrics/AuthSession.java +++ b/services/core/java/com/android/server/biometrics/AuthSession.java @@ -142,6 +142,7 @@ public final class AuthSession implements IBinder.DeathRecipient { @NonNull PromptInfo promptInfo, boolean debugEnabled, @NonNull List<FingerprintSensorPropertiesInternal> fingerprintSensorProperties) { + Slog.d(TAG, "Creating AuthSession with: " + preAuthInfo); mContext = context; mStatusBarService = statusBarService; mSysuiReceiver = sysuiReceiver; diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java index a471664b19b7..0194259d6289 100644 --- a/services/core/java/com/android/server/biometrics/BiometricService.java +++ b/services/core/java/com/android/server/biometrics/BiometricService.java @@ -500,7 +500,11 @@ public class BiometricService extends SystemService { final List<SensorPropertiesInternal> sensors = new ArrayList<>(); for (BiometricSensor sensor : mSensors) { - sensors.add(sensor.impl.getSensorProperties(opPackageName)); + // Explicitly re-create as the super class, since AIDL doesn't play nicely with + // "List<? extends SensorPropertiesInternal> ... + final SensorPropertiesInternal prop = SensorPropertiesInternal + .from(sensor.impl.getSensorProperties(opPackageName)); + sensors.add(prop); } return sensors; @@ -1043,7 +1047,7 @@ public class BiometricService extends SystemService { final Pair<Integer, Integer> preAuthStatus = preAuthInfo.getPreAuthenticateStatus(); Slog.d(TAG, "handleAuthenticate: modality(" + preAuthStatus.first - + "), status(" + preAuthStatus.second + ")"); + + "), status(" + preAuthStatus.second + "), preAuthInfo: " + preAuthInfo); if (preAuthStatus.second == BiometricConstants.BIOMETRIC_SUCCESS) { // If BIOMETRIC_WEAK or BIOMETRIC_STRONG are allowed, but not enrolled, but diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlNativeHandleUtils.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlNativeHandleUtils.java new file mode 100644 index 000000000000..77e6cebed789 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlNativeHandleUtils.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors.face.aidl; + +import android.annotation.Nullable; +import android.os.ParcelFileDescriptor; + +import java.io.FileDescriptor; +import java.io.IOException; + +/** + * A utility class for the AIDL implementation of NativeHandle - {@link + * android.hardware.common.NativeHandle}. + */ +public final class AidlNativeHandleUtils { + + /** + * Converts a {@link android.os.NativeHandle} to a {@link android.hardware.common.NativeHandle} + * by duplicating the underlying file descriptors. + * + * Both the original and new handle must be closed after use. + * + * @param handle {@link android.os.NativeHandle}. Can be null. + * @return a {@link android.hardware.common.NativeHandle} representation of {@code handle}. + * Returns null if {@code handle} is null. + * @throws IOException if any of the underlying calls to {@code dup} fail. + */ + public static @Nullable android.hardware.common.NativeHandle dup( + @Nullable android.os.NativeHandle handle) throws IOException { + if (handle == null) { + return null; + } + android.hardware.common.NativeHandle res = new android.hardware.common.NativeHandle(); + final FileDescriptor[] fds = handle.getFileDescriptors(); + res.ints = handle.getInts().clone(); + res.fds = new ParcelFileDescriptor[fds.length]; + for (int i = 0; i < fds.length; ++i) { + res.fds[i] = ParcelFileDescriptor.dup(fds[i]); + } + return res; + } + + /** + * Closes the file descriptors contained within a {@link android.hardware.common.NativeHandle}. + * This is a no-op if the handle is null. + * + * This should only be used for handles that own their file descriptors, for example handles + * obtained using {@link #dup(android.os.NativeHandle)}. + * + * @param handle {@link android.hardware.common.NativeHandle}. Can be null. + * @throws IOException if any of the underlying calls to {@code close} fail. + */ + public static void close(@Nullable android.hardware.common.NativeHandle handle) + throws IOException { + if (handle != null) { + for (ParcelFileDescriptor fd : handle.fds) { + if (fd != null) { + fd.close(); + } + } + } + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java index f950862bacd9..f09df1e1812e 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java @@ -39,6 +39,7 @@ import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.EnrollClient; import com.android.server.biometrics.sensors.face.FaceUtils; +import java.io.IOException; import java.util.ArrayList; /** @@ -51,6 +52,7 @@ public class FaceEnrollClient extends EnrollClient<ISession> { @NonNull private final int[] mEnrollIgnoreList; @NonNull private final int[] mEnrollIgnoreListVendor; @Nullable private ICancellationSignal mCancellationSignal; + @Nullable private android.hardware.common.NativeHandle mPreviewSurface; private final int mMaxTemplatesPerUser; FaceEnrollClient(@NonNull Context context, @NonNull LazyDaemon<ISession> lazyDaemon, @@ -66,6 +68,24 @@ public class FaceEnrollClient extends EnrollClient<ISession> { mEnrollIgnoreListVendor = getContext().getResources() .getIntArray(R.array.config_face_acquire_vendor_enroll_ignorelist); mMaxTemplatesPerUser = maxTemplatesPerUser; + try { + // We must manually close the duplicate handle after it's no longer needed. + // The caller is responsible for closing the original handle. + mPreviewSurface = AidlNativeHandleUtils.dup(previewSurface); + } catch (IOException e) { + mPreviewSurface = null; + Slog.e(TAG, "Failed to dup previewSurface", e); + } + } + + @Override + public void destroy() { + try { + AidlNativeHandleUtils.close(mPreviewSurface); + } catch (IOException e) { + Slog.e(TAG, "Failed to close mPreviewSurface", e); + } + super.destroy(); } @Override @@ -94,10 +114,9 @@ public class FaceEnrollClient extends EnrollClient<ISession> { try { // TODO(b/172593978): Pass features. - // TODO(b/172593521): Pass mPreviewSurface as android.hardware.common.NativeHandle. mCancellationSignal = getFreshDaemon().enroll(mSequentialId, HardwareAuthTokenUtils.toHardwareAuthToken(mHardwareAuthToken), - null /* mPreviewSurface */); + mPreviewSurface); } catch (RemoteException e) { Slog.e(TAG, "Remote exception when requesting enroll", e); onError(BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS, 0 /* vendorCode */); diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java index 26f62644e38b..cc9298603a3e 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java @@ -65,7 +65,6 @@ import java.util.List; * Provider for a single instance of the {@link IFace} HAL. */ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { - private static final String TAG = "FaceProvider"; private static final int ENROLL_TIMEOUT_SEC = 75; private boolean mTestHalEnabled; @@ -88,7 +87,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { public void onTaskStackChanged() { mHandler.post(() -> { for (int i = 0; i < mSensors.size(); i++) { - final ClientMonitor<?> client = mSensors.get(i).getScheduler() + final ClientMonitor<?> client = mSensors.valueAt(i).getScheduler() .getCurrentClient(); if (!(client instanceof AuthenticationClient)) { Slog.e(getTag(), "Task stack changed for client: " + client); @@ -108,7 +107,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { && !client.isAlreadyDone()) { Slog.e(getTag(), "Stopping background authentication, top: " + topPackage + " currentClient: " + client); - mSensors.get(i).getScheduler() + mSensors.valueAt(i).getScheduler() .cancelAuthentication(client.getToken()); } } @@ -205,7 +204,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { private void createNewSessionWithoutHandler(@NonNull IFace daemon, int sensorId, int userId) throws RemoteException { - // Note that per IFingerprint createSession contract, this method will block until all + // Note that per IFace createSession contract, this method will block until all // existing operations are canceled/finished. However, also note that this is fine, since // this method "withoutHandler" means it should only ever be invoked from the worker thread, // so callers will never be blocked. @@ -550,12 +549,13 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { JSONObject dump = new JSONObject(); try { - dump.put("service", "Face Manager"); + dump.put("service", getTag()); JSONArray sets = new JSONArray(); for (UserInfo user : UserManager.get(mContext).getUsers()) { final int userId = user.getUserHandle().getIdentifier(); - final int c = FaceUtils.getInstance().getBiometricsForUser(mContext, userId).size(); + final int c = FaceUtils.getInstance(sensorId) + .getBiometricsForUser(mContext, userId).size(); JSONObject set = new JSONObject(); set.put("id", userId); set.put("count", c); @@ -574,7 +574,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { dump.put("prints", sets); } catch (JSONException e) { - Slog.e(TAG, "dump formatting failure", e); + Slog.e(getTag(), "dump formatting failure", e); } pw.println(dump); pw.println("HAL deaths since last reboot: " + performanceTracker.getHALDeathCount()); diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestSession.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestSession.java index 9707eddfee74..fbc26c6498b3 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestSession.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestSession.java @@ -24,7 +24,6 @@ import android.hardware.common.NativeHandle; import android.hardware.keymaster.HardwareAuthToken; import android.os.Binder; import android.os.IBinder; -import android.os.RemoteException; /** * Test session that provides mostly no-ops. @@ -59,7 +58,7 @@ public class TestSession extends ISession.Stub { public ICancellationSignal authenticate(int cookie, long operationId) { return new ICancellationSignal() { @Override - public void cancel() throws RemoteException { + public void cancel() { mHalSessionCallback.onError(Error.CANCELED, 0 /* vendorCode */); } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java index 3555bbe629ae..d384bc645d61 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java @@ -71,7 +71,6 @@ import com.android.server.biometrics.sensors.face.FaceUtils; import com.android.server.biometrics.sensors.face.LockoutHalImpl; import com.android.server.biometrics.sensors.face.ServiceProvider; import com.android.server.biometrics.sensors.face.UsageStats; -import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils; import org.json.JSONArray; import org.json.JSONException; @@ -259,8 +258,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { if (!removed.isEmpty()) { // Convert to old fingerprint-like behavior, where remove() receives - // one removal - // at a time. This way, remove can share some more common code. + // one removal at a time. This way, remove can share some more common code. for (int i = 0; i < removed.size(); i++) { final int id = removed.get(i); final Face face = new Face("", id, deviceId); @@ -290,21 +288,16 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { final EnumerateConsumer enumerateConsumer = (EnumerateConsumer) client; if (!faceIds.isEmpty()) { - // Convert to old fingerprint-like behavior, where enumerate() - // receives one - // template at a time. This way, enumerate can share some more common - // code. + // Convert to old fingerprint-like behavior, where enumerate() receives one + // template at a time. This way, enumerate can share some more common code. for (int i = 0; i < faceIds.size(); i++) { final Face face = new Face("", faceIds.get(i), deviceId); enumerateConsumer.onEnumerationResult(face, faceIds.size() - i - 1); } } else { - // For face, the HIDL contract is to receive an empty list when there - // are no - // templates enrolled. Send a null identifier since we don't consume - // them - // anywhere, and send remaining == 0 so this code can be shared with - // Fingerprint@2.1 + // For face, the HIDL contract is to receive an empty list when there are no + // templates enrolled. Send a null identifier since we don't consume them + // anywhere, and send remaining == 0 so this code can be shared with Face@1.1 enumerateConsumer.onEnumerationResult(null /* identifier */, 0); } }); @@ -803,7 +796,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { JSONObject dump = new JSONObject(); try { - dump.put("service", "Face Manager"); + dump.put("service", TAG); JSONArray sets = new JSONArray(); for (UserInfo user : UserManager.get(mContext).getUsers()) { diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java index 437ecd7a0f8d..fd1181b616c7 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java @@ -19,7 +19,6 @@ package com.android.server.biometrics.sensors.fingerprint.aidl; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; -import android.hardware.biometrics.BiometricFaceConstants; import android.hardware.biometrics.BiometricFingerprintConstants; import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.common.ICancellationSignal; @@ -89,7 +88,8 @@ class FingerprintEnrollClient extends EnrollClient<ISession> implements Udfps { HardwareAuthTokenUtils.toHardwareAuthToken(mHardwareAuthToken)); } catch (RemoteException e) { Slog.e(TAG, "Remote exception when requesting enroll", e); - onError(BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS, 0 /* vendorCode */); + onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_UNABLE_TO_PROCESS, + 0 /* vendorCode */); mCallback.onClientFinished(this, false /* success */); } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java index db34d1444650..98c32cbcfc4a 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java @@ -53,6 +53,10 @@ import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDisp import com.android.server.biometrics.sensors.fingerprint.ServiceProvider; import com.android.server.biometrics.sensors.fingerprint.Udfps; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; @@ -84,7 +88,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi public void onTaskStackChanged() { mHandler.post(() -> { for (int i = 0; i < mSensors.size(); i++) { - final ClientMonitor<?> client = mSensors.get(i).getScheduler() + final ClientMonitor<?> client = mSensors.valueAt(i).getScheduler() .getCurrentClient(); if (!(client instanceof AuthenticationClient)) { Slog.e(getTag(), "Task stack changed for client: " + client); @@ -104,7 +108,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi && !client.isAlreadyDone()) { Slog.e(getTag(), "Stopping background authentication, top: " + topPackage + " currentClient: " + client); - mSensors.get(i).getScheduler() + mSensors.valueAt(i).getScheduler() .cancelAuthentication(client.getToken()); } } @@ -593,7 +597,42 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @Override public void dumpInternal(int sensorId, @NonNull PrintWriter pw) { + PerformanceTracker performanceTracker = + PerformanceTracker.getInstanceForSensorId(sensorId); + + JSONObject dump = new JSONObject(); + try { + dump.put("service", getTag()); + + JSONArray sets = new JSONArray(); + for (UserInfo user : UserManager.get(mContext).getUsers()) { + final int userId = user.getUserHandle().getIdentifier(); + final int c = FingerprintUtils.getInstance(sensorId) + .getBiometricsForUser(mContext, userId).size(); + JSONObject set = new JSONObject(); + set.put("id", userId); + set.put("count", c); + set.put("accept", performanceTracker.getAcceptForUser(userId)); + set.put("reject", performanceTracker.getRejectForUser(userId)); + set.put("acquire", performanceTracker.getAcquireForUser(userId)); + set.put("lockout", performanceTracker.getTimedLockoutForUser(userId)); + set.put("permanentLockout", performanceTracker.getPermanentLockoutForUser(userId)); + // cryptoStats measures statistics about secure fingerprint transactions + // (e.g. to unlock password storage, make secure purchases, etc.) + set.put("acceptCrypto", performanceTracker.getAcceptCryptoForUser(userId)); + set.put("rejectCrypto", performanceTracker.getRejectCryptoForUser(userId)); + set.put("acquireCrypto", performanceTracker.getAcquireCryptoForUser(userId)); + sets.put(set); + } + + dump.put("prints", sets); + } catch (JSONException e) { + Slog.e(getTag(), "dump formatting failure", e); + } + pw.println(dump); + pw.println("HAL deaths since last reboot: " + performanceTracker.getHALDeathCount()); + mSensors.get(sensorId).getScheduler().dump(pw); } @NonNull diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestSession.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestSession.java index d6378780594f..ddae1107ff77 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestSession.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestSession.java @@ -18,8 +18,11 @@ package com.android.server.biometrics.sensors.fingerprint.aidl; import android.annotation.NonNull; import android.hardware.biometrics.common.ICancellationSignal; +import android.hardware.biometrics.face.Error; import android.hardware.biometrics.fingerprint.ISession; import android.hardware.keymaster.HardwareAuthToken; +import android.os.Binder; +import android.os.IBinder; import android.util.Slog; /** @@ -37,24 +40,32 @@ class TestSession extends ISession.Stub { @Override public void generateChallenge(int cookie, int timeoutSec) { - + mHalSessionCallback.onChallengeGenerated(0 /* challenge */); } @Override public void revokeChallenge(int cookie, long challenge) { - + mHalSessionCallback.onChallengeRevoked(challenge); } @Override public ICancellationSignal enroll(int cookie, HardwareAuthToken hat) { - Slog.d(TAG, "enroll"); return null; } @Override public ICancellationSignal authenticate(int cookie, long operationId) { - Slog.d(TAG, "authenticate"); - return null; + return new ICancellationSignal() { + @Override + public void cancel() { + mHalSessionCallback.onError(Error.CANCELED, 0 /* vendorCode */); + } + + @Override + public IBinder asBinder() { + return new Binder(); + } + }; } @Override @@ -86,7 +97,7 @@ class TestSession extends ISession.Stub { @Override public void resetLockout(int cookie, HardwareAuthToken hat) { - + mHalSessionCallback.onLockoutCleared(); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java index 11372a30599d..f38dd092007a 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java @@ -776,7 +776,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider JSONObject dump = new JSONObject(); try { - dump.put("service", "Fingerprint Manager"); + dump.put("service", TAG); JSONArray sets = new JSONArray(); for (UserInfo user : UserManager.get(mContext).getUsers()) { diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java index c750b906c9b6..2a89a880b680 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java @@ -65,7 +65,7 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint final int enrolled = mBiometricUtils.getBiometricsForUser(getContext(), getTargetUserId()) .size(); if (enrolled >= limit) { - Slog.w(TAG, "Too many faces registered, user: " + getTargetUserId()); + Slog.w(TAG, "Too many fingerprints registered, user: " + getTargetUserId()); return true; } return false; diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java index a4ae9c87c694..2a814268da25 100644 --- a/services/core/java/com/android/server/camera/CameraServiceProxy.java +++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java @@ -22,6 +22,8 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.hardware.CameraSessionStats; +import android.hardware.CameraStreamStats; import android.hardware.ICameraService; import android.hardware.ICameraServiceProxy; import android.media.AudioManager; @@ -36,11 +38,13 @@ import android.os.RemoteException; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserManager; +import android.stats.camera.nano.CameraStreamProto; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; import com.android.internal.annotations.GuardedBy; +import com.android.framework.protobuf.nano.MessageNano; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.FrameworkStatsLog; @@ -97,7 +101,10 @@ public class CameraServiceProxy extends SystemService @interface DeviceStateFlags {} // Maximum entries to keep in usage history before dumping out - private static final int MAX_USAGE_HISTORY = 100; + private static final int MAX_USAGE_HISTORY = 20; + // Number of stream statistics being dumped for each camera session + // Must be equal to number of CameraStreamProto in CameraActionEvent + private static final int MAX_STREAM_STATISTICS = 5; private final Context mContext; private final ServiceThread mHandlerThread; @@ -123,7 +130,6 @@ public class CameraServiceProxy extends SystemService private final ArrayMap<String, CameraUsageEvent> mActiveCameraUsage = new ArrayMap<>(); private final List<CameraUsageEvent> mCameraUsageHistory = new ArrayList<>(); - private final MetricsLogger mLogger = new MetricsLogger(); private static final String NFC_NOTIFICATION_PROP = "ro.camera.notify_nfc"; private static final String NFC_SERVICE_BINDER_NAME = "nfc"; private static final IBinder nfcInterfaceToken = new Binder(); @@ -137,27 +143,50 @@ public class CameraServiceProxy extends SystemService * Structure to track camera usage */ private static class CameraUsageEvent { + public final String mCameraId; public final int mCameraFacing; public final String mClientName; public final int mAPILevel; + public final boolean mIsNdk; + public final int mAction; + public final int mLatencyMs; + public final int mOperatingMode; private boolean mCompleted; + public int mInternalReconfigure; + public long mRequestCount; + public long mResultErrorCount; + public boolean mDeviceError; + public List<CameraStreamStats> mStreamStats; private long mDurationOrStartTimeMs; // Either start time, or duration once completed - public CameraUsageEvent(int facing, String clientName, int apiLevel) { + CameraUsageEvent(String cameraId, int facing, String clientName, int apiLevel, + boolean isNdk, int action, int latencyMs, int operatingMode) { + mCameraId = cameraId; mCameraFacing = facing; mClientName = clientName; mAPILevel = apiLevel; mDurationOrStartTimeMs = SystemClock.elapsedRealtime(); mCompleted = false; + mIsNdk = isNdk; + mAction = action; + mLatencyMs = latencyMs; + mOperatingMode = operatingMode; } - public void markCompleted() { + public void markCompleted(int internalReconfigure, long requestCount, + long resultErrorCount, boolean deviceError, + List<CameraStreamStats> streamStats) { if (mCompleted) { return; } mCompleted = true; mDurationOrStartTimeMs = SystemClock.elapsedRealtime() - mDurationOrStartTimeMs; + mInternalReconfigure = internalReconfigure; + mRequestCount = requestCount; + mResultErrorCount = resultErrorCount; + mDeviceError = deviceError; + mStreamStats = streamStats; if (CameraServiceProxy.DEBUG) { Slog.v(TAG, "A camera facing " + cameraFacingToString(mCameraFacing) + " was in use by " + mClientName + " for " + @@ -211,19 +240,22 @@ public class CameraServiceProxy extends SystemService } @Override - public void notifyCameraState(String cameraId, int newCameraState, int facing, - String clientName, int apiLevel) { + public void notifyCameraState(CameraSessionStats cameraState) { if (Binder.getCallingUid() != Process.CAMERASERVER_UID) { Slog.e(TAG, "Calling UID: " + Binder.getCallingUid() + " doesn't match expected " + " camera service UID!"); return; } - String state = cameraStateToString(newCameraState); - String facingStr = cameraFacingToString(facing); - if (DEBUG) Slog.v(TAG, "Camera " + cameraId + " facing " + facingStr + " state now " + - state + " for client " + clientName + " API Level " + apiLevel); + String state = cameraStateToString(cameraState.getNewCameraState()); + String facingStr = cameraFacingToString(cameraState.getFacing()); + if (DEBUG) { + Slog.v(TAG, "Camera " + cameraState.getCameraId() + + " facing " + facingStr + " state now " + state + + " for client " + cameraState.getClientName() + + " API Level " + cameraState.getApiLevel()); + } - updateActivityCount(cameraId, newCameraState, facing, clientName, apiLevel); + updateActivityCount(cameraState); } }; @@ -385,20 +417,80 @@ public class CameraServiceProxy extends SystemService private void logCameraUsageEvent(CameraUsageEvent e) { int facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__UNKNOWN; switch(e.mCameraFacing) { - case ICameraServiceProxy.CAMERA_FACING_BACK: + case CameraSessionStats.CAMERA_FACING_BACK: facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__BACK; break; - case ICameraServiceProxy.CAMERA_FACING_FRONT: + case CameraSessionStats.CAMERA_FACING_FRONT: facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__FRONT; break; - case ICameraServiceProxy.CAMERA_FACING_EXTERNAL: + case CameraSessionStats.CAMERA_FACING_EXTERNAL: facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__EXTERNAL; break; default: Slog.w(TAG, "Unknown camera facing: " + e.mCameraFacing); } + + int streamCount = 0; + if (e.mStreamStats != null) { + streamCount = e.mStreamStats.size(); + } + if (CameraServiceProxy.DEBUG) { + Slog.v(TAG, "CAMERA_ACTION_EVENT: action " + e.mAction + + " clientName " + e.mClientName + + ", duration " + e.getDuration() + + ", APILevel " + e.mAPILevel + + ", cameraId " + e.mCameraId + + ", facing " + facing + + ", isNdk " + e.mIsNdk + + ", latencyMs " + e.mLatencyMs + + ", operatingMode " + e.mOperatingMode + + ", internalReconfigure " + e.mInternalReconfigure + + ", requestCount " + e.mRequestCount + + ", resultErrorCount " + e.mResultErrorCount + + ", deviceError " + e.mDeviceError + + ", streamCount is " + streamCount); + } + // Convert from CameraStreamStats to CameraStreamProto + CameraStreamProto[] streamProtos = new CameraStreamProto[MAX_STREAM_STATISTICS]; + for (int i = 0; i < MAX_STREAM_STATISTICS; i++) { + streamProtos[i] = new CameraStreamProto(); + if (i < streamCount) { + CameraStreamStats streamStats = e.mStreamStats.get(i); + streamProtos[i].width = streamStats.getWidth(); + streamProtos[i].height = streamStats.getHeight(); + streamProtos[i].format = streamStats.getFormat(); + streamProtos[i].dataSpace = streamStats.getDataSpace(); + streamProtos[i].usage = streamStats.getUsage(); + streamProtos[i].requestCount = streamStats.getRequestCount(); + streamProtos[i].errorCount = streamStats.getErrorCount(); + streamProtos[i].firstCaptureLatencyMillis = streamStats.getStartLatencyMs(); + streamProtos[i].maxHalBuffers = streamStats.getMaxHalBuffers(); + streamProtos[i].maxAppBuffers = streamStats.getMaxAppBuffers(); + + if (CameraServiceProxy.DEBUG) { + Slog.v(TAG, "Stream " + i + ": width " + streamProtos[i].width + + ", height " + streamProtos[i].height + + ", format " + streamProtos[i].format + + ", dataSpace " + streamProtos[i].dataSpace + + ", usage " + streamProtos[i].usage + + ", requestCount " + streamProtos[i].requestCount + + ", errorCount " + streamProtos[i].errorCount + + ", firstCaptureLatencyMillis " + + streamProtos[i].firstCaptureLatencyMillis + + ", maxHalBuffers " + streamProtos[i].maxHalBuffers + + ", maxAppBuffers " + streamProtos[i].maxAppBuffers); + } + } + } FrameworkStatsLog.write(FrameworkStatsLog.CAMERA_ACTION_EVENT, e.getDuration(), - e.mAPILevel, e.mClientName, facing); + e.mAPILevel, e.mClientName, facing, e.mCameraId, e.mAction, e.mIsNdk, + e.mLatencyMs, e.mOperatingMode, e.mInternalReconfigure, + e.mRequestCount, e.mResultErrorCount, e.mDeviceError, + streamCount, MessageNano.toByteArray(streamProtos[0]), + MessageNano.toByteArray(streamProtos[1]), + MessageNano.toByteArray(streamProtos[2]), + MessageNano.toByteArray(streamProtos[3]), + MessageNano.toByteArray(streamProtos[4])); } } @@ -410,35 +502,6 @@ public class CameraServiceProxy extends SystemService synchronized(mLock) { // Randomize order of events so that it's not meaningful Collections.shuffle(mCameraUsageHistory); - for (CameraUsageEvent e : mCameraUsageHistory) { - if (DEBUG) { - Slog.v(TAG, "Camera: " + e.mClientName + " used a camera facing " + - cameraFacingToString(e.mCameraFacing) + " for " + - e.getDuration() + " ms"); - } - int subtype = 0; - switch(e.mCameraFacing) { - case ICameraServiceProxy.CAMERA_FACING_BACK: - subtype = MetricsEvent.CAMERA_BACK_USED; - break; - case ICameraServiceProxy.CAMERA_FACING_FRONT: - subtype = MetricsEvent.CAMERA_FRONT_USED; - break; - case ICameraServiceProxy.CAMERA_FACING_EXTERNAL: - subtype = MetricsEvent.CAMERA_EXTERNAL_USED; - break; - default: - continue; - } - LogMaker l = new LogMaker(MetricsEvent.ACTION_CAMERA_EVENT) - .setType(MetricsEvent.TYPE_ACTION) - .setSubtype(subtype) - .setLatency(e.getDuration()) - .addTaggedData(MetricsEvent.FIELD_CAMERA_API_LEVEL, e.mAPILevel) - .setPackageName(e.mClientName); - mLogger.write(l); - } - mLogWriterService.execute(new EventWriterTask( new ArrayList<CameraUsageEvent>(mCameraUsageHistory))); @@ -569,13 +632,25 @@ public class CameraServiceProxy extends SystemService return true; } - private void updateActivityCount(String cameraId, int newCameraState, int facing, - String clientName, int apiLevel) { + private void updateActivityCount(CameraSessionStats cameraState) { + String cameraId = cameraState.getCameraId(); + int newCameraState = cameraState.getNewCameraState(); + int facing = cameraState.getFacing(); + String clientName = cameraState.getClientName(); + int apiLevel = cameraState.getApiLevel(); + boolean isNdk = cameraState.isNdk(); + int sessionType = cameraState.getSessionType(); + int internalReconfigureCount = cameraState.getInternalReconfigureCount(); + int latencyMs = cameraState.getLatencyMs(); + long requestCount = cameraState.getRequestCount(); + long resultErrorCount = cameraState.getResultErrorCount(); + boolean deviceError = cameraState.getDeviceErrorFlag(); + List<CameraStreamStats> streamStats = cameraState.getStreamStats(); synchronized(mLock) { // Update active camera list and notify NFC if necessary boolean wasEmpty = mActiveCameraUsage.isEmpty(); switch (newCameraState) { - case ICameraServiceProxy.CAMERA_STATE_OPEN: + case CameraSessionStats.CAMERA_STATE_OPEN: // Notify the audio subsystem about the facing of the most-recently opened // camera This can be used to select the best audio tuning in case video // recording with that camera will happen. Since only open events are used, if @@ -584,13 +659,18 @@ public class CameraServiceProxy extends SystemService AudioManager audioManager = getContext().getSystemService(AudioManager.class); if (audioManager != null) { // Map external to front for audio tuning purposes - String facingStr = (facing == ICameraServiceProxy.CAMERA_FACING_BACK) ? + String facingStr = (facing == CameraSessionStats.CAMERA_FACING_BACK) ? "back" : "front"; String facingParameter = "cameraFacing=" + facingStr; audioManager.setParameters(facingParameter); } + CameraUsageEvent openEvent = new CameraUsageEvent( + cameraId, facing, clientName, apiLevel, isNdk, + FrameworkStatsLog.CAMERA_ACTION_EVENT__ACTION__OPEN, + latencyMs, sessionType); + mCameraUsageHistory.add(openEvent); break; - case ICameraServiceProxy.CAMERA_STATE_ACTIVE: + case CameraSessionStats.CAMERA_STATE_ACTIVE: // Check current active camera IDs to see if this package is already talking to // some camera boolean alreadyActivePackage = false; @@ -609,40 +689,55 @@ public class CameraServiceProxy extends SystemService } // Update activity events - CameraUsageEvent newEvent = new CameraUsageEvent(facing, clientName, apiLevel); + CameraUsageEvent newEvent = new CameraUsageEvent( + cameraId, facing, clientName, apiLevel, isNdk, + FrameworkStatsLog.CAMERA_ACTION_EVENT__ACTION__SESSION, + latencyMs, sessionType); CameraUsageEvent oldEvent = mActiveCameraUsage.put(cameraId, newEvent); if (oldEvent != null) { Slog.w(TAG, "Camera " + cameraId + " was already marked as active"); - oldEvent.markCompleted(); + oldEvent.markCompleted(/*internalReconfigure*/0, /*requestCount*/0, + /*resultErrorCount*/0, /*deviceError*/false, streamStats); mCameraUsageHistory.add(oldEvent); } break; - case ICameraServiceProxy.CAMERA_STATE_IDLE: - case ICameraServiceProxy.CAMERA_STATE_CLOSED: + case CameraSessionStats.CAMERA_STATE_IDLE: + case CameraSessionStats.CAMERA_STATE_CLOSED: CameraUsageEvent doneEvent = mActiveCameraUsage.remove(cameraId); - if (doneEvent == null) break; - - doneEvent.markCompleted(); - mCameraUsageHistory.add(doneEvent); - if (mCameraUsageHistory.size() > MAX_USAGE_HISTORY) { - dumpUsageEvents(); + if (doneEvent != null) { + + doneEvent.markCompleted(internalReconfigureCount, requestCount, + resultErrorCount, deviceError, streamStats); + mCameraUsageHistory.add(doneEvent); + + // Check current active camera IDs to see if this package is still + // talking to some camera + boolean stillActivePackage = false; + for (int i = 0; i < mActiveCameraUsage.size(); i++) { + if (mActiveCameraUsage.valueAt(i).mClientName.equals(clientName)) { + stillActivePackage = true; + break; + } + } + // If not longer active, notify window manager about this package being done + // with camera + if (!stillActivePackage) { + WindowManagerInternal wmi = + LocalServices.getService(WindowManagerInternal.class); + wmi.removeNonHighRefreshRatePackage(clientName); + } } - // Check current active camera IDs to see if this package is still talking to - // some camera - boolean stillActivePackage = false; - for (int i = 0; i < mActiveCameraUsage.size(); i++) { - if (mActiveCameraUsage.valueAt(i).mClientName.equals(clientName)) { - stillActivePackage = true; - break; - } + if (newCameraState == CameraSessionStats.CAMERA_STATE_CLOSED) { + CameraUsageEvent closeEvent = new CameraUsageEvent( + cameraId, facing, clientName, apiLevel, isNdk, + FrameworkStatsLog.CAMERA_ACTION_EVENT__ACTION__CLOSE, + latencyMs, sessionType); + mCameraUsageHistory.add(closeEvent); } - // If not longer active, notify window manager about this package being done - // with camera - if (!stillActivePackage) { - WindowManagerInternal wmi = - LocalServices.getService(WindowManagerInternal.class); - wmi.removeNonHighRefreshRatePackage(clientName); + + if (mCameraUsageHistory.size() > MAX_USAGE_HISTORY) { + dumpUsageEvents(); } break; @@ -683,10 +778,10 @@ public class CameraServiceProxy extends SystemService private static String cameraStateToString(int newCameraState) { switch (newCameraState) { - case ICameraServiceProxy.CAMERA_STATE_OPEN: return "CAMERA_STATE_OPEN"; - case ICameraServiceProxy.CAMERA_STATE_ACTIVE: return "CAMERA_STATE_ACTIVE"; - case ICameraServiceProxy.CAMERA_STATE_IDLE: return "CAMERA_STATE_IDLE"; - case ICameraServiceProxy.CAMERA_STATE_CLOSED: return "CAMERA_STATE_CLOSED"; + case CameraSessionStats.CAMERA_STATE_OPEN: return "CAMERA_STATE_OPEN"; + case CameraSessionStats.CAMERA_STATE_ACTIVE: return "CAMERA_STATE_ACTIVE"; + case CameraSessionStats.CAMERA_STATE_IDLE: return "CAMERA_STATE_IDLE"; + case CameraSessionStats.CAMERA_STATE_CLOSED: return "CAMERA_STATE_CLOSED"; default: break; } return "CAMERA_STATE_UNKNOWN"; @@ -694,9 +789,9 @@ public class CameraServiceProxy extends SystemService private static String cameraFacingToString(int cameraFacing) { switch (cameraFacing) { - case ICameraServiceProxy.CAMERA_FACING_BACK: return "CAMERA_FACING_BACK"; - case ICameraServiceProxy.CAMERA_FACING_FRONT: return "CAMERA_FACING_FRONT"; - case ICameraServiceProxy.CAMERA_FACING_EXTERNAL: return "CAMERA_FACING_EXTERNAL"; + case CameraSessionStats.CAMERA_FACING_BACK: return "CAMERA_FACING_BACK"; + case CameraSessionStats.CAMERA_FACING_FRONT: return "CAMERA_FACING_FRONT"; + case CameraSessionStats.CAMERA_FACING_EXTERNAL: return "CAMERA_FACING_EXTERNAL"; default: break; } return "CAMERA_FACING_UNKNOWN"; diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java index a9f62d91592d..3270dd55218c 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java @@ -132,6 +132,16 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { // TODO: make this private with a getter. public NetworkCapabilities networkCapabilities; public final NetworkAgentConfig networkAgentConfig; + + // Underlying networks declared by the agent. Only set if supportsUnderlyingNetworks is true. + // The networks in this list might be declared by a VPN app using setUnderlyingNetworks and are + // not guaranteed to be current or correct, or even to exist. + public @Nullable Network[] declaredUnderlyingNetworks; + + // Whether this network is always metered even if its underlying networks are unmetered. + // Only relevant if #supportsUnderlyingNetworks is true. + public boolean declaredMetered; + // Indicates if netd has been told to create this Network. From this point on the appropriate // routing rules are setup and routes are added so packets can begin flowing over the Network. // This is a sticky bit; once set it is never cleared. @@ -474,10 +484,16 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { networkCapabilities); } + /** Whether this network is a VPN. */ public boolean isVPN() { return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN); } + /** Whether this network might have underlying networks. Currently only true for VPNs. */ + public boolean supportsUnderlyingNetworks() { + return isVPN(); + } + private int getCurrentScore(boolean pretendValidated) { // TODO: We may want to refactor this into a NetworkScore class that takes a base score from // the NetworkAgent and signals from the NetworkAgent and uses those signals to modify the diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 4d959d08d3ef..234dcc9d74a5 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -112,7 +112,6 @@ import com.android.internal.net.VpnConfig; import com.android.internal.net.VpnInfo; import com.android.internal.net.VpnProfile; import com.android.internal.util.ArrayUtils; -import com.android.server.ConnectivityService; import com.android.server.DeviceIdleInternal; import com.android.server.LocalServices; import com.android.server.net.BaseNetworkObserver; @@ -123,7 +122,6 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.math.BigInteger; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; @@ -153,36 +151,13 @@ import java.util.concurrent.atomic.AtomicInteger; public class Vpn { private static final String NETWORKTYPE = "VPN"; private static final String TAG = "Vpn"; + private static final String VPN_PROVIDER_NAME_BASE = "VpnNetworkProvider:"; private static final boolean LOGD = true; // Length of time (in milliseconds) that an app hosting an always-on VPN is placed on // the device idle allowlist during service launch and VPN bootstrap. private static final long VPN_LAUNCH_IDLE_ALLOWLIST_DURATION_MS = 60 * 1000; - // Settings for how much of the address space should be routed so that Vpn considers - // "most" of the address space is routed. This is used to determine whether this Vpn - // should be marked with the INTERNET capability. - private static final long MOST_IPV4_ADDRESSES_COUNT; - private static final BigInteger MOST_IPV6_ADDRESSES_COUNT; - static { - // 85% of the address space must be routed for Vpn to consider this VPN to provide - // INTERNET access. - final int howManyPercentIsMost = 85; - - final long twoPower32 = 1L << 32; - MOST_IPV4_ADDRESSES_COUNT = twoPower32 * howManyPercentIsMost / 100; - final BigInteger twoPower128 = BigInteger.ONE.shiftLeft(128); - MOST_IPV6_ADDRESSES_COUNT = twoPower128 - .multiply(BigInteger.valueOf(howManyPercentIsMost)) - .divide(BigInteger.valueOf(100)); - } - // How many routes to evaluate before bailing and declaring this Vpn should provide - // the INTERNET capability. This is necessary because computing the address space is - // O(n²) and this is running in the system service, so a limit is needed to alleviate - // the risk of attack. - // This is taken as a total of IPv4 + IPV6 routes for simplicity, but the algorithm - // is actually O(n²)+O(n²). - private static final int MAX_ROUTES_TO_EVALUATE = 150; private static final String LOCKDOWN_ALLOWLIST_SETTING_NAME = Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN_WHITELIST; /** @@ -199,6 +174,7 @@ public class Vpn { // automated reconnection private final Context mContext; + private final ConnectivityManager mConnectivityManager; // The context is for specific user which is created from mUserId private final Context mUserIdContext; @VisibleForTesting final Dependencies mDeps; @@ -207,7 +183,8 @@ public class Vpn { @VisibleForTesting protected String mPackage; private int mOwnerUID; private boolean mIsPackageTargetingAtLeastQ; - private String mInterface; + @VisibleForTesting + protected String mInterface; private Connection mConnection; /** Tracks the runners for all VPN types managed by the platform (eg. LegacyVpn, PlatformVpn) */ @@ -218,6 +195,7 @@ public class Vpn { private final INetworkManagementService mNetd; @VisibleForTesting protected VpnConfig mConfig; + private final NetworkProvider mNetworkProvider; @VisibleForTesting protected NetworkAgent mNetworkAgent; private final Looper mLooper; @@ -401,6 +379,7 @@ public class Vpn { int userId, @NonNull KeyStore keyStore, SystemServices systemServices, Ikev2SessionCreator ikev2SessionCreator) { mContext = context; + mConnectivityManager = mContext.getSystemService(ConnectivityManager.class); mUserIdContext = context.createContextAsUser(UserHandle.of(userId), 0 /* flags */); mDeps = deps; mNetd = netService; @@ -419,13 +398,16 @@ public class Vpn { Log.wtf(TAG, "Problem registering observer", e); } + mNetworkProvider = new NetworkProvider(context, looper, VPN_PROVIDER_NAME_BASE + mUserId); + // This constructor is called in onUserStart and registers the provider. The provider + // will be unregistered in onUserStop. + mConnectivityManager.registerNetworkProvider(mNetworkProvider); mLegacyState = LegacyVpnInfo.STATE_DISCONNECTED; mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_VPN, 0 /* subtype */, NETWORKTYPE, "" /* subtypeName */); mNetworkCapabilities = new NetworkCapabilities(); mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_VPN); mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN); - updateCapabilities(null /* defaultNetwork */); loadAlwaysOnPackage(keyStore); } @@ -443,12 +425,39 @@ public class Vpn { * Update current state, dispatching event to listeners. */ @VisibleForTesting + @GuardedBy("this") protected void updateState(DetailedState detailedState, String reason) { if (LOGD) Log.d(TAG, "setting state=" + detailedState + ", reason=" + reason); mLegacyState = LegacyVpnInfo.stateFromNetworkInfo(detailedState); mNetworkInfo.setDetailedState(detailedState, reason, null); - if (mNetworkAgent != null) { - mNetworkAgent.sendNetworkInfo(mNetworkInfo); + // TODO : only accept transitions when the agent is in the correct state (non-null for + // CONNECTED, DISCONNECTED and FAILED, null for CONNECTED). + // This will require a way for tests to pretend the VPN is connected that's not + // calling this method with CONNECTED. + // It will also require audit of where the code calls this method with DISCONNECTED + // with a null agent, which it was doing historically to make sure the agent is + // disconnected as this was a no-op if the agent was null. + switch (detailedState) { + case CONNECTED: + if (null != mNetworkAgent) { + mNetworkAgent.markConnected(); + } + break; + case DISCONNECTED: + case FAILED: + if (null != mNetworkAgent) { + mNetworkAgent.unregister(); + mNetworkAgent = null; + } + break; + case CONNECTING: + if (null != mNetworkAgent) { + throw new IllegalStateException("VPN can only go to CONNECTING state when" + + " the agent is null."); + } + break; + default: + throw new IllegalArgumentException("Illegal state argument " + detailedState); } updateAlwaysOnNotification(detailedState); } @@ -476,7 +485,7 @@ public class Vpn { final boolean isAlwaysMetered = mIsPackageTargetingAtLeastQ && mConfig.isMetered; applyUnderlyingCapabilities( - mContext.getSystemService(ConnectivityManager.class), + mConnectivityManager, underlyingNetworks, mNetworkCapabilities, isAlwaysMetered); @@ -486,10 +495,10 @@ public class Vpn { @VisibleForTesting public static void applyUnderlyingCapabilities( - ConnectivityManager cm, - Network[] underlyingNetworks, - NetworkCapabilities caps, - boolean isAlwaysMetered) { + @NonNull final ConnectivityManager cm, + @Nullable final Network[] underlyingNetworks, + @NonNull final NetworkCapabilities caps, + final boolean isAlwaysMetered) { int[] transportTypes = new int[] { NetworkCapabilities.TRANSPORT_VPN }; int downKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED; int upKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED; @@ -1016,7 +1025,7 @@ public class Vpn { } mConfig = null; - updateState(DetailedState.IDLE, "prepare"); + updateState(DetailedState.DISCONNECTED, "prepare"); setVpnForcedLocked(mLockdown); } finally { Binder.restoreCallingIdentity(token); @@ -1252,7 +1261,7 @@ public class Vpn { mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); mLegacyState = LegacyVpnInfo.STATE_CONNECTING; - mNetworkInfo.setDetailedState(DetailedState.CONNECTING, null, null); + updateState(DetailedState.CONNECTING, "agentConnect"); NetworkAgentConfig networkAgentConfig = new NetworkAgentConfig(); networkAgentConfig.allowBypass = mConfig.allowBypass && !mLockdown; @@ -1261,20 +1270,34 @@ public class Vpn { mNetworkCapabilities.setAdministratorUids(new int[] {mOwnerUID}); mNetworkCapabilities.setUids(createUserAndRestrictedProfilesRanges(mUserId, mConfig.allowedApplications, mConfig.disallowedApplications)); - final long token = Binder.clearCallingIdentity(); - try { - mNetworkAgent = new NetworkAgent(mLooper, mContext, NETWORKTYPE /* logtag */, - mNetworkInfo, mNetworkCapabilities, lp, - ConnectivityConstants.VPN_DEFAULT_SCORE, networkAgentConfig, - NetworkProvider.ID_VPN) { - @Override - public void unwanted() { - // We are user controlled, not driven by NetworkRequest. - } - }; - } finally { - Binder.restoreCallingIdentity(token); + + // Only apps targeting Q and above can explicitly declare themselves as metered. + // These VPNs are assumed metered unless they state otherwise. + if (mIsPackageTargetingAtLeastQ && mConfig.isMetered) { + mNetworkCapabilities.removeCapability(NET_CAPABILITY_NOT_METERED); + } else { + mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_METERED); } + + mNetworkAgent = new NetworkAgent(mContext, mLooper, NETWORKTYPE /* logtag */, + mNetworkCapabilities, lp, + ConnectivityConstants.VPN_DEFAULT_SCORE, networkAgentConfig, mNetworkProvider) { + @Override + public void unwanted() { + // We are user controlled, not driven by NetworkRequest. + } + }; + Binder.withCleanCallingIdentity(() -> { + try { + mNetworkAgent.register(); + } catch (final Exception e) { + // If register() throws, don't keep an unregistered agent. + mNetworkAgent = null; + throw e; + } + }); + mNetworkAgent.setUnderlyingNetworks((mConfig.underlyingNetworks != null) + ? Arrays.asList(mConfig.underlyingNetworks) : null); mNetworkInfo.setIsAvailable(true); updateState(DetailedState.CONNECTED, "agentConnect"); } @@ -1290,19 +1313,12 @@ public class Vpn { private void agentDisconnect(NetworkAgent networkAgent) { if (networkAgent != null) { - NetworkInfo networkInfo = new NetworkInfo(mNetworkInfo); - networkInfo.setIsAvailable(false); - networkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null); - networkAgent.sendNetworkInfo(networkInfo); + networkAgent.unregister(); } } private void agentDisconnect() { - if (mNetworkInfo.isConnected()) { - mNetworkInfo.setIsAvailable(false); - updateState(DetailedState.DISCONNECTED, "agentDisconnect"); - mNetworkAgent = null; - } + updateState(DetailedState.DISCONNECTED, "agentDisconnect"); } /** @@ -1391,6 +1407,8 @@ public class Vpn { && updateLinkPropertiesInPlaceIfPossible(mNetworkAgent, oldConfig)) { // Keep mNetworkAgent unchanged } else { + // Initialize the state for a new agent, while keeping the old one connected + // in case this new connection fails. mNetworkAgent = null; updateState(DetailedState.CONNECTING, "establish"); // Set up forwarding and DNS rules. @@ -1574,12 +1592,13 @@ public class Vpn { try { addUserToRanges(existingRanges, userId, mConfig.allowedApplications, mConfig.disallowedApplications); - // ConnectivityService will call {@link #updateCapabilities} and apply - // those for VPN network. mNetworkCapabilities.setUids(existingRanges); } catch (Exception e) { Log.wtf(TAG, "Failed to add restricted user to owner", e); } + if (mNetworkAgent != null) { + mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); + } } setVpnForcedLocked(mLockdown); } @@ -1602,12 +1621,13 @@ public class Vpn { final List<UidRange> removedRanges = uidRangesForUser(userId, existingRanges); existingRanges.removeAll(removedRanges); - // ConnectivityService will call {@link #updateCapabilities} and - // apply those for VPN network. mNetworkCapabilities.setUids(existingRanges); } catch (Exception e) { Log.wtf(TAG, "Failed to remove restricted user to owner", e); } + if (mNetworkAgent != null) { + mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); + } } setVpnForcedLocked(mLockdown); } @@ -1624,6 +1644,9 @@ public class Vpn { // Quit any active connections agentDisconnect(); + + // The provider has been registered in the constructor, which is called in onUserStart. + mConnectivityManager.unregisterNetworkProvider(mNetworkProvider); } /** @@ -1856,6 +1879,8 @@ public class Vpn { } } } + mNetworkAgent.setUnderlyingNetworks((mConfig.underlyingNetworks != null) + ? Arrays.asList(mConfig.underlyingNetworks) : null); return true; } @@ -2398,7 +2423,6 @@ public class Vpn { // When restricted to test networks, select any network with TRANSPORT_TEST. Since the // creator of the profile and the test network creator both have MANAGE_TEST_NETWORKS, // this is considered safe. - final ConnectivityManager cm = ConnectivityManager.from(mContext); final NetworkRequest req; if (mProfile.isRestrictedToTestNetworks()) { @@ -2417,7 +2441,7 @@ public class Vpn { .build(); } - cm.requestNetwork(req, mNetworkCallback); + mConnectivityManager.requestNetwork(req, mNetworkCallback); } private boolean isActiveNetwork(@Nullable Network network) { @@ -2704,8 +2728,7 @@ public class Vpn { resetIkeState(); - final ConnectivityManager cm = ConnectivityManager.from(mContext); - cm.unregisterNetworkCallback(mNetworkCallback); + mConnectivityManager.unregisterNetworkCallback(mNetworkCallback); mExecutor.shutdown(); } @@ -2786,13 +2809,12 @@ public class Vpn { mProfile = profile; if (!TextUtils.isEmpty(mOuterInterface)) { - final ConnectivityManager cm = ConnectivityManager.from(mContext); - for (Network network : cm.getAllNetworks()) { - final LinkProperties lp = cm.getLinkProperties(network); + for (Network network : mConnectivityManager.getAllNetworks()) { + final LinkProperties lp = mConnectivityManager.getLinkProperties(network); if (lp != null && lp.getAllInterfaceNames().contains(mOuterInterface)) { - final NetworkInfo networkInfo = cm.getNetworkInfo(network); - if (networkInfo != null) { - mOuterConnection.set(networkInfo.getType()); + final NetworkInfo netInfo = mConnectivityManager.getNetworkInfo(network); + if (netInfo != null) { + mOuterConnection.set(netInfo.getType()); break; } } diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java index 3172a04e9a3d..d7dcbde5692d 100644 --- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java +++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java @@ -100,7 +100,7 @@ public final class DeviceStateManagerService extends SystemService { private final SparseArray<CallbackRecord> mCallbacks = new SparseArray<>(); public DeviceStateManagerService(@NonNull Context context) { - this(context, new DeviceStatePolicyImpl()); + this(context, new DeviceStatePolicyImpl(context)); } @VisibleForTesting diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java index fe6500e8942c..468d8259db18 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java +++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java @@ -22,6 +22,7 @@ import android.util.DisplayMetrics; import android.view.Display; import android.view.DisplayAddress; import android.view.DisplayCutout; +import android.view.DisplayEventReceiver; import android.view.Surface; import java.util.Arrays; @@ -333,6 +334,9 @@ final class DisplayDeviceInfo { */ public String ownerPackageName; + public DisplayEventReceiver.FrameRateOverride[] frameRateOverrides = + new DisplayEventReceiver.FrameRateOverride[0]; + public void setAssumedDensityForExternalDisplay(int width, int height) { densityDpi = Math.min(width, height) * DisplayMetrics.DENSITY_XHIGH / 1080; // Technically, these values should be smaller than the apparent density @@ -386,7 +390,8 @@ final class DisplayDeviceInfo { || !Objects.equals(address, other.address) || !Objects.equals(deviceProductInfo, other.deviceProductInfo) || ownerUid != other.ownerUid - || !Objects.equals(ownerPackageName, other.ownerPackageName)) { + || !Objects.equals(ownerPackageName, other.ownerPackageName) + || !Objects.equals(frameRateOverrides, other.frameRateOverrides)) { diff |= DIFF_OTHER; } return diff; @@ -425,6 +430,7 @@ final class DisplayDeviceInfo { state = other.state; ownerUid = other.ownerUid; ownerPackageName = other.ownerPackageName; + frameRateOverrides = other.frameRateOverrides; } // For debugging purposes @@ -461,6 +467,10 @@ final class DisplayDeviceInfo { sb.append(", owner ").append(ownerPackageName); sb.append(" (uid ").append(ownerUid).append(")"); } + sb.append(", frameRateOverride "); + for (DisplayEventReceiver.FrameRateOverride frameRateOverride : frameRateOverrides) { + sb.append(frameRateOverride).append(" "); + } sb.append(flagsToString(flags)); sb.append("}"); return sb.toString(); diff --git a/services/core/java/com/android/server/display/DisplayGroup.java b/services/core/java/com/android/server/display/DisplayGroup.java index f2413edd1a3a..2ba875813734 100644 --- a/services/core/java/com/android/server/display/DisplayGroup.java +++ b/services/core/java/com/android/server/display/DisplayGroup.java @@ -22,10 +22,22 @@ import java.util.List; /** * Represents a collection of {@link LogicalDisplay}s which act in unison for certain behaviors and * operations. + * @hide */ public class DisplayGroup { - final List<LogicalDisplay> mDisplays = new ArrayList<>(); + public static final int DEFAULT = 0; + + private final List<LogicalDisplay> mDisplays = new ArrayList<>(); + private final int mGroupId; + + DisplayGroup(int groupId) { + mGroupId = groupId; + } + + int getGroupId() { + return mGroupId; + } void addDisplay(LogicalDisplay display) { if (!mDisplays.contains(display)) { diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 004e481354ec..29b413d921e1 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -37,6 +37,9 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.AppOpsManager; +import android.app.compat.CompatChanges; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledSince; import android.content.Context; import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; @@ -83,7 +86,9 @@ import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; +import android.sysprop.DisplayProperties; import android.text.TextUtils; +import android.util.ArraySet; import android.util.EventLog; import android.util.IntArray; import android.util.Pair; @@ -92,6 +97,7 @@ import android.util.SparseArray; import android.util.SparseIntArray; import android.util.Spline; import android.view.Display; +import android.view.DisplayEventReceiver; import android.view.DisplayInfo; import android.view.IDisplayFoldListener; import android.view.Surface; @@ -114,6 +120,7 @@ import com.android.server.wm.WindowManagerInternal; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.Optional; import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Consumer; @@ -181,6 +188,7 @@ public final class DisplayManagerService extends SystemService { private static final int MSG_REQUEST_TRAVERSAL = 4; private static final int MSG_UPDATE_VIEWPORT = 5; private static final int MSG_LOAD_BRIGHTNESS_CONFIGURATION = 6; + private static final int MSG_DELIVER_DISPLAY_EVENT_FRAME_RATE_OVERRIDE = 7; private final Context mContext; private final DisplayManagerHandler mHandler; @@ -357,6 +365,30 @@ public final class DisplayManagerService extends SystemService { // Received notifications of the display-fold action private DisplayFoldListener mDisplayFoldListener; + private final boolean mAllowNonNativeRefreshRateOverride; + + private static final float THRESHOLD_FOR_REFRESH_RATES_DIVIDERS = 0.1f; + + /** + * Applications use {@link android.view.Display#getRefreshRate} and + * {@link android.view.Display.Mode#getRefreshRate} to know what is the display refresh rate. + * Starting with Android S, the platform might throttle down applications frame rate to a + * divisor of the refresh rate if it is more preferable (for example if the application called + * to {@link android.view.Surface#setFrameRate}). + * Applications will experience {@link android.view.Choreographer#postFrameCallback} callbacks + * and backpressure at the throttled frame rate. + * + * {@link android.view.Display#getRefreshRate} will always return the application frame rate + * and not the physical display refresh rate to allow applications to do frame pacing correctly. + * + * {@link android.view.Display.Mode#getRefreshRate} will return the application frame rate if + * compiled to a previous release and starting with Android S it will return the physical + * display refresh rate. + */ + @ChangeId + @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.S) + static final long DISPLAY_MODE_RETURNS_PHYSICAL_REFRESH_RATE = 170503758L; + public DisplayManagerService(Context context) { this(context, new Injector()); } @@ -389,6 +421,7 @@ public final class DisplayManagerService extends SystemService { mCurrentUserId = UserHandle.USER_SYSTEM; ColorSpace[] colorSpaces = SurfaceControl.getCompositionColorSpaces(); mWideColorSpace = colorSpaces[1]; + mAllowNonNativeRefreshRateOverride = mInjector.getAllowNonNativeRefreshRateOverride(); mSystemReady = false; } @@ -677,11 +710,82 @@ public final class DisplayManagerService extends SystemService { Settings.Secure.MINIMAL_POST_PROCESSING_ALLOWED, 1, UserHandle.USER_CURRENT) != 0; } + private DisplayInfo getDisplayInfoForFrameRateOverride(DisplayEventReceiver.FrameRateOverride[] + frameRateOverrides, DisplayInfo info, int callingUid) { + float frameRateHz = 0; + for (DisplayEventReceiver.FrameRateOverride frameRateOverride : frameRateOverrides) { + if (frameRateOverride.uid == callingUid) { + frameRateHz = frameRateOverride.frameRateHz; + break; + } + } + if (frameRateHz == 0) { + return info; + } + + // Override the refresh rate only if it is a divider of the current + // refresh rate. This calculation needs to be in sync with the native code + // in RefreshRateConfigs::getRefreshRateDividerForUid + Display.Mode currentMode = info.getMode(); + float numPeriods = currentMode.getRefreshRate() / frameRateHz; + float numPeriodsRound = Math.round(numPeriods); + if (Math.abs(numPeriods - numPeriodsRound) > THRESHOLD_FOR_REFRESH_RATES_DIVIDERS) { + return info; + } + frameRateHz = currentMode.getRefreshRate() / numPeriodsRound; + + DisplayInfo overriddenInfo = new DisplayInfo(); + overriddenInfo.copyFrom(info); + for (Display.Mode mode : info.supportedModes) { + if (!mode.equalsExceptRefreshRate(currentMode)) { + continue; + } + + if (mode.getRefreshRate() >= frameRateHz - THRESHOLD_FOR_REFRESH_RATES_DIVIDERS + && mode.getRefreshRate() + <= frameRateHz + THRESHOLD_FOR_REFRESH_RATES_DIVIDERS) { + if (DEBUG) { + Slog.d(TAG, "found matching modeId " + mode.getModeId()); + } + overriddenInfo.refreshRateOverride = mode.getRefreshRate(); + + if (!CompatChanges.isChangeEnabled(DISPLAY_MODE_RETURNS_PHYSICAL_REFRESH_RATE, + callingUid)) { + overriddenInfo.modeId = mode.getModeId(); + } + return overriddenInfo; + } + } + + if (mAllowNonNativeRefreshRateOverride) { + overriddenInfo.refreshRateOverride = frameRateHz; + if (!CompatChanges.isChangeEnabled(DISPLAY_MODE_RETURNS_PHYSICAL_REFRESH_RATE, + callingUid)) { + overriddenInfo.supportedModes = Arrays.copyOf(info.supportedModes, + info.supportedModes.length + 1); + overriddenInfo.supportedModes[overriddenInfo.supportedModes.length - 1] = + new Display.Mode(Display.DISPLAY_MODE_ID_FOR_FRAME_RATE_OVERRIDE, + currentMode.getPhysicalWidth(), currentMode.getPhysicalHeight(), + overriddenInfo.refreshRateOverride); + overriddenInfo.modeId = + overriddenInfo.supportedModes[overriddenInfo.supportedModes.length - 1] + .getModeId(); + } + return overriddenInfo; + } + + + + return info; + } + private DisplayInfo getDisplayInfoInternal(int displayId, int callingUid) { synchronized (mSyncRoot) { LogicalDisplay display = mLogicalDisplayMapper.getLocked(displayId); if (display != null) { - DisplayInfo info = display.getDisplayInfoLocked(); + DisplayInfo info = + getDisplayInfoForFrameRateOverride(display.getFrameRateOverrides(), + display.getDisplayInfoLocked(), callingUid); if (info.hasAccess(callingUid) || isUidPresentOnDisplayInternal(callingUid, displayId)) { return info; @@ -691,14 +795,15 @@ public final class DisplayManagerService extends SystemService { } } - private void registerCallbackInternal(IDisplayManagerCallback callback, int callingPid) { + private void registerCallbackInternal(IDisplayManagerCallback callback, int callingPid, + int callingUid) { synchronized (mSyncRoot) { if (mCallbacks.get(callingPid) != null) { throw new SecurityException("The calling process has already " + "registered an IDisplayManagerCallback."); } - CallbackRecord record = new CallbackRecord(callingPid, callback); + CallbackRecord record = new CallbackRecord(callingPid, callingUid, callback); try { IBinder binder = callback.asBinder(); binder.linkToDeath(record, 0); @@ -1034,6 +1139,16 @@ public final class DisplayManagerService extends SystemService { scheduleTraversalLocked(false); } + private void handleLogicalDisplayFrameRateOverridesChangedLocked( + @NonNull LogicalDisplay display) { + final int displayId = display.getDisplayIdLocked(); + // We don't bother invalidating the display info caches here because any changes to the + // display info will trigger a cache invalidation inside of LogicalDisplay before we hit + // this point. + sendDisplayEventFrameRateOverrideLocked(displayId); + scheduleTraversalLocked(false); + } + private void handleLogicalDisplayRemovedLocked(@NonNull LogicalDisplay display) { final int displayId = display.getDisplayIdLocked(); mDisplayPowerControllers.delete(displayId); @@ -1545,6 +1660,12 @@ public final class DisplayManagerService extends SystemService { mHandler.sendMessage(msg); } + private void sendDisplayEventFrameRateOverrideLocked(int displayId) { + Message msg = mHandler.obtainMessage(MSG_DELIVER_DISPLAY_EVENT_FRAME_RATE_OVERRIDE, + displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED); + mHandler.sendMessage(msg); + } + // Requests that performTraversals be called at a // later time to apply changes to surfaces and displays. private void scheduleTraversalLocked(boolean inTraversal) { @@ -1558,7 +1679,7 @@ public final class DisplayManagerService extends SystemService { // Runs on Handler thread. // Delivers display event notifications to callbacks. - private void deliverDisplayEvent(int displayId, int event) { + private void deliverDisplayEvent(int displayId, ArraySet<Integer> uids, int event) { if (DEBUG) { Slog.d(TAG, "Delivering display event: displayId=" + displayId + ", event=" + event); @@ -1570,12 +1691,14 @@ public final class DisplayManagerService extends SystemService { count = mCallbacks.size(); mTempCallbacks.clear(); for (int i = 0; i < count; i++) { - mTempCallbacks.add(mCallbacks.valueAt(i)); + if (uids == null || uids.contains(mCallbacks.valueAt(i).mUid)) { + mTempCallbacks.add(mCallbacks.valueAt(i)); + } } } // After releasing the lock, send the notifications out. - for (int i = 0; i < count; i++) { + for (int i = 0; i < mTempCallbacks.size(); i++) { mTempCallbacks.get(i).notifyDisplayEventAsync(displayId, event); } mTempCallbacks.clear(); @@ -1691,6 +1814,11 @@ public final class DisplayManagerService extends SystemService { long getDefaultDisplayDelayTimeout() { return WAIT_FOR_DEFAULT_DISPLAY_TIMEOUT; } + + boolean getAllowNonNativeRefreshRateOverride() { + return DisplayProperties + .debug_allow_non_native_refresh_rate_override().orElse(false); + } } @VisibleForTesting @@ -1760,7 +1888,7 @@ public final class DisplayManagerService extends SystemService { break; case MSG_DELIVER_DISPLAY_EVENT: - deliverDisplayEvent(msg.arg1, msg.arg2); + deliverDisplayEvent(msg.arg1, null, msg.arg2); break; case MSG_REQUEST_TRAVERSAL: @@ -1787,6 +1915,17 @@ public final class DisplayManagerService extends SystemService { case MSG_LOAD_BRIGHTNESS_CONFIGURATION: loadBrightnessConfiguration(); break; + + case MSG_DELIVER_DISPLAY_EVENT_FRAME_RATE_OVERRIDE: + ArraySet<Integer> uids; + synchronized (mSyncRoot) { + int displayId = msg.arg1; + LogicalDisplay display = mLogicalDisplayMapper.getLocked(displayId); + uids = display.getPendingFrameRateOverrideUids(); + display.clearPendingFrameRateOverrideUids(); + } + deliverDisplayEvent(msg.arg1, uids, msg.arg2); + break; } } } @@ -1810,6 +1949,10 @@ public final class DisplayManagerService extends SystemService { case LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_SWAPPED: handleLogicalDisplaySwappedLocked(display); break; + + case LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED: + handleLogicalDisplayFrameRateOverridesChangedLocked(display); + break; } } @@ -1823,12 +1966,14 @@ public final class DisplayManagerService extends SystemService { private final class CallbackRecord implements DeathRecipient { public final int mPid; + public final int mUid; private final IDisplayManagerCallback mCallback; public boolean mWifiDisplayScanRequested; - public CallbackRecord(int pid, IDisplayManagerCallback callback) { + CallbackRecord(int pid, int uid, IDisplayManagerCallback callback) { mPid = pid; + mUid = uid; mCallback = callback; } @@ -1918,9 +2063,10 @@ public final class DisplayManagerService extends SystemService { } final int callingPid = Binder.getCallingPid(); + final int callingUid = Binder.getCallingUid(); final long token = Binder.clearCallingIdentity(); try { - registerCallbackInternal(callback, callingPid); + registerCallbackInternal(callback, callingPid, callingUid); } finally { Binder.restoreCallingIdentity(token); } @@ -2536,6 +2682,13 @@ public final class DisplayManagerService extends SystemService { } @Override + public int getDisplayGroupId(int displayId) { + synchronized (mSyncRoot) { + return mLogicalDisplayMapper.getDisplayGroupIdLocked(displayId); + } + } + + @Override public SurfaceControl.ScreenshotHardwareBuffer systemScreenshot(int displayId) { return systemScreenshotInternal(displayId); } diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index f6578584cb18..74ea2d7114fc 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -208,6 +208,9 @@ final class LocalDisplayAdapter extends DisplayAdapter { private DisplayDeviceConfig mDisplayDeviceConfig; + private DisplayEventReceiver.FrameRateOverride[] mFrameRateOverrides = + new DisplayEventReceiver.FrameRateOverride[0]; + LocalDisplayDevice(IBinder displayToken, long physicalDisplayId, SurfaceControl.DisplayInfo info, SurfaceControl.DisplayConfig[] configs, int activeConfigId, SurfaceControl.DesiredDisplayConfigSpecs configSpecs, @@ -625,6 +628,8 @@ final class LocalDisplayAdapter extends DisplayAdapter { mInfo.name = getContext().getResources().getString( com.android.internal.R.string.display_manager_hdmi_display_name); } + mInfo.frameRateOverrides = mFrameRateOverrides; + // The display is trusted since it is created by system. mInfo.flags |= DisplayDeviceInfo.FLAG_TRUSTED; } @@ -882,6 +887,13 @@ final class LocalDisplayAdapter extends DisplayAdapter { } } + public void onFrameRateOverridesChanged( + DisplayEventReceiver.FrameRateOverride[] overrides) { + if (updateFrameRateOverridesLocked(overrides)) { + updateDeviceInfoLocked(); + } + } + public boolean updateActiveModeLocked(int activeConfigId) { if (mActiveConfigId == activeConfigId) { return false; @@ -895,6 +907,16 @@ final class LocalDisplayAdapter extends DisplayAdapter { return true; } + public boolean updateFrameRateOverridesLocked( + DisplayEventReceiver.FrameRateOverride[] overrides) { + if (overrides.equals(mFrameRateOverrides)) { + return false; + } + + mFrameRateOverrides = overrides; + return true; + } + public void requestColorModeLocked(int colorMode) { if (mActiveColorMode == colorMode) { return; @@ -1102,23 +1124,39 @@ final class LocalDisplayAdapter extends DisplayAdapter { public interface DisplayEventListener { void onHotplug(long timestampNanos, long physicalDisplayId, boolean connected); void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId); + void onFrameRateOverridesChanged(long timestampNanos, long physicalDisplayId, + DisplayEventReceiver.FrameRateOverride[] overrides); + } public static final class ProxyDisplayEventReceiver extends DisplayEventReceiver { private final DisplayEventListener mListener; ProxyDisplayEventReceiver(Looper looper, DisplayEventListener listener) { - super(looper, VSYNC_SOURCE_APP, CONFIG_CHANGED_EVENT_DISPATCH); + super(looper, VSYNC_SOURCE_APP, + EVENT_REGISTRATION_CONFIG_CHANGED_FLAG + | EVENT_REGISTRATION_FRAME_RATE_OVERRIDE_FLAG); mListener = listener; } + + @Override public void onHotplug(long timestampNanos, long physicalDisplayId, boolean connected) { mListener.onHotplug(timestampNanos, physicalDisplayId, connected); } + + @Override public void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId) { mListener.onConfigChanged(timestampNanos, physicalDisplayId, configId); } + + @Override + public void onFrameRateOverridesChanged(long timestampNanos, long physicalDisplayId, + DisplayEventReceiver.FrameRateOverride[] overrides) { + mListener.onFrameRateOverridesChanged(timestampNanos, physicalDisplayId, overrides); + } } private final class LocalDisplayEventListener implements DisplayEventListener { + @Override public void onHotplug(long timestampNanos, long physicalDisplayId, boolean connected) { synchronized (getSyncRoot()) { if (connected) { @@ -1128,6 +1166,8 @@ final class LocalDisplayAdapter extends DisplayAdapter { } } } + + @Override public void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId) { if (DEBUG) { Slog.d(TAG, "onConfigChanged(" @@ -1147,5 +1187,26 @@ final class LocalDisplayAdapter extends DisplayAdapter { device.onActiveDisplayConfigChangedLocked(configId); } } + + @Override + public void onFrameRateOverridesChanged(long timestampNanos, long physicalDisplayId, + DisplayEventReceiver.FrameRateOverride[] overrides) { + if (DEBUG) { + Slog.d(TAG, "onFrameRateOverrideChanged(timestampNanos=" + timestampNanos + + ", physicalDisplayId=" + physicalDisplayId + " overrides=" + + Arrays.toString(overrides) + ")"); + } + synchronized (getSyncRoot()) { + LocalDisplayDevice device = mDevices.get(physicalDisplayId); + if (device == null) { + if (DEBUG) { + Slog.d(TAG, "Received frame rate override event for unhandled physical" + + " display: physicalDisplayId=" + physicalDisplayId); + } + return; + } + device.onFrameRateOverridesChanged(overrides); + } + } } } diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java index 979c3b871284..d80e1687f67f 100644 --- a/services/core/java/com/android/server/display/LogicalDisplay.java +++ b/services/core/java/com/android/server/display/LogicalDisplay.java @@ -20,8 +20,11 @@ import android.annotation.NonNull; import android.graphics.Point; import android.graphics.Rect; import android.hardware.display.DisplayManagerInternal; +import android.util.ArraySet; import android.util.Slog; +import android.util.SparseArray; import android.view.Display; +import android.view.DisplayEventReceiver; import android.view.DisplayInfo; import android.view.Surface; import android.view.SurfaceControl; @@ -123,10 +126,27 @@ final class LogicalDisplay { */ private boolean mIsEnabled = true; + /** + * The UID mappings for refresh rate override + */ + private DisplayEventReceiver.FrameRateOverride[] mFrameRateOverrides; + + /** + * Holds a set of UIDs that their frame rate override changed and needs to be notified + */ + private ArraySet<Integer> mPendingFrameRateOverrideUids; + + /** + * Temporary frame rate override list, used when needed. + */ + private final SparseArray<Float> mTempFrameRateOverride; + public LogicalDisplay(int displayId, int layerStack, DisplayDevice primaryDisplayDevice) { mDisplayId = displayId; mLayerStack = layerStack; mPrimaryDisplayDevice = primaryDisplayDevice; + mPendingFrameRateOverrideUids = new ArraySet<>(); + mTempFrameRateOverride = new SparseArray<>(); } /** @@ -179,6 +199,27 @@ final class LogicalDisplay { } /** + * Returns the frame rate overrides list + */ + public DisplayEventReceiver.FrameRateOverride[] getFrameRateOverrides() { + return mFrameRateOverrides; + } + + /** + * Returns the list of uids that needs to be updated about their frame rate override + */ + public ArraySet<Integer> getPendingFrameRateOverrideUids() { + return mPendingFrameRateOverrideUids; + } + + /** + * Clears the list of uids that needs to be updated about their frame rate override + */ + public void clearPendingFrameRateOverrideUids() { + mPendingFrameRateOverrideUids = new ArraySet<>(); + } + + /** * @see DisplayManagerInternal#getNonOverrideDisplayInfo(int, DisplayInfo) */ void getNonOverrideDisplayInfoLocked(DisplayInfo outInfo) { @@ -324,12 +365,40 @@ final class LogicalDisplay { (deviceInfo.flags & DisplayDeviceInfo.FLAG_MASK_DISPLAY_CUTOUT) != 0; mBaseDisplayInfo.displayCutout = maskCutout ? null : deviceInfo.displayCutout; mBaseDisplayInfo.displayId = mDisplayId; + updateFrameRateOverrides(deviceInfo); mPrimaryDisplayDeviceInfo = deviceInfo; mInfo.set(null); } } + private void updateFrameRateOverrides(DisplayDeviceInfo deviceInfo) { + mTempFrameRateOverride.clear(); + if (mFrameRateOverrides != null) { + for (DisplayEventReceiver.FrameRateOverride frameRateOverride + : mFrameRateOverrides) { + mTempFrameRateOverride.put(frameRateOverride.uid, + frameRateOverride.frameRateHz); + } + } + mFrameRateOverrides = deviceInfo.frameRateOverrides; + if (mFrameRateOverrides != null) { + for (DisplayEventReceiver.FrameRateOverride frameRateOverride + : mFrameRateOverrides) { + float refreshRate = mTempFrameRateOverride.get(frameRateOverride.uid, 0f); + if (refreshRate == 0 || frameRateOverride.frameRateHz != refreshRate) { + mTempFrameRateOverride.put(frameRateOverride.uid, + frameRateOverride.frameRateHz); + } else { + mTempFrameRateOverride.delete(frameRateOverride.uid); + } + } + } + for (int i = 0; i < mTempFrameRateOverride.size(); i++) { + mPendingFrameRateOverrideUids.add(mTempFrameRateOverride.keyAt(i)); + } + } + /** * Return the insets currently applied to the display. * @@ -638,5 +707,7 @@ final class LogicalDisplay { pw.println("mBaseDisplayInfo=" + mBaseDisplayInfo); pw.println("mOverrideDisplayInfo=" + mOverrideDisplayInfo); pw.println("mRequestedMinimalPostProcessing=" + mRequestedMinimalPostProcessing); + pw.println("mFrameRateOverrides=" + Arrays.toString(mFrameRateOverrides)); + pw.println("mPendingFrameRateOverrideUids=" + mPendingFrameRateOverrideUids); } } diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java index 45c38b456b0a..cdcbb4f123a1 100644 --- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java +++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java @@ -23,6 +23,7 @@ import android.text.TextUtils; import android.util.Slog; import android.util.SparseArray; import android.view.Display; +import android.view.DisplayEventReceiver; import android.view.DisplayInfo; import com.android.internal.util.IndentingPrintWriter; @@ -52,6 +53,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { public static final int LOGICAL_DISPLAY_EVENT_CHANGED = 2; public static final int LOGICAL_DISPLAY_EVENT_REMOVED = 3; public static final int LOGICAL_DISPLAY_EVENT_SWAPPED = 4; + public static final int LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED = 5; /** * Temporary display info, used for comparing display configurations. @@ -94,6 +96,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { private final SparseArray<LogicalDisplay> mLogicalDisplays = new SparseArray<LogicalDisplay>(); private int mNextNonDefaultDisplayId = Display.DEFAULT_DISPLAY + 1; + private int mNextNonDefaultGroupId = DisplayGroup.DEFAULT + 1; /** A mapping from logical display id to display group. */ private final SparseArray<DisplayGroup> mDisplayGroups = new SparseArray<>(); @@ -178,6 +181,15 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { } } + public int getDisplayGroupIdLocked(int displayId) { + final DisplayGroup displayGroup = mDisplayGroups.get(displayId); + if (displayGroup != null) { + return displayGroup.getGroupId(); + } + + return -1; + } + public void dumpLocked(PrintWriter pw) { pw.println("LogicalDisplayMapper:"); IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); @@ -309,7 +321,8 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { final DisplayGroup displayGroup; if (isDefault || (deviceInfo.flags & DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP) != 0) { - displayGroup = new DisplayGroup(); + final int groupId = assignDisplayGroupIdLocked(isDefault); + displayGroup = new DisplayGroup(groupId); } else { displayGroup = mDisplayGroups.get(Display.DEFAULT_DISPLAY); } @@ -331,6 +344,8 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { mTempDisplayInfo.copyFrom(display.getDisplayInfoLocked()); display.getNonOverrideDisplayInfoLocked(mTempNonOverrideDisplayInfo); + DisplayEventReceiver.FrameRateOverride[] frameRatesOverrides = + display.getFrameRateOverrides(); display.updateLocked(mDisplayDeviceRepo); if (!display.isValidLocked()) { mLogicalDisplays.removeAt(i); @@ -345,7 +360,8 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { if ((flags & Display.FLAG_OWN_DISPLAY_GROUP) != 0) { // The display should have its own DisplayGroup. if (defaultDisplayGroup.removeDisplay(display)) { - final DisplayGroup displayGroup = new DisplayGroup(); + final int groupId = assignDisplayGroupIdLocked(false); + final DisplayGroup displayGroup = new DisplayGroup(groupId); displayGroup.addDisplay(display); mDisplayGroups.append(display.getDisplayIdLocked(), displayGroup); } @@ -364,6 +380,9 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { final int eventMsg = TextUtils.equals(oldUniqueId, newUniqueId) ? LOGICAL_DISPLAY_EVENT_CHANGED : LOGICAL_DISPLAY_EVENT_SWAPPED; mListener.onLogicalDisplayEventLocked(display, eventMsg); + } else if (!display.getPendingFrameRateOverrideUids().isEmpty()) { + mListener.onLogicalDisplayEventLocked(display, + LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED); } else { // While applications shouldn't know nor care about the non-overridden info, we // still need to let WindowManager know so it can update its own internal state for @@ -381,6 +400,10 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { return isDefault ? Display.DEFAULT_DISPLAY : mNextNonDefaultDisplayId++; } + private int assignDisplayGroupIdLocked(boolean isDefault) { + return isDefault ? DisplayGroup.DEFAULT : mNextNonDefaultGroupId++; + } + private int assignLayerStackLocked(int displayId) { // Currently layer stacks and display ids are the same. // This need not be the case. diff --git a/services/core/java/com/android/server/display/OverlayDisplayWindow.java b/services/core/java/com/android/server/display/OverlayDisplayWindow.java index 49f0d35d2e78..cd3a453d5e64 100644 --- a/services/core/java/com/android/server/display/OverlayDisplayWindow.java +++ b/services/core/java/com/android/server/display/OverlayDisplayWindow.java @@ -16,8 +16,6 @@ package com.android.server.display; -import com.android.internal.util.DumpUtils; - import android.content.Context; import android.graphics.SurfaceTexture; import android.hardware.display.DisplayManager; @@ -30,12 +28,14 @@ import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.ScaleGestureDetector; import android.view.TextureView; +import android.view.TextureView.SurfaceTextureListener; import android.view.ThreadedRenderer; import android.view.View; import android.view.WindowManager; -import android.view.TextureView.SurfaceTextureListener; import android.widget.TextView; +import com.android.internal.util.DumpUtils; + import java.io.PrintWriter; /** @@ -319,7 +319,7 @@ final class OverlayDisplayWindow implements DumpUtils.Dump { public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) { mListener.onWindowCreated(surfaceTexture, - mDefaultDisplayInfo.getMode().getRefreshRate(), + mDefaultDisplayInfo.getRefreshRate(), mDefaultDisplayInfo.presentationDeadlineNanos, mDefaultDisplayInfo.state); } diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java index d0cc8e71dbfa..d0e7e459e7f2 100644 --- a/services/core/java/com/android/server/hdmi/Constants.java +++ b/services/core/java/com/android/server/hdmi/Constants.java @@ -501,21 +501,21 @@ final class Constants { static final int DISABLED = 0; static final int ENABLED = 1; - @IntDef({ - VERSION_1_4, - VERSION_2_0 - }) - @interface CecVersion {} - static final int VERSION_1_3 = 0x04; - static final int VERSION_1_4 = 0x05; - static final int VERSION_2_0 = 0x06; - static final int ALL_DEVICE_TYPES_TV = 7; static final int ALL_DEVICE_TYPES_RECORDER = 6; static final int ALL_DEVICE_TYPES_TUNER = 5; static final int ALL_DEVICE_TYPES_PLAYBACK = 4; static final int ALL_DEVICE_TYPES_AUDIO_SYSTEM = 3; static final int ALL_DEVICE_TYPES_SWITCH = 2; + @IntDef({ + ALL_DEVICE_TYPES_TV, + ALL_DEVICE_TYPES_RECORDER, + ALL_DEVICE_TYPES_TUNER, + ALL_DEVICE_TYPES_PLAYBACK, + ALL_DEVICE_TYPES_AUDIO_SYSTEM, + ALL_DEVICE_TYPES_SWITCH + }) + @interface DeviceType {} static final int DEVICE_FEATURE_TV_SUPPORTS_RECORD_TV_SCREEN = 6; static final int DEVICE_FEATURE_TV_SUPPORTS_SET_OSD_STRING = 5; @@ -523,21 +523,51 @@ final class Constants { static final int DEVICE_FEATURE_SUPPORTS_SET_AUDIO_RATE = 3; static final int DEVICE_FEATURE_SINK_SUPPORTS_ARC_TX = 2; static final int DEVICE_FEATURE_SOURCE_SUPPORTS_ARC_RX = 1; + @IntDef({ + DEVICE_FEATURE_TV_SUPPORTS_RECORD_TV_SCREEN, + DEVICE_FEATURE_TV_SUPPORTS_SET_OSD_STRING, + DEVICE_FEATURE_SUPPORTS_DECK_CONTROL, + DEVICE_FEATURE_SUPPORTS_SET_AUDIO_RATE, + DEVICE_FEATURE_SINK_SUPPORTS_ARC_TX, + DEVICE_FEATURE_SOURCE_SUPPORTS_ARC_RX + }) + @interface DeviceFeature {} static final int RC_PROFILE_TV = 0; static final int RC_PROFILE_SOURCE = 1; + @IntDef({ + RC_PROFILE_TV, + RC_PROFILE_SOURCE + }) + @interface RcProfile {} static final int RC_PROFILE_TV_NONE = 0x0; static final int RC_PROFILE_TV_ONE = 0x2; static final int RC_PROFILE_TV_TWO = 0x6; static final int RC_PROFILE_TV_THREE = 0xA; static final int RC_PROFILE_TV_FOUR = 0xE; + @IntDef({ + RC_PROFILE_TV_NONE, + RC_PROFILE_TV_ONE, + RC_PROFILE_TV_TWO, + RC_PROFILE_TV_THREE, + RC_PROFILE_TV_FOUR + }) + @interface RcProfileTv {} static final int RC_PROFILE_SOURCE_HANDLES_ROOT_MENU = 4; static final int RC_PROFILE_SOURCE_HANDLES_SETUP_MENU = 3; static final int RC_PROFILE_SOURCE_HANDLES_CONTENTS_MENU = 2; static final int RC_PROFILE_SOURCE_HANDLES_TOP_MENU = 1; static final int RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU = 0; + @IntDef({ + RC_PROFILE_SOURCE_HANDLES_ROOT_MENU, + RC_PROFILE_SOURCE_HANDLES_SETUP_MENU, + RC_PROFILE_SOURCE_HANDLES_CONTENTS_MENU, + RC_PROFILE_SOURCE_HANDLES_TOP_MENU, + RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU + }) + @interface RcProfileSource {} private Constants() { /* cannot be instantiated */ diff --git a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java index 0566d32160fc..2374ece1dd65 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java @@ -22,12 +22,19 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringDef; +import android.content.ContentResolver; import android.content.Context; import android.content.SharedPreferences; +import android.database.ContentObserver; import android.hardware.hdmi.HdmiControlManager; +import android.net.Uri; import android.os.Environment; +import android.os.Handler; +import android.os.Looper; import android.os.SystemProperties; +import android.os.UserHandle; import android.provider.Settings.Global; +import android.util.ArrayMap; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; @@ -85,9 +92,26 @@ public class HdmiCecConfig { @NonNull private final Context mContext; @NonNull private final StorageAdapter mStorageAdapter; - @Nullable private final CecSettings mSystemConfig; + @Nullable private final CecSettings mProductConfig; @Nullable private final CecSettings mVendorOverride; + private final ArrayMap<Setting, Set<SettingChangeListener>> + mSettingChangeListeners = new ArrayMap<>(); + + private SettingsObserver mSettingsObserver; + + /** + * Listener used to get notifications when value of a setting changes. + */ + public interface SettingChangeListener { + /** + * Called when value of a setting changes. + * + * @param setting name of a CEC setting that changed + */ + void onChange(@NonNull @CecSettingName String setting); + } + /** * Setting storage input/output helper class. */ @@ -159,17 +183,29 @@ public class HdmiCecConfig { } } + private class SettingsObserver extends ContentObserver { + SettingsObserver(Handler handler) { + super(handler); + } + + @Override + public void onChange(boolean selfChange, Uri uri) { + String setting = uri.getLastPathSegment(); + HdmiCecConfig.this.notifyGlobalSettingChanged(setting); + } + } + @VisibleForTesting HdmiCecConfig(@NonNull Context context, @NonNull StorageAdapter storageAdapter, - @Nullable CecSettings systemConfig, + @Nullable CecSettings productConfig, @Nullable CecSettings vendorOverride) { mContext = context; mStorageAdapter = storageAdapter; - mSystemConfig = systemConfig; + mProductConfig = productConfig; mVendorOverride = vendorOverride; - if (mSystemConfig == null) { - Slog.i(TAG, "CEC system configuration XML missing."); + if (mProductConfig == null) { + Slog.i(TAG, "CEC master configuration XML missing."); } if (mVendorOverride == null) { Slog.i(TAG, "CEC OEM configuration override XML missing."); @@ -178,7 +214,7 @@ public class HdmiCecConfig { HdmiCecConfig(@NonNull Context context) { this(context, new StorageAdapter(context), - readSettingsFromFile(Environment.buildPath(Environment.getRootDirectory(), + readSettingsFromFile(Environment.buildPath(Environment.getProductDirectory(), ETC_DIR, CONFIG_FILE)), readSettingsFromFile(Environment.buildPath(Environment.getVendorDirectory(), ETC_DIR, CONFIG_FILE))); @@ -205,14 +241,14 @@ public class HdmiCecConfig { @VisibleForTesting static HdmiCecConfig createFromStrings(@NonNull Context context, @NonNull StorageAdapter storageAdapter, - @Nullable String systemConfigXml, + @Nullable String productConfigXml, @Nullable String vendorOverrideXml) { - CecSettings systemConfig = null; + CecSettings productConfig = null; CecSettings vendorOverride = null; try { - if (systemConfigXml != null) { - systemConfig = XmlParser.read( - new ByteArrayInputStream(systemConfigXml.getBytes())); + if (productConfigXml != null) { + productConfig = XmlParser.read( + new ByteArrayInputStream(productConfigXml.getBytes())); } if (vendorOverrideXml != null) { vendorOverride = XmlParser.read( @@ -221,12 +257,12 @@ public class HdmiCecConfig { } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) { Slog.e(TAG, "Encountered an error while reading/parsing CEC config strings", e); } - return new HdmiCecConfig(context, storageAdapter, systemConfig, vendorOverride); + return new HdmiCecConfig(context, storageAdapter, productConfig, vendorOverride); } @Nullable private Setting getSetting(@NonNull String name) { - if (mSystemConfig == null) { + if (mProductConfig == null) { return null; } if (mVendorOverride != null) { @@ -237,8 +273,8 @@ public class HdmiCecConfig { } } } - // If not found, try the system config. - for (Setting setting : mSystemConfig.getSetting()) { + // If not found, try the product config. + for (Setting setting : mProductConfig.getSetting()) { if (setting.getName().equals(name)) { return setting; } @@ -311,6 +347,7 @@ public class HdmiCecConfig { } else if (storage == STORAGE_SHARED_PREFS) { Slog.d(TAG, "Setting '" + storageKey + "' shared pref."); mStorageAdapter.storeSharedPref(storageKey, value); + notifySettingChanged(setting); } } @@ -318,15 +355,112 @@ public class HdmiCecConfig { return Integer.decode(value.getIntValue()); } + private void notifyGlobalSettingChanged(String setting) { + switch (setting) { + case Global.HDMI_CONTROL_ENABLED: + notifySettingChanged(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED); + break; + case Global.HDMI_CEC_VERSION: + notifySettingChanged(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION); + break; + case Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP: + notifySettingChanged(HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP); + break; + } + } + + private void notifySettingChanged(@NonNull @CecSettingName String name) { + Setting setting = getSetting(name); + if (setting == null) { + throw new IllegalArgumentException("Setting '" + name + "' does not exist."); + } + notifySettingChanged(setting); + } + + private void notifySettingChanged(@NonNull Setting setting) { + Set<SettingChangeListener> listeners = mSettingChangeListeners.get(setting); + if (listeners == null) { + return; // No listeners registered, do nothing. + } + for (SettingChangeListener listener: listeners) { + listener.onChange(setting.getName()); + } + } + + /** + * This method registers Global Setting change observer. + * Needs to be called once after initialization of HdmiCecConfig. + */ + public void registerGlobalSettingsObserver(Looper looper) { + Handler handler = new Handler(looper); + mSettingsObserver = new SettingsObserver(handler); + ContentResolver resolver = mContext.getContentResolver(); + String[] settings = new String[] { + Global.HDMI_CONTROL_ENABLED, + Global.HDMI_CEC_VERSION, + Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP, + }; + for (String setting: settings) { + resolver.registerContentObserver(Global.getUriFor(setting), false, + mSettingsObserver, UserHandle.USER_ALL); + } + } + + /** + * This method unregisters Global Setting change observer. + */ + public void unregisterGlobalSettingsObserver() { + ContentResolver resolver = mContext.getContentResolver(); + resolver.unregisterContentObserver(mSettingsObserver); + } + + /** + * Register change listener for a given setting name. + */ + public void registerChangeListener(@NonNull @CecSettingName String name, + SettingChangeListener listener) { + Setting setting = getSetting(name); + if (setting == null) { + throw new IllegalArgumentException("Setting '" + name + "' does not exist."); + } + @Storage int storage = getStorage(setting); + if (storage != STORAGE_GLOBAL_SETTINGS && storage != STORAGE_SHARED_PREFS) { + throw new IllegalArgumentException("Change listeners for setting '" + name + + "' not supported."); + } + if (!mSettingChangeListeners.containsKey(setting)) { + mSettingChangeListeners.put(setting, new HashSet<>()); + } + mSettingChangeListeners.get(setting).add(listener); + } + + /** + * Remove change listener for a given setting name. + */ + public void removeChangeListener(@NonNull @CecSettingName String name, + SettingChangeListener listener) { + Setting setting = getSetting(name); + if (setting == null) { + throw new IllegalArgumentException("Setting '" + name + "' does not exist."); + } + if (mSettingChangeListeners.containsKey(setting)) { + Set<SettingChangeListener> listeners = mSettingChangeListeners.get(setting); + listeners.remove(listener); + if (listeners.isEmpty()) { + mSettingChangeListeners.remove(setting); + } + } + } + /** * Returns a list of all settings based on the XML metadata. */ public @CecSettingName List<String> getAllSettings() { - if (mSystemConfig == null) { + if (mProductConfig == null) { return new ArrayList<String>(); } List<String> allSettings = new ArrayList<String>(); - for (Setting setting : mSystemConfig.getSetting()) { + for (Setting setting : mProductConfig.getSetting()) { allSettings.add(setting.getName()); } return allSettings; @@ -336,12 +470,12 @@ public class HdmiCecConfig { * Returns a list of user-modifiable settings based on the XML metadata. */ public @CecSettingName List<String> getUserSettings() { - if (mSystemConfig == null) { + if (mProductConfig == null) { return new ArrayList<String>(); } Set<String> userSettings = new HashSet<String>(); - // First read from the system config. - for (Setting setting : mSystemConfig.getSetting()) { + // First read from the product config. + for (Setting setting : mProductConfig.getSetting()) { if (setting.getUserConfigurable()) { userSettings.add(setting.getName()); } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java index e7f302c1977a..9b3f788d7457 100755 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java @@ -18,6 +18,7 @@ package com.android.server.hdmi; import android.annotation.CallSuper; import android.annotation.Nullable; +import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.IHdmiControlCallback; import android.hardware.input.InputManager; @@ -553,6 +554,7 @@ abstract class HdmiCecLocalDevice { return false; } + @Constants.RcProfile protected abstract int getRcProfile(); protected abstract List<Integer> getRcFeatures(); @@ -560,10 +562,15 @@ abstract class HdmiCecLocalDevice { protected abstract List<Integer> getDeviceFeatures(); protected boolean handleGiveFeatures(HdmiCecMessage message) { - if (mService.getCecVersion() < Constants.VERSION_2_0) { + if (mService.getCecVersion() < HdmiControlManager.HDMI_CEC_VERSION_2_0) { return false; } + reportFeatures(); + return true; + } + + protected void reportFeatures() { List<Integer> localDeviceTypes = new ArrayList<>(); for (HdmiCecLocalDevice localDevice : mService.getAllLocalDevices()) { localDeviceTypes.add(localDevice.mDeviceType); @@ -577,7 +584,6 @@ abstract class HdmiCecLocalDevice { mService.sendCecCommand( HdmiCecMessageBuilder.buildReportFeatures(mAddress, mService.getCecVersion(), localDeviceTypes, rcProfile, rcFeatures, deviceFeatures)); - return true; } @ServiceThreadOnly @@ -789,6 +795,9 @@ abstract class HdmiCecLocalDevice { final void handleAddressAllocated(int logicalAddress, int reason) { assertRunOnServiceThread(); mAddress = mPreferredAddress = logicalAddress; + if (mService.getCecVersion() >= HdmiControlManager.HDMI_CEC_VERSION_2_0) { + reportFeatures(); + } onAddressAllocated(logicalAddress, reason); setPreferredAddress(logicalAddress); } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java index 46b09dc07e61..a945c90d30ef 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java @@ -47,8 +47,6 @@ import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly; import com.android.server.hdmi.HdmiUtils.CodecSad; import com.android.server.hdmi.HdmiUtils.DeviceConfig; -import com.google.android.collect.Lists; - import org.xmlpull.v1.XmlPullParserException; import java.io.File; @@ -179,7 +177,13 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { @Override protected List<Integer> getDeviceFeatures() { - return Lists.newArrayList(Constants.DEVICE_FEATURE_SOURCE_SUPPORTS_ARC_RX); + List<Integer> deviceFeatures = new ArrayList<>(); + + if (SystemProperties.getBoolean(Constants.PROPERTY_ARC_SUPPORT, true)) { + deviceFeatures.add(Constants.DEVICE_FEATURE_SOURCE_SUPPORTS_ARC_RX); + } + + return deviceFeatures; } @Override diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java index 109f6a7f56fe..9ca1f913f5ce 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java @@ -128,25 +128,6 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource { String.valueOf(addr)); } - @ServiceThreadOnly - void queryDisplayStatus(IHdmiControlCallback callback) { - assertRunOnServiceThread(); - List<DevicePowerStatusAction> actions = getActions(DevicePowerStatusAction.class); - if (!actions.isEmpty()) { - Slog.i(TAG, "queryDisplayStatus already in progress"); - actions.get(0).addCallback(callback); - return; - } - DevicePowerStatusAction action = DevicePowerStatusAction.create(this, Constants.ADDR_TV, - callback); - if (action == null) { - Slog.w(TAG, "Cannot initiate queryDisplayStatus"); - invokeCallback(callback, HdmiControlManager.RESULT_EXCEPTION); - return; - } - addAndStartAction(action); - } - @Override @ServiceThreadOnly void onHotplug(int portId, boolean connected) { diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java index 62b7d8f95f5a..59ebdd597279 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java @@ -70,6 +70,25 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice { super(service, deviceType); } + @ServiceThreadOnly + void queryDisplayStatus(IHdmiControlCallback callback) { + assertRunOnServiceThread(); + List<DevicePowerStatusAction> actions = getActions(DevicePowerStatusAction.class); + if (!actions.isEmpty()) { + Slog.i(TAG, "queryDisplayStatus already in progress"); + actions.get(0).addCallback(callback); + return; + } + DevicePowerStatusAction action = DevicePowerStatusAction.create(this, Constants.ADDR_TV, + callback); + if (action == null) { + Slog.w(TAG, "Cannot initiate queryDisplayStatus"); + invokeCallback(callback, HdmiControlManager.RESULT_EXCEPTION); + return; + } + addAndStartAction(action); + } + @Override @ServiceThreadOnly void onHotplug(int portId, boolean connected) { @@ -87,10 +106,14 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice { @ServiceThreadOnly protected void sendStandby(int deviceId) { assertRunOnServiceThread(); - - // Send standby to TV only for now - int targetAddress = Constants.ADDR_TV; - mService.sendCecCommand(HdmiCecMessageBuilder.buildStandby(mAddress, targetAddress)); + String sendStandbyOnSleep = mService.getHdmiCecConfig().getStringValue( + HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP); + if (sendStandbyOnSleep.equals(HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST)) { + mService.sendCecCommand( + HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_BROADCAST)); + return; + } + mService.sendCecCommand(HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_TV)); } @ServiceThreadOnly @@ -113,6 +136,44 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice { } @ServiceThreadOnly + void toggleAndFollowTvPower() { + assertRunOnServiceThread(); + // Wake up Android framework to take over CEC control from the microprocessor. + mService.wakeUp(); + mService.queryDisplayStatus(new IHdmiControlCallback.Stub() { + @Override + public void onComplete(int status) { + if (status == HdmiControlManager.POWER_STATUS_UNKNOWN) { + Slog.i(TAG, "TV power toggle: TV power status unknown"); + sendUserControlPressedAndReleased(Constants.ADDR_TV, + HdmiCecKeycode.CEC_KEYCODE_POWER_TOGGLE_FUNCTION); + // Source device remains awake. + } else if (status == HdmiControlManager.POWER_STATUS_ON + || status == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON) { + Slog.i(TAG, "TV power toggle: turning off TV"); + sendStandby(0 /*unused */); + // Source device goes to standby, to follow the toggled TV power state. + mService.standby(); + } else if (status == HdmiControlManager.POWER_STATUS_STANDBY + || status == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY) { + Slog.i(TAG, "TV power toggle: turning on TV"); + oneTouchPlay(new IHdmiControlCallback.Stub() { + @Override + public void onComplete(int result) { + if (result != HdmiControlManager.RESULT_SUCCESS) { + Slog.w(TAG, "Failed to complete One Touch Play. result=" + result); + sendUserControlPressedAndReleased(Constants.ADDR_TV, + HdmiCecKeycode.CEC_KEYCODE_POWER_TOGGLE_FUNCTION); + } + } + }); + // Source device remains awake, to follow the toggled TV power state. + } + } + }); + } + + @ServiceThreadOnly protected void onActiveSourceLost() { // Nothing to do. } @@ -235,6 +296,7 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice { // do nothing } + @Constants.RcProfile @Override protected int getRcProfile() { return Constants.RC_PROFILE_SOURCE; @@ -242,10 +304,8 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice { @Override protected List<Integer> getRcFeatures() { - return Lists.newArrayList(Constants.RC_PROFILE_SOURCE_HANDLES_CONTENTS_MENU, - Constants.RC_PROFILE_SOURCE_HANDLES_ROOT_MENU, - Constants.RC_PROFILE_SOURCE_HANDLES_SETUP_MENU, - Constants.RC_PROFILE_SOURCE_HANDLES_TOP_MENU); + return Lists.newArrayList(Constants.RC_PROFILE_SOURCE_HANDLES_ROOT_MENU, + Constants.RC_PROFILE_SOURCE_HANDLES_SETUP_MENU); } @Override diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java index 8cf6c9757fe8..11e18c54b1e4 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java @@ -53,6 +53,7 @@ import com.android.server.hdmi.HdmiControlService.SendMessageCallback; import com.google.android.collect.Lists; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -1480,6 +1481,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { return true; } + @Constants.RcProfile @Override protected int getRcProfile() { return Constants.RC_PROFILE_TV; @@ -1492,8 +1494,21 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { @Override protected List<Integer> getDeviceFeatures() { - return Lists.newArrayList(Constants.DEVICE_FEATURE_SINK_SUPPORTS_ARC_TX, - Constants.DEVICE_FEATURE_TV_SUPPORTS_RECORD_TV_SCREEN); + List<Integer> deviceFeatures = new ArrayList<>(); + + boolean hasArcPort = false; + List<HdmiPortInfo> ports = mService.getPortInfo(); + for (HdmiPortInfo port : ports) { + if (isArcFeatureEnabled(port.getId())) { + hasArcPort = true; + break; + } + } + if (hasArcPort) { + deviceFeatures.add(Constants.DEVICE_FEATURE_SINK_SUPPORTS_ARC_TX); + } + deviceFeatures.add(Constants.DEVICE_FEATURE_TV_SUPPORTS_RECORD_TV_SCREEN); + return deviceFeatures; } @Override diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java index 1a481b632606..0a8621b3e726 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java @@ -16,10 +16,10 @@ package com.android.server.hdmi; +import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; import com.android.server.hdmi.Constants.AudioCodec; -import com.android.server.hdmi.Constants.CecVersion; import java.io.UnsupportedEncodingException; import java.util.Arrays; @@ -696,8 +696,10 @@ public class HdmiCecMessageBuilder { return buildCommand(src, dest, Constants.MESSAGE_GIVE_FEATURES); } - static HdmiCecMessage buildReportFeatures(int src, @CecVersion int cecVersion, - List<Integer> allDeviceTypes, int rcProfile, List<Integer> rcFeatures, + static HdmiCecMessage buildReportFeatures(int src, + @HdmiControlManager.HdmiCecVersion int cecVersion, + List<Integer> allDeviceTypes, @Constants.RcProfile int rcProfile, + List<Integer> rcFeatures, List<Integer> deviceFeatures) { byte cecVersionByte = (byte) (cecVersion & 0xFF); byte deviceTypes = 0; @@ -708,16 +710,16 @@ public class HdmiCecMessageBuilder { byte rcProfileByte = 0; rcProfileByte |= rcProfile << 6; if (rcProfile == Constants.RC_PROFILE_SOURCE) { - for (Integer rcFeature : rcFeatures) { + for (@Constants.RcProfileSource Integer rcFeature : rcFeatures) { rcProfileByte |= 1 << rcFeature; } } else { - byte rcProfileTv = (byte) (rcFeatures.get(0) & 0xFFFF); + @Constants.RcProfileTv byte rcProfileTv = (byte) (rcFeatures.get(0) & 0xFFFF); rcProfileByte |= rcProfileTv; } byte deviceFeaturesByte = 0; - for (Integer deviceFeature : deviceFeatures) { + for (@Constants.DeviceFeature Integer deviceFeature : deviceFeatures) { deviceFeaturesByte |= 1 << deviceFeature; } @@ -777,6 +779,7 @@ public class HdmiCecMessageBuilder { }; } + @Constants.DeviceType private static int hdmiDeviceInfoDeviceTypeToShiftValue(int deviceType) { switch (deviceType) { case HdmiDeviceInfo.DEVICE_TV: diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java index baed9cc1b71d..d2a2d727b654 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java @@ -201,7 +201,7 @@ public class HdmiCecMessageValidator { addValidationInfo( Constants.MESSAGE_REPORT_POWER_STATUS, new OneByteRangeValidator(0x00, 0x03), - DEST_DIRECT); + DEST_DIRECT | DEST_BROADCAST); // Messages for the General Protocol. addValidationInfo(Constants.MESSAGE_FEATURE_ABORT, diff --git a/services/core/java/com/android/server/hdmi/HdmiCecPowerStatusController.java b/services/core/java/com/android/server/hdmi/HdmiCecPowerStatusController.java new file mode 100644 index 000000000000..c4dadaa19bd4 --- /dev/null +++ b/services/core/java/com/android/server/hdmi/HdmiCecPowerStatusController.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.hdmi; + +import static com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly; + +import android.hardware.hdmi.HdmiControlManager; + +/** + * Storage of HDMI-CEC power status and controls possible cases where power status changes must also + * broadcast {@code <Report Power Status>} messages. + * + * All HDMI-CEC related power status changes should be done through this class. + */ +class HdmiCecPowerStatusController { + + private final HdmiControlService mHdmiControlService; + + private int mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY; + + HdmiCecPowerStatusController(HdmiControlService hdmiControlService) { + mHdmiControlService = hdmiControlService; + } + + int getPowerStatus() { + return mPowerStatus; + } + + boolean isPowerStatusOn() { + return mPowerStatus == HdmiControlManager.POWER_STATUS_ON; + } + + boolean isPowerStatusStandby() { + return mPowerStatus == HdmiControlManager.POWER_STATUS_STANDBY; + } + + boolean isPowerStatusTransientToOn() { + return mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON; + } + + boolean isPowerStatusTransientToStandby() { + return mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY; + } + + @ServiceThreadOnly + void setPowerStatus(int powerStatus) { + setPowerStatus(powerStatus, true); + } + + @ServiceThreadOnly + void setPowerStatus(int powerStatus, boolean sendPowerStatusUpdate) { + if (powerStatus == mPowerStatus) { + return; + } + + mPowerStatus = powerStatus; + if (sendPowerStatusUpdate + && mHdmiControlService.getCecVersion() >= HdmiControlManager.HDMI_CEC_VERSION_2_0) { + sendReportPowerStatus(mPowerStatus); + } + } + + private void sendReportPowerStatus(int powerStatus) { + for (HdmiCecLocalDevice localDevice : mHdmiControlService.getAllLocalDevices()) { + mHdmiControlService.sendCecCommand( + HdmiCecMessageBuilder.buildReportPowerStatus(localDevice.mAddress, + Constants.ADDR_BROADCAST, powerStatus)); + } + } +} diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index dcd6c0260cff..fd825d6f6288 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -350,8 +350,8 @@ public class HdmiControlService extends SystemService { private HdmiCecMessageValidator mMessageValidator; - @ServiceThreadOnly - private int mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY; + private final HdmiCecPowerStatusController mPowerStatusController = + new HdmiCecPowerStatusController(this); @ServiceThreadOnly private String mMenuLanguage = localeToMenuLanguage(Locale.getDefault()); @@ -530,7 +530,8 @@ public class HdmiControlService extends SystemService { mIoThread.start(); mIoLooper = mIoThread.getLooper(); } - mPowerStatus = getInitialPowerStatus(); + + mPowerStatusController.setPowerStatus(getInitialPowerStatus()); mProhibitMode = false; mHdmiControlEnabled = readBooleanSetting(Global.HDMI_CONTROL_ENABLED, true); mHdmiCecVolumeControlEnabled = readBooleanSetting( @@ -562,6 +563,7 @@ public class HdmiControlService extends SystemService { if (mMessageValidator == null) { mMessageValidator = new HdmiCecMessageValidator(this); } + mHdmiCecConfig.registerGlobalSettingsObserver(mIoLooper); } private void bootCompleted() { @@ -673,10 +675,12 @@ public class HdmiControlService extends SystemService { * Updates the power status once the initialization of local devices is complete. */ private void updatePowerStatusOnInitializeCecComplete() { - if (mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON) { - mPowerStatus = HdmiControlManager.POWER_STATUS_ON; - } else if (mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY) { - mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY; + if (mPowerStatusController.isPowerStatusTransientToOn()) { + mHandler.post(() -> mPowerStatusController.setPowerStatus( + HdmiControlManager.POWER_STATUS_ON)); + } else if (mPowerStatusController.isPowerStatusTransientToStandby()) { + mHandler.post(() -> mPowerStatusController.setPowerStatus( + HdmiControlManager.POWER_STATUS_STANDBY)); } } @@ -1686,7 +1690,7 @@ public class HdmiControlService extends SystemService { public void oneTouchPlay(final IHdmiControlCallback callback) { enforceAccessPermission(); int pid = Binder.getCallingPid(); - Slog.d(TAG, "Proccess pid: " + pid + " is calling oneTouchPlay."); + Slog.d(TAG, "Process pid: " + pid + " is calling oneTouchPlay."); runOnServiceThread(new Runnable() { @Override public void run() { @@ -1696,6 +1700,19 @@ public class HdmiControlService extends SystemService { } @Override + public void toggleAndFollowTvPower() { + enforceAccessPermission(); + int pid = Binder.getCallingPid(); + Slog.d(TAG, "Process pid: " + pid + " is calling toggleAndFollowTvPower."); + runOnServiceThread(new Runnable() { + @Override + public void run() { + HdmiControlService.this.toggleAndFollowTvPower(); + } + }); + } + + @Override public void queryDisplayStatus(final IHdmiControlCallback callback) { enforceAccessPermission(); runOnServiceThread(new Runnable() { @@ -2191,7 +2208,7 @@ public class HdmiControlService extends SystemService { final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); pw.println("mProhibitMode: " + mProhibitMode); - pw.println("mPowerStatus: " + mPowerStatus); + pw.println("mPowerStatus: " + mPowerStatusController.getPowerStatus()); // System settings pw.println("System_settings:"); @@ -2362,7 +2379,23 @@ public class HdmiControlService extends SystemService { } @ServiceThreadOnly - private void queryDisplayStatus(final IHdmiControlCallback callback) { + @VisibleForTesting + protected void toggleAndFollowTvPower() { + assertRunOnServiceThread(); + HdmiCecLocalDeviceSource source = playback(); + if (source == null) { + source = audioSystem(); + } + + if (source == null) { + Slog.w(TAG, "Local source device not available"); + return; + } + source.toggleAndFollowTvPower(); + } + + @ServiceThreadOnly + protected void queryDisplayStatus(final IHdmiControlCallback callback) { assertRunOnServiceThread(); if (!mAddressAllocated) { mDisplayStatusCallback = callback; @@ -2371,9 +2404,13 @@ public class HdmiControlService extends SystemService { return; } - HdmiCecLocalDevicePlayback source = playback(); + HdmiCecLocalDeviceSource source = playback(); + if (source == null) { + source = audioSystem(); + } + if (source == null) { - Slog.w(TAG, "Local playback device not available"); + Slog.w(TAG, "Local source device not available"); invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE); return; } @@ -2831,34 +2868,34 @@ public class HdmiControlService extends SystemService { @ServiceThreadOnly int getPowerStatus() { assertRunOnServiceThread(); - return mPowerStatus; + return mPowerStatusController.getPowerStatus(); } @ServiceThreadOnly @VisibleForTesting void setPowerStatus(int powerStatus) { assertRunOnServiceThread(); - mPowerStatus = powerStatus; + mPowerStatusController.setPowerStatus(powerStatus); } @ServiceThreadOnly boolean isPowerOnOrTransient() { assertRunOnServiceThread(); - return mPowerStatus == HdmiControlManager.POWER_STATUS_ON - || mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON; + return mPowerStatusController.isPowerStatusOn() + || mPowerStatusController.isPowerStatusTransientToOn(); } @ServiceThreadOnly boolean isPowerStandbyOrTransient() { assertRunOnServiceThread(); - return mPowerStatus == HdmiControlManager.POWER_STATUS_STANDBY - || mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY; + return mPowerStatusController.isPowerStatusStandby() + || mPowerStatusController.isPowerStatusTransientToStandby(); } @ServiceThreadOnly boolean isPowerStandby() { assertRunOnServiceThread(); - return mPowerStatus == HdmiControlManager.POWER_STATUS_STANDBY; + return mPowerStatusController.isPowerStatusStandby(); } @ServiceThreadOnly @@ -2895,7 +2932,8 @@ public class HdmiControlService extends SystemService { @ServiceThreadOnly private void onWakeUp(@WakeReason final int wakeUpAction) { assertRunOnServiceThread(); - mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON; + mPowerStatusController.setPowerStatus(HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON, + false); if (mCecController != null) { if (mHdmiControlEnabled) { int startReason = -1; @@ -2926,14 +2964,15 @@ public class HdmiControlService extends SystemService { @VisibleForTesting protected void onStandby(final int standbyAction) { assertRunOnServiceThread(); - mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY; + mPowerStatusController.setPowerStatus(HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY, + false); invokeVendorCommandListenersOnControlStateChanged(false, HdmiControlManager.CONTROL_STATE_CHANGED_REASON_STANDBY); final List<HdmiCecLocalDevice> devices = getAllLocalDevices(); if (!isStandbyMessageReceived() && !canGoToStandby()) { - mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY; + mPowerStatusController.setPowerStatus(HdmiControlManager.POWER_STATUS_STANDBY); for (HdmiCecLocalDevice device : devices) { device.onStandby(mStandbyMessageReceived, standbyAction); } @@ -3013,10 +3052,10 @@ public class HdmiControlService extends SystemService { assertRunOnServiceThread(); Slog.v(TAG, "onStandbyCompleted"); - if (mPowerStatus != HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY) { + if (!mPowerStatusController.isPowerStatusTransientToStandby()) { return; } - mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY; + mPowerStatusController.setPowerStatus(HdmiControlManager.POWER_STATUS_STANDBY); for (HdmiCecLocalDevice device : mHdmiCecNetwork.getLocalDeviceList()) { device.onStandby(mStandbyMessageReceived, standbyAction); } diff --git a/services/core/java/com/android/server/hdmi/cec_key_handling.md b/services/core/java/com/android/server/hdmi/cec_key_handling.md index d150dd3fceb9..1b41a6740869 100644 --- a/services/core/java/com/android/server/hdmi/cec_key_handling.md +++ b/services/core/java/com/android/server/hdmi/cec_key_handling.md @@ -9,13 +9,13 @@ Android TV requires special handling of some keys. The general action for key handling is described in the table -| Android Key | TV Panel | OTT | Soundbar | -| ----------- | ----------------- | ------------------- | ------------------- | -| general | Send to active source | handle on device | handle on device | -| POWER | Toggle the device power state | Toggle the TV power state | Toggle the TV power state | -| TV_POWER | Toggle the device power state | Toggle the TV power state | Toggle the TV power state | -| HOME | Turn on TV, Set active Source to TV, go to home screen | OTP, and go to home screen | OTP, and go to home screen | -| volume keys | Handle on device or send to soundbar | Send to TV or soundbar | Handle on device or send to TV | +| Android Key | TV Panel | OTT | Soundbar | +| ----------- | ----------------- | ------------------- | ------------------- | +| general | Send to active source | handle on device | handle on device | +| POWER | Toggle the device power state | Toggle the OTT power state, TV power state follows | Toggle the soundbar power state, TV power state follows| +| TV_POWER | Toggle the device power state | Toggle the TV power state, OTT power state follows | Toggle the TV power state, soundbar power state follows| +| HOME | Turn on TV, Set active Source to TV, go to home screen | OTP, and go to home screen | OTP, and go to home screen | +| volume keys | Handle on device or send to soundbar | Send to TV or soundbar | Handle on device or send to TV | Special cases and flags for each key are described below diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index a4844f6931ed..0ceaf7748eba 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -15,6 +15,7 @@ package com.android.server.inputmethod; +import static android.inputmethodservice.InputMethodService.FINISH_INPUT_NO_FALLBACK_CONNECTION; import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL; import static android.os.IServiceManager.DUMP_FLAG_PROTO; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; @@ -45,6 +46,8 @@ import static android.server.inputmethod.InputMethodManagerServiceProto.SYSTEM_R import static android.util.imetracing.ImeTracing.IME_TRACING_FROM_IMMS; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; +import static android.view.WindowManager.DISPLAY_IME_POLICY_HIDE; +import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL; import static java.lang.annotation.RetentionPolicy.SOURCE; @@ -118,7 +121,6 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.EventLog; import android.util.IndentingPrintWriter; -import android.util.Log; import android.util.LruCache; import android.util.Pair; import android.util.PrintWriterPrinter; @@ -131,6 +133,8 @@ import android.view.DisplayInfo; import android.view.IWindowManager; import android.view.InputChannel; import android.view.View; +import android.view.WindowManager; +import android.view.WindowManager.DisplayImePolicy; import android.view.WindowManager.LayoutParams; import android.view.WindowManager.LayoutParams.SoftInputModeFlags; import android.view.autofill.AutofillId; @@ -152,6 +156,7 @@ import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodSubtype; import com.android.internal.annotations.GuardedBy; +import com.android.internal.compat.IPlatformCompat; import com.android.internal.content.PackageMonitor; import com.android.internal.inputmethod.IInputContentUriToken; import com.android.internal.inputmethod.IInputMethodPrivilegedOperations; @@ -474,6 +479,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } + @GuardedBy("mMethodMap") final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<>(); private static final class ActivityViewInfo { @@ -530,6 +536,13 @@ public class InputMethodManagerService extends IInputMethodManager.Stub int mCurSeq; /** + * {@code true} if the Ime policy has been set to {@link WindowManager#DISPLAY_IME_POLICY_HIDE}. + * + * This prevents the IME from showing when it otherwise may have shown. + */ + boolean mImeHiddenByDisplayPolicy; + + /** * The client that is currently bound to an input method. */ ClientState mCurClient; @@ -708,6 +721,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub */ boolean mIsInteractive = true; + private IPlatformCompat mPlatformCompat; + int mBackDisposition = InputMethodService.BACK_DISPOSITION_DEFAULT; /** @@ -1657,7 +1672,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); mInputManagerInternal = LocalServices.getService(InputManagerInternal.class); mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class); - mImeDisplayValidator = displayId -> mWindowManagerInternal.shouldShowIme(displayId); + mImeDisplayValidator = displayId -> mWindowManagerInternal.getDisplayImePolicy(displayId); mCaller = new HandlerCaller(context, null, new HandlerCaller.Callback() { @Override public void executeMessage(Message msg) { @@ -1669,6 +1684,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mUserManagerInternal = LocalServices.getService(UserManagerInternal.class); mHasFeature = context.getPackageManager().hasSystemFeature( PackageManager.FEATURE_INPUT_METHODS); + mPlatformCompat = IPlatformCompat.Stub.asInterface( + ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); mSlotIme = mContext.getString(com.android.internal.R.string.status_bar_ime); mIsLowRam = ActivityManager.isLowRamDeviceStatic(); @@ -2305,8 +2322,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } - executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIIO( - MSG_SET_ACTIVE, 0, 0, mCurClient)); + scheduleSetActiveToClient(mCurClient, false /* active */, false /* fullscreen */, + false /* reportToImeController */); executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIIO( MSG_UNBIND_CLIENT, mCurSeq, unbindClientReason, mCurClient.client)); mCurClient.sessionRequested = false; @@ -2437,6 +2454,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub final int displayIdToShowIme = computeImeDisplayIdForTarget(cs.selfReportedDisplayId, mImeDisplayValidator); + if (displayIdToShowIme == INVALID_DISPLAY) { + mImeHiddenByDisplayPolicy = true; + return InputBindResult.NO_IME; + } + mImeHiddenByDisplayPolicy = false; + if (mCurClient != cs) { // Was the keyguard locked when switching over to the new client? mCurClientInKeyguard = isKeyguardLocked(); @@ -2448,7 +2471,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // If the screen is on, inform the new client it is active if (mIsInteractive) { - executeOrSendMessage(cs.client, mCaller.obtainMessageIO(MSG_SET_ACTIVE, 1, cs)); + scheduleSetActiveToClient(cs, true /* active */, false /* fullscreen */, + false /* reportToImeController */); } } @@ -2548,7 +2572,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @FunctionalInterface interface ImeDisplayValidator { - boolean displayCanShowIme(int displayId); + @DisplayImePolicy int getDisplayImePolicy(int displayId); } /** @@ -2557,7 +2581,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub * @param displayId the ID of the display where the IME client target is. * @param checker instance of {@link ImeDisplayValidator} which is used for * checking display config to adjust the final target display. - * @return The ID of the display where the IME should be shown. + * @return The ID of the display where the IME should be shown or + * {@link android.view.Display#INVALID_DISPLAY} if the display has an ImePolicy of + * {@link WindowManager#DISPLAY_IME_POLICY_HIDE}. */ static int computeImeDisplayIdForTarget(int displayId, @NonNull ImeDisplayValidator checker) { if (displayId == DEFAULT_DISPLAY || displayId == INVALID_DISPLAY) { @@ -2566,7 +2592,14 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // Show IME window on fallback display when the display doesn't support system decorations // or the display is virtual and isn't owned by system for security concern. - return checker.displayCanShowIme(displayId) ? displayId : FALLBACK_DISPLAY_ID; + final int result = checker.getDisplayImePolicy(displayId); + if (result == DISPLAY_IME_POLICY_LOCAL) { + return displayId; + } else if (result == DISPLAY_IME_POLICY_HIDE) { + return INVALID_DISPLAY; + } else { + return FALLBACK_DISPLAY_ID; + } } @AnyThread @@ -4076,6 +4109,44 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return ImeTracing.getInstance().isEnabled(); } + @BinderThread + @Override + public void startImeTrace() { + ImeTracing.getInstance().startTrace(null /* printwriter */); + ArrayMap<IBinder, ClientState> clients; + synchronized (mMethodMap) { + clients = new ArrayMap<>(mClients); + } + for (ClientState state : clients.values()) { + if (state != null) { + try { + state.client.setImeTraceEnabled(true /* enabled */); + } catch (RemoteException e) { + Slog.e(TAG, "Error while trying to enable ime trace on client window", e); + } + } + } + } + + @BinderThread + @Override + public void stopImeTrace() { + ImeTracing.getInstance().stopTrace(null /* printwriter */); + ArrayMap<IBinder, ClientState> clients; + synchronized (mMethodMap) { + clients = new ArrayMap<>(mClients); + } + for (ClientState state : clients.values()) { + if (state != null) { + try { + state.client.setImeTraceEnabled(false /* enabled */); + } catch (RemoteException e) { + Slog.e(TAG, "Error while trying to disable ime trace on client window", e); + } + } + } + } + @GuardedBy("mMethodMap") private void dumpDebug(ProtoOutputStream proto, long fieldId) { synchronized (mMethodMap) { @@ -4451,15 +4522,20 @@ public class InputMethodManagerService extends IInputMethodManager.Stub args.recycle(); return true; } - case MSG_SET_ACTIVE: + case MSG_SET_ACTIVE: { + args = (SomeArgs) msg.obj; + final ClientState clientState = (ClientState) args.arg1; try { - ((ClientState)msg.obj).client.setActive(msg.arg1 != 0, msg.arg2 != 0); + clientState.client.setActive(args.argi1 != 0 /* active */, + args.argi2 != 0 /* fullScreen */, + args.argi3 != 0 /* reportToImeController */); } catch (RemoteException e) { Slog.w(TAG, "Got RemoteException sending setActive(false) notification to pid " - + ((ClientState)msg.obj).pid + " uid " - + ((ClientState)msg.obj).uid); + + clientState.pid + " uid " + clientState.uid); } + args.recycle(); return true; + } case MSG_SET_INTERACTIVE: handleSetInteractive(msg.arg1 != 0); return true; @@ -4545,13 +4621,24 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // Inform the current client of the change in active status if (mCurClient != null && mCurClient.client != null) { - executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIIO( - MSG_SET_ACTIVE, mIsInteractive ? 1 : 0, mInFullscreenMode ? 1 : 0, - mCurClient)); + boolean reportToImeController = false; + try { + reportToImeController = mPlatformCompat.isChangeEnabledByUid( + FINISH_INPUT_NO_FALLBACK_CONNECTION, mCurMethodUid); + } catch (RemoteException e) { + } + scheduleSetActiveToClient(mCurClient, mIsInteractive, mInFullscreenMode, + reportToImeController); } } } + private void scheduleSetActiveToClient(ClientState state, boolean active, boolean fullscreen, + boolean reportToImeController) { + executeOrSendMessage(state.client, mCaller.obtainMessageIIIIO(MSG_SET_ACTIVE, + active ? 1 : 0, fullscreen ? 1 : 0, reportToImeController ? 1 : 0, 0, state)); + } + private boolean chooseNewDefaultIMELocked() { final InputMethodInfo imi = InputMethodUtils.getMostApplicableDefaultIME( mSettings.getEnabledInputMethodListLocked()); @@ -5180,6 +5267,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub p.println(" mInFullscreenMode=" + mInFullscreenMode); p.println(" mSystemReady=" + mSystemReady + " mInteractive=" + mIsInteractive); p.println(" mSettingsObserver=" + mSettingsObserver); + p.println(" mImeHiddenByDisplayPolicy=" + mImeHiddenByDisplayPolicy); p.println(" mSwitchingController:"); mSwitchingController.dump(p); p.println(" mSettings:"); @@ -5322,20 +5410,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub case "reset": return mService.handleShellCommandResetInputMethod(this); case "tracing": - int result = ImeTracing.getInstance().onShellCommand(this); - boolean isImeTraceEnabled = ImeTracing.getInstance().isEnabled(); - for (ClientState state : mService.mClients.values()) { - if (state != null) { - try { - state.client.setImeTraceEnabled(isImeTraceEnabled); - } catch (RemoteException e) { - Log.e(TAG, - "Error while trying to enable/disable ime " - + "trace on client window", e); - } - } - } - return result; + return mService.handleShellCommandTraceInputMethod(this); default: getOutPrintWriter().println("Unknown command: " + imeCommand); return ShellCommandResult.FAILURE; @@ -5716,6 +5791,33 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } /** + * Handles {@code adb shell ime tracing start/stop}. + * @param shellCommand {@link ShellCommand} object that is handling this command. + * @return Exit code of the command. + */ + @BinderThread + @ShellCommandResult + private int handleShellCommandTraceInputMethod(@NonNull ShellCommand shellCommand) { + int result = ImeTracing.getInstance().onShellCommand(shellCommand); + boolean isImeTraceEnabled = ImeTracing.getInstance().isEnabled(); + ArrayMap<IBinder, ClientState> clients; + synchronized (mMethodMap) { + clients = new ArrayMap<>(mClients); + } + for (ClientState state : clients.values()) { + if (state != null) { + try { + state.client.setImeTraceEnabled(isImeTraceEnabled); + } catch (RemoteException e) { + Slog.e(TAG, "Error while trying to enable/disable ime trace on client window", + e); + } + } + } + return result; + } + + /** * @param userId the actual user handle obtained by {@link UserHandle#getIdentifier()} * and *not* pseudo ids like {@link UserHandle#USER_ALL etc}. * @return {@code true} if userId has debugging privileges. diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java index 7af27ca46f68..62d817c22ae6 100644 --- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java @@ -911,7 +911,8 @@ public final class MultiClientInputMethodManagerService { com.android.internal.R.string.input_method_binding_label) .putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity( context, 0, - new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS), 0)); + new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS), + PendingIntent.FLAG_MUTABLE)); // Note: Instead of re-dispatching callback from the main thread to the worker thread // where OnWorkerThreadCallback is running, we pass the Handler object here so that @@ -1338,7 +1339,7 @@ public final class MultiClientInputMethodManagerService { switch (clientInfo.mState) { case InputMethodClientState.WAITING_FOR_IME_SESSION: try { - clientInfo.mClient.setActive(true, false); + clientInfo.mClient.setActive(true, false, false); } catch (RemoteException e) { // TODO(yukawa): Remove this client. return; @@ -1400,7 +1401,7 @@ public final class MultiClientInputMethodManagerService { return; } try { - clientInfo.mClient.setActive(active, false /* fullscreen */); + clientInfo.mClient.setActive(active, false /* fullscreen */, false); } catch (RemoteException e) { return; } @@ -1816,5 +1817,15 @@ public final class MultiClientInputMethodManagerService { public boolean isImeTraceEnabled() { return false; } + + @BinderThread + @Override + public void startImeTrace() { + } + + @BinderThread + @Override + public void stopImeTrace() { + } } } diff --git a/services/core/java/com/android/server/location/LocationShellCommand.java b/services/core/java/com/android/server/location/LocationShellCommand.java index 8c1afab00b9a..7d3ccbf5a1b4 100644 --- a/services/core/java/com/android/server/location/LocationShellCommand.java +++ b/services/core/java/com/android/server/location/LocationShellCommand.java @@ -16,9 +16,10 @@ package com.android.server.location; -import android.os.BasicShellCommandHandler; import android.os.UserHandle; +import com.android.modules.utils.BasicShellCommandHandler; + import java.io.PrintWriter; import java.util.Objects; diff --git a/services/core/java/com/android/server/location/geofence/GeofenceManager.java b/services/core/java/com/android/server/location/geofence/GeofenceManager.java index 4a8534292eaf..5a90fa7a271c 100644 --- a/services/core/java/com/android/server/location/geofence/GeofenceManager.java +++ b/services/core/java/com/android/server/location/geofence/GeofenceManager.java @@ -322,12 +322,28 @@ public class GeofenceManager extends @Override protected boolean isActive(GeofenceRegistration registration) { - CallerIdentity identity = registration.getIdentity(); - return registration.isPermitted() - && (identity.isSystem() || mUserInfoHelper.isCurrentUserId(identity.getUserId())) - && mSettingsHelper.isLocationEnabled(identity.getUserId()) - && !mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(), - identity.getPackageName()); + return registration.isPermitted() && isActive(registration.getIdentity()); + } + + private boolean isActive(CallerIdentity identity) { + if (identity.isSystemServer()) { + if (!mSettingsHelper.isLocationEnabled(mUserInfoHelper.getCurrentUserId())) { + return false; + } + } else { + if (!mSettingsHelper.isLocationEnabled(identity.getUserId())) { + return false; + } + if (!mUserInfoHelper.isCurrentUserId(identity.getUserId())) { + return false; + } + if (mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(), + identity.getPackageName())) { + return false; + } + } + + return true; } @Override diff --git a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java index e68f595c3c43..7e848e03c3a0 100644 --- a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java +++ b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java @@ -266,11 +266,30 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter CallerIdentity identity = registration.getIdentity(); return registration.isPermitted() && (registration.isForeground() || isBackgroundRestrictionExempt(identity)) - && (identity.isSystem() || mUserInfoHelper.isCurrentUserId(identity.getUserId())) - && mLocationManagerInternal.isProviderEnabledForUser(GPS_PROVIDER, - identity.getUserId()) - && !mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(), - identity.getPackageName()); + && isActive(identity); + } + + private boolean isActive(CallerIdentity identity) { + if (identity.isSystemServer()) { + if (!mLocationManagerInternal.isProviderEnabledForUser(GPS_PROVIDER, + mUserInfoHelper.getCurrentUserId())) { + return false; + } + } else { + if (!mLocationManagerInternal.isProviderEnabledForUser(GPS_PROVIDER, + identity.getUserId())) { + return false; + } + if (!mUserInfoHelper.isCurrentUserId(identity.getUserId())) { + return false; + } + if (mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(), + identity.getPackageName())) { + return false; + } + } + + return true; } private boolean isBackgroundRestrictionExempt(CallerIdentity identity) { diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java index ceac10d717e4..a8889fd6b454 100644 --- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java @@ -709,8 +709,10 @@ public class GnssLocationProvider extends AbstractLocationProvider implements "GNSS HAL Requesting location updates from %s provider for %d millis.", provider, durationMillis)); - locationManager.requestLocationUpdates(provider, locationRequest.build(), - DIRECT_EXECUTOR, locationListener); + if (locationManager.getProvider(provider) != null) { + locationManager.requestLocationUpdates(provider, locationRequest.build(), + DIRECT_EXECUTOR, locationListener); + } } private void injectBestLocation(Location location) { diff --git a/services/core/java/com/android/server/location/injector/SystemUserInfoHelper.java b/services/core/java/com/android/server/location/injector/SystemUserInfoHelper.java index 56dd92ab1829..d4a8fbd0ceb0 100644 --- a/services/core/java/com/android/server/location/injector/SystemUserInfoHelper.java +++ b/services/core/java/com/android/server/location/injector/SystemUserInfoHelper.java @@ -88,11 +88,6 @@ public class SystemUserInfoHelper extends UserInfoHelper { return mUserManager; } - /** - * Returns an array of running user ids. This will include all running users, and will also - * include any profiles of the running users. The caller must never mutate the returned - * array. - */ @Override public int[] getRunningUserIds() { IActivityManager activityManager = getActivityManager(); @@ -110,10 +105,6 @@ public class SystemUserInfoHelper extends UserInfoHelper { } } - /** - * Returns true if the given user id is either the current user or a profile of the current - * user. - */ @Override public boolean isCurrentUserId(@UserIdInt int userId) { ActivityManagerInternal activityManagerInternal = getActivityManagerInternal(); @@ -130,6 +121,21 @@ public class SystemUserInfoHelper extends UserInfoHelper { } @Override + public @UserIdInt int getCurrentUserId() { + ActivityManagerInternal activityManagerInternal = getActivityManagerInternal(); + if (activityManagerInternal != null) { + final long identity = Binder.clearCallingIdentity(); + try { + return activityManagerInternal.getCurrentUserId(); + } finally { + Binder.restoreCallingIdentity(identity); + } + } else { + return UserHandle.USER_NULL; + } + } + + @Override protected int[] getProfileIds(@UserIdInt int userId) { UserManager userManager = getUserManager(); diff --git a/services/core/java/com/android/server/location/injector/UserInfoHelper.java b/services/core/java/com/android/server/location/injector/UserInfoHelper.java index 3b7e3b40324e..0fcc1ecc4c1a 100644 --- a/services/core/java/com/android/server/location/injector/UserInfoHelper.java +++ b/services/core/java/com/android/server/location/injector/UserInfoHelper.java @@ -132,6 +132,12 @@ public abstract class UserInfoHelper { */ public abstract boolean isCurrentUserId(@UserIdInt int userId); + /** + * Returns the current user id. Where possible, prefer to use {@link #isCurrentUserId(int)} + * instead, as that method has more flexibility. + */ + public abstract @UserIdInt int getCurrentUserId(); + protected abstract int[] getProfileIds(@UserIdInt int userId); /** diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java index eca607030444..c5d7f2c033dd 100644 --- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java +++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java @@ -1469,18 +1469,9 @@ public class LocationProviderManager extends public @Nullable Location getLastLocation(CallerIdentity identity, @PermissionLevel int permissionLevel, boolean ignoreLocationSettings) { - if (mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(), - identity.getPackageName())) { + if (!isActive(ignoreLocationSettings, identity)) { return null; } - if (!ignoreLocationSettings) { - if (!isEnabled(identity.getUserId())) { - return null; - } - if (!identity.isSystem() && !mUserHelper.isCurrentUserId(identity.getUserId())) { - return null; - } - } // lastly - note app ops if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel), @@ -1905,20 +1896,16 @@ public class LocationProviderManager extends Preconditions.checkState(Thread.holdsLock(mLock)); } - CallerIdentity identity = registration.getIdentity(); - if (!registration.isPermitted()) { return false; } - if (!registration.getRequest().isLocationSettingsIgnored()) { - if (!isEnabled(identity.getUserId())) { - return false; - } - if (!identity.isSystem() && !mUserHelper.isCurrentUserId(identity.getUserId())) { - return false; - } + boolean locationSettingsIgnored = registration.getRequest().isLocationSettingsIgnored(); + if (!isActive(locationSettingsIgnored, registration.getIdentity())) { + return false; + } + if (!locationSettingsIgnored) { switch (mLocationPowerSaveModeHelper.getLocationPowerSaveMode()) { case LOCATION_MODE_FOREGROUND_ONLY: if (!registration.isForeground()) { @@ -1944,8 +1931,32 @@ public class LocationProviderManager extends } } - return !mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(), - identity.getPackageName()); + return true; + } + + private boolean isActive(boolean locationSettingsIgnored, CallerIdentity identity) { + if (identity.isSystemServer()) { + if (!locationSettingsIgnored) { + if (!isEnabled(mUserHelper.getCurrentUserId())) { + return false; + } + } + } else { + if (!locationSettingsIgnored) { + if (!isEnabled(identity.getUserId())) { + return false; + } + if (!mUserHelper.isCurrentUserId(identity.getUserId())) { + return false; + } + } + if (mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(), + identity.getPackageName())) { + return false; + } + } + + return true; } @GuardedBy("mLock") diff --git a/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java b/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java index 280d59af7d2a..b4bcd7b10c42 100644 --- a/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java +++ b/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java @@ -25,10 +25,10 @@ import static com.android.server.location.timezone.LocationTimeZoneProvider.Prov import android.annotation.NonNull; import android.annotation.Nullable; -import android.location.timezone.LocationTimeZoneEvent; import android.util.IndentingPrintWriter; import android.util.Slog; +import com.android.internal.location.timezone.LocationTimeZoneEvent; import com.android.internal.location.timezone.LocationTimeZoneProviderRequest; import java.time.Duration; diff --git a/services/core/java/com/android/server/location/timezone/ControllerImpl.java b/services/core/java/com/android/server/location/timezone/ControllerImpl.java index 70c1aeff0ce8..07615ff58075 100644 --- a/services/core/java/com/android/server/location/timezone/ControllerImpl.java +++ b/services/core/java/com/android/server/location/timezone/ControllerImpl.java @@ -16,10 +16,9 @@ package com.android.server.location.timezone; -import static android.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_PERMANENT_FAILURE; -import static android.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_SUCCESS; -import static android.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_UNCERTAIN; - +import static com.android.internal.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_PERMANENT_FAILURE; +import static com.android.internal.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_SUCCESS; +import static com.android.internal.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_UNCERTAIN; import static com.android.server.location.timezone.LocationTimeZoneManagerService.debugLog; import static com.android.server.location.timezone.LocationTimeZoneManagerService.warnLog; import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState; @@ -31,10 +30,10 @@ import static com.android.server.location.timezone.LocationTimeZoneProvider.Prov import android.annotation.NonNull; import android.annotation.Nullable; -import android.location.timezone.LocationTimeZoneEvent; import android.util.IndentingPrintWriter; import com.android.internal.annotations.GuardedBy; +import com.android.internal.location.timezone.LocationTimeZoneEvent; import com.android.server.location.timezone.ThreadingDomain.SingleRunnableQueue; import com.android.server.timezonedetector.ConfigurationInternal; import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion; diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java b/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java index 83f4ca2839fb..b7c7476844a5 100644 --- a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java +++ b/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java @@ -42,7 +42,7 @@ import java.util.Objects; * A service class that acts as a container for the {@link LocationTimeZoneProviderController}, * which determines what {@link com.android.server.timezonedetector.GeolocationTimeZoneSuggestion} * are made to the {@link TimeZoneDetectorInternal}, and the {@link LocationTimeZoneProvider}s that - * offer {@link android.location.timezone.LocationTimeZoneEvent}s. + * (indirectly) generate {@link com.android.internal.location.timezone.LocationTimeZoneEvent}s. * * <p>For details of the time zone suggestion behavior, see {@link * LocationTimeZoneProviderController}. diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProvider.java b/services/core/java/com/android/server/location/timezone/LocationTimeZoneProvider.java index 4bbda43d0e60..dc56238e2f48 100644 --- a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProvider.java +++ b/services/core/java/com/android/server/location/timezone/LocationTimeZoneProvider.java @@ -16,10 +16,9 @@ package com.android.server.location.timezone; -import static android.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_PERMANENT_FAILURE; -import static android.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_SUCCESS; -import static android.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_UNCERTAIN; - +import static com.android.internal.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_PERMANENT_FAILURE; +import static com.android.internal.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_SUCCESS; +import static com.android.internal.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_UNCERTAIN; import static com.android.server.location.timezone.LocationTimeZoneManagerService.debugLog; import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DISABLED; import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_ENABLED_CERTAIN; @@ -30,12 +29,12 @@ import static com.android.server.location.timezone.LocationTimeZoneProvider.Prov import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; -import android.location.timezone.LocationTimeZoneEvent; import android.os.Handler; import android.os.SystemClock; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.location.timezone.LocationTimeZoneEvent; import com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.ProviderStateEnum; import com.android.server.location.timezone.ThreadingDomain.SingleRunnableQueue; import com.android.server.timezonedetector.ConfigurationInternal; diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderProxy.java index 3d889ae1856a..1b4706f8801e 100644 --- a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderProxy.java +++ b/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderProxy.java @@ -19,11 +19,11 @@ package com.android.server.location.timezone; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; -import android.location.timezone.LocationTimeZoneEvent; import android.os.Handler; import android.util.IndentingPrintWriter; import com.android.internal.annotations.GuardedBy; +import com.android.internal.location.timezone.LocationTimeZoneEvent; import com.android.internal.location.timezone.LocationTimeZoneProviderRequest; import com.android.server.timezonedetector.Dumpable; diff --git a/services/core/java/com/android/server/location/timezone/NullLocationTimeZoneProvider.java b/services/core/java/com/android/server/location/timezone/NullLocationTimeZoneProvider.java index 2bbae56e40c8..fbcc71fad20a 100644 --- a/services/core/java/com/android/server/location/timezone/NullLocationTimeZoneProvider.java +++ b/services/core/java/com/android/server/location/timezone/NullLocationTimeZoneProvider.java @@ -38,8 +38,8 @@ import java.time.Duration; * enters a {@link ProviderState#PROVIDER_STATE_PERM_FAILED} state immediately after being enabled * for the first time and sends the appropriate event, which ensures the {@link * LocationTimeZoneProviderController} won't expect any further {@link - * android.location.timezone.LocationTimeZoneEvent}s to come from it, and won't attempt to use it - * again. + * com.android.internal.location.timezone.LocationTimeZoneEvent}s to come from it, and won't attempt + * to use it again. */ class NullLocationTimeZoneProvider extends LocationTimeZoneProvider { diff --git a/services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java index 94062faa7a1b..cd6d3592af0e 100644 --- a/services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java +++ b/services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java @@ -20,7 +20,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ComponentName; import android.content.Context; -import android.location.timezone.LocationTimeZoneEvent; import android.os.IBinder; import android.os.RemoteException; import android.util.IndentingPrintWriter; @@ -28,6 +27,7 @@ import android.util.IndentingPrintWriter; import com.android.internal.annotations.GuardedBy; import com.android.internal.location.timezone.ILocationTimeZoneProvider; import com.android.internal.location.timezone.ILocationTimeZoneProviderManager; +import com.android.internal.location.timezone.LocationTimeZoneEvent; import com.android.internal.location.timezone.LocationTimeZoneProviderRequest; import com.android.server.ServiceWatcher; diff --git a/services/core/java/com/android/server/location/timezone/SimulatedBinderProviderEvent.java b/services/core/java/com/android/server/location/timezone/SimulatedBinderProviderEvent.java index 6bf6539f95dc..77253099d54f 100644 --- a/services/core/java/com/android/server/location/timezone/SimulatedBinderProviderEvent.java +++ b/services/core/java/com/android/server/location/timezone/SimulatedBinderProviderEvent.java @@ -16,19 +16,19 @@ package com.android.server.location.timezone; -import static android.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_PERMANENT_FAILURE; -import static android.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_SUCCESS; -import static android.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_UNCERTAIN; - +import static com.android.internal.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_PERMANENT_FAILURE; +import static com.android.internal.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_SUCCESS; +import static com.android.internal.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_UNCERTAIN; import static com.android.server.location.timezone.LocationTimeZoneManagerService.PRIMARY_PROVIDER_NAME; import static com.android.server.location.timezone.LocationTimeZoneManagerService.SECONDARY_PROVIDER_NAME; import android.annotation.NonNull; import android.annotation.Nullable; -import android.location.timezone.LocationTimeZoneEvent; import android.os.ShellCommand; import android.os.SystemClock; +import com.android.internal.location.timezone.LocationTimeZoneEvent; + import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; @@ -117,7 +117,7 @@ final class SimulatedBinderProviderEvent { private static LocationTimeZoneEvent parseLocationTimeZoneEventArgs(ShellCommand shellCommand) { LocationTimeZoneEvent.Builder eventBuilder = new LocationTimeZoneEvent.Builder() - .setElapsedRealtimeNanos(SystemClock.elapsedRealtime()); + .setElapsedRealtimeMillis(SystemClock.elapsedRealtime()); String eventTypeString = shellCommand.getNextArgRequired(); switch (eventTypeString.toUpperCase()) { diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index dfeb6822c8e8..b1289dd0e39f 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -7164,6 +7164,16 @@ public class NotificationManagerService extends SystemService { return true; } + // Suppressed since it's a non-interruptive update to a bubble-suppressed notification + final boolean isBubbleOrOverflowed = record.canBubble() && (record.isFlagBubbleRemoved() + || record.getNotification().isBubbleNotification()); + if (record.isUpdate && !record.isInterruptive() && isBubbleOrOverflowed + && record.getNotification().getBubbleMetadata() != null) { + if (record.getNotification().getBubbleMetadata().isNotificationSuppressed()) { + return true; + } + } + return false; } diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 5c2c8e25f6c9..c10d394d6344 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -2544,6 +2544,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "Full install must include a base package"); } + if (baseApk.isSplitRequired && stagedSplits.size() <= 1) { + throw new PackageManagerException(INSTALL_FAILED_MISSING_SPLIT, + "Missing split for " + mPackageName); + } } else { final ApplicationInfo appInfo = pkgInfo.applicationInfo; ParseResult<PackageLite> pkgLiteResult = ApkLiteParseUtils.parsePackageLite( @@ -2654,6 +2658,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mResolvedInheritedFiles.addAll(libFilesToInherit); } } + // For the case of split required, failed if no splits existed + if (baseApk.isSplitRequired) { + final int existingSplits = ArrayUtils.size(existing.splitNames); + final boolean allSplitsRemoved = (existingSplits == removeSplitList.size()); + final boolean onlyBaseFileStaged = (stagedSplits.size() == 1 + && stagedSplits.contains(null)); + if (allSplitsRemoved && (stagedSplits.isEmpty() || onlyBaseFileStaged)) { + throw new PackageManagerException(INSTALL_FAILED_MISSING_SPLIT, + "Missing split for " + mPackageName); + } + } } if (baseApk.useEmbeddedDex) { for (File file : mResolvedStagedFiles) { @@ -2665,10 +2680,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } } - if (baseApk.isSplitRequired && stagedSplits.size() <= 1) { - throw new PackageManagerException(INSTALL_FAILED_MISSING_SPLIT, - "Missing split for " + mPackageName); - } final boolean isInstallerShell = (mInstallerUid == Process.SHELL_UID); if (isInstallerShell && isIncrementalInstallation() && mIncrementalFileStorages != null) { diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index ad686f2cab89..db5eb844f1b4 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -94,6 +94,11 @@ import static android.content.pm.PackageManager.MOVE_FAILED_OPERATION_PENDING; import static android.content.pm.PackageManager.MOVE_FAILED_SYSTEM_PACKAGE; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.content.pm.PackageManager.RESTRICTION_NONE; +import static android.content.pm.PackageManager.TYPE_ACTIVITY; +import static android.content.pm.PackageManager.TYPE_PROVIDER; +import static android.content.pm.PackageManager.TYPE_RECEIVER; +import static android.content.pm.PackageManager.TYPE_SERVICE; +import static android.content.pm.PackageManager.TYPE_UNKNOWN; import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN; import static android.content.pm.PackageManagerInternal.LAST_KNOWN_PACKAGE; import static android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4; @@ -103,7 +108,6 @@ import static android.os.incremental.IncrementalManager.isIncrementalPath; import static android.os.storage.StorageManager.FLAG_STORAGE_CE; import static android.os.storage.StorageManager.FLAG_STORAGE_DE; import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL; -import static android.permission.PermissionManager.KILL_APP_REASON_GIDS_CHANGED; import static com.android.internal.annotations.VisibleForTesting.Visibility; import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE; @@ -137,6 +141,7 @@ import android.annotation.AppIdInt; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.StringRes; import android.annotation.UserIdInt; import android.annotation.WorkerThread; import android.app.ActivityManager; @@ -196,8 +201,11 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageInfoLite; import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.ComponentType; import android.content.pm.PackageManager.LegacyPackageDeleteObserver; import android.content.pm.PackageManager.ModuleInfoFlags; +import android.content.pm.PackageManager.Property; +import android.content.pm.PackageManager.PropertyLocation; import android.content.pm.PackageManagerInternal; import android.content.pm.PackageManagerInternal.PackageListObserver; import android.content.pm.PackageManagerInternal.PrivateResolveFlags; @@ -246,7 +254,6 @@ import android.database.ContentObserver; import android.graphics.Bitmap; import android.hardware.display.DisplayManager; import android.net.Uri; -import android.os.AsyncTask; import android.os.Binder; import android.os.Build; import android.os.Bundle; @@ -339,10 +346,8 @@ import com.android.internal.os.SomeArgs; import com.android.internal.os.Zygote; import com.android.internal.telephony.CarrierAppUtils; import com.android.internal.util.ArrayUtils; -import com.android.internal.util.CollectionUtils; import com.android.internal.util.ConcurrentUtils; import com.android.internal.util.DumpUtils; -import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; @@ -397,7 +402,6 @@ import libcore.util.HexEncoding; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; @@ -575,21 +579,6 @@ public class PackageManagerService extends IPackageManager.Stub private static final int[] EMPTY_INT_ARRAY = new int[0]; - private static final int TYPE_UNKNOWN = 0; - private static final int TYPE_ACTIVITY = 1; - private static final int TYPE_RECEIVER = 2; - private static final int TYPE_SERVICE = 3; - private static final int TYPE_PROVIDER = 4; - @IntDef(prefix = { "TYPE_" }, value = { - TYPE_UNKNOWN, - TYPE_ACTIVITY, - TYPE_RECEIVER, - TYPE_SERVICE, - TYPE_PROVIDER, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface ComponentType {} - /** * Timeout (in milliseconds) after which the watchdog should declare that * our handler thread is wedged. The usual default for such things is one @@ -1215,6 +1204,7 @@ public class PackageManagerService extends IPackageManager.Stub public ViewCompiler viewCompiler; public @Nullable String wellbeingPackage; public @Nullable String retailDemoPackage; + public @Nullable String recentsPackage; public ComponentName resolveComponentName; public ArrayMap<String, AndroidPackage> packages; public boolean enableFreeCacheV2; @@ -1320,6 +1310,8 @@ public class PackageManagerService extends IPackageManager.Stub private final IncrementalManager mIncrementalManager; + private final PackageProperty mPackageProperty = new PackageProperty(); + private static class IFVerificationParams { String packageName; boolean hasDomainUrls; @@ -1747,6 +1739,7 @@ public class PackageManagerService extends IPackageManager.Stub final @Nullable String mSharedSystemSharedLibraryPackageName; final @Nullable String mRetailDemoPackage; final @Nullable String mOverlayConfigSignaturePackage; + final @Nullable String mRecentsPackage; private final PackageUsage mPackageUsage = new PackageUsage(); private final CompilerStats mCompilerStats = new CompilerStats(); @@ -2990,6 +2983,7 @@ public class PackageManagerService extends IPackageManager.Stub mSystemTextClassifierPackageName = testParams.systemTextClassifierPackage; mWellbeingPackage = testParams.wellbeingPackage; mRetailDemoPackage = testParams.retailDemoPackage; + mRecentsPackage = testParams.recentsPackage; mDocumenterPackage = testParams.documenterPackage; mConfiguratorPackage = testParams.configuratorPackage; mAppPredictionServicePackage = testParams.appPredictionServicePackage; @@ -3574,6 +3568,7 @@ public class PackageManagerService extends IPackageManager.Stub mIncidentReportApproverPackage = getIncidentReportApproverPackageName(); mRetailDemoPackage = getRetailDemoPackageName(); mOverlayConfigSignaturePackage = getOverlayConfigSignaturePackageName(); + mRecentsPackage = getRecentsPackageName(); // Now that we know all of the shared libraries, update all clients to have // the correct library paths. @@ -3842,7 +3837,7 @@ public class PackageManagerService extends IPackageManager.Stub // are all flushed. Not really needed, but keeps things nice and // tidy. t.traceBegin("GC"); - Runtime.getRuntime().gc(); + VMRuntime.getRuntime().requestConcurrentGC(); t.traceEnd(); // The initial scanning above does many calls into installd while @@ -5722,7 +5717,8 @@ public class PackageManagerService extends IPackageManager.Stub getPackagesUsingSharedLibraryLPr(libInfo, flags, userId), (libInfo.getDependencies() == null ? null - : new ArrayList<>(libInfo.getDependencies()))); + : new ArrayList<>(libInfo.getDependencies())), + libInfo.isNative()); if (result == null) { result = new ArrayList<>(); @@ -5791,7 +5787,8 @@ public class PackageManagerService extends IPackageManager.Stub libraryInfo.getLongVersion(), libraryInfo.getType(), libraryInfo.getDeclaringPackage(), getPackagesUsingSharedLibraryLPr( libraryInfo, flags, userId), libraryInfo.getDependencies() == null - ? null : new ArrayList<>(libraryInfo.getDependencies())); + ? null : new ArrayList<>(libraryInfo.getDependencies()), + libraryInfo.isNative()); if (result == null) { result = new ArrayList<>(); @@ -11592,22 +11589,6 @@ public class PackageManagerService extends IPackageManager.Stub } pkgSetting.signatures.mSigningDetails = reconciledPkg.signingDetails; - if (!pkg.getAdoptPermissions().isEmpty()) { - // This package wants to adopt ownership of permissions from - // another package. - for (int i = pkg.getAdoptPermissions().size() - 1; i >= 0; i--) { - final String origName = pkg.getAdoptPermissions().get(i); - final PackageSetting orig = mSettings.getPackageLPr(origName); - if (orig != null) { - if (verifyPackageUpdateLPr(orig, pkg)) { - Slog.i(TAG, "Adopting permissions from " + origName + " to " - + pkg.getPackageName()); - mPermissionManager.transferPermissions(origName, pkg.getPackageName()); - } - } - } - } - if (changedAbiCodePath != null && changedAbiCodePath.size() > 0) { for (int i = changedAbiCodePath.size() - 1; i >= 0; --i) { final String codePathString = changedAbiCodePath.get(i); @@ -12727,6 +12708,37 @@ public class PackageManagerService extends IPackageManager.Stub return true; } + @Override + public Property getProperty(String propertyName, String packageName, String className) { + Objects.requireNonNull(propertyName); + Objects.requireNonNull(packageName); + synchronized (mLock) { + final PackageSetting ps = getPackageSetting(packageName); + if (shouldFilterApplicationLocked(ps, Binder.getCallingUid(), + UserHandle.getCallingUserId())) { + return null; + } + return mPackageProperty.getProperty(propertyName, packageName, className); + } + } + + @Override + public ParceledListSlice<Property> queryProperty( + String propertyName, @PropertyLocation int componentType) { + Objects.requireNonNull(propertyName); + final int callingUid = Binder.getCallingUid(); + final int callingUserId = UserHandle.getCallingUserId(); + final List<Property> result = + mPackageProperty.queryProperty(propertyName, componentType, packageName -> { + final PackageSetting ps = getPackageSetting(packageName); + return shouldFilterApplicationLocked(ps, callingUid, callingUserId); + }); + if (result == null) { + return ParceledListSlice.emptyList(); + } + return new ParceledListSlice<>(result); + } + /** * Adds a scanned package to the system. When this method is finished, the package will * be available for query, resolution, etc... @@ -12853,27 +12865,7 @@ public class PackageManagerService extends IPackageManager.Stub final boolean isReplace = reconciledPkg.prepareResult != null && reconciledPkg.prepareResult.replace; mAppsFilter.addPackage(pkgSetting, isReplace); - - // Don't allow ephemeral applications to define new permissions groups. - if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) { - Slog.w(TAG, "Permission groups from package " + pkg.getPackageName() - + " ignored: instant apps cannot define new permission groups."); - } else { - mPermissionManager.addAllPermissionGroups(pkg, chatty); - } - - // If a permission has had its defining app changed, or it has had its protection - // upgraded, we need to revoke apps that hold it - final List<String> permissionsWithChangedDefinition; - // Don't allow ephemeral applications to define new permissions. - if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) { - permissionsWithChangedDefinition = null; - Slog.w(TAG, "Permissions from package " + pkg.getPackageName() - + " ignored: instant apps cannot define new permissions."); - } else { - permissionsWithChangedDefinition = - mPermissionManager.addAllPermissions(pkg, chatty); - } + mPackageProperty.addAllProperties(pkg); int collectionSize = ArrayUtils.size(pkg.getInstrumentations()); StringBuilder r = null; @@ -12901,31 +12893,7 @@ public class PackageManagerService extends IPackageManager.Stub } } - boolean hasOldPkg = oldPkg != null; - boolean hasPermissionDefinitionChanges = - !CollectionUtils.isEmpty(permissionsWithChangedDefinition); - if (hasOldPkg || hasPermissionDefinitionChanges) { - // We need to call revokeRuntimePermissionsIfGroupChanged async as permission - // revoke callbacks from this method might need to kill apps which need the - // mPackages lock on a different thread. This would dead lock. - // - // Hence create a copy of all package names and pass it into - // revokeRuntimePermissionsIfGroupChanged. Only for those permissions might get - // revoked. If a new package is added before the async code runs the permission - // won't be granted yet, hence new packages are no problem. - final ArrayList<String> allPackageNames = new ArrayList<>(mPackages.keySet()); - - AsyncTask.execute(() -> { - if (hasOldPkg) { - mPermissionManager.revokeRuntimePermissionsIfGroupChanged(pkg, oldPkg, - allPackageNames); - } - if (hasPermissionDefinitionChanges) { - mPermissionManager.revokeRuntimePermissionsIfPermissionDefinitionChanged( - permissionsWithChangedDefinition, allPackageNames); - } - }); - } + mPermissionManager.onPackageAdded(pkg, (scanFlags & SCAN_AS_INSTANT_APP) != 0, oldPkg); } Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); @@ -13024,7 +12992,7 @@ public class PackageManagerService extends IPackageManager.Stub } } - void removePackageLI(String packageName, boolean chatty) { + private void removePackageLI(String packageName, boolean chatty) { if (DEBUG_INSTALL) { if (chatty) Log.d(TAG, "Removing package " + packageName); @@ -13039,9 +13007,10 @@ public class PackageManagerService extends IPackageManager.Stub } } - void cleanPackageDataStructuresLILPw(AndroidPackage pkg, boolean chatty) { + private void cleanPackageDataStructuresLILPw(AndroidPackage pkg, boolean chatty) { mComponentResolver.removeAllComponents(pkg, chatty); - mPermissionManager.removeAllPermissions(pkg, chatty); + mPermissionManager.onPackageRemoved(pkg); + mPackageProperty.removeAllProperties(pkg); final int instrumentationSize = ArrayUtils.size(pkg.getInstrumentations()); StringBuilder r = null; @@ -19539,33 +19508,13 @@ public class PackageManagerService extends IPackageManager.Stub if (outInfo != null) { outInfo.removedAppId = removedAppId; } - if ((deletedPs.sharedUser == null || deletedPs.sharedUser.packages.size() == 0) - && !isUpdatedSystemApp(deletedPs)) { - mPermissionManager.removeAppIdStateTEMP(removedAppId); - } - mPermissionManager.updatePermissions(deletedPs.name, null); - if (deletedPs.sharedUser != null) { - // Remove permissions associated with package. Since runtime - // permissions are per user we have to kill the removed package - // or packages running under the shared user of the removed - // package if revoking the permissions requested only by the removed - // package is successful and this causes a change in gids. - boolean shouldKill = false; - for (int userId : UserManagerService.getInstance().getUserIds()) { - final int userIdToKill = mPermissionManager - .revokeSharedUserPermissionsForDeletedPackageTEMP(deletedPs, - userId); - shouldKill |= userIdToKill != UserHandle.USER_NULL; - } - // If gids changed, kill all affected packages. - if (shouldKill) { - mHandler.post(() -> { - // This has to happen with no lock held. - killApplication(deletedPs.name, deletedPs.appId, - KILL_APP_REASON_GIDS_CHANGED); - }); - } + final SharedUserSetting sus = deletedPs.getSharedUser(); + List<AndroidPackage> sharedUserPkgs = sus != null ? sus.getPackages() : null; + if (sharedUserPkgs == null) { + sharedUserPkgs = Collections.emptyList(); } + mPermissionManager.onPackageStateRemoved(packageName, deletedPs.appId, + deletedPs.pkg, sharedUserPkgs); clearPackagePreferredActivitiesLPw( deletedPs.name, changedUsers, UserHandle.USER_ALL); } @@ -21387,15 +21336,8 @@ public class PackageManagerService extends IPackageManager.Stub @Override public @Nullable String getAttentionServicePackageName() { - final String flattenedComponentName = - mContext.getString(R.string.config_defaultAttentionService); - if (flattenedComponentName != null) { - ComponentName componentName = ComponentName.unflattenFromString(flattenedComponentName); - if (componentName != null && componentName.getPackageName() != null) { - return ensureSystemPackageName(componentName.getPackageName()); - } - } - return null; + return ensureSystemPackageName( + getPackageFromComponentString(R.string.config_defaultAttentionService)); } private @Nullable String getDocumenterPackageName() { @@ -21430,17 +21372,8 @@ public class PackageManagerService extends IPackageManager.Stub @Override public String getAppPredictionServicePackageName() { - String flattenedAppPredictionServiceComponentName = - mContext.getString(R.string.config_defaultAppPredictionService); - if (flattenedAppPredictionServiceComponentName == null) { - return null; - } - ComponentName appPredictionServiceComponentName = - ComponentName.unflattenFromString(flattenedAppPredictionServiceComponentName); - if (appPredictionServiceComponentName == null) { - return null; - } - return ensureSystemPackageName(appPredictionServiceComponentName.getPackageName()); + return ensureSystemPackageName( + getPackageFromComponentString(R.string.config_defaultAppPredictionService)); } private @NonNull String[] dropNonSystemPackages(@NonNull String[] pkgNames) { @@ -21457,19 +21390,8 @@ public class PackageManagerService extends IPackageManager.Stub @Override public String getSystemCaptionsServicePackageName() { - String flattenedSystemCaptionsServiceComponentName = - mContext.getString(R.string.config_defaultSystemCaptionsService); - - if (TextUtils.isEmpty(flattenedSystemCaptionsServiceComponentName)) { - return null; - } - - ComponentName systemCaptionsServiceComponentName = - ComponentName.unflattenFromString(flattenedSystemCaptionsServiceComponentName); - if (systemCaptionsServiceComponentName == null) { - return null; - } - return ensureSystemPackageName(systemCaptionsServiceComponentName.getPackageName()); + return ensureSystemPackageName( + getPackageFromComponentString(R.string.config_defaultSystemCaptionsService)); } @Override @@ -21487,19 +21409,8 @@ public class PackageManagerService extends IPackageManager.Stub @Override public String getContentCaptureServicePackageName() { - final String flattenedContentCaptureService = - mContext.getString(R.string.config_defaultContentCaptureService); - - if (TextUtils.isEmpty(flattenedContentCaptureService)) { - return null; - } - - final ComponentName contentCaptureServiceComponentName = - ComponentName.unflattenFromString(flattenedContentCaptureService); - if (contentCaptureServiceComponentName == null) { - return null; - } - return ensureSystemPackageName(contentCaptureServiceComponentName.getPackageName()); + return ensureSystemPackageName( + getPackageFromComponentString(R.string.config_defaultContentCaptureService)); } public String getOverlayConfigSignaturePackageName() { @@ -21543,6 +21454,26 @@ public class PackageManagerService extends IPackageManager.Stub } @Nullable + private String getRecentsPackageName() { + return ensureSystemPackageName( + getPackageFromComponentString(R.string.config_recentsComponentName)); + + } + + @Nullable + private String getPackageFromComponentString(@StringRes int stringResId) { + final String componentString = mContext.getString(stringResId); + if (TextUtils.isEmpty(componentString)) { + return null; + } + final ComponentName component = ComponentName.unflattenFromString(componentString); + if (component == null) { + return null; + } + return component.getPackageName(); + } + + @Nullable private String ensureSystemPackageName(@Nullable String packageName) { if (packageName == null) { return null; @@ -25150,6 +25081,8 @@ public class PackageManagerService extends IPackageManager.Stub : new String[] {mRetailDemoPackage}; case PackageManagerInternal.PACKAGE_OVERLAY_CONFIG_SIGNATURE: return filterOnlySystemPackages(getOverlayConfigSignaturePackageName()); + case PackageManagerInternal.PACKAGE_RECENTS: + return filterOnlySystemPackages(mRecentsPackage); default: return ArrayUtils.emptyArray(String.class); } diff --git a/services/core/java/com/android/server/pm/PackageProperty.java b/services/core/java/com/android/server/pm/PackageProperty.java new file mode 100644 index 000000000000..d18a02d9f315 --- /dev/null +++ b/services/core/java/com/android/server/pm/PackageProperty.java @@ -0,0 +1,287 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import static android.content.pm.PackageManager.TYPE_ACTIVITY; +import static android.content.pm.PackageManager.TYPE_APPLICATION; +import static android.content.pm.PackageManager.TYPE_PROVIDER; +import static android.content.pm.PackageManager.TYPE_RECEIVER; +import static android.content.pm.PackageManager.TYPE_SERVICE; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.Property; +import android.content.pm.PackageManager.PropertyLocation; +import android.content.pm.parsing.component.ParsedComponent; +import android.os.Binder; +import android.os.UserHandle; +import android.util.ArrayMap; + +import com.android.server.pm.parsing.pkg.AndroidPackage; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Predicate; + +/** + * Manages properties defined within a package using the <property> tag. + */ +public class PackageProperty { + /** + * Mapping of property name to all defined defined properties. + * <p>This is a mapping of property name --> package map. The package + * map is a mapping of package name -> list of properties. + */ + private ArrayMap<String, ArrayMap<String, ArrayList<Property>>> mApplicationProperties; + private ArrayMap<String, ArrayMap<String, ArrayList<Property>>> mActivityProperties; + private ArrayMap<String, ArrayMap<String, ArrayList<Property>>> mProviderProperties; + private ArrayMap<String, ArrayMap<String, ArrayList<Property>>> mReceiverProperties; + private ArrayMap<String, ArrayMap<String, ArrayList<Property>>> mServiceProperties; + + /** + * If the provided component is {@code null}, returns the property defined on the + * application. Otherwise, returns the property defined on the component. + */ + public Property getProperty(@NonNull String propertyName, @NonNull String packageName, + @Nullable String className) { + if (className == null) { + return getApplicationProperty(propertyName, packageName); + } + return getComponentProperty(propertyName, packageName, className); + } + + /** + * Returns all properties defined at the given location. + * <p>Valid locations are {@link PackageManager#TYPE_APPLICATION}, + * {@link PackageManager#TYPE_ACTIVITY}, {@link PackageManager#TYPE_PROVIDER}, + * {@link PackageManager#TYPE_RECEIVER}, or {@link PackageManager#TYPE_SERVICE}. + */ + public List<Property> queryProperty(@NonNull String propertyName, + @PropertyLocation int componentType, Predicate<String> filter) { + final ArrayMap<String, ArrayMap<String, ArrayList<Property>>> propertyMap; + if (componentType == TYPE_APPLICATION) { + propertyMap = mApplicationProperties; + } else if (componentType == TYPE_ACTIVITY) { + propertyMap = mActivityProperties; + } else if (componentType == TYPE_PROVIDER) { + propertyMap = mProviderProperties; + } else if (componentType == TYPE_RECEIVER) { + propertyMap = mReceiverProperties; + } else if (componentType == TYPE_SERVICE) { + propertyMap = mServiceProperties; + } else { + propertyMap = null; + } + if (propertyMap == null) { + return null; + } + final ArrayMap<String, ArrayList<Property>> packagePropertyMap = + propertyMap.get(propertyName); + if (packagePropertyMap == null) { + return null; + } + final int callingUid = Binder.getCallingUid(); + final int callingUserId = UserHandle.getCallingUserId(); + final int mapSize = packagePropertyMap.size(); + final List<Property> result = new ArrayList<>(mapSize); + for (int i = 0; i < mapSize; i++) { + final String packageName = packagePropertyMap.keyAt(i); + if (filter.test(packageName)) { + continue; + } + result.addAll(packagePropertyMap.valueAt(i)); + } + return result; + } + + /** Adds all properties defined for the given package */ + void addAllProperties(AndroidPackage pkg) { + mApplicationProperties = addProperties(pkg.getProperties(), mApplicationProperties); + mActivityProperties = addComponentProperties(pkg.getActivities(), mActivityProperties); + mProviderProperties = addComponentProperties(pkg.getProviders(), mProviderProperties); + mReceiverProperties = addComponentProperties(pkg.getReceivers(), mReceiverProperties); + mServiceProperties = addComponentProperties(pkg.getServices(), mServiceProperties); + } + + /** Adds all properties defined for the given package */ + void removeAllProperties(AndroidPackage pkg) { + mApplicationProperties = removeProperties(pkg.getProperties(), mApplicationProperties); + mActivityProperties = removeComponentProperties(pkg.getActivities(), mActivityProperties); + mProviderProperties = removeComponentProperties(pkg.getProviders(), mProviderProperties); + mReceiverProperties = removeComponentProperties(pkg.getReceivers(), mReceiverProperties); + mServiceProperties = removeComponentProperties(pkg.getServices(), mServiceProperties); + } + + /** Add the properties defined on the given components to the property collection */ + private static <T extends ParsedComponent> + ArrayMap<String, ArrayMap<String, ArrayList<Property>>> addComponentProperties( + @NonNull List<T> components, + @Nullable ArrayMap<String, ArrayMap<String, ArrayList<Property>>> propertyCollection) { + ArrayMap<String, ArrayMap<String, ArrayList<Property>>> returnCollection = + propertyCollection; + final int componentsSize = components.size(); + for (int i = 0; i < componentsSize; i++) { + final Map<String, Property> properties = components.get(i).getProperties(); + if (properties.size() == 0) { + continue; + } + returnCollection = addProperties(properties, returnCollection); + } + return returnCollection; + } + + /** Add the given properties to the property collection */ + private static ArrayMap<String, ArrayMap<String, ArrayList<Property>>> addProperties( + @NonNull Map<String, Property> properties, + @Nullable ArrayMap<String, ArrayMap<String, ArrayList<Property>>> propertyCollection) { + if (properties.size() == 0) { + return propertyCollection; + } + final ArrayMap<String, ArrayMap<String, ArrayList<Property>>> returnCollection = + propertyCollection == null ? new ArrayMap<>(10) : propertyCollection; + final Iterator<Property> iter = properties.values().iterator(); + while (iter.hasNext()) { + final Property property = iter.next(); + final String propertyName = property.getName(); + final String packageName = property.getPackageName(); + ArrayMap<String, ArrayList<Property>> propertyMap = returnCollection.get(propertyName); + if (propertyMap == null) { + propertyMap = new ArrayMap<>(); + returnCollection.put(propertyName, propertyMap); + } + ArrayList<Property> packageProperties = propertyMap.get(packageName); + if (packageProperties == null) { + packageProperties = new ArrayList<>(properties.size()); + propertyMap.put(packageName, packageProperties); + } + packageProperties.add(property); + } + return returnCollection; + } + + /** Removes the properties defined on the given components from the property collection */ + private static <T extends ParsedComponent> + ArrayMap<String, ArrayMap<String, ArrayList<Property>>> removeComponentProperties( + @NonNull List<T> components, + @Nullable ArrayMap<String, ArrayMap<String, ArrayList<Property>>> propertyCollection) { + ArrayMap<String, ArrayMap<String, ArrayList<Property>>> returnCollection = + propertyCollection; + final int componentsSize = components.size(); + for (int i = 0; returnCollection != null && i < componentsSize; i++) { + final Map<String, Property> properties = components.get(i).getProperties(); + if (properties.size() == 0) { + continue; + } + returnCollection = removeProperties(properties, returnCollection); + } + return returnCollection; + } + + /** Removes the given properties from the property collection */ + private static ArrayMap<String, ArrayMap<String, ArrayList<Property>>> removeProperties( + @NonNull Map<String, Property> properties, + @Nullable ArrayMap<String, ArrayMap<String, ArrayList<Property>>> propertyCollection) { + if (propertyCollection == null) { + return null; + } + final Iterator<Property> iter = properties.values().iterator(); + while (iter.hasNext()) { + final Property property = iter.next(); + final String propertyName = property.getName(); + final String packageName = property.getPackageName(); + ArrayMap<String, ArrayList<Property>> propertyMap = + propertyCollection.get(propertyName); + if (propertyMap == null) { + // error + continue; + } + ArrayList<Property> packageProperties = propertyMap.get(packageName); + if (packageProperties == null) { + //error + continue; + } + packageProperties.remove(property); + + // clean up empty structures + if (packageProperties.size() == 0) { + propertyMap.remove(packageName); + } + if (propertyMap.size() == 0) { + propertyCollection.remove(propertyName); + } + } + if (propertyCollection.size() == 0) { + return null; + } + return propertyCollection; + } + + private static Property getProperty(String propertyName, String packageName, String className, + ArrayMap<String, ArrayMap<String, ArrayList<Property>>> propertyMap) { + final ArrayMap<String, ArrayList<Property>> packagePropertyMap = + propertyMap.get(propertyName); + if (packagePropertyMap == null) { + return null; + } + final List<Property> propertyList = packagePropertyMap.get(packageName); + if (propertyList == null) { + return null; + } + for (int i = propertyList.size() - 1; i >= 0; i--) { + final Property property = propertyList.get(i); + if (Objects.equals(className, property.getClassName())) { + return property; + } + } + return null; + } + + private Property getComponentProperty( + String propertyName, String packageName, String className) { + Property property = null; + if (property == null && mActivityProperties != null) { + property = getProperty(propertyName, packageName, className, mActivityProperties); + } + if (property == null && mProviderProperties != null) { + property = getProperty(propertyName, packageName, className, mProviderProperties); + } + if (property == null && mReceiverProperties != null) { + property = getProperty(propertyName, packageName, className, mReceiverProperties); + } + if (property == null && mServiceProperties != null) { + property = getProperty(propertyName, packageName, className, mServiceProperties); + } + return property; + } + + private Property getApplicationProperty(String propertyName, String packageName) { + final ArrayMap<String, ArrayList<Property>> packagePropertyMap = + mApplicationProperties.get(propertyName); + if (packagePropertyMap == null) { + return null; + } + final List<Property> propertyList = packagePropertyMap.get(packageName); + if (propertyList == null) { + return null; + } + return propertyList.get(0); + } +} diff --git a/services/core/java/com/android/server/pm/PreferredComponent.java b/services/core/java/com/android/server/pm/PreferredComponent.java index 69721d0769cb..804faa1b7f0f 100644 --- a/services/core/java/com/android/server/pm/PreferredComponent.java +++ b/services/core/java/com/android/server/pm/PreferredComponent.java @@ -252,43 +252,6 @@ public class PreferredComponent { return numMatch == NS; } - public boolean sameSet(PreferredComponent pc) { - if (mSetPackages == null || pc == null || pc.mSetPackages == null - || !sameComponent(pc.mComponent)) { - return false; - } - final int otherPackageCount = pc.mSetPackages.length; - final int packageCount = mSetPackages.length; - int numMatch = 0; - for (int i = 0; i < otherPackageCount; i++) { - boolean good = false; - for (int j = 0; j < packageCount; j++) { - if (mSetPackages[j].equals(pc.mSetPackages[j]) - && mSetClasses[j].equals(pc.mSetClasses[j])) { - numMatch++; - good = true; - break; - } - } - if (!good) { - return false; - } - } - return numMatch == packageCount; - } - - /** Returns true if the preferred component represents the provided ComponentName. */ - private boolean sameComponent(ComponentName comp) { - if (mComponent == null || comp == null) { - return false; - } - if (mComponent.getPackageName().equals(comp.getPackageName()) - && mComponent.getClassName().equals(comp.getClassName())) { - return true; - } - return false; - } - public boolean isSuperset(List<ResolveInfo> query, boolean excludeSetupWizardPackage) { if (mSetPackages == null) { return query == null; diff --git a/services/core/java/com/android/server/pm/PreferredIntentResolver.java b/services/core/java/com/android/server/pm/PreferredIntentResolver.java index ff3df130a3cc..a261e29b05a7 100644 --- a/services/core/java/com/android/server/pm/PreferredIntentResolver.java +++ b/services/core/java/com/android/server/pm/PreferredIntentResolver.java @@ -22,7 +22,6 @@ import android.content.IntentFilter; import java.io.PrintWriter; import com.android.server.IntentResolver; -import java.util.ArrayList; public class PreferredIntentResolver extends IntentResolver<PreferredActivity, PreferredActivity> { @@ -46,24 +45,4 @@ public class PreferredIntentResolver protected IntentFilter getIntentFilter(@NonNull PreferredActivity input) { return input; } - - public boolean shouldAddPreferredActivity(PreferredActivity pa) { - ArrayList<PreferredActivity> pal = findFilters(pa); - if (pal == null || pal.isEmpty()) { - return true; - } - if (!pa.mPref.mAlways) { - return false; - } - final int activityCount = pal.size(); - for (int i = 0; i < activityCount; i++) { - PreferredActivity cur = pal.get(i); - if (cur.mPref.mAlways - && cur.mPref.mMatch == (pa.mPref.mMatch & IntentFilter.MATCH_CATEGORY_MASK) - && cur.mPref.sameSet(pa.mPref)) { - return false; - } - } - return true; - } } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 9a5e05f38819..966090cb96a7 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -1312,7 +1312,8 @@ public final class Settings { PreferredActivity pa = new PreferredActivity(parser); if (pa.mPref.getParseError() == null) { final PreferredIntentResolver resolver = editPreferredActivitiesLPw(userId); - if (resolver.shouldAddPreferredActivity(pa)) { + ArrayList<PreferredActivity> pal = resolver.findFilters(pa); + if (pal == null || pal.size() == 0 || pa.mPref.mAlways) { resolver.addFilter(pa); } } else { diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index c51e75c716cc..cc814bcc7760 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -119,7 +119,6 @@ import libcore.io.IoUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.File; import java.io.FileDescriptor; @@ -175,6 +174,7 @@ public class UserManagerService extends IUserManager.Stub { private static final String ATTR_CONVERTED_FROM_PRE_CREATED = "convertedFromPreCreated"; private static final String ATTR_GUEST_TO_REMOVE = "guestToRemove"; private static final String ATTR_USER_VERSION = "version"; + private static final String ATTR_USER_TYPE_VERSION = "userTypeConfigVersion"; private static final String ATTR_PROFILE_GROUP_ID = "profileGroupId"; private static final String ATTR_PROFILE_BADGE = "profileBadge"; private static final String ATTR_RESTRICTED_PROFILE_PARENT_ID = "restrictedProfileParentId"; @@ -422,6 +422,7 @@ public class UserManagerService extends IUserManager.Stub { @GuardedBy("mPackagesLock") private int mNextSerialNumber; private int mUserVersion = 0; + private int mUserTypeVersion = 0; private IAppOpsService mAppOpsService; @@ -2565,6 +2566,8 @@ public class UserManagerService extends IUserManager.Stub { parser.getAttributeInt(null, ATTR_NEXT_SERIAL_NO, mNextSerialNumber); mUserVersion = parser.getAttributeInt(null, ATTR_USER_VERSION, mUserVersion); + mUserTypeVersion = + parser.getAttributeInt(null, ATTR_USER_TYPE_VERSION, mUserTypeVersion); } // Pre-O global user restriction were stored as a single bundle (as opposed to per-user @@ -2627,7 +2630,7 @@ public class UserManagerService extends IUserManager.Stub { */ @GuardedBy({"mRestrictionsLock", "mPackagesLock"}) private void upgradeIfNecessaryLP(Bundle oldGlobalUserRestrictions) { - upgradeIfNecessaryLP(oldGlobalUserRestrictions, mUserVersion); + upgradeIfNecessaryLP(oldGlobalUserRestrictions, mUserVersion, mUserTypeVersion); } /** @@ -2636,9 +2639,11 @@ public class UserManagerService extends IUserManager.Stub { */ @GuardedBy({"mRestrictionsLock", "mPackagesLock"}) @VisibleForTesting - void upgradeIfNecessaryLP(Bundle oldGlobalUserRestrictions, int userVersion) { + void upgradeIfNecessaryLP(Bundle oldGlobalUserRestrictions, int userVersion, + int userTypeVersion) { Set<Integer> userIdsToWrite = new ArraySet<>(); final int originalVersion = mUserVersion; + final int originalUserTypeVersion = mUserTypeVersion; if (userVersion < 1) { // Assign a proper name for the owner, if not initialized correctly before UserData userData = getUserDataNoChecks(UserHandle.USER_SYSTEM); @@ -2771,13 +2776,24 @@ public class UserManagerService extends IUserManager.Stub { userVersion = 9; } + // Done with userVersion changes, moving on to deal with userTypeVersion upgrades + // Upgrade from previous user type to a new user type + final int newUserTypeVersion = UserTypeFactory.getUserTypeVersion(); + if (newUserTypeVersion > userTypeVersion) { + synchronized (mUsersLock) { + upgradeUserTypesLU(UserTypeFactory.getUserTypeUpgrades(), mUserTypes, + userTypeVersion, userIdsToWrite); + } + } + if (userVersion < USER_VERSION) { Slog.w(LOG_TAG, "User version " + mUserVersion + " didn't upgrade as expected to " + USER_VERSION); } else { mUserVersion = userVersion; + mUserTypeVersion = newUserTypeVersion; - if (originalVersion < mUserVersion) { + if (originalVersion < mUserVersion || originalUserTypeVersion < mUserTypeVersion) { for (int userId : userIdsToWrite) { UserData userData = getUserDataNoChecks(userId); if (userData != null) { @@ -2801,6 +2817,7 @@ public class UserManagerService extends IUserManager.Stub { UserData userData = putUserInfo(system); mNextSerialNumber = MIN_USER_ID; mUserVersion = USER_VERSION; + mUserTypeVersion = UserTypeFactory.getUserTypeVersion(); Bundle restrictions = new Bundle(); try { @@ -2991,6 +3008,7 @@ public class UserManagerService extends IUserManager.Stub { serializer.startTag(null, TAG_USERS); serializer.attributeInt(null, ATTR_NEXT_SERIAL_NO, mNextSerialNumber); serializer.attributeInt(null, ATTR_USER_VERSION, mUserVersion); + serializer.attributeInt(null, ATTR_USER_TYPE_VERSION, mUserTypeVersion); serializer.startTag(null, TAG_GUEST_RESTRICTIONS); synchronized (mGuestRestrictions) { @@ -4957,6 +4975,7 @@ public class UserManagerService extends IUserManager.Stub { // Dump UserTypes pw.println(); + pw.println("User types version: " + mUserTypeVersion); pw.println("User types (" + mUserTypes.size() + " types):"); for (int i = 0; i < mUserTypes.size(); i++) { pw.println(" " + mUserTypes.keyAt(i) + ": "); @@ -5447,6 +5466,9 @@ public class UserManagerService extends IUserManager.Stub { * Returns the maximum number of users allowed for the given userTypeDetails per parent user. * This is applicable for user types that are {@link UserTypeDetails#isProfile()}. * If there is no maximum, {@link UserTypeDetails#UNLIMITED_NUMBER_OF_USERS} is returned. + * Under certain circumstances (such as after a change-user-type) the max value can actually + * be exceeded: this is allowed in order to keep the device in a usable state. + * An error is logged in {@link UserManagerService#upgradeProfileToTypeLU} */ private static int getMaxUsersOfTypePerParent(UserTypeDetails userTypeDetails) { final int defaultMax = userTypeDetails.getMaxAllowedPerParent(); @@ -5534,4 +5556,98 @@ public class UserManagerService extends IUserManager.Stub { } return mDevicePolicyManagerInternal; } + + @GuardedBy("mUsersLock") + @VisibleForTesting + void upgradeUserTypesLU(@NonNull List<UserTypeFactory.UserTypeUpgrade> upgradeOps, + @NonNull ArrayMap<String, UserTypeDetails> userTypes, + final int formerUserTypeVersion, + @NonNull Set<Integer> userIdsToWrite) { + for (UserTypeFactory.UserTypeUpgrade userTypeUpgrade : upgradeOps) { + if (DBG) { + Slog.i(LOG_TAG, "Upgrade: " + userTypeUpgrade.getFromType() + " to: " + + userTypeUpgrade.getToType() + " maxVersion: " + + userTypeUpgrade.getUpToVersion()); + } + + // upgrade user type if version up to getUpToVersion() + if (formerUserTypeVersion <= userTypeUpgrade.getUpToVersion()) { + for (int i = 0; i < mUsers.size(); i++) { + UserData userData = mUsers.valueAt(i); + if (userTypeUpgrade.getFromType().equals(userData.info.userType)) { + final UserTypeDetails newUserType = userTypes.get( + userTypeUpgrade.getToType()); + + if (newUserType == null) { + throw new IllegalStateException( + "Upgrade destination user type not defined: " + + userTypeUpgrade.getToType()); + } + + upgradeProfileToTypeLU(userData.info, newUserType); + userIdsToWrite.add(userData.info.id); + } + } + } + } + } + + /** + * Changes the user type of a profile to a new user type. + * @param userInfo The user to be updated. + * @param newUserType The new user type. + */ + @GuardedBy("mUsersLock") + @VisibleForTesting + void upgradeProfileToTypeLU(@NonNull UserInfo userInfo, @NonNull UserTypeDetails newUserType) { + Slog.i(LOG_TAG, "Upgrading user " + userInfo.id + + " from " + userInfo.userType + + " to " + newUserType.getName()); + + if (!userInfo.isProfile()) { + throw new IllegalStateException( + "Can only upgrade profile types. " + userInfo.userType + + " is not a profile type."); + } + + // Exceeded maximum profiles for parent user: log error, but allow upgrade + if (!canAddMoreProfilesToUser(newUserType.getName(), userInfo.profileGroupId, false)) { + Slog.w(LOG_TAG, + "Exceeded maximum profiles of type " + newUserType.getName() + " for user " + + userInfo.id + ". Maximum allowed= " + + newUserType.getMaxAllowedPerParent()); + } + + final UserTypeDetails oldUserType = mUserTypes.get(userInfo.userType); + final int oldFlags; + if (oldUserType != null) { + oldFlags = oldUserType.getDefaultUserInfoFlags(); + } else { + // if oldUserType is missing from config_user_types.xml -> can only assume FLAG_PROFILE + oldFlags = UserInfo.FLAG_PROFILE; + } + + //convert userData to newUserType + userInfo.userType = newUserType.getName(); + // remove old default flags and add newUserType's default flags + userInfo.flags = newUserType.getDefaultUserInfoFlags() | (userInfo.flags ^ oldFlags); + + // merge existing base restrictions with the new type's default restrictions + synchronized (mRestrictionsLock) { + if (!UserRestrictionsUtils.isEmpty(newUserType.getDefaultRestrictions())) { + final Bundle newRestrictions = UserRestrictionsUtils.clone( + mBaseUserRestrictions.getRestrictions(userInfo.id)); + UserRestrictionsUtils.merge(newRestrictions, + newUserType.getDefaultRestrictions()); + updateUserRestrictionsInternalLR(newRestrictions, userInfo.id); + if (DBG) { + Slog.i(LOG_TAG, "Updated user " + userInfo.id + + " restrictions to " + newRestrictions); + } + } + } + + // re-compute badge index + userInfo.profileBadge = getFreeProfileBadgeLU(userInfo.profileGroupId, userInfo.userType); + } } diff --git a/services/core/java/com/android/server/pm/UserTypeDetails.java b/services/core/java/com/android/server/pm/UserTypeDetails.java index d840e5d8b882..5fa46b9e4635 100644 --- a/services/core/java/com/android/server/pm/UserTypeDetails.java +++ b/services/core/java/com/android/server/pm/UserTypeDetails.java @@ -174,6 +174,9 @@ public final class UserTypeDetails { /** * Returns the maximum number of this user type allowed per parent (for user types, like * profiles, that have parents). + * Under certain circumstances (such as after a change-user-type) the max value can actually + * be exceeded: this is allowed in order to keep the device in a usable state. + * An error is logged in {@link UserManagerService#upgradeProfileToTypeLU} * <p>Returns {@link #UNLIMITED_NUMBER_OF_USERS} to indicate that there is no hard limit. */ public int getMaxAllowedPerParent() { diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java index ba8a2ba6a14e..2e64a700de9e 100644 --- a/services/core/java/com/android/server/pm/UserTypeFactory.java +++ b/services/core/java/com/android/server/pm/UserTypeFactory.java @@ -49,6 +49,7 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.util.ArrayList; +import java.util.List; import java.util.function.Consumer; /** @@ -73,14 +74,7 @@ public final class UserTypeFactory { * @return mapping from the name of each user type to its {@link UserTypeDetails} object */ public static ArrayMap<String, UserTypeDetails> getUserTypes() { - final ArrayMap<String, UserTypeDetails.Builder> builders = new ArrayMap<>(); - builders.put(USER_TYPE_PROFILE_MANAGED, getDefaultTypeProfileManaged()); - builders.put(USER_TYPE_FULL_SYSTEM, getDefaultTypeFullSystem()); - builders.put(USER_TYPE_FULL_SECONDARY, getDefaultTypeFullSecondary()); - builders.put(USER_TYPE_FULL_GUEST, getDefaultTypeFullGuest()); - builders.put(USER_TYPE_FULL_DEMO, getDefaultTypeFullDemo()); - builders.put(USER_TYPE_FULL_RESTRICTED, getDefaultTypeFullRestricted()); - builders.put(USER_TYPE_SYSTEM_HEADLESS, getDefaultTypeSystemHeadless()); + final ArrayMap<String, UserTypeDetails.Builder> builders = getDefaultBuilders(); try (XmlResourceParser parser = Resources.getSystem().getXml(com.android.internal.R.xml.config_user_types)) { @@ -94,6 +88,20 @@ public final class UserTypeFactory { return types; } + private static ArrayMap<String, UserTypeDetails.Builder> getDefaultBuilders() { + final ArrayMap<String, UserTypeDetails.Builder> builders = new ArrayMap<>(); + + builders.put(USER_TYPE_PROFILE_MANAGED, getDefaultTypeProfileManaged()); + builders.put(USER_TYPE_FULL_SYSTEM, getDefaultTypeFullSystem()); + builders.put(USER_TYPE_FULL_SECONDARY, getDefaultTypeFullSecondary()); + builders.put(USER_TYPE_FULL_GUEST, getDefaultTypeFullGuest()); + builders.put(USER_TYPE_FULL_DEMO, getDefaultTypeFullDemo()); + builders.put(USER_TYPE_FULL_RESTRICTED, getDefaultTypeFullRestricted()); + builders.put(USER_TYPE_SYSTEM_HEADLESS, getDefaultTypeSystemHeadless()); + + return builders; + } + /** * Returns the Builder for the default {@link UserManager#USER_TYPE_PROFILE_MANAGED} * configuration. @@ -232,6 +240,10 @@ public final class UserTypeFactory { isProfile = true; } else if ("full-type".equals(elementName)) { isProfile = false; + } else if ("change-user-type".equals(elementName)) { + // parsed in parseUserUpgrades + XmlUtils.skipCurrentTag(parser); + continue; } else { Slog.w(LOG_TAG, "Skipping unknown element " + elementName + " in " + parser.getPositionDescription()); @@ -387,4 +399,132 @@ public final class UserTypeFactory { } fcn.accept(result); } + + /** + * Returns the user type version of the config XML file. + * @return user type version defined in XML file, 0 if none. + */ + public static int getUserTypeVersion() { + try (XmlResourceParser parser = + Resources.getSystem().getXml(com.android.internal.R.xml.config_user_types)) { + return getUserTypeVersion(parser); + } + } + + @VisibleForTesting + static int getUserTypeVersion(XmlResourceParser parser) { + int version = 0; + + try { + XmlUtils.beginDocument(parser, "user-types"); + String versionValue = parser.getAttributeValue(null, "version"); + if (versionValue != null) { + try { + version = Integer.parseInt(versionValue); + } catch (NumberFormatException e) { + Slog.e(LOG_TAG, "Cannot parse value of '" + versionValue + "' for version in " + + parser.getPositionDescription(), e); + throw e; + } + } + } catch (XmlPullParserException | IOException e) { + Slog.w(LOG_TAG, "Cannot read user type configuration file.", e); + } + + return version; + } + + /** + * Obtains the user type upgrades for this device. + * @return The list of user type upgrades. + */ + public static List<UserTypeUpgrade> getUserTypeUpgrades() { + final List<UserTypeUpgrade> userUpgrades; + try (XmlResourceParser parser = + Resources.getSystem().getXml(com.android.internal.R.xml.config_user_types)) { + userUpgrades = parseUserUpgrades(getDefaultBuilders(), parser); + } + return userUpgrades; + } + + @VisibleForTesting + static List<UserTypeUpgrade> parseUserUpgrades( + ArrayMap<String, UserTypeDetails.Builder> builders, XmlResourceParser parser) { + final List<UserTypeUpgrade> userUpgrades = new ArrayList<>(); + + try { + XmlUtils.beginDocument(parser, "user-types"); + for (XmlUtils.nextElement(parser); + parser.getEventType() != XmlResourceParser.END_DOCUMENT; + XmlUtils.nextElement(parser)) { + final String elementName = parser.getName(); + if ("change-user-type".equals(elementName)) { + final String fromType = parser.getAttributeValue(null, "from"); + final String toType = parser.getAttributeValue(null, "to"); + // Check that the base type doesn't change. + // Currently, only the base type of PROFILE is supported. + validateUserTypeIsProfile(fromType, builders); + validateUserTypeIsProfile(toType, builders); + + final int maxVersionToConvert; + try { + maxVersionToConvert = Integer.parseInt( + parser.getAttributeValue(null, "whenVersionLeq")); + } catch (NumberFormatException e) { + Slog.e(LOG_TAG, "Cannot parse value of whenVersionLeq in " + + parser.getPositionDescription(), e); + throw e; + } + + UserTypeUpgrade userTypeUpgrade = new UserTypeUpgrade(fromType, toType, + maxVersionToConvert); + userUpgrades.add(userTypeUpgrade); + continue; + } else { + XmlUtils.skipCurrentTag(parser); + continue; + } + } + } catch (XmlPullParserException | IOException e) { + Slog.w(LOG_TAG, "Cannot read user type configuration file.", e); + } + + return userUpgrades; + } + + private static void validateUserTypeIsProfile(String userType, + ArrayMap<String, UserTypeDetails.Builder> builders) { + UserTypeDetails.Builder builder = builders.get(userType); + if (builder != null && builder.getBaseType() != FLAG_PROFILE) { + throw new IllegalArgumentException("Illegal upgrade of user type " + userType + + " : Can only upgrade profiles user types"); + } + } + + /** + * Contains details required for an upgrade operation for {@link UserTypeDetails}; + */ + public static class UserTypeUpgrade { + private final String mFromType; + private final String mToType; + private final int mUpToVersion; + + public UserTypeUpgrade(String fromType, String toType, int upToVersion) { + mFromType = fromType; + mToType = toType; + mUpToVersion = upToVersion; + } + + public String getFromType() { + return mFromType; + } + + public String getToType() { + return mToType; + } + + public int getUpToVersion() { + return mUpToVersion; + } + } } diff --git a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java index 0a56e1343418..ab25a7c772c0 100644 --- a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java +++ b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java @@ -94,7 +94,7 @@ public class AndroidPackageUtils { SharedLibraryInfo.TYPE_STATIC, new VersionedPackage(pkg.getManifestPackageName(), pkg.getLongVersionCode()), - null, null); + null, null, false /* isNative */); } public static SharedLibraryInfo createSharedLibraryForDynamic(AndroidPackage pkg, String name) { @@ -103,7 +103,7 @@ public class AndroidPackageUtils { SharedLibraryInfo.VERSION_UNDEFINED, SharedLibraryInfo.TYPE_DYNAMIC, new VersionedPackage(pkg.getPackageName(), pkg.getLongVersionCode()), - null, null); + null, null, false /* isNative */); } /** diff --git a/services/core/java/com/android/server/pm/permission/Permission.java b/services/core/java/com/android/server/pm/permission/Permission.java index 0245b28884ea..995b59e97e91 100644 --- a/services/core/java/com/android/server/pm/permission/Permission.java +++ b/services/core/java/com/android/server/pm/permission/Permission.java @@ -331,6 +331,10 @@ public final class Permission { return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_RETAIL_DEMO) != 0; } + public boolean isRecents() { + return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_RECENTS) != 0; + } + public void transfer(@NonNull String oldPackageName, @NonNull String newPackageName) { if (!oldPackageName.equals(mPermissionInfo.packageName)) { return; @@ -399,8 +403,7 @@ public final class Permission { @NonNull public static Permission createOrUpdate(@Nullable Permission permission, @NonNull PermissionInfo permissionInfo, @NonNull AndroidPackage pkg, - @NonNull Collection<Permission> permissionTrees, boolean isOverridingSystemPermission, - boolean chatty) { + @NonNull Collection<Permission> permissionTrees, boolean isOverridingSystemPermission) { // Allow system apps to redefine non-system permissions boolean ownerChanged = false; if (permission != null && !Objects.equals(permission.mPermissionInfo.packageName, @@ -437,7 +440,7 @@ public final class Permission { permission.mPermissionInfo = permissionInfo; permission.mReconciled = true; permission.mUid = pkg.getUid(); - if (chatty) { + if (PackageManagerService.DEBUG_PACKAGE_SCANNING) { if (r == null) { r = new StringBuilder(256); } else { @@ -456,7 +459,7 @@ public final class Permission { + permissionInfo.packageName + " ignored: original from " + permission.mPermissionInfo.packageName); } - } else if (chatty) { + } else if (PackageManagerService.DEBUG_PACKAGE_SCANNING) { if (r == null) { r = new StringBuilder(256); } else { diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index 6c03a28d03c7..fb1ed2f6b58b 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -93,6 +93,7 @@ import android.content.pm.parsing.component.ParsedPermission; import android.content.pm.parsing.component.ParsedPermissionGroup; import android.content.pm.permission.SplitPermissionInfoParcelable; import android.metrics.LogMaker; +import android.os.AsyncTask; import android.os.Binder; import android.os.Build; import android.os.Debug; @@ -133,6 +134,7 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.os.RoSystemProperties; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.CollectionUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.IntPair; import com.android.internal.util.Preconditions; @@ -145,7 +147,6 @@ import com.android.server.Watchdog; import com.android.server.pm.ApexManager; import com.android.server.pm.PackageManagerServiceUtils; import com.android.server.pm.PackageSetting; -import com.android.server.pm.SharedUserSetting; import com.android.server.pm.UserManagerInternal; import com.android.server.pm.UserManagerService; import com.android.server.pm.parsing.PackageInfoUtils; @@ -2363,14 +2364,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { * * @param newPackage The new package that was installed * @param oldPackage The old package that was updated - * @param allPackageNames All package names - * @param permissionCallback Callback for permission changed */ - private void revokeRuntimePermissionsIfGroupChanged( - @NonNull AndroidPackage newPackage, - @NonNull AndroidPackage oldPackage, - @NonNull ArrayList<String> allPackageNames, - @NonNull PermissionCallback permissionCallback) { + private void revokeRuntimePermissionsIfGroupChangedInternal(@NonNull AndroidPackage newPackage, + @NonNull AndroidPackage oldPackage) { final int numOldPackagePermissions = ArrayUtils.size(oldPackage.getPermissions()); final ArrayMap<String, String> oldPermissionNameToGroupName = new ArrayMap<>(numOldPackagePermissions); @@ -2403,13 +2399,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (newPermissionGroupName != null && !newPermissionGroupName.equals(oldPermissionGroupName)) { final int[] userIds = mUserManagerInt.getUserIds(); - final int numUserIds = userIds.length; - for (int userIdNum = 0; userIdNum < numUserIds; userIdNum++) { - final int userId = userIds[userIdNum]; - - final int numPackages = allPackageNames.size(); - for (int packageNum = 0; packageNum < numPackages; packageNum++) { - final String packageName = allPackageNames.get(packageNum); + mPackageManagerInt.forEachPackage(pkg -> { + final String packageName = pkg.getPackageName(); + for (final int userId : userIds) { final int permissionState = checkPermission(permissionName, packageName, userId); if (permissionState == PackageManager.PERMISSION_GRANTED) { @@ -2422,14 +2414,15 @@ public class PermissionManagerService extends IPermissionManager.Stub { try { revokeRuntimePermissionInternal(permissionName, packageName, - false, callingUid, userId, null, permissionCallback); + false, callingUid, userId, null, + mDefaultPermissionCallback); } catch (IllegalArgumentException e) { Slog.e(TAG, "Could not revoke " + permissionName + " from " + packageName, e); } } } - } + }); } } } @@ -2440,18 +2433,11 @@ public class PermissionManagerService extends IPermissionManager.Stub { * granted permissions must be revoked. * * @param permissionsToRevoke A list of permission names to revoke - * @param allPackageNames All package names - * @param permissionCallback Callback for permission changed */ - private void revokeRuntimePermissionsIfPermissionDefinitionChanged( - @NonNull List<String> permissionsToRevoke, - @NonNull ArrayList<String> allPackageNames, - @NonNull PermissionCallback permissionCallback) { - + private void revokeRuntimePermissionsIfPermissionDefinitionChangedInternal( + @NonNull List<String> permissionsToRevoke) { final int[] userIds = mUserManagerInt.getUserIds(); final int numPermissions = permissionsToRevoke.size(); - final int numUserIds = userIds.length; - final int numPackages = allPackageNames.size(); final int callingUid = Binder.getCallingUid(); for (int permNum = 0; permNum < numPermissions; permNum++) { @@ -2462,15 +2448,14 @@ public class PermissionManagerService extends IPermissionManager.Stub { continue; } } - for (int userIdNum = 0; userIdNum < numUserIds; userIdNum++) { - final int userId = userIds[userIdNum]; - for (int packageNum = 0; packageNum < numPackages; packageNum++) { - final String packageName = allPackageNames.get(packageNum); - final int uid = mPackageManagerInt.getPackageUid(packageName, 0, userId); - if (uid < Process.FIRST_APPLICATION_UID) { - // do not revoke from system apps - continue; - } + mPackageManagerInt.forEachPackage(pkg -> { + final String packageName = pkg.getPackageName(); + final int appId = pkg.getUid(); + if (appId < Process.FIRST_APPLICATION_UID) { + // do not revoke from system apps + return; + } + for (final int userId : userIds) { final int permissionState = checkPermissionImpl(permName, packageName, userId); final int flags = getPermissionFlags(permName, packageName, userId); @@ -2480,6 +2465,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { | FLAG_PERMISSION_GRANTED_BY_ROLE; if (permissionState == PackageManager.PERMISSION_GRANTED && (flags & flagMask) == 0) { + final int uid = UserHandle.getUid(userId, appId); EventLog.writeEvent(0x534e4554, "154505240", uid, "Revoking permission " + permName + " from package " + packageName + " due to definition change"); @@ -2490,18 +2476,18 @@ public class PermissionManagerService extends IPermissionManager.Stub { + packageName + " due to definition change"); try { revokeRuntimePermissionInternal(permName, packageName, - false, callingUid, userId, null, permissionCallback); + false, callingUid, userId, null, mDefaultPermissionCallback); } catch (Exception e) { Slog.e(TAG, "Could not revoke " + permName + " from " + packageName, e); } } } - } + }); } } - private List<String> addAllPermissions(AndroidPackage pkg, boolean chatty) { + private List<String> addAllPermissionsInternal(@NonNull AndroidPackage pkg) { final int N = ArrayUtils.size(pkg.getPermissions()); ArrayList<String> definitionChangedPermissions = new ArrayList<>(); for (int i=0; i<N; i++) { @@ -2539,7 +2525,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { synchronized (mLock) { final Permission permission = Permission.createOrUpdate(oldPermission, permissionInfo, pkg, mRegistry.getPermissionTrees(), - isOverridingSystemPermission, chatty); + isOverridingSystemPermission); if (p.isTree()) { mRegistry.addPermissionTree(permission); } else { @@ -2557,7 +2543,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { return definitionChangedPermissions; } - private void addAllPermissionGroups(AndroidPackage pkg, boolean chatty) { + private void addAllPermissionGroupsInternal(@NonNull AndroidPackage pkg) { synchronized (mLock) { final int N = ArrayUtils.size(pkg.getPermissionGroups()); StringBuilder r = null; @@ -2568,7 +2554,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { final boolean isPackageUpdate = pg.getPackageName().equals(curPackageName); if (cur == null || isPackageUpdate) { mRegistry.addPermissionGroup(pg); - if (chatty && DEBUG_PACKAGE_SCANNING) { + if (DEBUG_PACKAGE_SCANNING) { if (r == null) { r = new StringBuilder(256); } else { @@ -2583,7 +2569,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { Slog.w(TAG, "Permission group " + pg.getName() + " from package " + pg.getPackageName() + " ignored: original from " + cur.getPackageName()); - if (chatty && DEBUG_PACKAGE_SCANNING) { + if (DEBUG_PACKAGE_SCANNING) { if (r == null) { r = new StringBuilder(256); } else { @@ -2600,7 +2586,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } - private void removeAllPermissions(AndroidPackage pkg, boolean chatty) { + private void removeAllPermissionsInternal(@NonNull AndroidPackage pkg) { synchronized (mLock) { int N = ArrayUtils.size(pkg.getPermissions()); StringBuilder r = null; @@ -2612,7 +2598,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { } if (bp != null && bp.isPermission(p)) { bp.setPermissionInfo(null); - if (DEBUG_REMOVE && chatty) { + if (DEBUG_REMOVE) { if (r == null) { r = new StringBuilder(256); } else { @@ -3630,6 +3616,13 @@ public class PermissionManagerService extends IPermissionManager.Stub { // Special permission granted only to the OEM specified retail demo app allowed = true; } + if (!allowed && bp.isRecents() + && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames( + PackageManagerInternal.PACKAGE_RECENTS, UserHandle.USER_SYSTEM), + pkg.getPackageName())) { + // Special permission for the recents app. + allowed = true; + } return allowed; } @@ -3990,34 +3983,30 @@ public class PermissionManagerService extends IPermissionManager.Stub { } @UserIdInt - private int revokeSharedUserPermissionsForDeletedPackage(@NonNull PackageSetting deletedPs, + private int revokeSharedUserPermissionsForDeletedPackageInternal( + @Nullable AndroidPackage pkg, @NonNull List<AndroidPackage> sharedUserPkgs, @UserIdInt int userId) { - if ((deletedPs == null) || (deletedPs.pkg == null)) { + if (pkg == null) { Slog.i(TAG, "Trying to update info for null package. Just ignoring"); return UserHandle.USER_NULL; } - SharedUserSetting sus = deletedPs.getSharedUser(); - - // No sharedUserId - if (sus == null) { + // No shared user packages + if (sharedUserPkgs.isEmpty()) { return UserHandle.USER_NULL; } int affectedUserId = UserHandle.USER_NULL; // Update permissions - for (String eachPerm : deletedPs.pkg.getRequestedPermissions()) { + for (String eachPerm : pkg.getRequestedPermissions()) { // Check if another package in the shared user needs the permission. boolean used = false; - final List<AndroidPackage> pkgs = sus.getPackages(); - if (pkgs != null) { - for (AndroidPackage pkg : pkgs) { - if (pkg != null - && !pkg.getPackageName().equals(deletedPs.pkg.getPackageName()) - && pkg.getRequestedPermissions().contains(eachPerm)) { - used = true; - break; - } + for (AndroidPackage sharedUserpkg : sharedUserPkgs) { + if (sharedUserpkg != null + && !sharedUserpkg.getPackageName().equals(pkg.getPackageName()) + && sharedUserpkg.getRequestedPermissions().contains(eachPerm)) { + used = true; + break; } } if (used) { @@ -4025,7 +4014,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { } PackageSetting disabledPs = mPackageManagerInt.getDisabledSystemPackage( - deletedPs.pkg.getPackageName()); + pkg.getPackageName()); // If the package is shadowing is a disabled system package, // do not drop permissions that the shadowed package requests. @@ -4043,9 +4032,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { } synchronized (mLock) { - UidPermissionState uidState = getUidStateLocked(deletedPs.pkg, userId); + UidPermissionState uidState = getUidStateLocked(pkg, userId); if (uidState == null) { - Slog.e(TAG, "Missing permissions state for " + deletedPs.pkg.getPackageName() + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user " + userId); continue; } @@ -4848,10 +4837,113 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } - private void transferPermissions(@NonNull String oldPackageName, - @NonNull String newPackageName) { - synchronized (mLock) { - mRegistry.transferPermissions(oldPackageName, newPackageName); + private void onPackageAddedInternal(@NonNull AndroidPackage pkg, boolean isInstantApp, + @Nullable AndroidPackage oldPkg) { + if (!pkg.getAdoptPermissions().isEmpty()) { + // This package wants to adopt ownership of permissions from + // another package. + for (int i = pkg.getAdoptPermissions().size() - 1; i >= 0; i--) { + final String origName = pkg.getAdoptPermissions().get(i); + if (canAdoptPermissionsInternal(origName, pkg)) { + Slog.i(TAG, "Adopting permissions from " + origName + " to " + + pkg.getPackageName()); + synchronized (mLock) { + mRegistry.transferPermissions(origName, pkg.getPackageName()); + } + } + } + } + + // Don't allow ephemeral applications to define new permissions groups. + if (isInstantApp) { + Slog.w(TAG, "Permission groups from package " + pkg.getPackageName() + + " ignored: instant apps cannot define new permission groups."); + } else { + addAllPermissionGroupsInternal(pkg); + } + + // If a permission has had its defining app changed, or it has had its protection + // upgraded, we need to revoke apps that hold it + final List<String> permissionsWithChangedDefinition; + // Don't allow ephemeral applications to define new permissions. + if (isInstantApp) { + permissionsWithChangedDefinition = null; + Slog.w(TAG, "Permissions from package " + pkg.getPackageName() + + " ignored: instant apps cannot define new permissions."); + } else { + permissionsWithChangedDefinition = addAllPermissionsInternal(pkg); + } + + boolean hasOldPkg = oldPkg != null; + boolean hasPermissionDefinitionChanges = + !CollectionUtils.isEmpty(permissionsWithChangedDefinition); + if (hasOldPkg || hasPermissionDefinitionChanges) { + // We need to call revokeRuntimePermissionsIfGroupChanged async as permission + // revoke callbacks from this method might need to kill apps which need the + // mPackages lock on a different thread. This would dead lock. + AsyncTask.execute(() -> { + if (hasOldPkg) { + revokeRuntimePermissionsIfGroupChangedInternal(pkg, oldPkg); + } + if (hasPermissionDefinitionChanges) { + revokeRuntimePermissionsIfPermissionDefinitionChangedInternal( + permissionsWithChangedDefinition); + } + }); + } + } + + private boolean canAdoptPermissionsInternal(@NonNull String oldPackageName, + @NonNull AndroidPackage newPkg) { + final PackageSetting oldPs = mPackageManagerInt.getPackageSetting(oldPackageName); + if (oldPs == null) { + return false; + } + if (!oldPs.isSystem()) { + Slog.w(TAG, "Unable to update from " + oldPs.name + + " to " + newPkg.getPackageName() + + ": old package not in system partition"); + return false; + } + if (mPackageManagerInt.getPackage(oldPs.name) != null) { + Slog.w(TAG, "Unable to update from " + oldPs.name + + " to " + newPkg.getPackageName() + + ": old package still exists"); + return false; + } + return true; + } + + private void onPackageRemovedInternal(@NonNull AndroidPackage pkg) { + removeAllPermissionsInternal(pkg); + } + + private void onPackageStateRemovedInternal(@NonNull String packageName, int appId, + @Nullable AndroidPackage pkg, @NonNull List<AndroidPackage> sharedUserPkgs) { + if (sharedUserPkgs.isEmpty() + && mPackageManagerInt.getDisabledSystemPackage(packageName) == null) { + removeAppIdState(appId); + } + updatePermissions(packageName, null, mDefaultPermissionCallback); + if (!sharedUserPkgs.isEmpty()) { + // Remove permissions associated with package. Since runtime + // permissions are per user we have to kill the removed package + // or packages running under the shared user of the removed + // package if revoking the permissions requested only by the removed + // package is successful and this causes a change in gids. + boolean shouldKill = false; + for (int userId : UserManagerService.getInstance().getUserIds()) { + final int userIdToKill = revokeSharedUserPermissionsForDeletedPackageInternal(pkg, + sharedUserPkgs, userId); + shouldKill |= userIdToKill != UserHandle.USER_NULL; + } + // If gids changed, kill all affected packages. + if (shouldKill) { + mHandler.post(() -> { + // This has to happen with no lock held. + killUid(appId, UserHandle.USER_ALL, KILL_APP_REASON_GIDS_CHANGED); + }); + } } } @@ -4955,35 +5047,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { } @Override - public void revokeRuntimePermissionsIfGroupChanged( - @NonNull AndroidPackage newPackage, - @NonNull AndroidPackage oldPackage, - @NonNull ArrayList<String> allPackageNames) { - PermissionManagerService.this.revokeRuntimePermissionsIfGroupChanged(newPackage, - oldPackage, allPackageNames, mDefaultPermissionCallback); - } - - @Override - public void revokeRuntimePermissionsIfPermissionDefinitionChanged( - @NonNull List<String> permissionsToRevoke, - @NonNull ArrayList<String> allPackageNames) { - PermissionManagerService.this.revokeRuntimePermissionsIfPermissionDefinitionChanged( - permissionsToRevoke, allPackageNames, mDefaultPermissionCallback); - } - - @Override - public List<String> addAllPermissions(AndroidPackage pkg, boolean chatty) { - return PermissionManagerService.this.addAllPermissions(pkg, chatty); - } - @Override - public void addAllPermissionGroups(AndroidPackage pkg, boolean chatty) { - PermissionManagerService.this.addAllPermissionGroups(pkg, chatty); - } - @Override - public void removeAllPermissions(AndroidPackage pkg, boolean chatty) { - PermissionManagerService.this.removeAllPermissions(pkg, chatty); - } - @Override public void readLegacyPermissionStateTEMP() { PermissionManagerService.this.readLegacyPermissionState(); } @@ -4995,17 +5058,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { public void onUserRemoved(@UserIdInt int userId) { PermissionManagerService.this.onUserRemoved(userId); } - @Override - public void removeAppIdStateTEMP(@AppIdInt int appId) { - PermissionManagerService.this.removeAppIdState(appId); - } - @Override - @UserIdInt - public int revokeSharedUserPermissionsForDeletedPackageTEMP( - @NonNull PackageSetting deletedPs, @UserIdInt int userId) { - return PermissionManagerService.this.revokeSharedUserPermissionsForDeletedPackage( - deletedPs, userId); - } @NonNull @Override public Set<String> getGrantedPermissions(@NonNull String packageName, @@ -5313,9 +5365,24 @@ public class PermissionManagerService extends IPermissionManager.Stub { } @Override - public void transferPermissions(@NonNull String oldPackageName, - @NonNull String newPackageName) { - PermissionManagerService.this.transferPermissions(oldPackageName, newPackageName); + public void onPackageAdded(@NonNull AndroidPackage pkg, boolean isInstantApp, + @Nullable AndroidPackage oldPkg) { + Objects.requireNonNull(pkg); + onPackageAddedInternal(pkg, isInstantApp, oldPkg); + } + + @Override + public void onPackageRemoved(@NonNull AndroidPackage pkg) { + Objects.requireNonNull(pkg); + onPackageRemovedInternal(pkg); + } + + @Override + public void onPackageStateRemoved(@NonNull String packageName, int appId, + @Nullable AndroidPackage pkg, @NonNull List<AndroidPackage> sharedUserPkgs) { + Objects.requireNonNull(packageName); + Objects.requireNonNull(sharedUserPkgs); + onPackageStateRemovedInternal(packageName, appId, pkg, sharedUserPkgs); } @Override diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java index df9d0d397c56..1becbedc29fb 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java @@ -24,7 +24,6 @@ import android.content.pm.PackageManager; import android.content.pm.PermissionInfo; import android.permission.PermissionManagerInternal; -import com.android.server.pm.PackageSetting; import com.android.server.pm.parsing.pkg.AndroidPackage; import java.util.ArrayList; @@ -277,44 +276,6 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager public abstract void resetAllRuntimePermissions(@UserIdInt int userId); /** - * We might auto-grant permissions if any permission of the group is already granted. Hence if - * the group of a granted permission changes we need to revoke it to avoid having permissions of - * the new group auto-granted. - * - * @param newPackage The new package that was installed - * @param oldPackage The old package that was updated - * @param allPackageNames All packages - */ - public abstract void revokeRuntimePermissionsIfGroupChanged( - @NonNull AndroidPackage newPackage, - @NonNull AndroidPackage oldPackage, - @NonNull ArrayList<String> allPackageNames); - - /** - * Some permissions might have been owned by a non-system package, and the system then defined - * said permission. Some other permissions may one have been install permissions, but are now - * runtime or higher. These permissions should be revoked. - * - * @param permissionsToRevoke A list of permission names to revoke - * @param allPackageNames All packages - */ - public abstract void revokeRuntimePermissionsIfPermissionDefinitionChanged( - @NonNull List<String> permissionsToRevoke, - @NonNull ArrayList<String> allPackageNames); - - /** - * Add all permissions in the given package. - * <p> - * NOTE: argument {@code groupTEMP} is temporary until mPermissionGroups is moved to - * the permission settings. - * - * @return A list of BasePermissions that were updated, and need to be revoked from packages - */ - public abstract List<String> addAllPermissions(@NonNull AndroidPackage pkg, boolean chatty); - public abstract void addAllPermissionGroups(@NonNull AndroidPackage pkg, boolean chatty); - public abstract void removeAllPermissions(@NonNull AndroidPackage pkg, boolean chatty); - - /** * Read legacy permission state from package settings. * * TODO(zhanghai): This is a temporary method because we should not expose @@ -337,30 +298,6 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager public abstract void onUserRemoved(@UserIdInt int userId); /** - * Remove the permission state associated with an app ID, called the same time as the - * removal of a {@code PackageSetitng}. - * - * TODO(zhanghai): This is a temporary method before we figure out a way to get notified of app - * ID removal via API. - */ - public abstract void removeAppIdStateTEMP(@AppIdInt int appId); - - /** - * Update the shared user setting when a package with a shared user id is removed. The gids - * associated with each permission of the deleted package are removed from the shared user' - * gid list only if its not in use by other permissions of packages in the shared user setting. - * - * TODO(zhanghai): We should not need this when permission no longer sees an incomplete package - * state where the updated system package is uninstalled but the disabled system package is yet - * to be installed. Then we should handle this in restorePermissionState(). - * - * @return the affected user id, may be a real user ID, USER_ALL, or USER_NULL when none. - */ - @UserIdInt - public abstract int revokeSharedUserPermissionsForDeletedPackageTEMP( - @NonNull PackageSetting deletedPs, @UserIdInt int userId); - - /** * Get all the permissions granted to a package. * * @param packageName the name of the package @@ -578,10 +515,35 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager @NonNull LegacyPermissionSettings legacyPermissionSettings); /** - * Transfers ownership of permissions from one package to another. + * Callback when a package has been added. + * + * @param pkg the added package + * @param isInstantApp whether the added package is an instant app + * @param oldPkg the old package, or {@code null} if none */ - public abstract void transferPermissions(@NonNull String oldPackageName, - @NonNull String newPackageName); + //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) + public abstract void onPackageAdded(@NonNull AndroidPackage pkg, boolean isInstantApp, + @Nullable AndroidPackage oldPkg); + + /** + * Callback when a package has been removed. + * + * @param pkg the removed package + */ + //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) + public abstract void onPackageRemoved(@NonNull AndroidPackage pkg); + + /** + * Callback when the state for a package has been removed. + * + * @param packageName the name of the removed package + * @param appId the app ID of the removed package + * @param pkg the removed package, or {@code null} if unavailable + * @param sharedUserPkgs the packages that are in the same shared user + */ + //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) + public abstract void onPackageStateRemoved(@NonNull String packageName, int appId, + @Nullable AndroidPackage pkg, @NonNull List<AndroidPackage> sharedUserPkgs); /** * Check whether a permission can be propagated to instant app. diff --git a/services/core/java/com/android/server/policy/DeviceStatePolicyImpl.java b/services/core/java/com/android/server/policy/DeviceStatePolicyImpl.java index 54f618327da8..154f9a455a1a 100644 --- a/services/core/java/com/android/server/policy/DeviceStatePolicyImpl.java +++ b/services/core/java/com/android/server/policy/DeviceStatePolicyImpl.java @@ -17,6 +17,7 @@ package com.android.server.policy; import android.annotation.NonNull; +import android.content.Context; import com.android.server.devicestate.DeviceStatePolicy; import com.android.server.devicestate.DeviceStateProvider; @@ -27,10 +28,12 @@ import com.android.server.devicestate.DeviceStateProvider; * @see DeviceStateProviderImpl */ public final class DeviceStatePolicyImpl implements DeviceStatePolicy { + private final Context mContext; private final DeviceStateProvider mProvider; - public DeviceStatePolicyImpl() { - mProvider = new DeviceStateProviderImpl(); + public DeviceStatePolicyImpl(Context context) { + mContext = context; + mProvider = DeviceStateProviderImpl.create(mContext); } public DeviceStateProvider getDeviceStateProvider() { diff --git a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java index 85ab0bc12cae..321bb8c0251d 100644 --- a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java +++ b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java @@ -16,30 +16,464 @@ package com.android.server.policy; +import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE; + +import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.Context; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.hardware.input.InputManagerInternal; +import android.os.Environment; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.Slog; +import android.util.SparseArray; +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.LocalServices; import com.android.server.devicestate.DeviceStateProvider; +import com.android.server.policy.devicestate.config.Conditions; +import com.android.server.policy.devicestate.config.DeviceState; +import com.android.server.policy.devicestate.config.DeviceStateConfig; +import com.android.server.policy.devicestate.config.LidSwitchCondition; +import com.android.server.policy.devicestate.config.NumericRange; +import com.android.server.policy.devicestate.config.SensorCondition; +import com.android.server.policy.devicestate.config.XmlParser; + +import org.xmlpull.v1.XmlPullParserException; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.function.BooleanSupplier; + +import javax.xml.datatype.DatatypeConfigurationException; /** - * Default implementation of {@link DeviceStateProvider}. Currently only supports - * {@link #DEFAULT_DEVICE_STATE}. - * - * @see DeviceStatePolicyImpl + * Implementation of {@link DeviceStateProvider} that reads the set of supported device states + * from a configuration file provided at either /vendor/etc/devicestate or + * /data/system/devicestate/. By default, the provider supports {@link #DEFAULT_DEVICE_STATE} when + * no configuration is provided. */ -final class DeviceStateProviderImpl implements DeviceStateProvider { - private static final int DEFAULT_DEVICE_STATE = 0; +public final class DeviceStateProviderImpl implements DeviceStateProvider, + InputManagerInternal.LidSwitchCallback, SensorEventListener { + private static final String TAG = "DeviceStateProviderImpl"; + + private static final BooleanSupplier TRUE_BOOLEAN_SUPPLIER = () -> true; + + @VisibleForTesting + static final int DEFAULT_DEVICE_STATE = 0; + + private static final String VENDOR_CONFIG_FILE_PATH = "etc/devicestate/"; + private static final String DATA_CONFIG_FILE_PATH = "system/devicestate/"; + private static final String CONFIG_FILE_NAME = "device_state_configuration.xml"; + + /** Interface that allows reading the device state configuration. */ + interface ReadableConfig { + @NonNull + InputStream openRead() throws IOException; + } + + /** + * Returns a new {@link DeviceStateProviderImpl} instance. + * + * @param context the {@link Context} that should be used to access system services. + */ + public static DeviceStateProviderImpl create(@NonNull Context context) { + File configFile = getConfigurationFile(); + if (configFile == null) { + return createFromConfig(context, null); + } + return createFromConfig(context, new ReadableFileConfig(configFile)); + } + + /** + * Returns a new {@link DeviceStateProviderImpl} instance. + * + * @param context the {@link Context} that should be used to access system services. + * @param readableConfig the config the provider instance should read supported states from. + */ + @VisibleForTesting + static DeviceStateProviderImpl createFromConfig(@NonNull Context context, + @Nullable ReadableConfig readableConfig) { + SparseArray<Conditions> conditionsForState = new SparseArray<>(); + if (readableConfig != null) { + DeviceStateConfig config = parseConfig(readableConfig); + if (config != null) { + for (DeviceState stateConfig : config.getDeviceState()) { + int state = stateConfig.getIdentifier().intValue(); + Conditions conditions = stateConfig.getConditions(); + conditionsForState.put(state, conditions); + } + } + } + + if (conditionsForState.size() == 0) { + conditionsForState.put(DEFAULT_DEVICE_STATE, null); + } + return new DeviceStateProviderImpl(context, conditionsForState); + } + + // Lock for internal state. + private final Object mLock = new Object(); + private final Context mContext; + // List of supported states in ascending order. + private final int[] mOrderedStates; + // Map of state to a boolean supplier that returns true when all required conditions are met for + // the device to be in the state. + private final SparseArray<BooleanSupplier> mStateConditions; @Nullable + @GuardedBy("mLock") private Listener mListener = null; + @GuardedBy("mLock") + private int mLastReportedState = INVALID_DEVICE_STATE; + + @GuardedBy("mLock") + private boolean mIsLidOpen; + @GuardedBy("mLock") + private final Map<Sensor, SensorEvent> mLatestSensorEvent = new ArrayMap<>(); + + private DeviceStateProviderImpl(@NonNull Context context, + @NonNull SparseArray<Conditions> conditionsForState) { + mContext = context; + mOrderedStates = new int[conditionsForState.size()]; + for (int i = 0; i < conditionsForState.size(); i++) { + mOrderedStates[i] = conditionsForState.keyAt(i); + } + + // Whether or not this instance should register to receive lid switch notifications from + // InputManagerInternal. If there are no device state conditions that are based on the lid + // switch there is no need to register for a callback. + boolean shouldListenToLidSwitch = false; + + final SensorManager sensorManager = mContext.getSystemService(SensorManager.class); + // The set of Sensor(s) that this instance should register to receive SensorEvent(s) from. + final ArraySet<Sensor> sensorsToListenTo = new ArraySet<>(); + + mStateConditions = new SparseArray<>(); + for (int i = 0; i < mOrderedStates.length; i++) { + int state = mOrderedStates[i]; + Conditions conditions = conditionsForState.get(state); + if (conditions == null) { + mStateConditions.put(state, TRUE_BOOLEAN_SUPPLIER); + continue; + } + + List<BooleanSupplier> suppliers = new ArrayList<>(); + + LidSwitchCondition lidSwitchCondition = conditions.getLidSwitch(); + if (lidSwitchCondition != null) { + suppliers.add(new LidSwitchBooleanSupplier(lidSwitchCondition.getOpen())); + shouldListenToLidSwitch = true; + } + + List<SensorCondition> sensorConditions = conditions.getSensor(); + for (int j = 0; j < sensorConditions.size(); j++) { + SensorCondition sensorCondition = sensorConditions.get(j); + final int expectedSensorType = sensorCondition.getType().intValue(); + final String expectedSensorName = sensorCondition.getName(); + + List<Sensor> sensors = sensorManager.getSensorList(expectedSensorType); + Sensor foundSensor = null; + for (int sensorIndex = 0; sensorIndex < sensors.size(); sensorIndex++) { + Sensor sensor = sensors.get(sensorIndex); + if (sensor.getName().equals(expectedSensorName)) { + foundSensor = sensor; + break; + } + } + + if (foundSensor == null) { + throw new IllegalStateException("Failed to find Sensor with type: " + + expectedSensorType + " and name: " + expectedSensorName); + } + + suppliers.add(new SensorBooleanSupplier(foundSensor, sensorCondition.getValue())); + sensorsToListenTo.add(foundSensor); + } + + if (suppliers.size() > 1) { + mStateConditions.put(state, new AndBooleanSupplier(suppliers)); + } else if (suppliers.size() > 0) { + // No need to wrap with an AND supplier if there is only 1. + mStateConditions.put(state, suppliers.get(0)); + } else { + // There are no conditions for this state. Default to always true. + mStateConditions.put(state, TRUE_BOOLEAN_SUPPLIER); + } + } + + if (shouldListenToLidSwitch) { + InputManagerInternal inputManager = LocalServices.getService( + InputManagerInternal.class); + inputManager.registerLidSwitchCallback(this); + } + + for (int i = 0; i < sensorsToListenTo.size(); i++) { + Sensor sensor = sensorsToListenTo.valueAt(i); + sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_FASTEST); + } + } @Override public void setListener(Listener listener) { - if (mListener != null) { - throw new RuntimeException("Provider already has a listener set."); + synchronized (mLock) { + if (mListener != null) { + throw new RuntimeException("Provider already has a listener set."); + } + mListener = listener; + } + notifySupportedStatesChanged(); + notifyDeviceStateChangedIfNeeded(); + } + + /** Notifies the listener that the set of supported device states has changed. */ + private void notifySupportedStatesChanged() { + int[] supportedStates; + synchronized (mLock) { + if (mListener == null) { + return; + } + + supportedStates = Arrays.copyOf(mOrderedStates, mOrderedStates.length); } - mListener = listener; - mListener.onSupportedDeviceStatesChanged(new int[]{ DEFAULT_DEVICE_STATE }); - mListener.onStateChanged(DEFAULT_DEVICE_STATE); + mListener.onSupportedDeviceStatesChanged(supportedStates); + } + + /** Computes the current device state and notifies the listener of a change, if needed. */ + void notifyDeviceStateChangedIfNeeded() { + int stateToReport = INVALID_DEVICE_STATE; + synchronized (mLock) { + if (mListener == null) { + return; + } + + int newState = mOrderedStates[0]; + for (int i = 1; i < mOrderedStates.length; i++) { + int state = mOrderedStates[i]; + if (mStateConditions.get(state).getAsBoolean()) { + newState = state; + break; + } + } + + if (newState != mLastReportedState) { + mLastReportedState = newState; + stateToReport = newState; + } + } + + if (stateToReport != INVALID_DEVICE_STATE) { + mListener.onStateChanged(stateToReport); + } + } + + @Override + public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) { + synchronized (mLock) { + mIsLidOpen = lidOpen; + } + notifyDeviceStateChangedIfNeeded(); + } + + @Override + public void onSensorChanged(SensorEvent event) { + synchronized (mLock) { + mLatestSensorEvent.put(event.sensor, event); + } + notifyDeviceStateChangedIfNeeded(); + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + // Do nothing. + } + + /** + * Implementation of {@link BooleanSupplier} that returns {@code true} if the expected lid + * switch open state matches {@link #mIsLidOpen}. + */ + private final class LidSwitchBooleanSupplier implements BooleanSupplier { + private final boolean mExpectedOpen; + + LidSwitchBooleanSupplier(boolean expectedOpen) { + mExpectedOpen = expectedOpen; + } + + @Override + public boolean getAsBoolean() { + synchronized (mLock) { + return mIsLidOpen == mExpectedOpen; + } + } + } + + /** + * Implementation of {@link BooleanSupplier} that returns {@code true} if the latest + * {@link SensorEvent#values sensor event values} for the specified {@link Sensor} adhere to + * the supplied {@link NumericRange ranges}. + */ + private final class SensorBooleanSupplier implements BooleanSupplier { + @NonNull + private final Sensor mSensor; + @NonNull + private final List<NumericRange> mExpectedValues; + + SensorBooleanSupplier(@NonNull Sensor sensor, @NonNull List<NumericRange> expectedValues) { + mSensor = sensor; + mExpectedValues = expectedValues; + } + + @Override + public boolean getAsBoolean() { + synchronized (mLock) { + SensorEvent latestEvent = mLatestSensorEvent.get(mSensor); + if (latestEvent == null) { + // Default to returning false if we have not yet received a sensor event for the + // sensor. + return false; + } + + if (latestEvent.values.length != mExpectedValues.size()) { + throw new IllegalStateException("Number of supplied numeric range(s) does not " + + "match the number of values in the latest sensor event for sensor: " + + mSensor); + } + + for (int i = 0; i < latestEvent.values.length; i++) { + if (!adheresToRange(latestEvent.values[i], mExpectedValues.get(i))) { + return false; + } + } + return true; + } + } + + /** + * Returns {@code true} if the supplied {@code value} adheres to the constraints specified + * in {@code range}. + */ + private boolean adheresToRange(float value, @NonNull NumericRange range) { + final BigDecimal min = range.getMin_optional(); + if (min != null) { + if (value <= min.floatValue()) { + return false; + } + } + + final BigDecimal minInclusive = range.getMinInclusive_optional(); + if (minInclusive != null) { + if (value < minInclusive.floatValue()) { + return false; + } + } + + final BigDecimal max = range.getMax_optional(); + if (max != null) { + if (value >= max.floatValue()) { + return false; + } + } + + final BigDecimal maxInclusive = range.getMaxInclusive_optional(); + if (maxInclusive != null) { + if (value > maxInclusive.floatValue()) { + return false; + } + } + + return true; + } + } + + /** + * Implementation of {@link BooleanSupplier} whose result is the product of an AND operation + * applied to the result of all child suppliers. + */ + private static final class AndBooleanSupplier implements BooleanSupplier { + @NonNull + List<BooleanSupplier> mBooleanSuppliers; + + AndBooleanSupplier(@NonNull List<BooleanSupplier> booleanSuppliers) { + mBooleanSuppliers = booleanSuppliers; + } + + @Override + public boolean getAsBoolean() { + for (int i = 0; i < mBooleanSuppliers.size(); i++) { + if (!mBooleanSuppliers.get(i).getAsBoolean()) { + return false; + } + } + return true; + } + } + + /** + * Returns the device state configuration file that should be used, or {@code null} if no file + * is present on the device. + * <p> + * Defaults to returning a config file present in the data/ dir at + * {@link #DATA_CONFIG_FILE_PATH}, and then falls back to the config file in the vendor/ dir + * at {@link #VENDOR_CONFIG_FILE_PATH} if no config file is found in the data/ dir. + */ + @Nullable + private static File getConfigurationFile() { + final File configFileFromDataDir = Environment.buildPath(Environment.getDataDirectory(), + DATA_CONFIG_FILE_PATH, CONFIG_FILE_NAME); + if (configFileFromDataDir.exists()) { + return configFileFromDataDir; + } + + final File configFileFromVendorDir = Environment.buildPath(Environment.getVendorDirectory(), + VENDOR_CONFIG_FILE_PATH, CONFIG_FILE_NAME); + if (configFileFromVendorDir.exists()) { + return configFileFromVendorDir; + } + + return null; + } + + /** + * Tries to parse the provided file into a {@link DeviceStateConfig} object. Returns + * {@code null} if the file could not be successfully parsed. + */ + @Nullable + private static DeviceStateConfig parseConfig(@NonNull ReadableConfig readableConfig) { + try (InputStream in = readableConfig.openRead(); + InputStream bin = new BufferedInputStream(in)) { + return XmlParser.read(bin); + } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) { + Slog.e(TAG, "Encountered an error while reading device state config", e); + } + return null; + } + + /** Implementation of {@link ReadableConfig} that reads config data from a file. */ + private static final class ReadableFileConfig implements ReadableConfig { + @NonNull + private final File mFile; + + private ReadableFileConfig(@NonNull File file) { + mFile = file; + } + + @Override + public InputStream openRead() throws IOException { + return new FileInputStream(mFile); + } } } diff --git a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java index cc369356c1c9..9026262db897 100644 --- a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java +++ b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java @@ -127,9 +127,11 @@ public abstract class SoftRestrictedPermissionPolicy { final boolean isWhiteListed; boolean shouldApplyRestriction; final int targetSDK; + final boolean hasLegacyExternalStorage; final boolean hasRequestedLegacyExternalStorage; - final boolean shouldPreserveLegacyExternalStorage; + final boolean hasRequestedPreserveLegacyExternalStorage; final boolean hasWriteMediaStorageGrantedForUid; + final boolean isForcedScopedStorage; if (appInfo != null) { PackageManager pm = context.getPackageManager(); @@ -137,27 +139,27 @@ public abstract class SoftRestrictedPermissionPolicy { LocalServices.getService(StorageManagerInternal.class); int flags = pm.getPermissionFlags(permission, appInfo.packageName, user); isWhiteListed = (flags & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0; + hasLegacyExternalStorage = smInternal.hasLegacyExternalStorage(appInfo.uid); hasRequestedLegacyExternalStorage = hasUidRequestedLegacyExternalStorage( appInfo.uid, context); hasWriteMediaStorageGrantedForUid = hasWriteMediaStorageGrantedForUid( appInfo.uid, context); - shouldPreserveLegacyExternalStorage = pkg.hasPreserveLegacyExternalStorage() - && smInternal.hasLegacyExternalStorage(appInfo.uid); + hasRequestedPreserveLegacyExternalStorage = + pkg.hasPreserveLegacyExternalStorage(); targetSDK = getMinimumTargetSDK(context, appInfo, user); - shouldApplyRestriction = (flags & FLAG_PERMISSION_APPLY_RESTRICTION) != 0 - || (targetSDK > Build.VERSION_CODES.Q - && !shouldPreserveLegacyExternalStorage) - // If the device is configured to force this app into scoped storage, - // then we should apply the restriction - || sForcedScopedStorageAppWhitelist.contains(appInfo.packageName); + shouldApplyRestriction = (flags & FLAG_PERMISSION_APPLY_RESTRICTION) != 0; + isForcedScopedStorage = sForcedScopedStorageAppWhitelist + .contains(appInfo.packageName); } else { isWhiteListed = false; shouldApplyRestriction = false; targetSDK = 0; + hasLegacyExternalStorage = false; hasRequestedLegacyExternalStorage = false; - shouldPreserveLegacyExternalStorage = false; + hasRequestedPreserveLegacyExternalStorage = false; hasWriteMediaStorageGrantedForUid = false; + isForcedScopedStorage = false; } // We have a check in PermissionPolicyService.PermissionToOpSynchroniser.setUidMode @@ -175,14 +177,53 @@ public abstract class SoftRestrictedPermissionPolicy { } @Override public boolean mayAllowExtraAppOp() { - return !shouldApplyRestriction - && (hasRequestedLegacyExternalStorage - || hasWriteMediaStorageGrantedForUid - || shouldPreserveLegacyExternalStorage); + // The only way to get LEGACY_STORAGE (if you didn't already have it) + // is that all of the following must be true: + // 1. The flag shouldn't be restricted + if (shouldApplyRestriction) { + return false; + } + + // 2. The app shouldn't be in sForcedScopedStorageAppWhitelist + if (isForcedScopedStorage) { + return false; + } + + // 3. The app has WRITE_MEDIA_STORAGE, OR + // the app already has legacy external storage or requested it, + // and is < R. + return hasWriteMediaStorageGrantedForUid + || ((hasLegacyExternalStorage || hasRequestedLegacyExternalStorage) + && targetSDK < Build.VERSION_CODES.R); } @Override public boolean mayDenyExtraAppOpIfGranted() { - return shouldApplyRestriction; + // If you're an app targeting < R, you can keep the app op for + // as long as you meet the conditions required to acquire it. + if (targetSDK < Build.VERSION_CODES.R) { + return !mayAllowExtraAppOp(); + } + + // For an app targeting R, the only way to lose LEGACY_STORAGE if you + // already had it is in one or more of the following conditions: + // 1. The flag became restricted + if (shouldApplyRestriction) { + return true; + } + + // The package is now a part of the forced scoped storage whitelist + if (isForcedScopedStorage) { + return true; + } + + // The package doesn't have WRITE_MEDIA_STORAGE, + // AND didn't request legacy storage to be preserved + if (!hasWriteMediaStorageGrantedForUid + && !hasRequestedPreserveLegacyExternalStorage) { + return true; + } + + return false; } }; } diff --git a/services/core/java/com/android/server/power/DisplayPowerRequestMapper.java b/services/core/java/com/android/server/power/DisplayPowerRequestMapper.java new file mode 100644 index 000000000000..6477552eb550 --- /dev/null +++ b/services/core/java/com/android/server/power/DisplayPowerRequestMapper.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.power; + +import android.hardware.display.DisplayManager; +import android.hardware.display.DisplayManagerInternal; +import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest; +import android.os.Handler; +import android.util.SparseArray; +import android.util.SparseIntArray; +import android.view.Display; + +import com.android.internal.annotations.GuardedBy; +import com.android.server.display.DisplayGroup; + +/** + * Responsible for creating {@link DisplayPowerRequest}s and associating them with + * {@link com.android.server.display.DisplayGroup}s. + * + * Each {@link com.android.server.display.DisplayGroup} has a single {@link DisplayPowerRequest} + * which is used to request power state changes to every display in the group. + */ +class DisplayPowerRequestMapper { + + private final Object mLock = new Object(); + + /** A mapping from LogicalDisplay Id to DisplayGroup Id. */ + @GuardedBy("mLock") + private final SparseIntArray mDisplayGroupIds = new SparseIntArray(); + + /** A mapping from DisplayGroup Id to DisplayPowerRequest. */ + @GuardedBy("mLock") + private final SparseArray<DisplayPowerRequest> mDisplayPowerRequests = new SparseArray<>(); + + private final DisplayManagerInternal mDisplayManagerInternal; + + private final DisplayManager.DisplayListener mDisplayListener = + new DisplayManager.DisplayListener() { + + @Override + public void onDisplayAdded(int displayId) { + synchronized (mLock) { + if (mDisplayGroupIds.indexOfKey(displayId) >= 0) { + return; + } + final int displayGroupId = mDisplayManagerInternal.getDisplayGroupId( + displayId); + if (!mDisplayPowerRequests.contains(displayGroupId)) { + // A new DisplayGroup was created; create a new DisplayPowerRequest. + mDisplayPowerRequests.append(displayGroupId, new DisplayPowerRequest()); + } + mDisplayGroupIds.append(displayId, displayGroupId); + } + } + + @Override + public void onDisplayRemoved(int displayId) { + synchronized (mLock) { + final int index = mDisplayGroupIds.indexOfKey(displayId); + if (index < 0) { + return; + } + final int displayGroupId = mDisplayGroupIds.valueAt(index); + mDisplayGroupIds.removeAt(index); + + if (mDisplayGroupIds.indexOfValue(displayGroupId) < 0) { + // The DisplayGroup no longer exists; delete the DisplayPowerRequest. + mDisplayPowerRequests.delete(displayGroupId); + } + } + } + + @Override + public void onDisplayChanged(int displayId) { + synchronized (mLock) { + final int newDisplayGroupId = mDisplayManagerInternal.getDisplayGroupId( + displayId); + final int oldDisplayGroupId = mDisplayGroupIds.get(displayId); + + if (!mDisplayPowerRequests.contains(newDisplayGroupId)) { + // A new DisplayGroup was created; create a new DisplayPowerRequest. + mDisplayPowerRequests.append(newDisplayGroupId, + new DisplayPowerRequest()); + } + mDisplayGroupIds.put(displayId, newDisplayGroupId); + + if (mDisplayGroupIds.indexOfValue(oldDisplayGroupId) < 0) { + // The DisplayGroup no longer exists; delete the DisplayPowerRequest. + mDisplayPowerRequests.delete(oldDisplayGroupId); + } + } + } + }; + + DisplayPowerRequestMapper(DisplayManager displayManager, + DisplayManagerInternal displayManagerInternal, Handler handler) { + mDisplayManagerInternal = displayManagerInternal; + displayManager.registerDisplayListener(mDisplayListener, handler); + mDisplayPowerRequests.append(DisplayGroup.DEFAULT, new DisplayPowerRequest()); + mDisplayGroupIds.append(Display.DEFAULT_DISPLAY, DisplayGroup.DEFAULT); + } + + DisplayPowerRequest get(int displayId) { + synchronized (mLock) { + return mDisplayPowerRequests.get(mDisplayGroupIds.get(displayId)); + } + } +} diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index ccd659dcf5a4..955e1cdfeaaa 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -42,6 +42,7 @@ import android.database.ContentObserver; import android.hardware.SensorManager; import android.hardware.SystemSensorManager; import android.hardware.display.AmbientDisplayConfiguration; +import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManagerInternal; import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest; import android.hardware.power.Boost; @@ -348,9 +349,9 @@ public final class PowerManagerService extends SystemService // A bitfield that summarizes the effect of the user activity timer. private int mUserActivitySummary; - // The desired display power state. The actual state may lag behind the + // Manages the desired power state of displays. The actual state may lag behind the // requested because it is updated asynchronously by the display power controller. - private final DisplayPowerRequest mDisplayPowerRequest = new DisplayPowerRequest(); + private DisplayPowerRequestMapper mDisplayPowerRequestMapper; // True if the display power state has been fully applied, which means the display // is actually on or actually off or whatever was requested. @@ -1054,6 +1055,8 @@ public final class PowerManagerService extends SystemService mPolicy = getLocalService(WindowManagerPolicy.class); mBatteryManagerInternal = getLocalService(BatteryManagerInternal.class); mAttentionDetector.systemReady(mContext); + mDisplayPowerRequestMapper = new DisplayPowerRequestMapper(mContext.getSystemService( + DisplayManager.class), mDisplayManagerInternal, mHandler); SensorManager sensorManager = new SystemSensorManager(mContext, mHandler.getLooper()); @@ -2296,10 +2299,12 @@ public final class PowerManagerService extends SystemService && mLastUserActivityTimeNoChangeLights >= mLastWakeTime) { nextTimeout = mLastUserActivityTimeNoChangeLights + screenOffTimeout; if (now < nextTimeout) { - if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_BRIGHT - || mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_VR) { + final DisplayPowerRequest displayPowerRequest = + mDisplayPowerRequestMapper.get(Display.DEFAULT_DISPLAY); + if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_BRIGHT + || displayPowerRequest.policy == DisplayPowerRequest.POLICY_VR) { mUserActivitySummary = USER_ACTIVITY_SCREEN_BRIGHT; - } else if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_DIM) { + } else if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_DIM) { mUserActivitySummary = USER_ACTIVITY_SCREEN_DIM; } } @@ -2771,11 +2776,13 @@ public final class PowerManagerService extends SystemService * Returns true if the device is allowed to dream in its current state. */ private boolean canDreamLocked() { + final DisplayPowerRequest displayPowerRequest = + mDisplayPowerRequestMapper.get(Display.DEFAULT_DISPLAY); if (getWakefulnessLocked() != WAKEFULNESS_DREAMING || !mDreamsSupportedConfig || !mDreamsEnabledSetting - || !mDisplayPowerRequest.isBrightOrDim() - || mDisplayPowerRequest.isVr() + || !displayPowerRequest.isBrightOrDim() + || displayPowerRequest.isVr() || (mUserActivitySummary & (USER_ACTIVITY_SCREEN_BRIGHT | USER_ACTIVITY_SCREEN_DIM | USER_ACTIVITY_SCREEN_DREAM)) == 0 || !mBootCompleted) { @@ -2826,7 +2833,9 @@ public final class PowerManagerService extends SystemService sQuiescent = false; } - mDisplayPowerRequest.policy = getDesiredScreenPolicyLocked(); + final DisplayPowerRequest displayPowerRequest = mDisplayPowerRequestMapper.get( + Display.DEFAULT_DISPLAY); + displayPowerRequest.policy = getDesiredScreenPolicyLocked(); // Determine appropriate screen brightness and auto-brightness adjustments. final boolean autoBrightness; @@ -2846,39 +2855,39 @@ public final class PowerManagerService extends SystemService } // Update display power request. - mDisplayPowerRequest.screenBrightnessOverride = screenBrightnessOverride; - mDisplayPowerRequest.useAutoBrightness = autoBrightness; - mDisplayPowerRequest.useProximitySensor = shouldUseProximitySensorLocked(); - mDisplayPowerRequest.boostScreenBrightness = shouldBoostScreenBrightness(); + displayPowerRequest.screenBrightnessOverride = screenBrightnessOverride; + displayPowerRequest.useAutoBrightness = autoBrightness; + displayPowerRequest.useProximitySensor = shouldUseProximitySensorLocked(); + displayPowerRequest.boostScreenBrightness = shouldBoostScreenBrightness(); - updatePowerRequestFromBatterySaverPolicy(mDisplayPowerRequest); + updatePowerRequestFromBatterySaverPolicy(displayPowerRequest); - if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE) { - mDisplayPowerRequest.dozeScreenState = mDozeScreenStateOverrideFromDreamManager; + if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE) { + displayPowerRequest.dozeScreenState = mDozeScreenStateOverrideFromDreamManager; if ((mWakeLockSummary & WAKE_LOCK_DRAW) != 0 && !mDrawWakeLockOverrideFromSidekick) { - if (mDisplayPowerRequest.dozeScreenState == Display.STATE_DOZE_SUSPEND) { - mDisplayPowerRequest.dozeScreenState = Display.STATE_DOZE; + if (displayPowerRequest.dozeScreenState == Display.STATE_DOZE_SUSPEND) { + displayPowerRequest.dozeScreenState = Display.STATE_DOZE; } - if (mDisplayPowerRequest.dozeScreenState == Display.STATE_ON_SUSPEND) { - mDisplayPowerRequest.dozeScreenState = Display.STATE_ON; + if (displayPowerRequest.dozeScreenState == Display.STATE_ON_SUSPEND) { + displayPowerRequest.dozeScreenState = Display.STATE_ON; } } - mDisplayPowerRequest.dozeScreenBrightness = + displayPowerRequest.dozeScreenBrightness = mDozeScreenBrightnessOverrideFromDreamManagerFloat; } else { - mDisplayPowerRequest.dozeScreenState = Display.STATE_UNKNOWN; - mDisplayPowerRequest.dozeScreenBrightness = + displayPowerRequest.dozeScreenState = Display.STATE_UNKNOWN; + displayPowerRequest.dozeScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; } - mDisplayReady = mDisplayManagerInternal.requestPowerState(mDisplayPowerRequest, + mDisplayReady = mDisplayManagerInternal.requestPowerState(displayPowerRequest, mRequestWaitForNegativeProximity); mRequestWaitForNegativeProximity = false; if (DEBUG_SPEW) { Slog.d(TAG, "updateDisplayPowerStateLocked: mDisplayReady=" + mDisplayReady - + ", policy=" + mDisplayPowerRequest.policy + + ", policy=" + displayPowerRequest.policy + ", mWakefulness=" + getWakefulnessLocked() + ", mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary) + ", mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary) @@ -3049,7 +3058,9 @@ public final class PowerManagerService extends SystemService final boolean needWakeLockSuspendBlocker = ((mWakeLockSummary & WAKE_LOCK_CPU) != 0); final boolean needDisplaySuspendBlocker = needDisplaySuspendBlockerLocked(); final boolean autoSuspend = !needDisplaySuspendBlocker; - final boolean interactive = mDisplayPowerRequest.isBrightOrDim(); + final DisplayPowerRequest displayPowerRequest = mDisplayPowerRequestMapper.get( + Display.DEFAULT_DISPLAY); + final boolean interactive = displayPowerRequest.isBrightOrDim(); // Disable auto-suspend if needed. // FIXME We should consider just leaving auto-suspend enabled forever since @@ -3108,19 +3119,21 @@ public final class PowerManagerService extends SystemService if (!mDisplayReady) { return true; } - if (mDisplayPowerRequest.isBrightOrDim()) { + final DisplayPowerRequest displayPowerRequest = mDisplayPowerRequestMapper.get( + Display.DEFAULT_DISPLAY); + if (displayPowerRequest.isBrightOrDim()) { // If we asked for the screen to be on but it is off due to the proximity // sensor then we may suspend but only if the configuration allows it. // On some hardware it may not be safe to suspend because the proximity // sensor may not be correctly configured as a wake-up source. - if (!mDisplayPowerRequest.useProximitySensor || !mProximityPositive + if (!displayPowerRequest.useProximitySensor || !mProximityPositive || !mSuspendWhenScreenOffDueToProximityConfig) { return true; } } - if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE - && mDisplayPowerRequest.dozeScreenState == Display.STATE_ON) { + if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE + && displayPowerRequest.dozeScreenState == Display.STATE_ON) { // Although we are in DOZE and would normally allow the device to suspend, // the doze service has explicitly requested the display to remain in the ON // state which means we should hold the display suspend blocker. @@ -5575,7 +5588,10 @@ public final class PowerManagerService extends SystemService // DisplayPowerController only reports proximity positive (near) if it's // positive and the proximity wasn't already being ignored. So it reliably // also tells us that we're not already ignoring the proximity sensor. - if (mDisplayPowerRequest.useProximitySensor && mProximityPositive) { + + final DisplayPowerRequest displayPowerRequest = + mDisplayPowerRequestMapper.get(Display.DEFAULT_DISPLAY); + if (displayPowerRequest.useProximitySensor && mProximityPositive) { mDisplayManagerInternal.ignoreProximitySensorUntilChanged(); return true; } diff --git a/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java b/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java index 18646b9cc06c..88e5f69e02c4 100644 --- a/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java +++ b/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java @@ -38,11 +38,41 @@ public final class PowerStatsHALWrapper { */ public interface IPowerStatsHALWrapper { /** + * Returns information related to all supported PowerEntity(s) for which state residency + * data is available. + * + * A PowerEntity is defined as a platform subsystem, peripheral, or power domain that + * impacts the total device power consumption. + * + * @return List of information on each PowerEntity. + */ + android.hardware.power.stats.PowerEntityInfo[] getPowerEntityInfo(); + + /** + * Reports the accumulated state residency for each requested PowerEntity. + * + * Each PowerEntity may reside in one of multiple states. It may also transition from one + * state to another. StateResidency is defined as an accumulation of time that a + * PowerEntity resided in each of its possible states, the number of times that each state + * was entered, and a timestamp corresponding to the last time that state was entered. + * + * Data is accumulated starting at device boot. + * + * @param powerEntityIds List of IDs of PowerEntities for which data is requested. Passing + * an empty list will return state residency for all available + * PowerEntities. ID of each PowerEntity is contained in + * PowerEntityInfo. + * + * @return StateResidency since boot for each requested PowerEntity + */ + android.hardware.power.stats.StateResidencyResult[] getStateResidency(int[] powerEntityIds); + + /** * Returns the energy consumer IDs for all available energy consumers (power models) on the - * device. Examples of subsystems for which energy consumer results (power models) - * may be available are GPS, display, wifi, etc. The default list of energy - * consumers can be found in the PowerStats HAL definition (EnergyConsumerId.aidl). - * The availability of energy consumer IDs is hardware dependent. + * device. Examples of subsystems for which energy consumer results (power models) may be + * available are GPS, display, wifi, etc. The default list of energy consumers can be + * found in the PowerStats HAL definition (EnergyConsumerId.aidl). The availability of + * energy consumer IDs is hardware dependent. * * @return List of EnergyConsumerIds all available energy consumers. */ @@ -50,14 +80,19 @@ public final class PowerStatsHALWrapper { /** * Returns the energy consumer result for all available energy consumers (power models). - * Available consumers can be retrieved by calling getEnergyConsumerInfo(). The - * subsystem corresponding to the energy consumer result is defined by the energy - * consumer ID. + * Available consumers can be retrieved by calling getEnergyConsumerInfo(). The subsystem + * corresponding to the energy consumer result is defined by the energy consumer ID. + * + * @param energyConsumerIds Array of energy consumer IDs for which energy consumed is being + * requested. Energy consumers available on the device can be + * queried by calling getEnergyConsumerInfo(). Passing an empty + * array will return results for all energy consumers. * * @return List of EnergyConsumerResult objects containing energy consumer results for all * available energy consumers (power models). */ - android.hardware.power.stats.EnergyConsumerResult[] getEnergyConsumed(); + android.hardware.power.stats.EnergyConsumerResult[] getEnergyConsumed( + int[] energyConsumerIds); /** * Returns channel info for all available energy meters. @@ -69,17 +104,21 @@ public final class PowerStatsHALWrapper { /** * Returns energy measurements for all available energy meters. Available channels can be - * retrieved by calling getEnergyMeterInfo(). Energy measurements and channel info - * can be linked through the channelId field. + * retrieved by calling getEnergyMeterInfo(). Energy measurements and channel info can be + * linked through the channelId field. + * + * @param channelIds Array of channel IDs for which energy measurements are being requested. + * Channel IDs available on the device can be queried by calling + * getEnergyMeterInfo(). Passing an empty array will return energy + * measurements for all channels. * * @return List of EnergyMeasurement objects containing energy measurements for all * available energy meters. */ - android.hardware.power.stats.EnergyMeasurement[] readEnergyMeters(); + android.hardware.power.stats.EnergyMeasurement[] readEnergyMeters(int[] channelIds); /** - * Returns boolean indicating if connection to power stats HAL was - * established. + * Returns boolean indicating if connection to power stats HAL was established. * * @return true if connection to power stats HAL was correctly established. */ @@ -95,6 +134,38 @@ public final class PowerStatsHALWrapper { private static Supplier<IPowerStats> sVintfPowerStats; @Override + public android.hardware.power.stats.PowerEntityInfo[] getPowerEntityInfo() { + android.hardware.power.stats.PowerEntityInfo[] powerEntityInfoHAL = null; + + if (sVintfPowerStats != null) { + try { + powerEntityInfoHAL = sVintfPowerStats.get().getPowerEntityInfo(); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to get power entity info from PowerStats HAL"); + } + } + + return powerEntityInfoHAL; + } + + @Override + public android.hardware.power.stats.StateResidencyResult[] getStateResidency( + int[] powerEntityIds) { + android.hardware.power.stats.StateResidencyResult[] stateResidencyResultHAL = null; + + if (sVintfPowerStats != null) { + try { + stateResidencyResultHAL = + sVintfPowerStats.get().getStateResidency(powerEntityIds); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to get state residency from PowerStats HAL"); + } + } + + return stateResidencyResultHAL; + } + + @Override public int[] getEnergyConsumerInfo() { int[] energyConsumerInfoHAL = null; @@ -110,13 +181,14 @@ public final class PowerStatsHALWrapper { } @Override - public android.hardware.power.stats.EnergyConsumerResult[] getEnergyConsumed() { + public android.hardware.power.stats.EnergyConsumerResult[] getEnergyConsumed( + int[] energyConsumerIds) { android.hardware.power.stats.EnergyConsumerResult[] energyConsumedHAL = null; if (sVintfPowerStats != null) { try { energyConsumedHAL = - sVintfPowerStats.get().getEnergyConsumed(new int[0]); + sVintfPowerStats.get().getEnergyConsumed(energyConsumerIds); } catch (RemoteException e) { Slog.e(TAG, "Failed to get energy consumer results from PowerStats HAL"); } @@ -141,13 +213,13 @@ public final class PowerStatsHALWrapper { } @Override - public android.hardware.power.stats.EnergyMeasurement[] readEnergyMeters() { + public android.hardware.power.stats.EnergyMeasurement[] readEnergyMeters(int[] channelIds) { android.hardware.power.stats.EnergyMeasurement[] energyMeasurementHAL = null; if (sVintfPowerStats != null) { try { energyMeasurementHAL = - sVintfPowerStats.get().readEnergyMeters(new int[0]); + sVintfPowerStats.get().readEnergyMeters(channelIds); } catch (RemoteException e) { Slog.e(TAG, "Failed to get energy measurements from PowerStats HAL"); } diff --git a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java index f5131c4b371d..409cd826b6bc 100644 --- a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java +++ b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java @@ -61,14 +61,15 @@ public final class PowerStatsLogger extends Handler { if (DEBUG) Slog.d(TAG, "Logging to data storage"); // Log power meter data. - EnergyMeasurement[] energyMeasurements = mPowerStatsHALWrapper.readEnergyMeters(); + EnergyMeasurement[] energyMeasurements = + mPowerStatsHALWrapper.readEnergyMeters(new int[0]); mPowerStatsMeterStorage.write( EnergyMeasurementUtils.getProtoBytes(energyMeasurements)); if (DEBUG) EnergyMeasurementUtils.print(energyMeasurements); // Log power model data. EnergyConsumerResult[] energyConsumerResults = - mPowerStatsHALWrapper.getEnergyConsumed(); + mPowerStatsHALWrapper.getEnergyConsumed(new int[0]); mPowerStatsModelStorage.write( EnergyConsumerResultUtils.getProtoBytes(energyConsumerResults)); if (DEBUG) EnergyConsumerResultUtils.print(energyConsumerResults); diff --git a/services/core/java/com/android/server/powerstats/PowerStatsService.java b/services/core/java/com/android/server/powerstats/PowerStatsService.java index bf3919e7344f..1150d4bbe770 100644 --- a/services/core/java/com/android/server/powerstats/PowerStatsService.java +++ b/services/core/java/com/android/server/powerstats/PowerStatsService.java @@ -19,6 +19,7 @@ package com.android.server.powerstats; import android.annotation.Nullable; import android.content.Context; import android.hardware.power.stats.ChannelInfo; +import android.hardware.power.stats.PowerEntityInfo; import android.os.Binder; import android.os.Environment; import android.os.UserHandle; @@ -31,6 +32,7 @@ import com.android.server.powerstats.PowerStatsHALWrapper.IPowerStatsHALWrapper; import com.android.server.powerstats.PowerStatsHALWrapper.PowerStatsHALWrapperImpl; import com.android.server.powerstats.ProtoStreamUtils.ChannelInfoUtils; import com.android.server.powerstats.ProtoStreamUtils.EnergyConsumerIdUtils; +import com.android.server.powerstats.ProtoStreamUtils.PowerEntityInfoUtils; import java.io.File; import java.io.FileDescriptor; @@ -110,6 +112,10 @@ public class PowerStatsService extends SystemService { mPowerStatsLogger.writeMeterDataToFile(fd); } } else if (args.length == 0) { + pw.println("PowerStatsService dumpsys: available PowerEntityInfos"); + PowerEntityInfo[] powerEntityInfo = mPowerStatsHALWrapper.getPowerEntityInfo(); + PowerEntityInfoUtils.dumpsys(powerEntityInfo, pw); + pw.println("PowerStatsService dumpsys: available ChannelInfos"); ChannelInfo[] channelInfo = mPowerStatsHALWrapper.getEnergyMeterInfo(); ChannelInfoUtils.dumpsys(channelInfo, pw); diff --git a/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java b/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java index 43afeed8b925..5a4256ac0264 100644 --- a/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java +++ b/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java @@ -19,6 +19,8 @@ package com.android.server.powerstats; import android.hardware.power.stats.ChannelInfo; import android.hardware.power.stats.EnergyConsumerResult; import android.hardware.power.stats.EnergyMeasurement; +import android.hardware.power.stats.PowerEntityInfo; +import android.hardware.power.stats.StateResidencyResult; import android.util.Slog; import android.util.proto.ProtoInputStream; import android.util.proto.ProtoOutputStream; @@ -42,6 +44,48 @@ import java.util.List; public class ProtoStreamUtils { private static final String TAG = ProtoStreamUtils.class.getSimpleName(); + static class PowerEntityInfoUtils { + public static void print(PowerEntityInfo[] powerEntityInfo) { + for (int i = 0; i < powerEntityInfo.length; i++) { + Slog.d(TAG, "PowerEntityId: " + powerEntityInfo[i].powerEntityId + + ", PowerEntityName: " + powerEntityInfo[i].powerEntityName); + for (int j = 0; j < powerEntityInfo[i].states.length; j++) { + Slog.d(TAG, " StateId: " + powerEntityInfo[i].states[j].stateId + + ", StateName: " + powerEntityInfo[i].states[j].stateName); + } + } + } + + public static void dumpsys(PowerEntityInfo[] powerEntityInfo, PrintWriter pw) { + for (int i = 0; i < powerEntityInfo.length; i++) { + pw.println("PowerEntityId: " + powerEntityInfo[i].powerEntityId + + ", PowerEntityName: " + powerEntityInfo[i].powerEntityName); + for (int j = 0; j < powerEntityInfo[i].states.length; j++) { + pw.println(" StateId: " + powerEntityInfo[i].states[j].stateId + + ", StateName: " + powerEntityInfo[i].states[j].stateName); + } + } + } + } + + static class StateResidencyResultUtils { + public static void print(StateResidencyResult[] stateResidencyResult) { + for (int i = 0; i < stateResidencyResult.length; i++) { + Slog.d(TAG, "PowerEntityId: " + stateResidencyResult[i].powerEntityId); + for (int j = 0; j < stateResidencyResult[i].stateResidencyData.length; j++) { + Slog.d(TAG, " StateId: " + + stateResidencyResult[i].stateResidencyData[j].stateId + + ", TotalTimeInStateMs: " + + stateResidencyResult[i].stateResidencyData[j].totalTimeInStateMs + + ", TotalStateEntryCount: " + + stateResidencyResult[i].stateResidencyData[j].totalStateEntryCount + + ", LastEntryTimestampMs: " + + stateResidencyResult[i].stateResidencyData[j].lastEntryTimestampMs); + } + } + } + } + static class ChannelInfoUtils { public static void packProtoMessage(ChannelInfo[] channelInfo, ProtoOutputStream pos) { long token; @@ -57,15 +101,15 @@ public class ProtoStreamUtils { public static void print(ChannelInfo[] channelInfo) { for (int i = 0; i < channelInfo.length; i++) { - Slog.d(TAG, "ChannelId = " + channelInfo[i].channelId - + ", ChannelName = " + channelInfo[i].channelName); + Slog.d(TAG, "ChannelId: " + channelInfo[i].channelId + + ", ChannelName: " + channelInfo[i].channelName); } } public static void dumpsys(ChannelInfo[] channelInfo, PrintWriter pw) { for (int i = 0; i < channelInfo.length; i++) { - pw.println("ChannelId = " + channelInfo[i].channelId - + ", ChannelName = " + channelInfo[i].channelName); + pw.println("ChannelId: " + channelInfo[i].channelId + + ", ChannelName: " + channelInfo[i].channelName); } } } @@ -157,9 +201,9 @@ public class ProtoStreamUtils { public static void print(EnergyMeasurement[] energyMeasurement) { for (int i = 0; i < energyMeasurement.length; i++) { - Slog.d(TAG, "ChannelId = " + energyMeasurement[i].channelId - + ", Timestamp (ms) = " + energyMeasurement[i].timestampMs - + ", Energy (uWs) = " + energyMeasurement[i].energyUWs); + Slog.d(TAG, "ChannelId: " + energyMeasurement[i].channelId + + ", Timestamp (ms): " + energyMeasurement[i].timestampMs + + ", Energy (uWs): " + energyMeasurement[i].energyUWs); } } } @@ -177,13 +221,13 @@ public class ProtoStreamUtils { public static void print(int[] energyConsumerId) { for (int i = 0; i < energyConsumerId.length; i++) { - Slog.d(TAG, "EnergyConsumerId = " + energyConsumerId[i]); + Slog.d(TAG, "EnergyConsumerId: " + energyConsumerId[i]); } } public static void dumpsys(int[] energyConsumerId, PrintWriter pw) { for (int i = 0; i < energyConsumerId.length; i++) { - pw.println("EnergyConsumerId = " + energyConsumerId[i]); + pw.println("EnergyConsumerId: " + energyConsumerId[i]); } } } @@ -278,9 +322,9 @@ public class ProtoStreamUtils { public static void print(EnergyConsumerResult[] energyConsumerResult) { for (int i = 0; i < energyConsumerResult.length; i++) { - Slog.d(TAG, "EnergyConsumerId = " + energyConsumerResult[i].energyConsumerId - + ", Timestamp (ms) = " + energyConsumerResult[i].timestampMs - + ", Energy (uWs) = " + energyConsumerResult[i].energyUWs); + Slog.d(TAG, "EnergyConsumerId: " + energyConsumerResult[i].energyConsumerId + + ", Timestamp (ms): " + energyConsumerResult[i].timestampMs + + ", Energy (uWs): " + energyConsumerResult[i].energyUWs); } } } diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java index 2d51cf9a5083..63ed416f2859 100644 --- a/services/core/java/com/android/server/rollback/Rollback.java +++ b/services/core/java/com/android/server/rollback/Rollback.java @@ -141,11 +141,17 @@ class Rollback { /** * The current state of the rollback. - * ENABLING, AVAILABLE, or COMMITTED. + * ENABLING, AVAILABLE, DELETED, or COMMITTED. */ private @RollbackState int mState; /** + * The detailed description of the current state. For a DELETED state, it describes + * the reason why the rollback is deleted. + */ + private @NonNull String mStateDescription = ""; + + /** * True if we are expecting the package manager to call restoreUserData * for this rollback because it has just been committed but the rollback * has not yet been fully applied. @@ -231,7 +237,7 @@ class Rollback { * Constructs a pre-populated Rollback instance. */ Rollback(RollbackInfo info, File backupDir, Instant timestamp, int stagedSessionId, - @RollbackState int state, boolean restoreUserDataInProgress, + @RollbackState int state, String stateDescription, boolean restoreUserDataInProgress, int userId, String installerPackageName, SparseIntArray extensionVersions) { this.info = info; mUserId = userId; @@ -240,6 +246,7 @@ class Rollback { mTimestamp = timestamp; mStagedSessionId = stagedSessionId; mState = state; + mStateDescription = stateDescription; mRestoreUserDataInProgress = restoreUserDataInProgress; mExtensionVersions = Objects.requireNonNull(extensionVersions); // TODO(b/120200473): Include this field during persistence. This field will be used to @@ -478,7 +485,7 @@ class Rollback { Slog.w(TAG, "Cannot make deleted rollback available."); return; } - mState = ROLLBACK_STATE_AVAILABLE; + setState(ROLLBACK_STATE_AVAILABLE, ""); mTimestamp = Instant.now(); RollbackStore.saveRollback(this); } @@ -598,7 +605,7 @@ class Rollback { // Why would we expect commit not to fail again? // TODO: Could this cause a rollback to be resurrected // if it should otherwise have expired by now? - mState = ROLLBACK_STATE_AVAILABLE; + setState(ROLLBACK_STATE_AVAILABLE, "Commit failed"); mRestoreUserDataInProgress = false; info.setCommittedSessionId(-1); sendFailure(context, statusReceiver, @@ -642,7 +649,7 @@ class Rollback { }; final LocalIntentReceiver receiver = new LocalIntentReceiver(onResult); - mState = ROLLBACK_STATE_COMMITTED; + setState(ROLLBACK_STATE_COMMITTED, ""); info.setCommittedSessionId(parentSessionId); mRestoreUserDataInProgress = true; parentSession.commit(receiver.getIntentSender()); @@ -691,7 +698,7 @@ class Rollback { * Deletes app data snapshots associated with this rollback, and moves to the DELETED state. */ @WorkerThread - void delete(AppDataRollbackHelper dataHelper) { + void delete(AppDataRollbackHelper dataHelper, @NonNull String reason) { assertInWorkerThread(); boolean containsApex = false; Set<Integer> apexUsers = new ArraySet<>(); @@ -717,7 +724,7 @@ class Rollback { } RollbackStore.deleteRollback(this); - mState = ROLLBACK_STATE_DELETED; + setState(ROLLBACK_STATE_DELETED, reason); } /** @@ -847,6 +854,7 @@ class Rollback { case Rollback.ROLLBACK_STATE_ENABLING: return "enabling"; case Rollback.ROLLBACK_STATE_AVAILABLE: return "available"; case Rollback.ROLLBACK_STATE_COMMITTED: return "committed"; + case Rollback.ROLLBACK_STATE_DELETED: return "deleted"; } throw new AssertionError("Invalid rollback state: " + state); } @@ -858,6 +866,7 @@ class Rollback { case "enabling": return Rollback.ROLLBACK_STATE_ENABLING; case "available": return Rollback.ROLLBACK_STATE_AVAILABLE; case "committed": return Rollback.ROLLBACK_STATE_COMMITTED; + case "deleted": return Rollback.ROLLBACK_STATE_DELETED; } throw new ParseException("Invalid rollback state: " + state, 0); } @@ -926,6 +935,7 @@ class Rollback { ipw.println(info.getRollbackId() + ":"); ipw.increaseIndent(); ipw.println("-state: " + getStateAsString()); + ipw.println("-stateDescription: " + mStateDescription); ipw.println("-timestamp: " + getTimestamp()); if (getStagedSessionId() != -1) { ipw.println("-stagedSessionId: " + getStagedSessionId()); @@ -955,4 +965,17 @@ class Rollback { } ipw.decreaseIndent(); } + + @WorkerThread + String getStateDescription() { + assertInWorkerThread(); + return mStateDescription; + } + + @VisibleForTesting + void setState(@RollbackState int state, String description) { + assertInWorkerThread(); + mState = state; + mStateDescription = description; + } } diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java index b34d46ff1a0c..192a00303a30 100644 --- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java +++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java @@ -179,7 +179,9 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba mInstaller = new Installer(mContext); mInstaller.onStart(); - mRollbackStore = new RollbackStore(new File(Environment.getDataDirectory(), "rollback")); + mRollbackStore = new RollbackStore( + new File(Environment.getDataDirectory(), "rollback"), + new File(Environment.getDataDirectory(), "rollback-history")); mPackageHealthObserver = new RollbackPackageHealthObserver(mContext); mAppDataRollbackHelper = new AppDataRollbackHelper(mInstaller); @@ -201,7 +203,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba } else { // Delete rollbacks when build fingerprint has changed. for (Rollback rollback : mRollbacks) { - rollback.delete(mAppDataRollbackHelper); + deleteRollback(rollback, "Fingerprint changed"); } mRollbacks.clear(); } @@ -271,7 +273,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba Rollback rollback = getRollbackForSession(sessionId); if (rollback != null && rollback.isEnabling()) { mRollbacks.remove(rollback); - rollback.delete(mAppDataRollbackHelper); + deleteRollback(rollback, "Rollback canceled"); } } } @@ -477,14 +479,14 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba } @WorkerThread - private void expireRollbackForPackageInternal(String packageName) { + private void expireRollbackForPackageInternal(String packageName, String reason) { assertInWorkerThread(); Iterator<Rollback> iter = mRollbacks.iterator(); while (iter.hasNext()) { Rollback rollback = iter.next(); if (rollback.includesPackage(packageName)) { iter.remove(); - rollback.delete(mAppDataRollbackHelper); + deleteRollback(rollback, reason); } } } @@ -496,7 +498,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba mContext.enforceCallingOrSelfPermission( Manifest.permission.TEST_MANAGE_ROLLBACKS, "expireRollbackForPackage"); - awaitResult(() -> expireRollbackForPackageInternal(packageName)); + awaitResult(() -> expireRollbackForPackageInternal(packageName, "Expired by API")); } @ExtThread @@ -612,7 +614,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba .getPackageInstaller().getSessionInfo(rollback.getStagedSessionId()); if (session == null || session.isStagedSessionFailed()) { iter.remove(); - rollback.delete(mAppDataRollbackHelper); + deleteRollback(rollback, + "Session " + session.getSessionId() + " not existed or failed"); continue; } @@ -666,7 +669,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba && rollback.includesPackageWithDifferentVersion(packageName, installedVersion)) { iter.remove(); - rollback.delete(mAppDataRollbackHelper); + deleteRollback(rollback, "Package " + packageName + " replaced"); } } } @@ -678,7 +681,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba @WorkerThread private void onPackageFullyRemoved(String packageName) { assertInWorkerThread(); - expireRollbackForPackageInternal(packageName); + expireRollbackForPackageInternal(packageName, "Package " + packageName + " removed"); } /** @@ -713,14 +716,14 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba Iterator<Rollback> iter = mRollbacks.iterator(); while (iter.hasNext()) { Rollback rollback = iter.next(); - if (!rollback.isAvailable()) { + if (!rollback.isAvailable() && !rollback.isCommitted()) { continue; } Instant rollbackTimestamp = rollback.getTimestamp(); if (!now.isBefore(rollbackTimestamp.plusMillis(mRollbackLifetimeDurationInMillis))) { Slog.i(TAG, "runExpiration id=" + rollback.info.getRollbackId()); iter.remove(); - rollback.delete(mAppDataRollbackHelper); + deleteRollback(rollback, "Expired by timeout"); } else if (oldest == null || oldest.isAfter(rollbackTimestamp)) { oldest = rollbackTimestamp; } @@ -1132,7 +1135,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba Slog.w(TAG, "Delete rollback id=" + rollback.info.getRollbackId() + " for failed session id=" + sessionId); mRollbacks.remove(rollback); - rollback.delete(mAppDataRollbackHelper); + deleteRollback(rollback, "Session " + sessionId + " failed"); } } } @@ -1159,7 +1162,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba if (!rollback.allPackagesEnabled()) { Slog.e(TAG, "Failed to enable rollback for all packages in session."); mRollbacks.remove(rollback); - rollback.delete(mAppDataRollbackHelper); + deleteRollback(rollback, "Failed to enable rollback for all packages in session"); return false; } @@ -1240,6 +1243,18 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba rollback.dump(ipw); } ipw.println(); + + List<Rollback> historicalRollbacks = mRollbackStore.loadHistorialRollbacks(); + if (!historicalRollbacks.isEmpty()) { + ipw.println("Historical rollbacks:"); + ipw.increaseIndent(); + for (Rollback rollback : historicalRollbacks) { + rollback.dump(ipw); + } + ipw.decreaseIndent(); + ipw.println(); + } + PackageWatchdog.getInstance(mContext).dump(ipw); }); } @@ -1329,4 +1344,11 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba } return null; } + + @WorkerThread + private void deleteRollback(Rollback rollback, String reason) { + assertInWorkerThread(); + rollback.delete(mAppDataRollbackHelper, reason); + mRollbackStore.saveRollbackToHistory(rollback); + } } diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java index 2ee87e67e467..35c9f9ae6683 100644 --- a/services/core/java/com/android/server/rollback/RollbackStore.java +++ b/services/core/java/com/android/server/rollback/RollbackStore.java @@ -69,18 +69,20 @@ class RollbackStore { // * XXX, YYY are the rollbackIds for the corresponding rollbacks. // * rollback.json contains all relevant metadata for the rollback. private final File mRollbackDataDir; + private final File mRollbackHistoryDir; - RollbackStore(File rollbackDataDir) { + RollbackStore(File rollbackDataDir, File rollbackHistoryDir) { mRollbackDataDir = rollbackDataDir; + mRollbackHistoryDir = rollbackHistoryDir; } /** * Reads the rollbacks from persistent storage. */ - List<Rollback> loadRollbacks() { + private static List<Rollback> loadRollbacks(File rollbackDataDir) { List<Rollback> rollbacks = new ArrayList<>(); - mRollbackDataDir.mkdirs(); - for (File rollbackDir : mRollbackDataDir.listFiles()) { + rollbackDataDir.mkdirs(); + for (File rollbackDir : rollbackDataDir.listFiles()) { if (rollbackDir.isDirectory()) { try { rollbacks.add(loadRollback(rollbackDir)); @@ -93,6 +95,14 @@ class RollbackStore { return rollbacks; } + List<Rollback> loadRollbacks() { + return loadRollbacks(mRollbackDataDir); + } + + List<Rollback> loadHistorialRollbacks() { + return loadRollbacks(mRollbackHistoryDir); + } + /** * Converts a {@code JSONArray} of integers to a {@code List<Integer>}. */ @@ -258,15 +268,17 @@ class RollbackStore { /** * Saves the given rollback to persistent storage. */ - static void saveRollback(Rollback rollback) { + private static void saveRollback(Rollback rollback, File backDir) { FileOutputStream fos = null; - AtomicFile file = new AtomicFile(new File(rollback.getBackupDir(), "rollback.json")); + AtomicFile file = new AtomicFile(new File(backDir, "rollback.json")); try { + backDir.mkdirs(); JSONObject dataJson = new JSONObject(); dataJson.put("info", rollbackInfoToJson(rollback.info)); dataJson.put("timestamp", rollback.getTimestamp().toString()); dataJson.put("stagedSessionId", rollback.getStagedSessionId()); dataJson.put("state", rollback.getStateAsString()); + dataJson.put("stateDescription", rollback.getStateDescription()); dataJson.put("restoreUserDataInProgress", rollback.isRestoreUserDataInProgress()); dataJson.put("userId", rollback.getUserId()); dataJson.putOpt("installerPackageName", rollback.getInstallerPackageName()); @@ -286,6 +298,22 @@ class RollbackStore { } } + static void saveRollback(Rollback rollback) { + saveRollback(rollback, rollback.getBackupDir()); + } + + /** + * Saves the rollback to $mRollbackHistoryDir/ROLLBACKID-HEX for debugging purpose. + */ + void saveRollbackToHistory(Rollback rollback) { + // The same id might be allocated to different historical rollbacks. + // Let's add a suffix to avoid naming collision. + String suffix = Long.toHexString(rollback.getTimestamp().getEpochSecond()); + String dirName = Integer.toString(rollback.info.getRollbackId()); + File backupDir = new File(mRollbackHistoryDir, dirName + "-" + suffix); + saveRollback(rollback, backupDir); + } + /** * Removes all persistent storage associated with the given rollback. */ @@ -318,6 +346,7 @@ class RollbackStore { Instant.parse(dataJson.getString("timestamp")), dataJson.getInt("stagedSessionId"), rollbackStateFromString(dataJson.getString("state")), + dataJson.optString("stateDescription"), dataJson.getBoolean("restoreUserDataInProgress"), dataJson.optInt("userId", UserHandle.SYSTEM.getIdentifier()), dataJson.optString("installerPackageName", ""), diff --git a/services/core/java/com/android/server/storage/StorageUserConnection.java b/services/core/java/com/android/server/storage/StorageUserConnection.java index b79fc8dd1e1a..af2628971bdd 100644 --- a/services/core/java/com/android/server/storage/StorageUserConnection.java +++ b/services/core/java/com/android/server/storage/StorageUserConnection.java @@ -23,6 +23,7 @@ import static android.service.storage.ExternalStorageService.FLAG_SESSION_TYPE_F import static com.android.server.storage.StorageSessionController.ExternalStorageServiceException; import android.annotation.MainThread; +import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ComponentName; import android.content.Context; @@ -34,28 +35,27 @@ import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.os.ParcelableException; import android.os.RemoteCallback; +import android.os.RemoteException; import android.os.UserHandle; import android.os.storage.StorageManagerInternal; import android.os.storage.StorageVolume; import android.service.storage.ExternalStorageService; import android.service.storage.IExternalStorageService; -import android.text.TextUtils; import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.Preconditions; import com.android.server.LocalServices; -import com.android.server.pm.UserManagerInternal; import java.io.IOException; +import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Objects; import java.util.Set; -import java.util.concurrent.CountDownLatch; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; /** * Controls the lifecycle of the {@link ActiveConnection} to an {@link ExternalStorageService} @@ -66,25 +66,20 @@ public final class StorageUserConnection { private static final int DEFAULT_REMOTE_TIMEOUT_SECONDS = 20; - private final Object mLock = new Object(); + private final Object mSessionsLock = new Object(); private final Context mContext; private final int mUserId; private final StorageSessionController mSessionController; private final ActiveConnection mActiveConnection = new ActiveConnection(); - private final boolean mIsDemoUser; @GuardedBy("mLock") private final Map<String, Session> mSessions = new HashMap<>(); - @GuardedBy("mLock") @Nullable private HandlerThread mHandlerThread; + private final HandlerThread mHandlerThread; public StorageUserConnection(Context context, int userId, StorageSessionController controller) { mContext = Objects.requireNonNull(context); mUserId = Preconditions.checkArgumentNonnegative(userId); mSessionController = controller; - mIsDemoUser = LocalServices.getService(UserManagerInternal.class) - .getUserInfo(userId).isDemo(); - if (mIsDemoUser) { - mHandlerThread = new HandlerThread("StorageUserConnectionThread-" + mUserId); - mHandlerThread.start(); - } + mHandlerThread = new HandlerThread("StorageUserConnectionThread-" + mUserId); + mHandlerThread.start(); } /** @@ -101,13 +96,12 @@ public final class StorageUserConnection { Objects.requireNonNull(upperPath); Objects.requireNonNull(lowerPath); - prepareRemote(); - synchronized (mLock) { + Session session = new Session(sessionId, upperPath, lowerPath); + synchronized (mSessionsLock) { Preconditions.checkArgument(!mSessions.containsKey(sessionId)); - Session session = new Session(sessionId, upperPath, lowerPath); mSessions.put(sessionId, session); - mActiveConnection.startSessionLocked(session, pfd); } + mActiveConnection.startSession(session, pfd); } /** @@ -121,10 +115,13 @@ public final class StorageUserConnection { Objects.requireNonNull(sessionId); Objects.requireNonNull(vol); - prepareRemote(); - synchronized (mLock) { - mActiveConnection.notifyVolumeStateChangedLocked(sessionId, vol); + synchronized (mSessionsLock) { + if (!mSessions.containsKey(sessionId)) { + Slog.i(TAG, "No session found for sessionId: " + sessionId); + return; + } } + mActiveConnection.notifyVolumeStateChanged(sessionId, vol); } /** @@ -135,7 +132,7 @@ public final class StorageUserConnection { * with {@link #waitForExit}. **/ public Session removeSession(String sessionId) { - synchronized (mLock) { + synchronized (mSessionsLock) { return mSessions.remove(sessionId); } } @@ -153,10 +150,7 @@ public final class StorageUserConnection { } Slog.i(TAG, "Waiting for session end " + session + " ..."); - prepareRemote(); - synchronized (mLock) { - mActiveConnection.endSessionLocked(session); - } + mActiveConnection.endSession(session); } /** Restarts all available sessions for a user without blocking. @@ -164,7 +158,7 @@ public final class StorageUserConnection { * Any failures will be ignored. **/ public void resetUserSessions() { - synchronized (mLock) { + synchronized (mSessionsLock) { if (mSessions.isEmpty()) { // Nothing to reset if we have no sessions to restart; we typically // hit this path if the user was consciously shut down. @@ -179,7 +173,7 @@ public final class StorageUserConnection { * Removes all sessions, without waiting. */ public void removeAllSessions() { - synchronized (mLock) { + synchronized (mSessionsLock) { Slog.i(TAG, "Removing " + mSessions.size() + " sessions for user: " + mUserId + "..."); mSessions.clear(); } @@ -191,68 +185,54 @@ public final class StorageUserConnection { */ public void close() { mActiveConnection.close(); - if (mIsDemoUser) { - mHandlerThread.quit(); - } + mHandlerThread.quit(); } /** Returns all created sessions. */ public Set<String> getAllSessionIds() { - synchronized (mLock) { + synchronized (mSessionsLock) { return new HashSet<>(mSessions.keySet()); } } - private void prepareRemote() throws ExternalStorageServiceException { - try { - waitForLatch(mActiveConnection.bind(), "remote_prepare_user " + mUserId); - } catch (IllegalStateException | TimeoutException e) { - throw new ExternalStorageServiceException("Failed to prepare remote", e); - } - } - - private void waitForLatch(CountDownLatch latch, String reason) throws TimeoutException { - try { - if (!latch.await(DEFAULT_REMOTE_TIMEOUT_SECONDS, TimeUnit.SECONDS)) { - // TODO(b/140025078): Call ActivityManager ANR API? - Slog.wtf(TAG, "Failed to bind to the ExternalStorageService for user " + mUserId); - throw new TimeoutException("Latch wait for " + reason + " elapsed"); - } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new IllegalStateException("Latch wait for " + reason + " interrupted"); - } + @FunctionalInterface + interface AsyncStorageServiceCall { + void run(@NonNull IExternalStorageService service, RemoteCallback callback) throws + RemoteException; } private final class ActiveConnection implements AutoCloseable { + private final Object mLock = new Object(); + // Lifecycle connection to the external storage service, needed to unbind. @GuardedBy("mLock") @Nullable private ServiceConnection mServiceConnection; - // True if we are connecting, either bound or binding - // False && mRemote != null means we are connected - // False && mRemote == null means we are neither connecting nor connected - @GuardedBy("mLock") @Nullable private boolean mIsConnecting; - // Binder object representing the external storage service. - // Non-null indicates we are connected - @GuardedBy("mLock") @Nullable private IExternalStorageService mRemote; - // Exception, if any, thrown from #startSessionLocked or #endSessionLocked - // Local variables cannot be referenced from a lambda expression :( so we - // save the exception received in the callback here. Since we guard access - // (and clear the exception state) with the same lock which we hold during - // the entire transaction, there is no risk of race. - @GuardedBy("mLock") @Nullable private ParcelableException mLastException; - // Not guarded by any lock intentionally and non final because we cannot - // reset latches so need to create a new one after one use - private CountDownLatch mLatch; + + // A future that holds the remote interface + @GuardedBy("mLock") + @Nullable private CompletableFuture<IExternalStorageService> mRemoteFuture; + + // A list of outstanding futures for async calls, for which we are still waiting + // for a callback. Used to unblock waiters if the service dies. + @GuardedBy("mLock") + private ArrayList<CompletableFuture<Void>> mOutstandingOps = new ArrayList<>(); @Override public void close() { ServiceConnection oldConnection = null; synchronized (mLock) { Slog.i(TAG, "Closing connection for user " + mUserId); - mIsConnecting = false; oldConnection = mServiceConnection; mServiceConnection = null; - mRemote = null; + if (mRemoteFuture != null) { + // Let folks who are waiting for the connection know it ain't gonna happen + mRemoteFuture.cancel(true); + mRemoteFuture = null; + } + // Let folks waiting for callbacks from the remote know it ain't gonna happen + for (CompletableFuture<Void> op : mOutstandingOps) { + op.cancel(true); + } + mOutstandingOps.clear(); } if (oldConnection != null) { @@ -266,37 +246,37 @@ public final class StorageUserConnection { } } - public boolean isActiveLocked(Session session) { - if (!session.isInitialisedLocked()) { - Slog.i(TAG, "Session not initialised " + session); - return false; - } + private void waitForAsync(AsyncStorageServiceCall asyncCall) throws Exception { + CompletableFuture<IExternalStorageService> serviceFuture = connectIfNeeded(); + CompletableFuture<Void> opFuture = new CompletableFuture<>(); - if (mRemote == null) { - throw new IllegalStateException("Valid session with inactive connection"); - } - return true; - } + try { + synchronized (mLock) { + mOutstandingOps.add(opFuture); + } + serviceFuture.thenCompose(service -> { + try { + asyncCall.run(service, + new RemoteCallback(result -> setResult(result, opFuture))); + } catch (RemoteException e) { + opFuture.completeExceptionally(e); + } - public void startSessionLocked(Session session, ParcelFileDescriptor fd) - throws ExternalStorageServiceException { - if (!isActiveLocked(session)) { - try { - fd.close(); - } catch (IOException e) { - // ignore + return opFuture; + }).get(DEFAULT_REMOTE_TIMEOUT_SECONDS, TimeUnit.SECONDS); + } finally { + synchronized (mLock) { + mOutstandingOps.remove(opFuture); } - return; } + } - CountDownLatch latch = new CountDownLatch(1); + public void startSession(Session session, ParcelFileDescriptor fd) + throws ExternalStorageServiceException { try { - mRemote.startSession(session.sessionId, + waitForAsync((service, callback) -> service.startSession(session.sessionId, FLAG_SESSION_TYPE_FUSE | FLAG_SESSION_ATTRIBUTE_INDEXABLE, - fd, session.upperPath, session.lowerPath, new RemoteCallback(result -> - setResultLocked(latch, result))); - waitForLatch(latch, "start_session " + session); - maybeThrowExceptionLocked(); + fd, session.upperPath, session.lowerPath, callback)); } catch (Exception e) { throw new ExternalStorageServiceException("Failed to start session: " + session, e); } finally { @@ -308,73 +288,49 @@ public final class StorageUserConnection { } } - public void endSessionLocked(Session session) throws ExternalStorageServiceException { - if (!isActiveLocked(session)) { - // Nothing to end, not started yet - return; - } - - CountDownLatch latch = new CountDownLatch(1); + public void endSession(Session session) throws ExternalStorageServiceException { try { - mRemote.endSession(session.sessionId, new RemoteCallback(result -> - setResultLocked(latch, result))); - waitForLatch(latch, "end_session " + session); - maybeThrowExceptionLocked(); + waitForAsync((service, callback) -> + service.endSession(session.sessionId, callback)); } catch (Exception e) { throw new ExternalStorageServiceException("Failed to end session: " + session, e); } } - public void notifyVolumeStateChangedLocked(String sessionId, StorageVolume vol) throws + + public void notifyVolumeStateChanged(String sessionId, StorageVolume vol) throws ExternalStorageServiceException { - CountDownLatch latch = new CountDownLatch(1); try { - mRemote.notifyVolumeStateChanged(sessionId, vol, new RemoteCallback( - result -> setResultLocked(latch, result))); - waitForLatch(latch, "notify_volume_state_changed " + vol); - maybeThrowExceptionLocked(); + waitForAsync((service, callback) -> + service.notifyVolumeStateChanged(sessionId, vol, callback)); } catch (Exception e) { throw new ExternalStorageServiceException("Failed to notify volume state changed " + "for vol : " + vol, e); } } - private void setResultLocked(CountDownLatch latch, Bundle result) { - mLastException = result.getParcelable(EXTRA_ERROR); - latch.countDown(); - } - - private void maybeThrowExceptionLocked() throws IOException { - if (mLastException != null) { - ParcelableException lastException = mLastException; - mLastException = null; - try { - lastException.maybeRethrow(IOException.class); - } catch (IOException e) { - throw e; - } - throw new RuntimeException(lastException); + private void setResult(Bundle result, CompletableFuture<Void> future) { + ParcelableException ex = result.getParcelable(EXTRA_ERROR); + if (ex != null) { + future.completeExceptionally(ex); + } else { + future.complete(null); } } - public CountDownLatch bind() throws ExternalStorageServiceException { + private CompletableFuture<IExternalStorageService> connectIfNeeded() throws + ExternalStorageServiceException { ComponentName name = mSessionController.getExternalStorageServiceComponentName(); if (name == null) { // Not ready to bind throw new ExternalStorageServiceException( "Not ready to bind to the ExternalStorageService for user " + mUserId); } - synchronized (mLock) { - if (mRemote != null || mIsConnecting) { - // Connected or connecting (bound or binding) - // Will wait on a latch that will countdown when we connect, unless we are - // connected and the latch has already countdown, yay! - return mLatch; - } // else neither connected nor connecting - - mLatch = new CountDownLatch(1); - mIsConnecting = true; + if (mRemoteFuture != null) { + return mRemoteFuture; + } + CompletableFuture<IExternalStorageService> future = new CompletableFuture<>(); mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { @@ -406,16 +362,9 @@ public final class StorageUserConnection { private void handleConnection(IBinder service) { synchronized (mLock) { - if (mIsConnecting) { - mRemote = IExternalStorageService.Stub.asInterface(service); - mIsConnecting = false; - mLatch.countDown(); - // Separate thread so we don't block the main thead - return; - } + future.complete( + IExternalStorageService.Stub.asInterface(service)); } - Slog.wtf(TAG, "Connection closed to the ExternalStorageService for user " - + mUserId); } private void handleDisconnection() { @@ -429,32 +378,19 @@ public final class StorageUserConnection { }; Slog.i(TAG, "Binding to the ExternalStorageService for user " + mUserId); - if (mIsDemoUser) { - // Schedule on a worker thread for demo user to avoid deadlock - if (mContext.bindServiceAsUser(new Intent().setComponent(name), - mServiceConnection, - Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, - mHandlerThread.getThreadHandler(), - UserHandle.of(mUserId))) { - Slog.i(TAG, "Bound to the ExternalStorageService for user " + mUserId); - return mLatch; - } else { - mIsConnecting = false; - throw new ExternalStorageServiceException( - "Failed to bind to the ExternalStorageService for user " + mUserId); - } + // Schedule on a worker thread, because the system server main thread can be + // very busy early in boot. + if (mContext.bindServiceAsUser(new Intent().setComponent(name), + mServiceConnection, + Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, + mHandlerThread.getThreadHandler(), + UserHandle.of(mUserId))) { + Slog.i(TAG, "Bound to the ExternalStorageService for user " + mUserId); + mRemoteFuture = future; + return future; } else { - if (mContext.bindServiceAsUser(new Intent().setComponent(name), - mServiceConnection, - Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, - UserHandle.of(mUserId))) { - Slog.i(TAG, "Bound to the ExternalStorageService for user " + mUserId); - return mLatch; - } else { - mIsConnecting = false; - throw new ExternalStorageServiceException( - "Failed to bind to the ExternalStorageService for user " + mUserId); - } + throw new ExternalStorageServiceException( + "Failed to bind to the ExternalStorageService for user " + mUserId); } } } @@ -476,10 +412,5 @@ public final class StorageUserConnection { return "[SessionId: " + sessionId + ". UpperPath: " + upperPath + ". LowerPath: " + lowerPath + "]"; } - - @GuardedBy("mLock") - public boolean isInitialisedLocked() { - return !TextUtils.isEmpty(upperPath) && !TextUtils.isEmpty(lowerPath); - } } } diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java index 59cebf769a4e..7de0e87f91c3 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java @@ -71,9 +71,8 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub { @NonNull private final TimeDetectorStrategy mTimeDetectorStrategy; private static TimeDetectorService create(@NonNull Context context) { - TimeDetectorStrategy timeDetectorStrategy = new TimeDetectorStrategyImpl(); - TimeDetectorStrategyCallbackImpl callback = new TimeDetectorStrategyCallbackImpl(context); - timeDetectorStrategy.initialize(callback); + TimeDetectorStrategyImpl.Callback callback = new TimeDetectorStrategyCallbackImpl(context); + TimeDetectorStrategy timeDetectorStrategy = new TimeDetectorStrategyImpl(callback); Handler handler = FgThread.getHandler(); TimeDetectorService timeDetectorService = @@ -133,7 +132,7 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub { /** Internal method for handling the auto time setting being changed. */ @VisibleForTesting public void handleAutoTimeDetectionChanged() { - mHandler.post(mTimeDetectorStrategy::handleAutoTimeDetectionChanged); + mHandler.post(mTimeDetectorStrategy::handleAutoTimeConfigChanged); } @Override diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java index e943978cfc14..f278ef68c536 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java @@ -16,6 +16,7 @@ package com.android.server.timedetector; +import android.annotation.IntDef; import android.annotation.NonNull; import android.app.timedetector.ManualTimeSuggestion; import android.app.timedetector.NetworkTimeSuggestion; @@ -25,6 +26,9 @@ import android.util.IndentingPrintWriter; import com.android.server.timezonedetector.Dumpable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * The interface for the class that implements the time detection algorithm used by the * {@link TimeDetectorService}. @@ -37,62 +41,41 @@ import com.android.server.timezonedetector.Dumpable; */ public interface TimeDetectorStrategy extends Dumpable { - /** - * The interface used by the strategy to interact with the surrounding service. - * - * <p>Note: Because the system properties-derived value {@link #isAutoTimeDetectionEnabled()} - * can be modified independently and from different threads (and processes!). its use is prone - * to race conditions. That will be true until the responsibility for setting their values is - * moved to {@link TimeDetectorStrategy}. There are similar issues with - * {@link #systemClockMillis()} while any process can modify the system clock. - */ - interface Callback { - - /** - * The absolute threshold below which the system clock need not be updated. i.e. if setting - * the system clock would adjust it by less than this (either backwards or forwards) then it - * need not be set. - */ - int systemClockUpdateThresholdMillis(); - - /** Returns true if automatic time detection is enabled. */ - boolean isAutoTimeDetectionEnabled(); + @IntDef({ ORIGIN_TELEPHONY, ORIGIN_MANUAL, ORIGIN_NETWORK }) + @Retention(RetentionPolicy.SOURCE) + @interface Origin {} - /** Acquire a suitable wake lock. Must be followed by {@link #releaseWakeLock()} */ - void acquireWakeLock(); + /** Used when a time value originated from a telephony signal. */ + @Origin + int ORIGIN_TELEPHONY = 1; - /** Returns the elapsedRealtimeMillis clock value. */ - long elapsedRealtimeMillis(); + /** Used when a time value originated from a user / manual settings. */ + @Origin + int ORIGIN_MANUAL = 2; - /** Returns the system clock value. */ - long systemClockMillis(); + /** Used when a time value originated from a network signal. */ + @Origin + int ORIGIN_NETWORK = 3; - /** Sets the device system clock. The WakeLock must be held. */ - void setSystemClock(long newTimeMillis); - - /** Release the wake lock acquired by a call to {@link #acquireWakeLock()}. */ - void releaseWakeLock(); - } - - /** Initialize the strategy. */ - void initialize(@NonNull Callback callback); - - /** Process the suggested time from telephony sources. */ + /** Processes the suggested time from telephony sources. */ void suggestTelephonyTime(@NonNull TelephonyTimeSuggestion timeSuggestion); /** - * Process the suggested manually entered time. Returns {@code false} if the suggestion was + * Processes the suggested manually entered time. Returns {@code false} if the suggestion was * invalid, or the device configuration prevented the suggestion being used, {@code true} if the * suggestion was accepted. A suggestion that is valid but does not change the time because it * matches the current device time is considered accepted. */ boolean suggestManualTime(@NonNull ManualTimeSuggestion timeSuggestion); - /** Process the suggested time from network sources. */ + /** Processes the suggested time from network sources. */ void suggestNetworkTime(@NonNull NetworkTimeSuggestion timeSuggestion); - /** Handle the auto-time setting being toggled on or off. */ - void handleAutoTimeDetectionChanged(); + /** + * Handles the auto-time configuration changing For example, when the auto-time setting is + * toggled on or off. + */ + void handleAutoTimeConfigChanged(); // Utility methods below are to be moved to a better home when one becomes more obvious. @@ -104,4 +87,38 @@ public interface TimeDetectorStrategy extends Dumpable { return (referenceClockMillisNow - timeValue.getReferenceTimeMillis()) + timeValue.getValue(); } + + /** + * Converts one of the {@code ORIGIN_} constants to a human readable string suitable for config + * and debug usage. Throws an {@link IllegalArgumentException} if the value is unrecognized. + */ + static String originToString(@Origin int origin) { + switch (origin) { + case ORIGIN_MANUAL: + return "manual"; + case ORIGIN_NETWORK: + return "network"; + case ORIGIN_TELEPHONY: + return "telephony"; + default: + throw new IllegalArgumentException("origin=" + origin); + } + } + + /** + * Converts a human readable config string to one of the {@code ORIGIN_} constants. + * Throws an {@link IllegalArgumentException} if the value is unrecognized. + */ + static @Origin int stringToOrigin(String originString) { + switch (originString) { + case "manual": + return ORIGIN_MANUAL; + case "network": + return ORIGIN_NETWORK; + case "telephony": + return ORIGIN_TELEPHONY; + default: + throw new IllegalArgumentException("originString=" + originString); + } + } } diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java index 19484db149b1..5b6de0518999 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java @@ -16,28 +16,40 @@ package com.android.server.timedetector; +import static com.android.server.timedetector.TimeDetectorStrategy.stringToOrigin; + import android.annotation.NonNull; import android.app.AlarmManager; import android.content.ContentResolver; import android.content.Context; +import android.os.Build; +import android.os.Environment; import android.os.PowerManager; import android.os.SystemClock; import android.os.SystemProperties; import android.provider.Settings; import android.util.Slog; +import java.time.Instant; import java.util.Objects; /** - * The real implementation of {@link TimeDetectorStrategy.Callback} used on device. + * The real implementation of {@link TimeDetectorStrategyImpl.Callback} used on device. */ -public final class TimeDetectorStrategyCallbackImpl implements TimeDetectorStrategy.Callback { +public final class TimeDetectorStrategyCallbackImpl implements TimeDetectorStrategyImpl.Callback { private final static String TAG = "timedetector.TimeDetectorStrategyCallbackImpl"; private static final int SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS_DEFAULT = 2 * 1000; /** + * Time in the past. If automatic time suggestion is before this point, it's + * incorrect for sure. + */ + private static final Instant TIME_LOWER_BOUND = Instant.ofEpochMilli( + Long.max(Environment.getRootDirectory().lastModified(), Build.TIME)); + + /** * If a newly calculated system clock time and the current system clock time differs by this or * more the system clock will actually be updated. Used to prevent the system clock being set * for only minor differences. @@ -48,6 +60,7 @@ public final class TimeDetectorStrategyCallbackImpl implements TimeDetectorStrat @NonNull private final ContentResolver mContentResolver; @NonNull private final PowerManager.WakeLock mWakeLock; @NonNull private final AlarmManager mAlarmManager; + @NonNull private final int[] mOriginPriorities; public TimeDetectorStrategyCallbackImpl(@NonNull Context context) { mContext = Objects.requireNonNull(context); @@ -62,6 +75,15 @@ public final class TimeDetectorStrategyCallbackImpl implements TimeDetectorStrat mSystemClockUpdateThresholdMillis = SystemProperties.getInt("ro.sys.time_detector_update_diff", SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS_DEFAULT); + + // TODO(b/172230856): Obtain these values from configuration. + String[] originStrings = { "telephony", "network" }; + int[] origins = new int[originStrings.length]; + for (int i = 0; i < originStrings.length; i++) { + int origin = stringToOrigin(originStrings[i]); + origins[i] = origin; + } + mOriginPriorities = origins; } @Override @@ -79,6 +101,16 @@ public final class TimeDetectorStrategyCallbackImpl implements TimeDetectorStrat } @Override + public Instant autoTimeLowerBound() { + return TIME_LOWER_BOUND; + } + + @Override + public int[] getAutoOriginPriorities() { + return mOriginPriorities; + } + + @Override public void acquireWakeLock() { if (mWakeLock.isHeld()) { Slog.wtf(TAG, "WakeLock " + mWakeLock + " already held"); diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java index 9c18aadb79ec..d8cada55781c 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java @@ -16,7 +16,8 @@ package com.android.server.timedetector; -import android.annotation.IntDef; +import static com.android.server.timedetector.TimeDetectorStrategy.originToString; + import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AlarmManager; @@ -33,8 +34,8 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.server.timezonedetector.ArrayMapWithHistory; import com.android.server.timezonedetector.ReferenceWithHistory; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; +import java.time.Instant; +import java.util.Arrays; /** * An implementation of {@link TimeDetectorStrategy} that passes telephony and manual suggestions to @@ -62,22 +63,6 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { static final long MAX_UTC_TIME_AGE_MILLIS = TELEPHONY_BUCKET_COUNT * TELEPHONY_BUCKET_SIZE_MILLIS; - @IntDef({ ORIGIN_TELEPHONY, ORIGIN_MANUAL, ORIGIN_NETWORK }) - @Retention(RetentionPolicy.SOURCE) - public @interface Origin {} - - /** Used when a time value originated from a telephony signal. */ - @Origin - private static final int ORIGIN_TELEPHONY = 1; - - /** Used when a time value originated from a user / manual settings. */ - @Origin - private static final int ORIGIN_MANUAL = 2; - - /** Used when a time value originated from a network signal. */ - @Origin - private static final int ORIGIN_NETWORK = 3; - /** * CLOCK_PARANOIA: The maximum difference allowed between the expected system clock time and the * actual system clock time before a warning is logged. Used to help identify situations where @@ -98,8 +83,8 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { @NonNull private final LocalLog mTimeChangesLog = new LocalLog(30, false /* useLocalTimestamps */); - // @NonNull after initialize() - private Callback mCallback; + @NonNull + private final Callback mCallback; // Used to store the last time the system clock state was set automatically. It is used to // detect (and log) issues with the realtime clock or whether the clock is being set without @@ -121,8 +106,59 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { private final ReferenceWithHistory<NetworkTimeSuggestion> mLastNetworkSuggestion = new ReferenceWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE); - @Override - public void initialize(@NonNull Callback callback) { + /** + * The interface used by the strategy to interact with the surrounding service. + * + * <p>Note: Because the system properties-derived value {@link #isAutoTimeDetectionEnabled()} + * can be modified independently and from different threads (and processes!), its use is prone + * to race conditions. That will be true until the responsibility for setting their values is + * moved to {@link TimeDetectorStrategy}. There are similar issues with + * {@link #systemClockMillis()} while any process can modify the system clock. + */ + public interface Callback { + + /** + * The absolute threshold below which the system clock need not be updated. i.e. if setting + * the system clock would adjust it by less than this (either backwards or forwards) then it + * need not be set. + */ + int systemClockUpdateThresholdMillis(); + + /** Returns true if automatic time detection is enabled. */ + boolean isAutoTimeDetectionEnabled(); + + /** + * Returns a lower bound for valid automatic times. It is guaranteed to be in the past, + * i.e. it is unrelated to the current system clock time. + * It holds no other meaning; it could be related to when the device system image was built, + * or could be updated by a mainline module. + */ + @NonNull + Instant autoTimeLowerBound(); + + /** + * Returns the order to look at time suggestions when automatically detecting time. + * See {@code #ORIGIN_} constants + */ + @Origin int[] getAutoOriginPriorities(); + + /** Acquire a suitable wake lock. Must be followed by {@link #releaseWakeLock()} */ + void acquireWakeLock(); + + /** Returns the elapsedRealtimeMillis clock value. */ + long elapsedRealtimeMillis(); + + /** Returns the system clock value. */ + long systemClockMillis(); + + /** Sets the device system clock. The WakeLock must be held. */ + void setSystemClock(long newTimeMillis); + + /** Release the wake lock acquired by a call to {@link #acquireWakeLock()}. */ + void releaseWakeLock(); + } + + TimeDetectorStrategyImpl(@NonNull Callback callback) { mCallback = callback; } @@ -140,7 +176,7 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { @Override public synchronized void suggestNetworkTime(@NonNull NetworkTimeSuggestion timeSuggestion) { - if (!validateSuggestionTime(timeSuggestion.getUtcTime(), timeSuggestion)) { + if (!validateAutoSuggestionTime(timeSuggestion.getUtcTime(), timeSuggestion)) { return; } @@ -174,9 +210,12 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { return; } - // Perform validation / input filtering and record the validated suggestion against the - // slotIndex. - if (!validateAndStoreTelephonySuggestion(timeSuggestion)) { + if (!validateAutoSuggestionTime(timeSuggestion.getUtcTime(), timeSuggestion)) { + return; + } + + // Perform input filtering and record the validated suggestion against the slotIndex. + if (!storeTelephonySuggestion(timeSuggestion)) { return; } @@ -187,12 +226,12 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { } @Override - public synchronized void handleAutoTimeDetectionChanged() { + public synchronized void handleAutoTimeConfigChanged() { boolean enabled = mCallback.isAutoTimeDetectionEnabled(); // When automatic time detection is enabled we update the system clock instantly if we can. // Conversely, when automatic time detection is disabled we leave the clock as it is. if (enabled) { - String reason = "Auto time zone detection setting enabled."; + String reason = "Auto time zone detection config changed."; doAutoTimeDetection(reason); } else { // CLOCK_PARANOIA: We are losing "control" of the system clock so we cannot predict what @@ -233,14 +272,9 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { } @GuardedBy("this") - private boolean validateAndStoreTelephonySuggestion( + private boolean storeTelephonySuggestion( @NonNull TelephonyTimeSuggestion suggestion) { TimestampedValue<Long> newUtcTime = suggestion.getUtcTime(); - if (!validateSuggestionTime(newUtcTime, suggestion)) { - // There's probably nothing useful we can do: elsewhere we assume that reference - // times are in the past so just stop here. - return false; - } int slotIndex = suggestion.getSlotIndex(); TelephonyTimeSuggestion previousSuggestion = mSuggestionBySlotIndex.get(slotIndex); @@ -291,6 +325,26 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { return true; } + private boolean validateAutoSuggestionTime( + @NonNull TimestampedValue<Long> newUtcTime, @NonNull Object suggestion) { + return validateSuggestionTime(newUtcTime, suggestion) + && validateSuggestionAgainstLowerBound(newUtcTime, suggestion); + } + + private boolean validateSuggestionAgainstLowerBound( + @NonNull TimestampedValue<Long> newUtcTime, @NonNull Object suggestion) { + Instant lowerBound = mCallback.autoTimeLowerBound(); + + // Suggestion is definitely wrong if it comes before lower time bound. + if (lowerBound.isAfter(Instant.ofEpochMilli(newUtcTime.getValue()))) { + Slog.w(LOG_TAG, "Suggestion points to time before lower bound, skipping it. " + + "suggestion=" + suggestion + ", lower bound=" + lowerBound); + return false; + } + + return true; + } + @GuardedBy("this") private void doAutoTimeDetection(@NonNull String detectionReason) { if (!mCallback.isAutoTimeDetectionEnabled()) { @@ -298,33 +352,44 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { return; } - // Android devices currently prioritize any telephony over network signals. There are - // carrier compliance tests that would need to be changed before we could ignore NITZ or - // prefer NTP generally. This check is cheap on devices without telephony hardware. - TelephonyTimeSuggestion bestTelephonySuggestion = findBestTelephonySuggestion(); - if (bestTelephonySuggestion != null) { - final TimestampedValue<Long> newUtcTime = bestTelephonySuggestion.getUtcTime(); - String cause = "Found good telephony suggestion." - + ", bestTelephonySuggestion=" + bestTelephonySuggestion - + ", detectionReason=" + detectionReason; - setSystemClockIfRequired(ORIGIN_TELEPHONY, newUtcTime, cause); - return; - } + // Try the different origins one at a time. + int[] originPriorities = mCallback.getAutoOriginPriorities(); + for (int origin : originPriorities) { + TimestampedValue<Long> newUtcTime = null; + String cause = null; + if (origin == ORIGIN_TELEPHONY) { + TelephonyTimeSuggestion bestTelephonySuggestion = findBestTelephonySuggestion(); + if (bestTelephonySuggestion != null) { + newUtcTime = bestTelephonySuggestion.getUtcTime(); + cause = "Found good telephony suggestion." + + ", bestTelephonySuggestion=" + bestTelephonySuggestion + + ", detectionReason=" + detectionReason; + } + } else if (origin == ORIGIN_NETWORK) { + NetworkTimeSuggestion networkSuggestion = findLatestValidNetworkSuggestion(); + if (networkSuggestion != null) { + newUtcTime = networkSuggestion.getUtcTime(); + cause = "Found good network suggestion." + + ", networkSuggestion=" + networkSuggestion + + ", detectionReason=" + detectionReason; + } + } else { + Slog.w(LOG_TAG, "Unknown or unsupported origin=" + origin + + " in " + Arrays.toString(originPriorities) + + ": Skipping"); + } - // There is no good telephony suggestion, try network. - NetworkTimeSuggestion networkSuggestion = findLatestValidNetworkSuggestion(); - if (networkSuggestion != null) { - final TimestampedValue<Long> newUtcTime = networkSuggestion.getUtcTime(); - String cause = "Found good network suggestion." - + ", networkSuggestion=" + networkSuggestion - + ", detectionReason=" + detectionReason; - setSystemClockIfRequired(ORIGIN_NETWORK, newUtcTime, cause); - return; + // Update the system clock if a good suggestion has been found. + if (newUtcTime != null) { + setSystemClockIfRequired(origin, newUtcTime, cause); + return; + } } if (DBG) { - Slog.d(LOG_TAG, "Could not determine time: No best telephony or network suggestion." - + " detectionReason=" + detectionReason); + Slog.d(LOG_TAG, "Could not determine time: No suggestion found in" + + " originPriorities=" + Arrays.toString(originPriorities) + + ", detectionReason=" + detectionReason); } } @@ -409,7 +474,7 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { // Validate first. TimestampedValue<Long> utcTime = timeSuggestion.getUtcTime(); if (!validateSuggestionUtcTime(elapsedRealtimeMillis, utcTime)) { - Slog.w(LOG_TAG, "Existing suggestion found to be invalid " + Slog.w(LOG_TAG, "Existing suggestion found to be invalid" + " elapsedRealtimeMillis=" + elapsedRealtimeMillis + ", timeSuggestion=" + timeSuggestion); return TELEPHONY_INVALID_SCORE; @@ -458,7 +523,7 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { if (!mCallback.isAutoTimeDetectionEnabled()) { if (DBG) { Slog.d(LOG_TAG, "Auto time detection is not enabled." - + " origin=" + origin + + " origin=" + originToString(origin) + ", time=" + time + ", cause=" + cause); } @@ -468,7 +533,7 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { if (mCallback.isAutoTimeDetectionEnabled()) { if (DBG) { Slog.d(LOG_TAG, "Auto time detection is enabled." - + " origin=" + origin + + " origin=" + originToString(origin) + ", time=" + time + ", cause=" + cause); } @@ -490,7 +555,7 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { @GuardedBy("this") private boolean setSystemClockUnderWakeLock( - int origin, @NonNull TimestampedValue<Long> newTime, @NonNull Object cause) { + @Origin int origin, @NonNull TimestampedValue<Long> newTime, @NonNull String cause) { long elapsedRealtimeMillis = mCallback.elapsedRealtimeMillis(); boolean isOriginAutomatic = isOriginAutomatic(origin); diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index afbe5527a5cb..510893b2940b 100755 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -87,6 +87,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.content.PackageMonitor; import com.android.internal.os.SomeArgs; import com.android.internal.util.DumpUtils; +import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.IndentingPrintWriter; import com.android.server.IoThread; import com.android.server.SystemService; @@ -337,6 +338,7 @@ public final class TvInputManagerService extends SystemService { inputState = new TvInputState(); } inputState.info = info; + inputState.uid = getInputUid(info); inputMap.put(info.getId(), inputState); } @@ -371,6 +373,16 @@ public final class TvInputManagerService extends SystemService { userState.inputMap = inputMap; } + private int getInputUid(TvInputInfo info) { + try { + return getContext().getPackageManager().getApplicationInfo( + info.getServiceInfo().packageName, 0).uid; + } catch (NameNotFoundException e) { + Slog.w(TAG, "Unable to get UID for " + info, e); + return Process.INVALID_UID; + } + } + @GuardedBy("mLock") private void buildTvContentRatingSystemListLocked(int userId) { UserState userState = getOrCreateUserStateLocked(userId); @@ -405,7 +417,7 @@ public final class TvInputManagerService extends SystemService { return; } if (mUserStates.contains(mCurrentUserId)) { - UserState userState = mUserStates.get(mCurrentUserId); + UserState userState = getUserStateLocked(mCurrentUserId); List<SessionState> sessionStatesToRelease = new ArrayList<>(); for (SessionState sessionState : userState.sessionStateMap.values()) { if (sessionState.session != null && !sessionState.isRecordingSession) { @@ -474,7 +486,7 @@ public final class TvInputManagerService extends SystemService { private void removeUser(int userId) { synchronized (mLock) { - UserState userState = mUserStates.get(userId); + UserState userState = getUserStateLocked(userId); if (userState == null) { return; } @@ -535,7 +547,7 @@ public final class TvInputManagerService extends SystemService { @GuardedBy("mLock") private UserState getOrCreateUserStateLocked(int userId) { - UserState userState = mUserStates.get(userId); + UserState userState = getUserStateLocked(userId); if (userState == null) { userState = new UserState(mContext, userId); mUserStates.put(userId, userState); @@ -715,7 +727,8 @@ public final class TvInputManagerService extends SystemService { } @GuardedBy("mLock") - private void releaseSessionLocked(IBinder sessionToken, int callingUid, int userId) { + @Nullable + private SessionState releaseSessionLocked(IBinder sessionToken, int callingUid, int userId) { SessionState sessionState = null; try { sessionState = getSessionStateLocked(sessionToken, callingUid, userId); @@ -738,6 +751,7 @@ public final class TvInputManagerService extends SystemService { } } removeSessionStateLocked(sessionToken, userId); + return sessionState; } @GuardedBy("mLock") @@ -908,6 +922,7 @@ public final class TvInputManagerService extends SystemService { return; } inputState.info = inputInfo; + inputState.uid = getInputUid(inputInfo); int n = userState.mCallbacks.beginBroadcast(); for (int i = 0; i < n; ++i) { @@ -1248,7 +1263,22 @@ public final class TvInputManagerService extends SystemService { final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId, "createSession"); final long identity = Binder.clearCallingIdentity(); - // Generate a unique session id with a random UUID. + /** + * A randomly generated id for this this session. + * + * <p>This field contains no user or device reference and is large enough to be + * effectively globally unique. + * + * <p><b>WARNING</b> Any changes to this field should be carefully reviewed for privacy. + * Inspect the code at: + * + * <ul> + * <li>framework/base/cmds/statsd/src/atoms.proto#TifTuneState + * <li>{@link #logTuneStateChanged} + * <li>{@link TvInputManagerService.BinderService#createSession} + * <li>{@link SessionState#sessionId} + * </ul> + */ String uniqueSessionId = UUID.randomUUID().toString(); try { synchronized (mLock) { @@ -1269,6 +1299,8 @@ public final class TvInputManagerService extends SystemService { TvInputInfo info = inputState.info; ServiceState serviceState = userState.serviceStateMap.get(info.getComponent()); if (serviceState == null) { + int tisUid = PackageManager.getApplicationInfoAsUserCached( + info.getComponent().getPackageName(), 0, resolvedUserId).uid; serviceState = new ServiceState(info.getComponent(), resolvedUserId); userState.serviceStateMap.put(info.getComponent(), serviceState); } @@ -1301,6 +1333,8 @@ public final class TvInputManagerService extends SystemService { } else { updateServiceConnectionLocked(info.getComponent(), resolvedUserId); } + logTuneStateChanged(FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__CREATED, + sessionState, inputState); } } finally { Binder.restoreCallingIdentity(identity); @@ -1317,8 +1351,17 @@ public final class TvInputManagerService extends SystemService { userId, "releaseSession"); final long identity = Binder.clearCallingIdentity(); try { + SessionState sessionState = null; + UserState userState = null; synchronized (mLock) { - releaseSessionLocked(sessionToken, callingUid, resolvedUserId); + sessionState = releaseSessionLocked(sessionToken, callingUid, resolvedUserId); + userState = getUserStateLocked(userId); + } + if (sessionState != null) { + TvInputState tvInputState = TvInputManagerService.getTvInputState(sessionState, + userState); + logTuneStateChanged(FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__RELEASED, + sessionState, tvInputState); } } finally { Binder.restoreCallingIdentity(identity); @@ -1372,10 +1415,13 @@ public final class TvInputManagerService extends SystemService { final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, userId, "setSurface"); final long identity = Binder.clearCallingIdentity(); + SessionState sessionState = null; + UserState userState = null; try { synchronized (mLock) { try { - SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, + userState = getUserStateLocked(userId); + sessionState = getSessionStateLocked(sessionToken, callingUid, resolvedUserId); if (sessionState.hardwareSessionToken == null) { getSessionLocked(sessionState).setSurface(surface); @@ -1392,6 +1438,14 @@ public final class TvInputManagerService extends SystemService { // surface is not used in TvInputManagerService. surface.release(); } + if (sessionState != null) { + int state = surface == null + ? + FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__SURFACE_ATTACHED + : FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__SURFACE_DETACHED; + logTuneStateChanged(state, sessionState, + TvInputManagerService.getTvInputState(sessionState, userState)); + } Binder.restoreCallingIdentity(identity); } } @@ -1479,6 +1533,10 @@ public final class TvInputManagerService extends SystemService { return; } + logTuneStateChanged( + FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__TUNE_STARTED, + sessionState, + TvInputManagerService.getTvInputState(sessionState, userState)); // Log the start of watch. SomeArgs args = SomeArgs.obtain(); args.arg1 = sessionState.componentName.getPackageName(); @@ -2302,6 +2360,16 @@ public final class TvInputManagerService extends SystemService { } } + @Nullable + private static TvInputState getTvInputState( + SessionState sessionState, + @Nullable UserState userState) { + if (userState != null) { + return userState.inputMap.get(sessionState.inputId); + } + return null; + } + @GuardedBy("mLock") private List<TunedInfo> getCurrentTunedInfosInternalLocked( UserState userState, int callingPid, int callingUid) { @@ -2367,6 +2435,28 @@ public final class TvInputManagerService extends SystemService { } } + /** + * Log Tune state changes to {@link FrameworkStatsLog}. + * + * <p><b>WARNING</b> Any changes to this field should be carefully reviewed for privacy. + * Inspect the code at: + * + * <ul> + * <li>framework/base/cmds/statsd/src/atoms.proto#TifTuneState + * <li>{@link #logTuneStateChanged} + * <li>{@link TvInputManagerService.BinderService#createSession} + * <li>{@link SessionState#sessionId} + * </ul> + */ + private void logTuneStateChanged(int state, SessionState sessionState, + @Nullable TvInputState inputState) { + // TODO(b/173536904): log input type and id + FrameworkStatsLog.write(FrameworkStatsLog.TIF_TUNE_CHANGED, + new int[]{sessionState.callingUid, + inputState == null ? Process.INVALID_UID : inputState.uid}, + new String[]{"tif_player", "tv_input_service"}, state, sessionState.sessionId); + } + private static final class UserState { // A mapping from the TV input id to its TvInputState. private Map<String, TvInputState> inputMap = new HashMap<>(); @@ -2464,10 +2554,25 @@ public final class TvInputManagerService extends SystemService { } private static final class TvInputState { - // A TvInputInfo object which represents the TV input. + + /** A TvInputInfo object which represents the TV input. */ private TvInputInfo info; - // The state of TV input. Connected by default. + /** + * The kernel user-ID that has been assigned to the application the TvInput is a part of. + * + * <p> + * Currently this is not a unique ID (multiple applications can have + * the same uid). + */ + private int uid; + + /** + * The state of TV input. + * + * <p> + * Connected by default + */ private int state = INPUT_STATE_CONNECTED; @Override @@ -2478,6 +2583,23 @@ public final class TvInputManagerService extends SystemService { private final class SessionState implements IBinder.DeathRecipient { private final String inputId; + + /** + * A randomly generated id for this this session. + * + * <p>This field contains no user or device reference and is large enough to be + * effectively globally unique. + * + * <p><b>WARNING</b> Any changes to this field should be carefully reviewed for privacy. + * Inspect the code at: + * + * <ul> + * <li>framework/base/cmds/statsd/src/atoms.proto#TifTuneState + * <li>{@link #logTuneStateChanged} + * <li>{@link TvInputManagerService.BinderService#createSession} + * <li>{@link SessionState#sessionId} + * </ul> + */ private final String sessionId; private final ComponentName componentName; private final boolean isRecordingSession; @@ -2545,7 +2667,7 @@ public final class TvInputManagerService extends SystemService { Slog.d(TAG, "onServiceConnected(component=" + component + ")"); } synchronized (mLock) { - UserState userState = mUserStates.get(mUserId); + UserState userState = getUserStateLocked(mUserId); if (userState == null) { // The user was removed while connecting. mContext.unbindService(this); @@ -2815,8 +2937,13 @@ public final class TvInputManagerService extends SystemService { if (mSessionState.session == null || mSessionState.client == null) { return; } + TvInputState tvInputState = getTvInputState(mSessionState, + getUserStateLocked(mCurrentUserId)); try { mSessionState.client.onVideoAvailable(mSessionState.seq); + logTuneStateChanged( + FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__VIDEO_AVAILABLE, + mSessionState, tvInputState); } catch (RemoteException e) { Slog.e(TAG, "error in onVideoAvailable", e); } @@ -2832,8 +2959,20 @@ public final class TvInputManagerService extends SystemService { if (mSessionState.session == null || mSessionState.client == null) { return; } + TvInputState tvInputState = getTvInputState(mSessionState, + getUserStateLocked(mCurrentUserId)); try { mSessionState.client.onVideoUnavailable(reason, mSessionState.seq); + int loggedReason = reason + FrameworkStatsLog + .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_UNKNOWN; + if (loggedReason < FrameworkStatsLog + .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_UNKNOWN + || loggedReason > FrameworkStatsLog + .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_CAS_UNKNOWN) { + loggedReason = FrameworkStatsLog + .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_UNKNOWN; + } + logTuneStateChanged(loggedReason, mSessionState, tvInputState); } catch (RemoteException e) { Slog.e(TAG, "error in onVideoUnavailable", e); } @@ -3019,6 +3158,10 @@ public final class TvInputManagerService extends SystemService { } } + private UserState getUserStateLocked(int userId) { + return mUserStates.get(userId); + } + private static final class WatchLogHandler extends Handler { // There are only two kinds of watch events that can happen on the system: // 1. The current TV input session is tuned to a new channel. diff --git a/services/core/java/com/android/server/uri/TEST_MAPPING b/services/core/java/com/android/server/uri/TEST_MAPPING index e5cda03be25d..a9525f4a82ad 100644 --- a/services/core/java/com/android/server/uri/TEST_MAPPING +++ b/services/core/java/com/android/server/uri/TEST_MAPPING @@ -7,6 +7,26 @@ "include-filter": "com.android.server.uri." } ] + }, + { + "name": "CtsAppSecurityHostTestCases", + "options": [ + { + "include-filter": "android.appsecurity.cts.ExternalStorageHostTest#testGrantUriPermission" + }, + { + "include-filter": "android.appsecurity.cts.ExternalStorageHostTest#testGrantUriPermission29" + }, + { + "include-filter": "android.appsecurity.cts.ExternalStorageHostTest#testMediaNone" + }, + { + "include-filter": "android.appsecurity.cts.ExternalStorageHostTest#testMediaNone28" + }, + { + "include-filter": "android.appsecurity.cts.ExternalStorageHostTest#testMediaNone29" + } + ] } ], "postsubmit": [ diff --git a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java new file mode 100644 index 000000000000..e1feb5aab869 --- /dev/null +++ b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.vcn; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.LinkProperties; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.os.Handler; +import android.os.ParcelUuid; + +import java.util.Objects; + +/** + * Tracks a set of Networks underpinning a VcnGatewayConnection. + * + * <p>A single UnderlyingNetworkTracker is built to serve a SINGLE VCN Gateway Connection, and MUST + * be torn down with the VcnGatewayConnection in order to ensure underlying networks are allowed to + * be reaped. + * + * @hide + */ +public class UnderlyingNetworkTracker extends Handler { + @NonNull private static final String TAG = UnderlyingNetworkTracker.class.getSimpleName(); + + @NonNull private final VcnContext mVcnContext; + @NonNull private final ParcelUuid mSubscriptionGroup; + @NonNull private final UnderlyingNetworkTrackerCallback mCb; + @NonNull private final Dependencies mDeps; + + public UnderlyingNetworkTracker( + @NonNull VcnContext vcnContext, + @NonNull ParcelUuid subscriptionGroup, + @NonNull UnderlyingNetworkTrackerCallback cb) { + this(vcnContext, subscriptionGroup, cb, new Dependencies()); + } + + private UnderlyingNetworkTracker( + @NonNull VcnContext vcnContext, + @NonNull ParcelUuid subscriptionGroup, + @NonNull UnderlyingNetworkTrackerCallback cb, + @NonNull Dependencies deps) { + super(Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper()); + mVcnContext = vcnContext; + mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup"); + mCb = Objects.requireNonNull(cb, "Missing cb"); + mDeps = Objects.requireNonNull(deps, "Missing deps"); + } + + /** Tears down this Tracker, and releases all underlying network requests. */ + public void teardown() {} + + /** An record of a single underlying network, caching relevant fields. */ + public static class UnderlyingNetworkRecord { + @NonNull public final Network network; + @NonNull public final NetworkCapabilities networkCapabilities; + @NonNull public final LinkProperties linkProperties; + public final boolean blocked; + + private UnderlyingNetworkRecord( + @NonNull Network network, + @NonNull NetworkCapabilities networkCapabilities, + @NonNull LinkProperties linkProperties, + boolean blocked) { + this.network = network; + this.networkCapabilities = networkCapabilities; + this.linkProperties = linkProperties; + this.blocked = blocked; + } + } + + /** Callbacks for being notified of the changes in, or to the selected underlying network. */ + public interface UnderlyingNetworkTrackerCallback { + /** + * Fired when a new underlying network is selected, or properties have changed. + * + * <p>This callback does NOT signal a mobility event. + * + * @param underlying The details of the new underlying network + */ + void onSelectedUnderlyingNetworkChanged(@Nullable UnderlyingNetworkRecord underlying); + } + + private static class Dependencies {} +} diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java new file mode 100644 index 000000000000..d51d16b1b4df --- /dev/null +++ b/services/core/java/com/android/server/vcn/Vcn.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.vcn; + +import android.annotation.NonNull; +import android.net.NetworkRequest; +import android.net.vcn.VcnConfig; +import android.os.Handler; +import android.os.Message; +import android.os.ParcelUuid; + +import java.util.Objects; + +/** + * Represents an single instance of a VCN. + * + * <p>Each Vcn instance manages all tunnels for a given subscription group, including per-capability + * networks, network selection, and multi-homing. + * + * @hide + */ +public class Vcn extends Handler { + private static final String TAG = Vcn.class.getSimpleName(); + + @NonNull private final VcnContext mVcnContext; + @NonNull private final ParcelUuid mSubscriptionGroup; + @NonNull private final Dependencies mDeps; + + @NonNull private VcnConfig mConfig; + + public Vcn( + @NonNull VcnContext vcnContext, + @NonNull ParcelUuid subscriptionGroup, + @NonNull VcnConfig config) { + this(vcnContext, subscriptionGroup, config, new Dependencies()); + } + + private Vcn( + @NonNull VcnContext vcnContext, + @NonNull ParcelUuid subscriptionGroup, + @NonNull VcnConfig config, + @NonNull Dependencies deps) { + super(Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper()); + mVcnContext = vcnContext; + mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup"); + mDeps = Objects.requireNonNull(deps, "Missing deps"); + + mConfig = Objects.requireNonNull(config, "Missing config"); + } + + /** Asynchronously updates the configuration and triggers a re-evaluation of Networks */ + public void updateConfig(@NonNull VcnConfig config) { + Objects.requireNonNull(config, "Missing config"); + // TODO: Proxy to handler, and make config there. + } + + /** Asynchronously tears down this Vcn instance, along with all tunnels and Networks */ + public void teardown() { + // TODO: Proxy to handler, and teardown there. + } + + /** Notifies this Vcn instance of a new NetworkRequest */ + public void onNetworkRequested(@NonNull NetworkRequest request, int score, int providerId) { + Objects.requireNonNull(request, "Missing request"); + + // TODO: Proxy to handler, and handle there. + } + + @Override + public void handleMessage(@NonNull Message msg) { + // TODO: Do something + } + + /** Retrieves the network score for a VCN Network */ + private int getNetworkScore() { + // TODO: STOPSHIP (b/173549607): Make this use new NetworkSelection, or some magic "max in + // subGrp" value + return 52; + } + + private static class Dependencies {} +} diff --git a/services/core/java/com/android/server/vcn/VcnContext.java b/services/core/java/com/android/server/vcn/VcnContext.java new file mode 100644 index 000000000000..8ab52931cae3 --- /dev/null +++ b/services/core/java/com/android/server/vcn/VcnContext.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.vcn; + +import android.annotation.NonNull; +import android.content.Context; +import android.os.Looper; + +import com.android.server.VcnManagementService.VcnNetworkProvider; + +import java.util.Objects; + +/** + * A simple class to pass around context information. + * + * @hide + */ +public class VcnContext { + @NonNull private final Context mContext; + @NonNull private final Looper mLooper; + @NonNull private final VcnNetworkProvider mVcnNetworkProvider; + + public VcnContext( + @NonNull Context context, + @NonNull Looper looper, + @NonNull VcnNetworkProvider vcnNetworkProvider) { + mContext = Objects.requireNonNull(context, "Missing context"); + mLooper = Objects.requireNonNull(looper, "Missing looper"); + mVcnNetworkProvider = Objects.requireNonNull(vcnNetworkProvider, "Missing networkProvider"); + } + + @NonNull + public Context getContext() { + return mContext; + } + + @NonNull + public Looper getLooper() { + return mLooper; + } + + @NonNull + public VcnNetworkProvider getVcnNetworkProvider() { + return mVcnNetworkProvider; + } +} diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java new file mode 100644 index 000000000000..49c9b3297c77 --- /dev/null +++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.vcn; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.vcn.VcnGatewayConnectionConfig; +import android.os.Handler; +import android.os.ParcelUuid; + +import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord; +import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkTrackerCallback; + +import java.util.Objects; + +/** + * A single VCN Gateway Connection, providing a single public-facing VCN network. + * + * <p>This class handles mobility events, performs retries, and tracks safe-mode conditions. + * + * @hide + */ +public class VcnGatewayConnection extends Handler implements UnderlyingNetworkTrackerCallback { + private static final String TAG = VcnGatewayConnection.class.getSimpleName(); + + @NonNull private final VcnContext mVcnContext; + @NonNull private final ParcelUuid mSubscriptionGroup; + @NonNull private final UnderlyingNetworkTracker mUnderlyingNetworkTracker; + @NonNull private final VcnGatewayConnectionConfig mConnectionConfig; + @NonNull private final Dependencies mDeps; + + public VcnGatewayConnection( + @NonNull VcnContext vcnContext, + @NonNull ParcelUuid subscriptionGroup, + @NonNull VcnGatewayConnectionConfig connectionConfig) { + this(vcnContext, subscriptionGroup, connectionConfig, new Dependencies()); + } + + private VcnGatewayConnection( + @NonNull VcnContext vcnContext, + @NonNull ParcelUuid subscriptionGroup, + @NonNull VcnGatewayConnectionConfig connectionConfig, + @NonNull Dependencies deps) { + super(Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper()); + mVcnContext = vcnContext; + mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup"); + mConnectionConfig = Objects.requireNonNull(connectionConfig, "Missing connectionConfig"); + mDeps = Objects.requireNonNull(deps, "Missing deps"); + + mUnderlyingNetworkTracker = + mDeps.newUnderlyingNetworkTracker(mVcnContext, subscriptionGroup, this); + } + + /** Tears down this GatewayConnection, and any resources used */ + public void teardown() { + mUnderlyingNetworkTracker.teardown(); + } + + private static class Dependencies { + public UnderlyingNetworkTracker newUnderlyingNetworkTracker( + VcnContext vcnContext, + ParcelUuid subscriptionGroup, + UnderlyingNetworkTrackerCallback callback) { + return new UnderlyingNetworkTracker(vcnContext, subscriptionGroup, callback); + } + } + + @Override + public void onSelectedUnderlyingNetworkChanged(@Nullable UnderlyingNetworkRecord underlying) {} +} diff --git a/services/core/java/com/android/server/vibrator/InputDeviceDelegate.java b/services/core/java/com/android/server/vibrator/InputDeviceDelegate.java new file mode 100644 index 000000000000..edbc05802697 --- /dev/null +++ b/services/core/java/com/android/server/vibrator/InputDeviceDelegate.java @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.vibrator; + +import android.content.Context; +import android.hardware.input.InputManager; +import android.os.Handler; +import android.os.VibrationAttributes; +import android.os.VibrationEffect; +import android.os.Vibrator; +import android.util.SparseArray; +import android.view.InputDevice; + +import com.android.internal.annotations.GuardedBy; + +/** Delegates vibrations to all connected {@link InputDevice} with available {@link Vibrator}. */ +// TODO(b/159207608): Make this package-private once vibrator services are moved to this package +public final class InputDeviceDelegate implements InputManager.InputDeviceListener { + private static final String TAG = "InputDeviceDelegate"; + + private final Object mLock = new Object(); + private final Handler mHandler; + private final InputManager mInputManager; + + @GuardedBy("mLock") + private final SparseArray<Vibrator> mInputDeviceVibrators = new SparseArray<>(); + + /** + * Flag updated via {@link #updateInputDeviceVibrators(boolean)}, holding the value of {@link + * android.provider.Settings.System#VIBRATE_INPUT_DEVICES}. + */ + @GuardedBy("mLock") + private boolean mShouldVibrateInputDevices; + + public InputDeviceDelegate(Context context, Handler handler) { + mHandler = handler; + mInputManager = context.getSystemService(InputManager.class); + } + + @Override + public void onInputDeviceAdded(int deviceId) { + updateInputDevice(deviceId); + } + + @Override + public void onInputDeviceChanged(int deviceId) { + updateInputDevice(deviceId); + } + + @Override + public void onInputDeviceRemoved(int deviceId) { + synchronized (mLock) { + mInputDeviceVibrators.remove(deviceId); + } + } + + /** + * Return {@code true} is there are input devices with vibrators available and vibrations should + * be delegated to them. + */ + public boolean isAvailable() { + synchronized (mLock) { + // mInputDeviceVibrators is cleared when settings are disabled, so this check is enough. + return mInputDeviceVibrators.size() > 0; + } + } + + /** + * Vibrate all {@link InputDevice} with {@link Vibrator} available using given effect. + * + * @return {@link #isAvailable()} + */ + public boolean vibrateIfAvailable(int uid, String opPkg, VibrationEffect effect, + String reason, VibrationAttributes attrs) { + synchronized (mLock) { + for (int i = 0; i < mInputDeviceVibrators.size(); i++) { + mInputDeviceVibrators.valueAt(i).vibrate(uid, opPkg, effect, reason, attrs); + } + return mInputDeviceVibrators.size() > 0; + } + } + + /** + * Cancel vibration on all {@link InputDevice} with {@link Vibrator} available. + * + * @return {@link #isAvailable()} + */ + public boolean cancelVibrateIfAvailable() { + synchronized (mLock) { + for (int i = 0; i < mInputDeviceVibrators.size(); i++) { + mInputDeviceVibrators.valueAt(i).cancel(); + } + return mInputDeviceVibrators.size() > 0; + } + } + + /** + * Updates the list of {@link InputDevice} vibrators based on the {@link + * VibrationSettings#shouldVibrateInputDevices()} setting current value and the + * devices currently available in {@link InputManager#getInputDeviceIds()}. + * + * @return true if there was any change in input devices available or related settings. + */ + public boolean updateInputDeviceVibrators(boolean vibrateInputDevices) { + synchronized (mLock) { + if (vibrateInputDevices == mShouldVibrateInputDevices) { + // No need to update if settings haven't changed. + return false; + } + + mShouldVibrateInputDevices = vibrateInputDevices; + mInputDeviceVibrators.clear(); + + if (vibrateInputDevices) { + // Register the listener first so any device added/updated/removed after the call to + // getInputDeviceIds() will trigger the callbacks (which will wait on the lock for + // this loop to finish). + mInputManager.registerInputDeviceListener(this, mHandler); + + for (int deviceId : mInputManager.getInputDeviceIds()) { + InputDevice device = mInputManager.getInputDevice(deviceId); + if (device == null) { + continue; + } + Vibrator vibrator = device.getVibrator(); + if (vibrator.hasVibrator()) { + mInputDeviceVibrators.put(device.getId(), vibrator); + } + } + } else { + mInputManager.unregisterInputDeviceListener(this); + } + } + + return true; + } + + private void updateInputDevice(int deviceId) { + synchronized (mLock) { + if (!mShouldVibrateInputDevices) { + // No need to keep this device vibrator if setting is off. + return; + } + InputDevice device = mInputManager.getInputDevice(deviceId); + if (device == null) { + mInputDeviceVibrators.remove(deviceId); + return; + } + Vibrator vibrator = device.getVibrator(); + if (vibrator.hasVibrator()) { + mInputDeviceVibrators.put(deviceId, vibrator); + } else { + mInputDeviceVibrators.remove(deviceId); + } + } + } +} diff --git a/services/core/java/com/android/server/vibrator/Vibration.java b/services/core/java/com/android/server/vibrator/Vibration.java new file mode 100644 index 000000000000..e2cdd02deab9 --- /dev/null +++ b/services/core/java/com/android/server/vibrator/Vibration.java @@ -0,0 +1,287 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.vibrator; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.IBinder; +import android.os.SystemClock; +import android.os.VibrationAttributes; +import android.os.VibrationEffect; +import android.util.proto.ProtoOutputStream; + +import com.android.server.ComposedProto; +import com.android.server.OneShotProto; +import com.android.server.PrebakedProto; +import com.android.server.VibrationAttributesProto; +import com.android.server.VibrationEffectProto; +import com.android.server.VibrationProto; +import com.android.server.WaveformProto; + +import java.text.SimpleDateFormat; +import java.util.Date; + +/** Represents a vibration request to the vibrator service. */ +// TODO(b/159207608): Make this package-private once vibrator services are moved to this package +public class Vibration { + private static final String TAG = "Vibration"; + private static final SimpleDateFormat DEBUG_DATE_FORMAT = + new SimpleDateFormat("MM-dd HH:mm:ss.SSS"); + + public enum Status { + RUNNING, + FINISHED, + FORWARDED_TO_INPUT_DEVICES, + CANCELLED, + ERROR_APP_OPS, + IGNORED, + IGNORED_APP_OPS, + IGNORED_BACKGROUND, + IGNORED_RINGTONE, + IGNORED_UNKNOWN_VIBRATION, + IGNORED_UNSUPPORTED, + IGNORED_FOR_ALARM, + IGNORED_FOR_EXTERNAL, + IGNORED_FOR_ONGOING, + IGNORED_FOR_POWER, + IGNORED_FOR_SETTINGS, + } + + /** Start time in CLOCK_BOOTTIME base. */ + public final long startTime; + public final VibrationAttributes attrs; + public final long id; + public final int uid; + public final String opPkg; + public final String reason; + public final IBinder token; + + /** The actual effect to be played. */ + @Nullable + private VibrationEffect mEffect; + + /** + * The original effect that was requested. Typically these two things differ because the effect + * was scaled based on the users vibration intensity settings. + */ + @Nullable + private VibrationEffect mOriginalEffect; + + /** + * Start/end times in unix epoch time. Only to be used for debugging purposes and to correlate + * with other system events, any duration calculations should be done use {@link #startTime} so + * as not to be affected by discontinuities created by RTC adjustments. + */ + private final long mStartTimeDebug; + private long mEndTimeDebug; + private Status mStatus; + + public Vibration(IBinder token, int id, VibrationEffect effect, + VibrationAttributes attrs, int uid, String opPkg, String reason) { + this.token = token; + this.mEffect = effect; + this.id = id; + this.startTime = SystemClock.elapsedRealtime(); + this.attrs = attrs; + this.uid = uid; + this.opPkg = opPkg; + this.reason = reason; + mStartTimeDebug = System.currentTimeMillis(); + mStatus = Status.RUNNING; + } + + /** + * Set the {@link Status} of this vibration and the current system time as this + * vibration end time, for debugging purposes. + * + * <p>This method will only accept given value if the current status is {@link + * Status#RUNNING}. + */ + public void end(Status status) { + if (hasEnded()) { + // Vibration already ended, keep first ending status set and ignore this one. + return; + } + mStatus = status; + mEndTimeDebug = System.currentTimeMillis(); + } + + /** + * Replace this vibration effect if given {@code scaledEffect} is different, preserving the + * original one for debug purposes. + */ + public void updateEffect(@NonNull VibrationEffect newEffect) { + if (newEffect.equals(mEffect)) { + return; + } + mOriginalEffect = mEffect; + mEffect = newEffect; + } + + /** Return true is current status is different from {@link Status#RUNNING}. */ + public boolean hasEnded() { + return mStatus != Status.RUNNING; + } + + /** Return the effect that should be played by this vibration. */ + @Nullable + public VibrationEffect getEffect() { + return mEffect; + } + + /** Return {@link Vibration.DebugInfo} with read-only debug information about this vibration. */ + public Vibration.DebugInfo getDebugInfo() { + return new Vibration.DebugInfo( + mStartTimeDebug, mEndTimeDebug, mEffect, mOriginalEffect, /* scale= */ 0, attrs, + uid, opPkg, reason, mStatus); + } + + /** Debug information about vibrations. */ + public static final class DebugInfo { + private final long mStartTimeDebug; + private final long mEndTimeDebug; + private final VibrationEffect mEffect; + private final VibrationEffect mOriginalEffect; + private final float mScale; + private final VibrationAttributes mAttrs; + private final int mUid; + private final String mOpPkg; + private final String mReason; + private final Status mStatus; + + public DebugInfo(long startTimeDebug, long endTimeDebug, VibrationEffect effect, + VibrationEffect originalEffect, float scale, VibrationAttributes attrs, + int uid, String opPkg, String reason, Status status) { + mStartTimeDebug = startTimeDebug; + mEndTimeDebug = endTimeDebug; + mEffect = effect; + mOriginalEffect = originalEffect; + mScale = scale; + mAttrs = attrs; + mUid = uid; + mOpPkg = opPkg; + mReason = reason; + mStatus = status; + } + + @Override + public String toString() { + return new StringBuilder() + .append("startTime: ") + .append(DEBUG_DATE_FORMAT.format(new Date(mStartTimeDebug))) + .append(", endTime: ") + .append(mEndTimeDebug == 0 ? null + : DEBUG_DATE_FORMAT.format(new Date(mEndTimeDebug))) + .append(", status: ") + .append(mStatus.name().toLowerCase()) + .append(", effect: ") + .append(mEffect) + .append(", originalEffect: ") + .append(mOriginalEffect) + .append(", scale: ") + .append(String.format("%.2f", mScale)) + .append(", attrs: ") + .append(mAttrs) + .append(", uid: ") + .append(mUid) + .append(", opPkg: ") + .append(mOpPkg) + .append(", reason: ") + .append(mReason) + .toString(); + } + + /** Write this info into given {@code fieldId} on {@link ProtoOutputStream}. */ + public void dumpProto(ProtoOutputStream proto, long fieldId) { + final long token = proto.start(fieldId); + proto.write(VibrationProto.START_TIME, mStartTimeDebug); + proto.write(VibrationProto.END_TIME, mEndTimeDebug); + proto.write(VibrationProto.STATUS, mStatus.ordinal()); + + final long attrsToken = proto.start(VibrationProto.ATTRIBUTES); + proto.write(VibrationAttributesProto.USAGE, mAttrs.getUsage()); + proto.write(VibrationAttributesProto.AUDIO_USAGE, mAttrs.getAudioUsage()); + proto.write(VibrationAttributesProto.FLAGS, mAttrs.getFlags()); + proto.end(attrsToken); + + if (mEffect != null) { + dumpEffect(proto, VibrationProto.EFFECT, mEffect); + } + if (mOriginalEffect != null) { + dumpEffect(proto, VibrationProto.ORIGINAL_EFFECT, mOriginalEffect); + } + + proto.end(token); + } + + private void dumpEffect(ProtoOutputStream proto, long fieldId, VibrationEffect effect) { + final long token = proto.start(fieldId); + if (effect instanceof VibrationEffect.OneShot) { + dumpEffect(proto, VibrationEffectProto.ONESHOT, (VibrationEffect.OneShot) effect); + } else if (effect instanceof VibrationEffect.Waveform) { + dumpEffect(proto, VibrationEffectProto.WAVEFORM, (VibrationEffect.Waveform) effect); + } else if (effect instanceof VibrationEffect.Prebaked) { + dumpEffect(proto, VibrationEffectProto.PREBAKED, (VibrationEffect.Prebaked) effect); + } else if (effect instanceof VibrationEffect.Composed) { + dumpEffect(proto, VibrationEffectProto.COMPOSED, (VibrationEffect.Composed) effect); + } + proto.end(token); + } + + private void dumpEffect(ProtoOutputStream proto, long fieldId, + VibrationEffect.OneShot effect) { + final long token = proto.start(fieldId); + proto.write(OneShotProto.DURATION, (int) effect.getDuration()); + proto.write(OneShotProto.AMPLITUDE, effect.getAmplitude()); + proto.end(token); + } + + private void dumpEffect(ProtoOutputStream proto, long fieldId, + VibrationEffect.Waveform effect) { + final long token = proto.start(fieldId); + for (long timing : effect.getTimings()) { + proto.write(WaveformProto.TIMINGS, (int) timing); + } + for (int amplitude : effect.getAmplitudes()) { + proto.write(WaveformProto.AMPLITUDES, amplitude); + } + proto.write(WaveformProto.REPEAT, effect.getRepeatIndex() >= 0); + proto.end(token); + } + + private void dumpEffect(ProtoOutputStream proto, long fieldId, + VibrationEffect.Prebaked effect) { + final long token = proto.start(fieldId); + proto.write(PrebakedProto.EFFECT_ID, effect.getId()); + proto.write(PrebakedProto.EFFECT_STRENGTH, effect.getEffectStrength()); + proto.write(PrebakedProto.FALLBACK, effect.shouldFallback()); + proto.end(token); + } + + private void dumpEffect(ProtoOutputStream proto, long fieldId, + VibrationEffect.Composed effect) { + final long token = proto.start(fieldId); + for (VibrationEffect.Composition.PrimitiveEffect primitive : + effect.getPrimitiveEffects()) { + proto.write(ComposedProto.EFFECT_IDS, primitive.id); + proto.write(ComposedProto.EFFECT_SCALES, primitive.scale); + proto.write(ComposedProto.DELAYS, primitive.delay); + } + proto.end(token); + } + } +} diff --git a/services/core/java/com/android/server/vibrator/VibrationScaler.java b/services/core/java/com/android/server/vibrator/VibrationScaler.java index 42a0a706a1c7..5f7e47d6ca29 100644 --- a/services/core/java/com/android/server/vibrator/VibrationScaler.java +++ b/services/core/java/com/android/server/vibrator/VibrationScaler.java @@ -31,7 +31,6 @@ public final class VibrationScaler { // Scale levels. Each level, except MUTE, is defined as the delta between the current setting // and the default intensity for that type of vibration (i.e. current - default). - private static final int SCALE_MUTE = IExternalVibratorService.SCALE_MUTE; // -100 private static final int SCALE_VERY_LOW = IExternalVibratorService.SCALE_VERY_LOW; // -2 private static final int SCALE_LOW = IExternalVibratorService.SCALE_LOW; // -1 private static final int SCALE_NONE = IExternalVibratorService.SCALE_NONE; // 0 diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java index 06a1f599f491..6a5d1c4173c9 100644 --- a/services/core/java/com/android/server/vibrator/VibrationSettings.java +++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java @@ -17,14 +17,17 @@ package com.android.server.vibrator; import android.content.Context; +import android.content.res.Resources; import android.database.ContentObserver; import android.media.AudioManager; import android.net.Uri; import android.os.Handler; import android.os.UserHandle; import android.os.VibrationAttributes; +import android.os.VibrationEffect; import android.os.Vibrator; import android.provider.Settings; +import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -37,6 +40,8 @@ import java.util.List; public final class VibrationSettings { private static final String TAG = "VibrationSettings"; + private static final long[] DOUBLE_CLICK_EFFECT_FALLBACK_TIMINGS = {0, 30, 100, 30}; + /** Listener for changes on vibration settings. */ public interface OnVibratorSettingsChanged { /** Callback triggered when any of the vibrator settings change. */ @@ -51,6 +56,7 @@ public final class VibrationSettings { @GuardedBy("mLock") private final List<OnVibratorSettingsChanged> mListeners = new ArrayList<>(); + private final SparseArray<VibrationEffect> mFallbackEffects; @GuardedBy("mLock") private boolean mVibrateInputDevices; @@ -84,6 +90,23 @@ public final class VibrationSettings { registerSettingsObserver( Settings.System.getUriFor(Settings.System.RING_VIBRATION_INTENSITY)); + VibrationEffect clickEffect = createEffectFromResource( + com.android.internal.R.array.config_virtualKeyVibePattern); + VibrationEffect doubleClickEffect = VibrationEffect.createWaveform( + DOUBLE_CLICK_EFFECT_FALLBACK_TIMINGS, -1 /*repeatIndex*/); + VibrationEffect heavyClickEffect = createEffectFromResource( + com.android.internal.R.array.config_longPressVibePattern); + VibrationEffect tickEffect = createEffectFromResource( + com.android.internal.R.array.config_clockTickVibePattern); + + mFallbackEffects = new SparseArray<>(); + mFallbackEffects.put(VibrationEffect.EFFECT_CLICK, clickEffect); + mFallbackEffects.put(VibrationEffect.EFFECT_DOUBLE_CLICK, doubleClickEffect); + mFallbackEffects.put(VibrationEffect.EFFECT_TICK, tickEffect); + mFallbackEffects.put(VibrationEffect.EFFECT_HEAVY_CLICK, heavyClickEffect); + mFallbackEffects.put(VibrationEffect.EFFECT_TEXTURE_TICK, + VibrationEffect.get(VibrationEffect.EFFECT_TICK, false)); + // Update with current values from settings. updateSettings(); } @@ -150,6 +173,17 @@ public final class VibrationSettings { } /** + * Return a {@link VibrationEffect} that should be played if the device do not support given + * {@code effectId}. + * + * @param effectId one of VibrationEffect.EFFECT_* + * @return The effect to be played as a fallback + */ + public VibrationEffect getFallbackEffect(int effectId) { + return mFallbackEffects.get(effectId); + } + + /** * Return {@code true} if the device should vibrate for ringtones. * * <p>This checks the current {@link AudioManager#getRingerModeInternal()} against user settings @@ -270,6 +304,33 @@ public final class VibrationSettings { UserHandle.USER_ALL); } + private VibrationEffect createEffectFromResource(int resId) { + long[] timings = getLongIntArray(mContext.getResources(), resId); + return createEffectFromTimings(timings); + } + + private static VibrationEffect createEffectFromTimings(long[] timings) { + if (timings == null || timings.length == 0) { + return null; + } else if (timings.length == 1) { + return VibrationEffect.createOneShot(timings[0], VibrationEffect.DEFAULT_AMPLITUDE); + } else { + return VibrationEffect.createWaveform(timings, -1); + } + } + + private static long[] getLongIntArray(Resources r, int resid) { + int[] ar = r.getIntArray(resid); + if (ar == null) { + return null; + } + long[] out = new long[ar.length]; + for (int i = 0; i < ar.length; i++) { + out[i] = ar[i]; + } + return out; + } + /** Implementation of {@link ContentObserver} to be registered to a setting {@link Uri}. */ private final class SettingsObserver extends ContentObserver { SettingsObserver(Handler handler) { diff --git a/services/core/java/com/android/server/vibrator/VibratorController.java b/services/core/java/com/android/server/vibrator/VibratorController.java new file mode 100644 index 000000000000..f76c1a1b2b9d --- /dev/null +++ b/services/core/java/com/android/server/vibrator/VibratorController.java @@ -0,0 +1,472 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.vibrator; + +import android.annotation.Nullable; +import android.hardware.vibrator.IVibrator; +import android.os.Binder; +import android.os.IVibratorStateListener; +import android.os.RemoteCallbackList; +import android.os.RemoteException; +import android.os.VibrationEffect; +import android.os.Vibrator; +import android.util.Slog; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; + +import libcore.util.NativeAllocationRegistry; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +/** Controls a single vibrator. */ +// TODO(b/159207608): Make this package-private once vibrator services are moved to this package +public final class VibratorController { + private static final String TAG = "VibratorController"; + + private final Object mLock = new Object(); + private final NativeWrapper mNativeWrapper; + private final int mVibratorId; + private final long mCapabilities; + @Nullable + private final Set<Integer> mSupportedEffects; + @Nullable + private final Set<Integer> mSupportedPrimitives; + + @GuardedBy("mLock") + private final RemoteCallbackList<IVibratorStateListener> mVibratorStateListeners = + new RemoteCallbackList<>(); + @GuardedBy("mLock") + private boolean mIsVibrating; + @GuardedBy("mLock") + private boolean mIsUnderExternalControl; + + /** Listener for vibration completion callbacks from native. */ + public interface OnVibrationCompleteListener { + + /** Callback triggered when vibration is complete. */ + void onComplete(int vibratorId, long vibrationId); + } + + /** + * Initializes the native part of this controller, creating a global reference to given + * {@link OnVibrationCompleteListener} and returns a newly allocated native pointer. This + * wrapper is responsible for deleting this pointer by calling the method pointed + * by {@link #vibratorGetFinalizer()}. + * + * <p><b>Note:</b> Make sure the given implementation of {@link OnVibrationCompleteListener} + * do not hold any strong reference to the instance responsible for deleting the returned + * pointer, to avoid creating a cyclic GC root reference. + */ + static native long vibratorInit(int vibratorId, OnVibrationCompleteListener listener); + + /** + * Returns pointer to native function responsible for cleaning up the native pointer allocated + * and returned by {@link #vibratorInit(int, OnVibrationCompleteListener)}. + */ + static native long vibratorGetFinalizer(); + + static native boolean vibratorIsAvailable(long nativePtr); + + static native void vibratorOn(long nativePtr, long milliseconds, long vibrationId); + + static native void vibratorOff(long nativePtr); + + static native void vibratorSetAmplitude(long nativePtr, int amplitude); + + static native int[] vibratorGetSupportedEffects(long nativePtr); + + static native int[] vibratorGetSupportedPrimitives(long nativePtr); + + static native long vibratorPerformEffect( + long nativePtr, long effect, long strength, long vibrationId); + + static native void vibratorPerformComposedEffect(long nativePtr, + VibrationEffect.Composition.PrimitiveEffect[] effect, long vibrationId); + + static native void vibratorSetExternalControl(long nativePtr, boolean enabled); + + static native long vibratorGetCapabilities(long nativePtr); + + static native void vibratorAlwaysOnEnable(long nativePtr, long id, long effect, long strength); + + static native void vibratorAlwaysOnDisable(long nativePtr, long id); + + public VibratorController(int vibratorId, OnVibrationCompleteListener listener) { + this(vibratorId, listener, new NativeWrapper()); + } + + @VisibleForTesting + public VibratorController(int vibratorId, OnVibrationCompleteListener listener, + NativeWrapper nativeWrapper) { + mVibratorId = vibratorId; + mNativeWrapper = nativeWrapper; + + nativeWrapper.init(vibratorId, listener); + mCapabilities = nativeWrapper.getCapabilities(); + mSupportedEffects = asSet(nativeWrapper.getSupportedEffects()); + mSupportedPrimitives = asSet(nativeWrapper.getSupportedPrimitives()); + } + + /** Register state listener for this vibrator. */ + public boolean registerVibratorStateListener(IVibratorStateListener listener) { + synchronized (mLock) { + final long token = Binder.clearCallingIdentity(); + try { + if (!mVibratorStateListeners.register(listener)) { + return false; + } + // Notify its callback after new client registered. + notifyStateListenerLocked(listener); + return true; + } finally { + Binder.restoreCallingIdentity(token); + } + } + } + + /** Remove registered state listener for this vibrator. */ + public boolean unregisterVibratorStateListener(IVibratorStateListener listener) { + synchronized (mLock) { + final long token = Binder.clearCallingIdentity(); + try { + return mVibratorStateListeners.unregister(listener); + } finally { + Binder.restoreCallingIdentity(token); + } + } + } + + /** Return the id of the vibrator controlled by this instance. */ + public int getVibratorId() { + return mVibratorId; + } + + /** + * Return {@code true} is this vibrator is currently vibrating, false otherwise. + * + * <p>This state is controlled by calls to {@link #on} and {@link #off} methods, and is + * automatically notified to any registered {@link IVibratorStateListener} on change. + */ + public boolean isVibrating() { + synchronized (mLock) { + return mIsVibrating; + } + } + + /** Return {@code true} if this vibrator is under external control, false otherwise. */ + public boolean isUnderExternalControl() { + synchronized (mLock) { + return mIsUnderExternalControl; + } + } + + /** + * Check against this vibrator capabilities. + * + * @param capability one of IVibrator.CAP_* + * @return true if this vibrator has this capability, false otherwise + */ + public boolean hasCapability(long capability) { + return (mCapabilities & capability) == capability; + } + + /** + * Check against this vibrator supported effects. + * + * @param effectIds list of effects, one of VibrationEffect.EFFECT_* + * @return one entry per requested effectId, with one of Vibrator.VIBRATION_EFFECT_SUPPORT_* + */ + public int[] areEffectsSupported(int[] effectIds) { + int[] supported = new int[effectIds.length]; + if (mSupportedEffects == null) { + Arrays.fill(supported, Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN); + } else { + for (int i = 0; i < effectIds.length; i++) { + supported[i] = mSupportedEffects.contains(effectIds[i]) + ? Vibrator.VIBRATION_EFFECT_SUPPORT_YES + : Vibrator.VIBRATION_EFFECT_SUPPORT_NO; + } + } + return supported; + } + + /** + * Check against this vibrator supported primitives. + * + * @param primitiveIds list of primitives, one of VibrationEffect.Composition.EFFECT_* + * @return one entry per requested primitiveId, with true if it is supported + */ + public boolean[] arePrimitivesSupported(int[] primitiveIds) { + boolean[] supported = new boolean[primitiveIds.length]; + if (mSupportedPrimitives != null && hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) { + for (int i = 0; i < primitiveIds.length; i++) { + supported[i] = mSupportedPrimitives.contains(primitiveIds[i]); + } + } + return supported; + } + + /** Return {@code true} if the underlying vibrator is currently available, false otherwise. */ + public boolean isAvailable() { + return mNativeWrapper.isAvailable(); + } + + /** + * Set the vibrator control to be external or not, based on given flag. + * + * <p>This will affect the state of {@link #isUnderExternalControl()}. + */ + public void setExternalControl(boolean externalControl) { + if (!hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) { + return; + } + synchronized (mLock) { + mIsUnderExternalControl = externalControl; + mNativeWrapper.setExternalControl(externalControl); + } + } + + /** + * Update the predefined vibration effect saved with given id. This will remove the saved effect + * if given {@code effect} is {@code null}. + */ + public void updateAlwaysOn(int id, @Nullable VibrationEffect.Prebaked effect) { + if (!hasCapability(IVibrator.CAP_ALWAYS_ON_CONTROL)) { + return; + } + synchronized (mLock) { + if (effect == null) { + mNativeWrapper.alwaysOnDisable(id); + } else { + mNativeWrapper.alwaysOnEnable(id, effect.getId(), effect.getEffectStrength()); + } + } + } + + /** Set the vibration amplitude. This will NOT affect the state of {@link #isVibrating()}. */ + public void setAmplitude(int amplitude) { + synchronized (mLock) { + if (hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL)) { + mNativeWrapper.setAmplitude(amplitude); + } + } + } + + /** + * Turn on the vibrator for {@code milliseconds} time, using {@code vibrationId} or completion + * callback to {@link OnVibrationCompleteListener}. + * + * <p>This will affect the state of {@link #isVibrating()}. + */ + public void on(long milliseconds, long vibrationId) { + synchronized (mLock) { + mNativeWrapper.on(milliseconds, vibrationId); + notifyVibratorOnLocked(); + } + } + + /** + * Plays predefined vibration effect, using {@code vibrationId} or completion callback to + * {@link OnVibrationCompleteListener}. + * + * <p>This will affect the state of {@link #isVibrating()}. + */ + public long on(VibrationEffect.Prebaked effect, long vibrationId) { + synchronized (mLock) { + long duration = mNativeWrapper.perform(effect.getId(), effect.getEffectStrength(), + vibrationId); + if (duration > 0) { + notifyVibratorOnLocked(); + } + return duration; + } + } + + /** + * Plays composited vibration effect, using {@code vibrationId} or completion callback to + * {@link OnVibrationCompleteListener}. + * + * <p>This will affect the state of {@link #isVibrating()}. + */ + public void on(VibrationEffect.Composed effect, long vibrationId) { + if (!hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) { + return; + } + synchronized (mLock) { + mNativeWrapper.compose(effect.getPrimitiveEffects().toArray( + new VibrationEffect.Composition.PrimitiveEffect[0]), vibrationId); + notifyVibratorOnLocked(); + } + } + + /** Turns off the vibrator.This will affect the state of {@link #isVibrating()}. */ + public void off() { + synchronized (mLock) { + mNativeWrapper.off(); + notifyVibratorOffLocked(); + } + } + + @Override + public String toString() { + return "VibratorController{" + + "mVibratorId=" + mVibratorId + + ", mCapabilities=" + mCapabilities + + ", mSupportedEffects=" + mSupportedEffects + + ", mSupportedPrimitives=" + mSupportedPrimitives + + ", mIsVibrating=" + mIsVibrating + + ", mIsUnderExternalControl=" + mIsUnderExternalControl + + ", mVibratorStateListeners count=" + + mVibratorStateListeners.getRegisteredCallbackCount() + + '}'; + } + + @GuardedBy("mLock") + private void notifyVibratorOnLocked() { + if (!mIsVibrating) { + mIsVibrating = true; + notifyStateListenersLocked(); + } + } + + @GuardedBy("mLock") + private void notifyVibratorOffLocked() { + if (mIsVibrating) { + mIsVibrating = false; + notifyStateListenersLocked(); + } + } + + @GuardedBy("mLock") + private void notifyStateListenersLocked() { + final int length = mVibratorStateListeners.beginBroadcast(); + try { + for (int i = 0; i < length; i++) { + notifyStateListenerLocked(mVibratorStateListeners.getBroadcastItem(i)); + } + } finally { + mVibratorStateListeners.finishBroadcast(); + } + } + + @GuardedBy("mLock") + private void notifyStateListenerLocked(IVibratorStateListener listener) { + try { + listener.onVibrating(mIsVibrating); + } catch (RemoteException | RuntimeException e) { + Slog.e(TAG, "Vibrator state listener failed to call", e); + } + } + + @Nullable + private static Set<Integer> asSet(int[] values) { + if (values == null) { + return null; + } + HashSet<Integer> set = new HashSet<>(); + for (int value : values) { + set.add(value); + } + return set; + } + + /** Wrapper around the static-native methods of {@link VibratorController} for tests. */ + @VisibleForTesting + public static class NativeWrapper { + + private long mNativePtr = 0; + + /** Initializes native controller and allocation registry to destroy native instances. */ + public void init(int vibratorId, OnVibrationCompleteListener listener) { + mNativePtr = VibratorController.vibratorInit(vibratorId, listener); + long finalizerPtr = VibratorController.vibratorGetFinalizer(); + + if (finalizerPtr != 0) { + NativeAllocationRegistry registry = + NativeAllocationRegistry.createMalloced( + VibratorController.class.getClassLoader(), finalizerPtr); + registry.registerNativeAllocation(this, mNativePtr); + } + } + + /** Check if the vibrator is currently available. */ + public boolean isAvailable() { + return VibratorController.vibratorIsAvailable(mNativePtr); + } + + /** Turns vibrator on for given time. */ + public void on(long milliseconds, long vibrationId) { + VibratorController.vibratorOn(mNativePtr, milliseconds, vibrationId); + } + + /** Turns vibrator off. */ + public void off() { + VibratorController.vibratorOff(mNativePtr); + } + + /** Sets the amplitude for the vibrator to run. */ + public void setAmplitude(int amplitude) { + VibratorController.vibratorSetAmplitude(mNativePtr, amplitude); + } + + /** Returns all predefined effects supported by the device vibrator. */ + public int[] getSupportedEffects() { + return VibratorController.vibratorGetSupportedEffects(mNativePtr); + } + + /** Returns all compose primitives supported by the device vibrator. */ + public int[] getSupportedPrimitives() { + return VibratorController.vibratorGetSupportedPrimitives(mNativePtr); + } + + /** Turns vibrator on to perform one of the supported effects. */ + public long perform(long effect, long strength, long vibrationId) { + return VibratorController.vibratorPerformEffect( + mNativePtr, effect, strength, vibrationId); + } + + /** Turns vibrator on to perform one of the supported composed effects. */ + public void compose( + VibrationEffect.Composition.PrimitiveEffect[] effect, long vibrationId) { + VibratorController.vibratorPerformComposedEffect(mNativePtr, effect, + vibrationId); + } + + /** Enabled the device vibrator to be controlled by another service. */ + public void setExternalControl(boolean enabled) { + VibratorController.vibratorSetExternalControl(mNativePtr, enabled); + } + + /** Returns all capabilities of the device vibrator. */ + public long getCapabilities() { + return VibratorController.vibratorGetCapabilities(mNativePtr); + } + + /** Enable always-on vibration with given id and effect. */ + public void alwaysOnEnable(long id, long effect, long strength) { + VibratorController.vibratorAlwaysOnEnable(mNativePtr, id, effect, strength); + } + + /** Disable always-on vibration for given id. */ + public void alwaysOnDisable(long id) { + VibratorController.vibratorAlwaysOnDisable(mNativePtr, id); + } + } +} diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java index d8d1a6563675..b4ca7c5f6ff1 100644 --- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java @@ -3,8 +3,10 @@ package com.android.server.wm; import static android.app.ActivityManager.START_SUCCESS; import static android.app.ActivityManager.START_TASK_TO_FRONT; import static android.app.ActivityManager.processStateAmToProto; +import static android.app.WaitResult.INVALID_DELAY; import static android.app.WaitResult.LAUNCH_STATE_COLD; import static android.app.WaitResult.LAUNCH_STATE_HOT; +import static android.app.WaitResult.LAUNCH_STATE_RELAUNCH; import static android.app.WaitResult.LAUNCH_STATE_WARM; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; @@ -130,7 +132,6 @@ class ActivityMetricsLogger { * transition, in the case the launch is standalone (e.g. from recents). */ private static final int IGNORE_CALLER = -1; - private static final int INVALID_DELAY = -1; // Preallocated strings we are sending to tron, so we don't have to allocate a new one every // time we log. @@ -220,6 +221,8 @@ class ActivityMetricsLogger { boolean mLoggedStartingWindowDrawn; /** If the any app transitions have been logged as starting. */ boolean mLoggedTransitionStarting; + /** Whether any activity belonging to this transition has relaunched. */ + boolean mRelaunched; /** Non-null if the application has reported drawn but its window hasn't. */ @Nullable Runnable mPendingFullyDrawn; @@ -351,6 +354,7 @@ class ActivityMetricsLogger { */ final int windowsFullyDrawnDelayMs; final int activityRecordIdHashCode; + final boolean relaunched; private TransitionInfoSnapshot(TransitionInfo info) { this(info, info.mLastLaunchedActivity, INVALID_DELAY); @@ -379,6 +383,7 @@ class ActivityMetricsLogger { launchedActivityShortComponentName = launchedActivity.shortComponentName; activityRecordIdHashCode = System.identityHashCode(launchedActivity); this.windowsFullyDrawnDelayMs = windowsFullyDrawnDelayMs; + relaunched = info.mRelaunched; } @WaitResult.LaunchState int getLaunchState() { @@ -386,7 +391,7 @@ class ActivityMetricsLogger { case TYPE_TRANSITION_WARM_LAUNCH: return LAUNCH_STATE_WARM; case TYPE_TRANSITION_HOT_LAUNCH: - return LAUNCH_STATE_HOT; + return relaunched ? LAUNCH_STATE_RELAUNCH : LAUNCH_STATE_HOT; case TYPE_TRANSITION_COLD_LAUNCH: return LAUNCH_STATE_COLD; default: @@ -673,6 +678,13 @@ class ActivityMetricsLogger { } } + void notifyActivityRelaunched(ActivityRecord r) { + final TransitionInfo info = getActiveTransitionInfo(r); + if (info != null) { + info.mRelaunched = true; + } + } + /** Makes sure that the reference to the removed activity is cleared. */ void notifyActivityRemoved(@NonNull ActivityRecord r) { mLastTransitionInfo.remove(r); @@ -800,13 +812,13 @@ class ActivityMetricsLogger { FrameworkStatsLog.APP_START_CANCELED, activity.info.applicationInfo.uid, activity.packageName, - convertAppStartTransitionType(type), + getAppStartTransitionType(type, info.mRelaunched), activity.info.name); if (DEBUG_METRICS) { Slog.i(TAG, String.format("APP_START_CANCELED(%s, %s, %s, %s)", activity.info.applicationInfo.uid, activity.packageName, - convertAppStartTransitionType(type), + getAppStartTransitionType(type, info.mRelaunched), activity.info.name)); } } @@ -871,7 +883,7 @@ class ActivityMetricsLogger { FrameworkStatsLog.APP_START_OCCURRED, info.applicationInfo.uid, info.packageName, - convertAppStartTransitionType(info.type), + getAppStartTransitionType(info.type, info.relaunched), info.launchedActivityName, info.launchedActivityLaunchedFromPackage, isInstantApp, @@ -891,7 +903,7 @@ class ActivityMetricsLogger { Slog.i(TAG, String.format("APP_START_OCCURRED(%s, %s, %s, %s, %s)", info.applicationInfo.uid, info.packageName, - convertAppStartTransitionType(info.type), + getAppStartTransitionType(info.type, info.relaunched), info.launchedActivityName, info.launchedActivityLaunchedFromPackage)); } @@ -918,7 +930,7 @@ class ActivityMetricsLogger { Log.i(TAG, sb.toString()); } - private int convertAppStartTransitionType(int tronType) { + private static int getAppStartTransitionType(int tronType, boolean relaunched) { if (tronType == TYPE_TRANSITION_COLD_LAUNCH) { return FrameworkStatsLog.APP_START_OCCURRED__TYPE__COLD; } @@ -926,17 +938,13 @@ class ActivityMetricsLogger { return FrameworkStatsLog.APP_START_OCCURRED__TYPE__WARM; } if (tronType == TYPE_TRANSITION_HOT_LAUNCH) { - return FrameworkStatsLog.APP_START_OCCURRED__TYPE__HOT; + return relaunched + ? FrameworkStatsLog.APP_START_OCCURRED__TYPE__RELAUNCH + : FrameworkStatsLog.APP_START_OCCURRED__TYPE__HOT; } return FrameworkStatsLog.APP_START_OCCURRED__TYPE__UNKNOWN; } - /** @return the last known window drawn delay of the given activity. */ - int getLastDrawnDelayMs(ActivityRecord r) { - final TransitionInfo info = mLastTransitionInfo.get(r); - return info != null ? info.mWindowsDrawnDelayMs : INVALID_DELAY; - } - /** @see android.app.Activity#reportFullyDrawn */ TransitionInfoSnapshot logAppTransitionReportedDrawn(ActivityRecord r, boolean restoredFromBundle) { diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 359177968b04..913c3e580adf 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -226,7 +226,7 @@ import android.app.ActivityOptions; import android.app.PendingIntent; import android.app.PictureInPictureParams; import android.app.ResultInfo; -import android.app.WaitResult.LaunchState; +import android.app.WaitResult; import android.app.servertransaction.ActivityConfigurationChangeItem; import android.app.servertransaction.ActivityLifecycleItem; import android.app.servertransaction.ActivityRelaunchItem; @@ -1558,10 +1558,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } } - // Application tokens start out hidden. - setVisible(false); - mVisibleRequested = false; - ColorDisplayService.ColorDisplayServiceInternal cds = LocalServices.getService( ColorDisplayService.ColorDisplayServiceInternal.class); cds.attachColorTransformController(packageName, mUserId, @@ -3130,6 +3126,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } void finishRelaunching() { + mTaskSupervisor.getActivityMetricsLogger().notifyActivityRelaunched(this); unfreezeBounds(); if (mPendingRelaunchCount > 0) { @@ -3506,7 +3503,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } if (fromActivity.isVisible()) { setVisible(true); - mVisibleRequested = true; + setVisibleRequested(true); mVisibleSetFromTransferredStartingWindow = true; } setClientVisible(fromActivity.mClientVisible); @@ -4122,11 +4119,28 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A void setVisible(boolean visible) { if (visible != mVisible) { mVisible = visible; + if (app != null) { + mTaskSupervisor.onProcessActivityStateChanged(app, false /* forceBatch */); + } scheduleAnimation(); } } /** + * This is the only place that writes {@link #mVisibleRequested} (except unit test). The caller + * outside of this class should use {@link #setVisibility}. + */ + private void setVisibleRequested(boolean visible) { + if (visible == mVisibleRequested) { + return; + } + mVisibleRequested = visible; + if (app != null) { + mTaskSupervisor.onProcessActivityStateChanged(app, false /* forceBatch */); + } + } + + /** * Set visibility on this {@link ActivityRecord} * * <p class="note"><strong>Note: </strong>This function might not update the visibility of @@ -4189,7 +4203,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A displayContent.mOpeningApps.remove(this); displayContent.mClosingApps.remove(this); waitingToShow = false; - mVisibleRequested = visible; + setVisibleRequested(visible); mLastDeferHidingClient = deferHidingClient; if (!visible) { @@ -4331,7 +4345,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A ANIMATION_TYPE_APP_TRANSITION)); } setVisible(visible); - mVisibleRequested = visible; + setVisibleRequested(visible); if (!visible) { stopFreezingScreen(true, true); } else { @@ -4519,7 +4533,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A detachChildren(); } if (app != null) { - app.computeProcessActivityState(); + mTaskSupervisor.onProcessActivityStateChanged(app, false /* forceBatch */); } switch (state) { @@ -5435,14 +5449,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A .getActivityMetricsLogger().notifyWindowsDrawn(this, timestampNs); final boolean validInfo = info != null; final int windowsDrawnDelayMs = validInfo ? info.windowsDrawnDelayMs : INVALID_DELAY; - final @LaunchState int launchState = validInfo ? info.getLaunchState() : -1; + final @WaitResult.LaunchState int launchState = + validInfo ? info.getLaunchState() : WaitResult.LAUNCH_STATE_UNKNOWN; // The activity may have been requested to be invisible (another activity has been launched) // so there is no valid info. But if it is the current top activity (e.g. sleeping), the // invalid state is still reported to make sure the waiting result is notified. if (validInfo || this == getDisplayArea().topRunningActivity()) { mTaskSupervisor.reportActivityLaunchedLocked(false /* timeout */, this, windowsDrawnDelayMs, launchState); - mTaskSupervisor.stopWaitingForActivityVisible(this, windowsDrawnDelayMs); + mTaskSupervisor.stopWaitingForActivityVisible(this, windowsDrawnDelayMs, launchState); } finishLaunchTickingLocked(); if (task != null) { @@ -6393,13 +6408,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // The app is just becoming visible, and the parent Task has updated with the // orientation request. Update the size compat mode. updateSizeCompatMode(); - if (task.isOrganized()) { - // WM Shell can override WM Core positioning (e.g. for letterboxing) so ensure - // that WM Shell is called when an activity becomes visible. Without this, WM Core - // will handle positioning instead of WM Shell when an app is reopened. - mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged( - task, /* force= */ true); - } + // WM Shell can override WM Core positioning (e.g. for letterboxing) so ensure + // that WM Shell is called when an activity becomes visible. Without this, WM Core + // will handle positioning instead of WM Shell when an app is reopened. + task.dispatchTaskInfoChangedIfNeeded(/* force= */ true); } } @@ -7163,11 +7175,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } else { mRelaunchReason = RELAUNCH_REASON_NONE; } - if (!attachedToProcess()) { - ProtoLog.v(WM_DEBUG_CONFIGURATION, - "Config is destroying non-running %s", this); - destroyImmediately("config"); - } else if (mState == PAUSING) { + if (mState == PAUSING) { // A little annoying: we are waiting for this activity to finish pausing. Let's not // do anything now, but just flag that it needs to be restarted when done pausing. ProtoLog.v(WM_DEBUG_CONFIGURATION, @@ -7576,6 +7584,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return (config.uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_VR_HEADSET; } + String getProcessName() { + return info.applicationInfo.processName; + } + int getUid() { return info.applicationInfo.uid; } @@ -7588,6 +7600,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return app != null ? app.getPid() : 0; } + int getLaunchedFromPid() { + return launchedFromPid; + } + + int getLaunchedFromUid() { + return launchedFromUid; + } + /** * Determines whether this ActivityRecord can turn the screen on. It checks whether the flag * {@link ActivityRecord#getTurnScreenOnFlag} is set and checks whether the ActivityRecord diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index f472a5dd7ba9..b88b54ef08f5 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -28,8 +28,6 @@ import static android.app.ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION; import static android.app.ActivityManager.START_SUCCESS; import static android.app.ActivityManager.START_TASK_TO_FRONT; import static android.app.ActivityTaskManager.INVALID_TASK_ID; -import static android.app.WaitResult.LAUNCH_STATE_COLD; -import static android.app.WaitResult.LAUNCH_STATE_HOT; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; @@ -59,9 +57,6 @@ import static android.view.WindowManager.TRANSIT_OPEN; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS; -import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME; -import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP; -import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS; @@ -73,6 +68,9 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_USER_ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.ActivityTaskManagerService.ANIMATE; +import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME; +import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP; +import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS; import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS; import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_DISPLAY; import static com.android.server.wm.Task.ActivityState.RESUMED; @@ -819,8 +817,6 @@ class ActivityStarter { break; } case START_TASK_TO_FRONT: { - mRequest.waitResult.launchState = - r.attachedToProcess() ? LAUNCH_STATE_HOT : LAUNCH_STATE_COLD; // ActivityRecord may represent a different activity, but it should not be // in the resumed state. if (r.nowVisible && r.isState(RESUMED)) { @@ -1285,6 +1281,15 @@ class ActivityStarter { return false; } + // IME should always be allowed to start activity, like IME settings. + final WindowState imeWindow = mRootWindowContainer.getCurrentInputMethodWindow(); + if (imeWindow != null && callingAppId == imeWindow.mOwnerUid) { + if (DEBUG_ACTIVITY_STARTS) { + Slog.d(TAG, "Activity start allowed for active ime (" + callingUid + ")"); + } + return false; + } + // App switching will be allowed if BAL app switching flag is not enabled, or if // its app switching rule allows it. // This is used to block background activity launch even if the app is still @@ -1292,9 +1297,8 @@ class ActivityStarter { final boolean appSwitchAllowed = mService.getBalAppSwitchesAllowed(); // don't abort if the callingUid has a visible window or is a persistent system process - final int callingUidProcState = mService.getUidState(callingUid); - final boolean callingUidHasAnyVisibleWindow = - mService.mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(callingUid); + final int callingUidProcState = mService.mActiveUids.getUidState(callingUid); + final boolean callingUidHasAnyVisibleWindow = mService.hasActiveVisibleWindow(callingUid); final boolean isCallingUidForeground = callingUidHasAnyVisibleWindow || callingUidProcState == ActivityManager.PROCESS_STATE_TOP || callingUidProcState == ActivityManager.PROCESS_STATE_BOUND_TOP; @@ -1312,10 +1316,10 @@ class ActivityStarter { // take realCallingUid into consideration final int realCallingUidProcState = (callingUid == realCallingUid) ? callingUidProcState - : mService.getUidState(realCallingUid); + : mService.mActiveUids.getUidState(realCallingUid); final boolean realCallingUidHasAnyVisibleWindow = (callingUid == realCallingUid) ? callingUidHasAnyVisibleWindow - : mService.mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(realCallingUid); + : mService.hasActiveVisibleWindow(realCallingUid); final boolean isRealCallingUidForeground = (callingUid == realCallingUid) ? isCallingUidForeground : realCallingUidHasAnyVisibleWindow @@ -1866,7 +1870,7 @@ class ActivityStarter { } mSupervisor.getLaunchParamsController().calculate(targetTask, r.info.windowLayout, r, - sourceRecord, mOptions, PHASE_BOUNDS, mLaunchParams); + sourceRecord, mOptions, PHASE_BOUNDS, mLaunchParams, mRequest); mPreferredTaskDisplayArea = mLaunchParams.hasPreferredTaskDisplayArea() ? mLaunchParams.mPreferredTaskDisplayArea : mRootWindowContainer.getDefaultTaskDisplayArea(); @@ -2027,12 +2031,12 @@ class ActivityStarter { */ private int deliverToCurrentTopIfNeeded(Task topStack, NeededUriGrants intentGrants) { final ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(mNotTop); - final boolean dontStart = top != null && mStartActivity.resultTo == null + final boolean dontStart = top != null && top.mActivityComponent.equals(mStartActivity.mActivityComponent) && top.mUserId == mStartActivity.mUserId && top.attachedToProcess() && ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0 - || isLaunchModeOneOf(LAUNCH_SINGLE_TOP, LAUNCH_SINGLE_TASK)) + || LAUNCH_SINGLE_TOP == mLaunchMode) // This allows home activity to automatically launch on secondary task display area // when it was added, if home was the top activity on default task display area, // instead of sending new intent to the home activity on default display area. @@ -2053,6 +2057,13 @@ class ActivityStarter { return START_RETURN_INTENT_TO_CALLER; } + if (mStartActivity.resultTo != null) { + mStartActivity.resultTo.sendResult(INVALID_UID, mStartActivity.resultWho, + mStartActivity.requestCode, RESULT_CANCELED, + null /* data */, null /* dataGrants */); + mStartActivity.resultTo = null; + } + deliverNewIntent(top, intentGrants); // Don't use mStartActivity.task to show the toast. We're not starting a new activity but @@ -2250,7 +2261,7 @@ class ActivityStarter { // Preferred display id is the only state we need for now and it could be updated again // after we located a reusable task (which might be resided in another display). mSupervisor.getLaunchParamsController().calculate(inTask, r.info.windowLayout, r, - sourceRecord, options, PHASE_DISPLAY, mLaunchParams); + sourceRecord, options, PHASE_DISPLAY, mLaunchParams, mRequest); mPreferredTaskDisplayArea = mLaunchParams.hasPreferredTaskDisplayArea() ? mLaunchParams.mPreferredTaskDisplayArea : mRootWindowContainer.getDefaultTaskDisplayArea(); diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java index f649a2f9f5a6..a5df2a604808 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java @@ -514,7 +514,6 @@ public abstract class ActivityTaskManagerInternal { public abstract void onUidActive(int uid, int procState); public abstract void onUidInactive(int uid); - public abstract void onActiveUidsCleared(); public abstract void onUidProcStateChanged(int uid, int procState); public abstract void onUidAddedToPendingTempAllowlist(int uid, String tag); diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index e4c607dd8cdd..e12dc2bd56a8 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -390,7 +390,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { private UserManagerService mUserManager; private AppOpsManager mAppOpsManager; /** All active uids in the system. */ - private final MirrorActiveUids mActiveUids = new MirrorActiveUids(); + final MirrorActiveUids mActiveUids = new MirrorActiveUids(); private final SparseArray<String> mPendingTempAllowlist = new SparseArray<>(); /** All processes currently running that might have a window organized by name. */ final ProcessMap<WindowProcessController> mProcessNames = new ProcessMap<>(); @@ -1056,7 +1056,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public void onUserUnlocking(@NonNull TargetUser user) { + public void onUserUnlocked(@NonNull TargetUser user) { synchronized (mService.getGlobalLock()) { mService.mTaskSupervisor.onUserUnlocked(user.getUserIdentifier()); } @@ -3646,7 +3646,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { */ private static int checkCallingPermission(String permission) { return checkPermission( - permission, Binder.getCallingPid(), UserHandle.getAppId(Binder.getCallingUid())); + permission, Binder.getCallingPid(), Binder.getCallingUid()); } /** This can be called with or without the global lock held. */ @@ -4956,6 +4956,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { printedAnything = true; mTaskSupervisor.dump(pw, " "); mTaskOrganizerController.dump(pw, " "); + mVisibleActivityProcessTracker.dump(pw, " "); + mActiveUids.dump(pw, " "); } if (!printedAnything) { @@ -5986,13 +5988,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return null; } - int getUidState(int uid) { - return mActiveUids.getUidState(uid); - } - - boolean isUidForeground(int uid) { - // A uid is considered to be foreground if it has a visible non-toast window. - return mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(uid); + /** A uid is considered to be foreground if it has a visible non-toast window. */ + boolean hasActiveVisibleWindow(int uid) { + if (mVisibleActivityProcessTracker.hasVisibleActivity(uid)) { + return true; + } + return mActiveUids.hasNonAppVisibleWindow(uid); } boolean isDeviceOwner(int uid) { @@ -7283,12 +7284,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @HotPath(caller = HotPath.OOM_ADJUSTMENT) @Override - public void onActiveUidsCleared() { - mActiveUids.onActiveUidsCleared(); - } - - @HotPath(caller = HotPath.OOM_ADJUSTMENT) - @Override public void onUidProcStateChanged(int uid, int procState) { mActiveUids.onUidProcStateChanged(uid, procState); } @@ -7432,9 +7427,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public boolean isUidForeground(int uid) { - synchronized (mGlobalLock) { - return ActivityTaskManagerService.this.isUidForeground(uid); - } + return ActivityTaskManagerService.this.hasActiveVisibleWindow(uid); } @Override diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index e91a6d8e2439..d0c26af82d99 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -186,7 +186,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { private static final int RESTART_ACTIVITY_PROCESS_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG + 13; private static final int REPORT_MULTI_WINDOW_MODE_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 14; private static final int REPORT_PIP_MODE_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 15; - private static final int REPORT_HOME_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 16; + private static final int START_HOME_MSG = FIRST_SUPERVISOR_STACK_MSG + 16; private static final int TOP_RESUMED_STATE_LOSS_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG + 17; // Used to indicate that windows of activities should be preserved during the resize. @@ -254,6 +254,12 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { private LaunchParamsController mLaunchParamsController; /** + * The processes with changed states that should eventually call + * {@link WindowProcessController#computeProcessActivityState}. + */ + private final ArrayList<WindowProcessController> mActivityStateChangedProcs = new ArrayList<>(); + + /** * Maps the task identifier that activities are currently being started in to the userId of the * task. Each time a new task is created, the entry for the userId of the task is incremented */ @@ -440,6 +446,9 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { // unlocked. mPersisterQueue.startPersisting(); mLaunchParamsPersister.onUnlockUser(userId); + + // Need to launch home again for those displays that do not have encryption aware home app. + scheduleStartHome("userUnlocked"); } public ActivityMetricsLogger getActivityMetricsLogger() { @@ -553,14 +562,16 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { // down to the max limit while they are still waiting to finish. mFinishingActivities.remove(r); - stopWaitingForActivityVisible(r, WaitResult.INVALID_DELAY); + stopWaitingForActivityVisible(r); } + /** There is no valid launch time, just stop waiting. */ void stopWaitingForActivityVisible(ActivityRecord r) { - stopWaitingForActivityVisible(r, getActivityMetricsLogger().getLastDrawnDelayMs(r)); + stopWaitingForActivityVisible(r, WaitResult.INVALID_DELAY, WaitResult.LAUNCH_STATE_UNKNOWN); } - void stopWaitingForActivityVisible(ActivityRecord r, long totalTime) { + void stopWaitingForActivityVisible(ActivityRecord r, long totalTime, + @WaitResult.LaunchState int launchState) { boolean changed = false; for (int i = mWaitingForActivityVisible.size() - 1; i >= 0; --i) { final WaitInfo w = mWaitingForActivityVisible.get(i); @@ -570,6 +581,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { result.timeout = false; result.who = w.getComponent(); result.totalTime = totalTime; + result.launchState = launchState; mWaitingForActivityVisible.remove(w); } } @@ -947,13 +959,17 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { void updateHomeProcess(WindowProcessController app) { if (app != null && mService.mHomeProcess != app) { - if (!mHandler.hasMessages(REPORT_HOME_CHANGED_MSG)) { - mHandler.sendEmptyMessage(REPORT_HOME_CHANGED_MSG); - } + scheduleStartHome("homeChanged"); mService.mHomeProcess = app; } } + private void scheduleStartHome(String reason) { + if (!mHandler.hasMessages(START_HOME_MSG)) { + mHandler.obtainMessage(START_HOME_MSG, reason).sendToTarget(); + } + } + private void logIfTransactionTooLarge(Intent intent, Bundle icicle) { int extrasSize = 0; if (intent != null) { @@ -1306,6 +1322,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { if (mRootWindowContainer.allResumedActivitiesIdle()) { if (r != null) { mService.scheduleAppGcsLocked(); + mRecentTasks.onActivityIdle(r); } if (mLaunchingActivityWakeLock.isHeld()) { @@ -2307,6 +2324,9 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { /** Ends a batch of visibility updates. */ void endActivityVisibilityUpdate() { mVisibilityTransactionDepth--; + if (mVisibilityTransactionDepth == 0) { + computeProcessActivityStateBatch(); + } } /** Returns {@code true} if the caller is on the path to update visibility. */ @@ -2315,6 +2335,34 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { } /** + * Called when the state or visibility of an attached activity is changed. + * + * @param wpc The process who owns the activity. + * @param forceBatch Whether to put the changed record to a pending list. If the caller is not + * in the path of visibility update ({@link #inActivityVisibilityUpdate}), it + * must call {@link #computeProcessActivityStateBatch} manually. + */ + void onProcessActivityStateChanged(WindowProcessController wpc, boolean forceBatch) { + if (forceBatch || inActivityVisibilityUpdate()) { + if (!mActivityStateChangedProcs.contains(wpc)) { + mActivityStateChangedProcs.add(wpc); + } + return; + } + wpc.computeProcessActivityState(); + } + + void computeProcessActivityStateBatch() { + if (mActivityStateChangedProcs.isEmpty()) { + return; + } + for (int i = mActivityStateChangedProcs.size() - 1; i >= 0; i--) { + mActivityStateChangedProcs.get(i).computeProcessActivityState(); + } + mActivityStateChangedProcs.clear(); + } + + /** * Begin deferring resume to avoid duplicate resumes in one pass. */ void beginDeferResume() { @@ -2432,11 +2480,11 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { handleLaunchTaskBehindCompleteLocked(r); } } break; - case REPORT_HOME_CHANGED_MSG: { - mHandler.removeMessages(REPORT_HOME_CHANGED_MSG); + case START_HOME_MSG: { + mHandler.removeMessages(START_HOME_MSG); // Start home activities on displays with no activities. - mRootWindowContainer.startHomeOnEmptyDisplays("homeChanged"); + mRootWindowContainer.startHomeOnEmptyDisplays((String) msg.obj); } break; case TOP_RESUMED_STATE_LOSS_TIMEOUT_MSG: { final ActivityRecord r = (ActivityRecord) msg.obj; diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java index 8ad2958f9a76..48e030050b07 100644 --- a/services/core/java/com/android/server/wm/DisplayArea.java +++ b/services/core/java/com/android/server/wm/DisplayArea.java @@ -382,6 +382,13 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> { @Nullable @Override + <R> R getItemFromDisplayAreas(Function<DisplayArea, R> callback) { + final R item = super.getItemFromDisplayAreas(callback); + return item != null ? item : callback.apply(this); + } + + @Nullable + @Override <R> R getItemFromTaskDisplayAreas(Function<TaskDisplayArea, R> callback, boolean traverseTopToBottom) { // Only DisplayArea of Type.ANY may contain TaskDisplayArea as children. diff --git a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java index 43b9a218d072..c475da354dda 100644 --- a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java +++ b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java @@ -16,7 +16,10 @@ package com.android.server.wm; +import static android.window.DisplayAreaOrganizer.FEATURE_RUNTIME_TASK_CONTAINER_FIRST; + import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER; +import static com.android.server.wm.DisplayArea.Type.ANY; import android.content.pm.ParceledListSlice; import android.os.Binder; @@ -26,6 +29,7 @@ import android.view.SurfaceControl; import android.window.DisplayAreaAppearedInfo; import android.window.IDisplayAreaOrganizer; import android.window.IDisplayAreaOrganizerController; +import android.window.WindowContainerToken; import com.android.internal.protolog.common.ProtoLog; @@ -36,6 +40,12 @@ import java.util.List; public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerController.Stub { private static final String TAG = "DisplayAreaOrganizerController"; + /** + * Next available feature id for a runtime task display area. + * @see #createTaskDisplayArea(IDisplayAreaOrganizer organizer, int, int, String) + */ + private int mNextTaskDisplayAreaFeatureId = FEATURE_RUNTIME_TASK_CONTAINER_FIRST; + final ActivityTaskManagerService mService; private final WindowManagerGlobalLock mGlobalLock; private final HashMap<Integer, IDisplayAreaOrganizer> mOrganizersByFeatureIds = new HashMap(); @@ -92,10 +102,8 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl final List<DisplayAreaAppearedInfo> displayAreaInfos = new ArrayList<>(); mService.mRootWindowContainer.forAllDisplayAreas((da) -> { if (da.mFeatureId != feature) return; - da.setOrganizer(organizer, true /* skipDisplayAreaAppeared */); - displayAreaInfos.add(new DisplayAreaAppearedInfo(da.getDisplayAreaInfo(), - new SurfaceControl(da.getSurfaceControl(), - "DisplayAreaOrganizerController.registerOrganizer"))); + displayAreaInfos.add(organizeDisplayArea(organizer, da, + "DisplayAreaOrganizerController.registerOrganizer")); }); mOrganizersByFeatureIds.put(feature, organizer); @@ -124,6 +132,77 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl } } + @Override + public DisplayAreaAppearedInfo createTaskDisplayArea(IDisplayAreaOrganizer organizer, + int displayId, int rootFeatureId, String name) { + enforceTaskPermission("createTaskDisplayArea()"); + final long uid = Binder.getCallingUid(); + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Create TaskDisplayArea uid=%d", uid); + + final DisplayContent display = + mService.mRootWindowContainer.getDisplayContent(displayId); + if (display == null) { + throw new IllegalArgumentException("createTaskDisplayArea unknown displayId=" + + displayId); + } + + final DisplayArea root = display.getItemFromDisplayAreas(da -> + da.asRootDisplayArea() != null && da.mFeatureId == rootFeatureId + ? da + : null); + if (root == null) { + throw new IllegalArgumentException("Can't find RootDisplayArea with featureId=" + + rootFeatureId); + } + + final int taskDisplayAreaFeatureId = mNextTaskDisplayAreaFeatureId++; + final DeathRecipient dr = new DeathRecipient(organizer, taskDisplayAreaFeatureId); + try { + organizer.asBinder().linkToDeath(dr, 0); + } catch (RemoteException e) { + // Oh well... + } + + final TaskDisplayArea tda = createTaskDisplayArea(root.asRootDisplayArea(), name, + taskDisplayAreaFeatureId); + return organizeDisplayArea(organizer, tda, + "DisplayAreaOrganizerController.createTaskDisplayArea"); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override + public void deleteTaskDisplayArea(WindowContainerToken token) { + enforceTaskPermission("deleteTaskDisplayArea()"); + final long uid = Binder.getCallingUid(); + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Delete TaskDisplayArea uid=%d", uid); + + final WindowContainer wc = WindowContainer.fromBinder(token.asBinder()); + if (wc == null || wc.asTaskDisplayArea() == null) { + throw new IllegalArgumentException("Can't resolve TaskDisplayArea from token"); + } + final TaskDisplayArea taskDisplayArea = wc.asTaskDisplayArea(); + if (!taskDisplayArea.mCreatedByOrganizer) { + throw new IllegalArgumentException( + "Attempt to delete TaskDisplayArea not created by organizer " + + "TaskDisplayArea=" + taskDisplayArea); + } + + deleteTaskDisplayArea(taskDisplayArea); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + void onDisplayAreaAppeared(IDisplayAreaOrganizer organizer, DisplayArea da) { ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "DisplayArea appeared name=%s", da.getName()); try { @@ -157,8 +236,71 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl IBinder organizerBinder = organizer.asBinder(); mService.mRootWindowContainer.forAllDisplayAreas((da) -> { if (da.mOrganizer != null && da.mOrganizer.asBinder().equals(organizerBinder)) { - da.setOrganizer(null); + if (da.isTaskDisplayArea() && da.asTaskDisplayArea().mCreatedByOrganizer) { + // Delete the organizer created TDA when unregister. + deleteTaskDisplayArea(da.asTaskDisplayArea()); + } else { + da.setOrganizer(null); + } + } + }); + } + + private DisplayAreaAppearedInfo organizeDisplayArea(IDisplayAreaOrganizer organizer, + DisplayArea displayArea, String callsite) { + displayArea.setOrganizer(organizer, true /* skipDisplayAreaAppeared */); + return new DisplayAreaAppearedInfo(displayArea.getDisplayAreaInfo(), + new SurfaceControl(displayArea.getSurfaceControl(), callsite)); + } + + private TaskDisplayArea createTaskDisplayArea(RootDisplayArea root, String name, + int taskDisplayAreaFeatureId) { + final TaskDisplayArea taskDisplayArea = new TaskDisplayArea(root.mDisplayContent, + root.mWmService, name, taskDisplayAreaFeatureId, true /* createdByOrganizer */); + + // Find the top most DA that can contain Task (either a TDA or a DisplayAreaGroup). + final DisplayArea topTaskContainer = root.getItemFromDisplayAreas(da -> { + if (da.mType != ANY) { + return null; + } + + final RootDisplayArea rootDA = da.getRootDisplayArea(); + if (rootDA == root || rootDA == da) { + // Either it is the top TDA below the root or it is a DisplayAreaGroup. + return da; } + return null; }); + if (topTaskContainer == null) { + throw new IllegalStateException("Root must either contain TDA or DAG root=" + root); + } + + // Insert the TaskDisplayArea as the top Task container. + final WindowContainer parent = topTaskContainer.getParent(); + final int index = parent.mChildren.indexOf(topTaskContainer) + 1; + parent.addChild(taskDisplayArea, index); + + return taskDisplayArea; + } + + private void deleteTaskDisplayArea(TaskDisplayArea taskDisplayArea) { + taskDisplayArea.setOrganizer(null); + mService.mRootWindowContainer.mTaskSupervisor.beginDeferResume(); + + // TaskDisplayArea#remove() move the stacks to the default TaskDisplayArea. + Task lastReparentedStack; + try { + lastReparentedStack = taskDisplayArea.remove(); + } finally { + mService.mRootWindowContainer.mTaskSupervisor.endDeferResume(); + } + + taskDisplayArea.removeImmediately(); + + // Only update focus/visibility for the last one because there may be many stacks are + // reparented and the intermediate states are unnecessary. + if (lastReparentedStack != null) { + lastReparentedStack.postReparent(); + } } } diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java index 80ec722bc274..6a420874b924 100644 --- a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java +++ b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java @@ -172,10 +172,16 @@ class DisplayAreaPolicyBuilder { throw new IllegalStateException("Root must be set for the display area policy."); } + final Set<Integer> rootIdSet = new ArraySet<>(); + rootIdSet.add(mRootHierarchyBuilder.mRoot.mFeatureId); boolean containsImeContainer = mRootHierarchyBuilder.mImeContainer != null; boolean containsDefaultTda = containsDefaultTaskDisplayArea(mRootHierarchyBuilder); for (int i = 0; i < mDisplayAreaGroupHierarchyBuilders.size(); i++) { HierarchyBuilder hierarchyBuilder = mDisplayAreaGroupHierarchyBuilders.get(i); + if (!rootIdSet.add(hierarchyBuilder.mRoot.mFeatureId)) { + throw new IllegalStateException("There should not be two RootDisplayAreas with id " + + hierarchyBuilder.mRoot.mFeatureId); + } if (hierarchyBuilder.mTaskDisplayAreas.isEmpty()) { throw new IllegalStateException( "DisplayAreaGroup must contain at least one TaskDisplayArea."); diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index ec95089ef415..ccc85f834bc1 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -73,6 +73,8 @@ import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; +import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL; +import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.window.DisplayAreaOrganizer.FEATURE_ROOT; @@ -91,7 +93,7 @@ import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_C import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; import static com.android.server.wm.DisplayContentProto.APP_TRANSITION; -import static com.android.server.wm.DisplayContentProto.CAN_SHOW_IME; +import static com.android.server.wm.DisplayContentProto.IME_POLICY; import static com.android.server.wm.DisplayContentProto.CLOSING_APPS; import static com.android.server.wm.DisplayContentProto.CURRENT_FOCUS; import static com.android.server.wm.DisplayContentProto.DISPLAY_FRAMES; @@ -203,6 +205,7 @@ import android.view.SurfaceControl.Transaction; import android.view.SurfaceSession; import android.view.WindowInsets; import android.view.WindowManager; +import android.view.WindowManager.DisplayImePolicy; import android.view.WindowManagerPolicyConstants.PointerEventListener; import com.android.internal.annotations.VisibleForTesting; @@ -626,12 +629,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp */ private boolean mInEnsureActivitiesVisible = false; - /** - * Last window to be requested focus via {@code SurfaceControl.Transaction#setFocusedWindow} to - * prevent duplicate requests to input. - */ - WindowState mLastRequestedFocus = null; - private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> { WindowStateAnimator winAnimator = w.mWinAnimator; final ActivityRecord activity = w.mActivityRecord; @@ -2936,7 +2933,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp mInsetsStateController.getImeSourceProvider().dumpDebug(proto, IME_INSETS_SOURCE_PROVIDER, logLevel); } - proto.write(CAN_SHOW_IME, canShowIme()); + proto.write(IME_POLICY, getImePolicy()); proto.end(token); } @@ -3567,7 +3564,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp * @return {@link InsetsControlTarget} that can host IME. */ InsetsControlTarget getImeHostOrFallback(WindowState target) { - if (target != null && target.getDisplayContent().canShowIme()) { + if (target != null + && target.getDisplayContent().getImePolicy() == DISPLAY_IME_POLICY_LOCAL) { return target; } return getImeFallback(); @@ -3581,12 +3579,17 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp return statusBar != null ? statusBar : defaultDc.mRemoteInsetsControlTarget; } - boolean canShowIme() { + @DisplayImePolicy int getImePolicy() { if (!isTrusted()) { - return false; + return DISPLAY_IME_POLICY_FALLBACK_DISPLAY; + } + final int imePolicy = mWmService.mDisplayWindowSettings.getImePolicyLocked(this); + if (imePolicy == DISPLAY_IME_POLICY_FALLBACK_DISPLAY && forceDesktopMode()) { + // If the display has not explicitly requested for the IME to be hidden then it shall + // show the IME locally. + return DISPLAY_IME_POLICY_LOCAL; } - return mWmService.mDisplayWindowSettings.shouldShowImeLocked(this) - || forceDesktopMode(); + return imePolicy; } boolean forceDesktopMode() { @@ -5384,20 +5387,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp forAllTasks((t) -> { t.getRootTask().removeChild(t, "removeAllTasks"); }); } - /** - * Similar to {@link RootWindowContainer#isAnyNonToastWindowVisibleForUid(int)}, but - * used for pid. - */ - boolean isAnyNonToastWindowVisibleForPid(int pid) { - final PooledPredicate p = PooledLambda.obtainPredicate( - WindowState::isNonToastWindowVisibleForPid, - PooledLambda.__(WindowState.class), pid); - - final WindowState w = getWindow(p); - p.recycle(); - return w != null; - } - Context getDisplayUiContext() { return mDisplayPolicy.getSystemUiContext(); } diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index f14a2ee8e7ee..826b7259a9ff 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -66,7 +66,6 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BA import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; @@ -279,7 +278,6 @@ public class DisplayPolicy { private volatile boolean mKeyguardDrawComplete; private volatile boolean mWindowManagerDrawComplete; - private final ArraySet<WindowState> mScreenDecorWindows = new ArraySet<>(); private WindowState mStatusBar = null; private WindowState mNotificationShade = null; private final int[] mStatusBarHeightForRotation = new int[4]; @@ -864,19 +862,7 @@ public class DisplayPolicy { * @param attrs The window layout parameters to be modified. These values * are modified in-place. */ - public void adjustWindowParamsLw(WindowState win, WindowManager.LayoutParams attrs, - int callingPid, int callingUid) { - - final boolean isScreenDecor = (attrs.privateFlags & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0; - if (mScreenDecorWindows.contains(win)) { - if (!isScreenDecor) { - // No longer has the flag set, so remove from the set. - mScreenDecorWindows.remove(win); - } - } else if (isScreenDecor && hasStatusBarServicePermission(callingPid, callingUid)) { - mScreenDecorWindows.add(win); - } - + public void adjustWindowParamsLw(WindowState win, WindowManager.LayoutParams attrs) { switch (attrs.type) { case TYPE_SYSTEM_OVERLAY: case TYPE_SECURE_SYSTEM_OVERLAY: @@ -966,11 +952,6 @@ public class DisplayPolicy { * WindowManagerImpl.ADD_MULTIPLE_SINGLETON */ int validateAddingWindowLw(WindowManager.LayoutParams attrs, int callingPid, int callingUid) { - if ((attrs.privateFlags & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0) { - mContext.enforcePermission( - android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid, - "DisplayPolicy"); - } if ((attrs.privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0) { mContext.enforcePermission( android.Manifest.permission.INTERNAL_SYSTEM_WINDOW, callingPid, callingUid, @@ -1090,10 +1071,6 @@ public class DisplayPolicy { * @param attrs Information about the window to be added. */ void addWindowLw(WindowState win, WindowManager.LayoutParams attrs) { - if ((attrs.privateFlags & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0) { - mScreenDecorWindows.add(win); - } - switch (attrs.type) { case TYPE_NOTIFICATION_SHADE: mNotificationShade = win; @@ -1275,7 +1252,6 @@ public class DisplayPolicy { if (mLastFocusedWindow == win) { mLastFocusedWindow = null; } - mScreenDecorWindows.remove(win); } private int getStatusBarHeight(DisplayFrames displayFrames) { @@ -1457,14 +1433,12 @@ public class DisplayPolicy { } final int fl = attrs.flags; - final int pfl = attrs.privateFlags; final boolean layoutInScreenAndInsetDecor = (fl & FLAG_LAYOUT_IN_SCREEN) != 0 && (fl & FLAG_LAYOUT_INSET_DECOR) != 0; - final boolean screenDecor = (pfl & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0; final DisplayFrames displayFrames = isFixedRotationTransforming ? windowToken.getFixedRotationTransformDisplayFrames() : mDisplayContent.mDisplayFrames; - if (layoutInScreenAndInsetDecor && !screenDecor) { + if (layoutInScreenAndInsetDecor) { outDisplayCutout.set( displayFrames.mDisplayCutout.calculateRelativeTo(outFrame).getDisplayCutout()); } else { @@ -1564,7 +1538,6 @@ public class DisplayPolicy { simulatedWindowFrames, barContentFrames, contentFrame -> layoutStatusBar(displayFrames, contentFrame)); } - layoutScreenDecorWindows(displayFrames, simulatedWindowFrames); } /** @@ -1585,7 +1558,6 @@ public class DisplayPolicy { layoutNavigationBar(displayFrames, uiMode, null /* simulatedContentFrame */); layoutStatusBar(displayFrames, null /* simulatedContentFrame */); - layoutScreenDecorWindows(displayFrames, null /* simulatedFrames */); } void updateHideNavInputEventReceiver() { @@ -1640,47 +1612,6 @@ public class DisplayPolicy { state.getSource(ITYPE_BOTTOM_DISPLAY_CUTOUT).setFrame(u.left, s.bottom, u.right, u.bottom); } - /** - * Layout the decor windows with {@link #PRIVATE_FLAG_IS_SCREEN_DECOR}. - * - * @param displayFrames The display frames to be layouted. - * @param simulatedFrames Non-null if the caller only needs the result of display frames (see - * {@link WindowState#mSimulatedWindowFrames}). - */ - private void layoutScreenDecorWindows(DisplayFrames displayFrames, - WindowFrames simulatedFrames) { - if (mScreenDecorWindows.isEmpty()) { - return; - } - - sTmpRect.setEmpty(); - final int displayId = displayFrames.mDisplayId; - - for (int i = mScreenDecorWindows.size() - 1; i >= 0; --i) { - final WindowState w = mScreenDecorWindows.valueAt(i); - if (w.getDisplayId() != displayId || !w.isVisible()) { - // Skip if not on the same display or not visible. - continue; - } - - final boolean isSimulatedLayout = simulatedFrames != null; - if (isSimulatedLayout) { - w.setSimulatedWindowFrames(simulatedFrames); - } - getRotatedWindowBounds(displayFrames, w, sTmpScreenDecorFrame); - final WindowFrames windowFrames = w.getLayoutingWindowFrames(); - windowFrames.setFrames(sTmpScreenDecorFrame /* parentFrame */, - sTmpScreenDecorFrame /* displayFrame */); - try { - w.computeFrame(displayFrames); - } finally { - if (isSimulatedLayout) { - w.setSimulatedWindowFrames(null); - } - } - } - } - private void layoutStatusBar(DisplayFrames displayFrames, Rect simulatedContentFrame) { // decide where the status bar goes ahead of time if (mStatusBar == null) { @@ -1819,8 +1750,7 @@ public class DisplayPolicy { // We've already done the navigation bar, status bar, and all screen decor windows. If the // status bar can receive input, we need to layout it again to accommodate for the IME // window. - if ((win == mStatusBar && !canReceiveInput(win)) || win == mNavigationBar - || mScreenDecorWindows.contains(win)) { + if ((win == mStatusBar && !canReceiveInput(win)) || win == mNavigationBar) { return; } final WindowManager.LayoutParams attrs = win.getAttrs(); @@ -2772,7 +2702,11 @@ public class DisplayPolicy { && mLastDockedStackBounds.equals(mDockedStackBounds)) { return false; } - + if (mDisplayContent.isDefaultDisplay && mLastFocusIsFullscreen != isFullscreen + && ((mLastAppearance ^ appearance) & APPEARANCE_LOW_PROFILE_BARS) != 0) { + mService.mInputManager.setSystemUiLightsOut( + isFullscreen || (appearance & APPEARANCE_LOW_PROFILE_BARS) != 0); + } mLastDisableFlags = disableFlags; mLastAppearance = appearance; mLastFullscreenAppearance = fullscreenAppearance; @@ -2802,10 +2736,6 @@ public class DisplayPolicy { } }); - if (mDisplayContent.isDefaultDisplay) { - mService.mInputManager.setSystemUiLightsOut( - isFullscreen || (appearance & APPEARANCE_LOW_PROFILE_BARS) != 0); - } return true; } diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java index 472678cf8e37..5d4dbc8388c4 100644 --- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java +++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java @@ -19,6 +19,8 @@ package com.android.server.wm; import static android.view.WindowManager.REMOVE_CONTENT_MODE_DESTROY; import static android.view.WindowManager.REMOVE_CONTENT_MODE_MOVE_TO_PRIMARY; import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED; +import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL; +import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY; import static com.android.server.wm.DisplayContent.FORCE_SCALING_MODE_AUTO; import static com.android.server.wm.DisplayContent.FORCE_SCALING_MODE_DISABLED; @@ -31,6 +33,7 @@ import android.view.Display; import android.view.DisplayInfo; import android.view.IWindowManager; import android.view.Surface; +import android.view.WindowManager.DisplayImePolicy; import com.android.server.policy.WindowManagerPolicy; import com.android.server.wm.DisplayContent.ForceScalingMode; @@ -212,22 +215,23 @@ class DisplayWindowSettings { mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings); } - boolean shouldShowImeLocked(DisplayContent dc) { + @DisplayImePolicy int getImePolicyLocked(DisplayContent dc) { if (dc.getDisplayId() == Display.DEFAULT_DISPLAY) { // Default display should show IME. - return true; + return DISPLAY_IME_POLICY_LOCAL; } final DisplayInfo displayInfo = dc.getDisplayInfo(); final SettingsProvider.SettingsEntry settings = mSettingsProvider.getSettings(displayInfo); - return settings.mShouldShowIme != null ? settings.mShouldShowIme : false; + return settings.mImePolicy != null ? settings.mImePolicy + : DISPLAY_IME_POLICY_FALLBACK_DISPLAY; } - void setShouldShowImeLocked(DisplayContent dc, boolean shouldShow) { + void setDisplayImePolicy(DisplayContent dc, @DisplayImePolicy int imePolicy) { final DisplayInfo displayInfo = dc.getDisplayInfo(); final SettingsProvider.SettingsEntry overrideSettings = mSettingsProvider.getOverrideSettings(displayInfo); - overrideSettings.mShouldShowIme = shouldShow; + overrideSettings.mImePolicy = imePolicy; mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings); } @@ -343,7 +347,7 @@ class DisplayWindowSettings { @Nullable Boolean mShouldShowSystemDecors; @Nullable - Boolean mShouldShowIme; + Integer mImePolicy; @Nullable Integer mFixedToUserRotation; @Nullable @@ -406,8 +410,8 @@ class DisplayWindowSettings { mShouldShowSystemDecors = other.mShouldShowSystemDecors; changed = true; } - if (other.mShouldShowIme != mShouldShowIme) { - mShouldShowIme = other.mShouldShowIme; + if (!Objects.equals(other.mImePolicy, mImePolicy)) { + mImePolicy = other.mImePolicy; changed = true; } if (!Objects.equals(other.mFixedToUserRotation, mFixedToUserRotation)) { @@ -481,9 +485,9 @@ class DisplayWindowSettings { mShouldShowSystemDecors = delta.mShouldShowSystemDecors; changed = true; } - if (delta.mShouldShowIme != null - && delta.mShouldShowIme != mShouldShowIme) { - mShouldShowIme = delta.mShouldShowIme; + if (delta.mImePolicy != null + && !Objects.equals(delta.mImePolicy, mImePolicy)) { + mImePolicy = delta.mImePolicy; changed = true; } if (delta.mFixedToUserRotation != null @@ -509,7 +513,7 @@ class DisplayWindowSettings { && mRemoveContentMode == REMOVE_CONTENT_MODE_UNDEFINED && mShouldShowWithInsecureKeyguard == null && mShouldShowSystemDecors == null - && mShouldShowIme == null + && mImePolicy == null && mFixedToUserRotation == null && mIgnoreOrientationRequest == null; } @@ -530,7 +534,7 @@ class DisplayWindowSettings { && Objects.equals(mShouldShowWithInsecureKeyguard, that.mShouldShowWithInsecureKeyguard) && Objects.equals(mShouldShowSystemDecors, that.mShouldShowSystemDecors) - && Objects.equals(mShouldShowIme, that.mShouldShowIme) + && Objects.equals(mImePolicy, that.mImePolicy) && Objects.equals(mFixedToUserRotation, that.mFixedToUserRotation) && Objects.equals(mIgnoreOrientationRequest, that.mIgnoreOrientationRequest); @@ -540,7 +544,7 @@ class DisplayWindowSettings { public int hashCode() { return Objects.hash(mWindowingMode, mUserRotationMode, mUserRotation, mForcedWidth, mForcedHeight, mForcedDensity, mForcedScalingMode, mRemoveContentMode, - mShouldShowWithInsecureKeyguard, mShouldShowSystemDecors, mShouldShowIme, + mShouldShowWithInsecureKeyguard, mShouldShowSystemDecors, mImePolicy, mFixedToUserRotation, mIgnoreOrientationRequest); } @@ -557,7 +561,7 @@ class DisplayWindowSettings { + ", mRemoveContentMode=" + mRemoveContentMode + ", mShouldShowWithInsecureKeyguard=" + mShouldShowWithInsecureKeyguard + ", mShouldShowSystemDecors=" + mShouldShowSystemDecors - + ", mShouldShowIme=" + mShouldShowIme + + ", mShouldShowIme=" + mImePolicy + ", mFixedToUserRotation=" + mFixedToUserRotation + ", mIgnoreOrientationRequest=" + mIgnoreOrientationRequest + '}'; diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java index 83da136e2005..78f1426348a7 100644 --- a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java +++ b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java @@ -16,7 +16,9 @@ package com.android.server.wm; +import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL; import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED; +import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; @@ -35,13 +37,11 @@ import android.view.DisplayAddress; import android.view.DisplayInfo; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.XmlUtils; import com.android.server.wm.DisplayWindowSettings.SettingsProvider; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.File; import java.io.FileNotFoundException; @@ -49,7 +49,6 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; @@ -390,8 +389,15 @@ class DisplayWindowSettingsProvider implements SettingsProvider { "shouldShowWithInsecureKeyguard", null /* defaultValue */); settingsEntry.mShouldShowSystemDecors = getBooleanAttribute(parser, "shouldShowSystemDecors", null /* defaultValue */); - settingsEntry.mShouldShowIme = getBooleanAttribute(parser, "shouldShowIme", + final Boolean shouldShowIme = getBooleanAttribute(parser, "shouldShowIme", null /* defaultValue */); + if (shouldShowIme != null) { + settingsEntry.mImePolicy = shouldShowIme ? DISPLAY_IME_POLICY_LOCAL + : DISPLAY_IME_POLICY_FALLBACK_DISPLAY; + } else { + settingsEntry.mImePolicy = getIntegerAttribute(parser, "imePolicy", + null /* defaultValue */); + } settingsEntry.mFixedToUserRotation = getIntegerAttribute(parser, "fixedToUserRotation", null /* defaultValue */); settingsEntry.mIgnoreOrientationRequest = getBooleanAttribute(parser, @@ -478,9 +484,8 @@ class DisplayWindowSettingsProvider implements SettingsProvider { out.attribute(null, "shouldShowSystemDecors", settingsEntry.mShouldShowSystemDecors.toString()); } - if (settingsEntry.mShouldShowIme != null) { - out.attribute(null, "shouldShowIme", - settingsEntry.mShouldShowIme.toString()); + if (settingsEntry.mImePolicy != null) { + out.attributeInt(null, "imePolicy", settingsEntry.mImePolicy); } if (settingsEntry.mFixedToUserRotation != null) { out.attribute(null, "fixedToUserRotation", diff --git a/services/core/java/com/android/server/wm/ImpressionAttestationController.java b/services/core/java/com/android/server/wm/ImpressionAttestationController.java index d00faef1c147..4793e1b6fb9f 100644 --- a/services/core/java/com/android/server/wm/ImpressionAttestationController.java +++ b/services/core/java/com/android/server/wm/ImpressionAttestationController.java @@ -47,6 +47,7 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import java.util.ArrayList; +import java.util.UUID; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.function.BiConsumer; @@ -78,6 +79,8 @@ public class ImpressionAttestationController { private final Handler mHandler; + private final String mSalt; + private interface Command { void run(IImpressionAttestationService service) throws RemoteException; } @@ -85,6 +88,7 @@ public class ImpressionAttestationController { ImpressionAttestationController(Context context) { mContext = context; mHandler = new Handler(Looper.getMainLooper()); + mSalt = UUID.randomUUID().toString(); } String[] getSupportedImpressionAlgorithms() { @@ -118,17 +122,17 @@ public class ImpressionAttestationController { } } - int verifyImpressionToken(ImpressionToken impressionToken) { + boolean verifyImpressionToken(ImpressionToken impressionToken) { final SyncCommand syncCommand = new SyncCommand(); Bundle results = syncCommand.run((service, remoteCallback) -> { try { - service.verifyImpressionToken(impressionToken, remoteCallback); + service.verifyImpressionToken(mSalt, impressionToken, remoteCallback); } catch (RemoteException e) { Slog.e(TAG, "Failed to invoke verifyImpressionToken command"); } }); - return results.getInt(ImpressionAttestationService.EXTRA_VERIFICATION_STATUS); + return results.getBoolean(ImpressionAttestationService.EXTRA_VERIFICATION_STATUS); } ImpressionToken generateImpressionToken(HardwareBuffer screenshot, Rect bounds, @@ -136,7 +140,8 @@ public class ImpressionAttestationController { final SyncCommand syncCommand = new SyncCommand(); Bundle results = syncCommand.run((service, remoteCallback) -> { try { - service.generateImpressionToken(screenshot, bounds, hashAlgorithm, remoteCallback); + service.generateImpressionToken(mSalt, screenshot, bounds, hashAlgorithm, + remoteCallback); } catch (RemoteException e) { Slog.e(TAG, "Failed to invoke generateImpressionToken command", e); } diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 457df4e47689..b49d83d93d54 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -73,8 +73,8 @@ import java.util.function.Consumer; final class InputMonitor { private final WindowManagerService mService; - // Current window with input focus for keys and other non-touch events. May be null. - private WindowState mInputFocus; + // Current input focus token for keys and other non-touch events. May be null. + private IBinder mInputFocus = null; // When true, need to call updateInputWindowsLw(). private boolean mUpdateInputWindowsNeeded = true; @@ -377,31 +377,62 @@ final class InputMonitor { } /** - * Called when the current input focus changes. + * Called when the current input focus changes. Will apply it in next updateInputWindows. * Layer assignment is assumed to be complete by the time this is called. */ - public void setInputFocusLw(WindowState newWindow, boolean updateInputWindows) { + void setInputFocusLw(WindowState newWindow, boolean updateInputWindows) { ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Input focus has changed to %s display=%d", newWindow, mDisplayId); + final IBinder focus = newWindow != null ? newWindow.mInputChannelToken : null; + if (focus == mInputFocus) { + return; + } - if (newWindow != mInputFocus) { - if (newWindow != null && newWindow.canReceiveKeys()) { - // Displaying a window implicitly causes dispatching to be unpaused. - // This is to protect against bugs if someone pauses dispatching but - // forgets to resume. - newWindow.mToken.paused = false; - } + if (newWindow != null && newWindow.canReceiveKeys()) { + // Displaying a window implicitly causes dispatching to be unpaused. + // This is to protect against bugs if someone pauses dispatching but + // forgets to resume. + newWindow.mToken.paused = false; + } - mInputFocus = newWindow; - setUpdateInputWindowsNeededLw(); + setUpdateInputWindowsNeededLw(); - if (updateInputWindows) { - updateInputWindowsLw(false /*force*/); - } + if (updateInputWindows) { + updateInputWindowsLw(false /*force*/); + } + } + + /** + * Called when the current input focus changes. + */ + private void updateInputFocusRequest() { + final WindowState focus = mDisplayContent.mCurrentFocus; + final IBinder focusToken = focus != null ? focus.mInputChannelToken : null; + + if (focusToken == null) { + mInputFocus = null; + return; + } + + if (!focus.mWinAnimator.hasSurface() || !focus.mInputWindowHandle.isFocusable()) { + Slog.v(TAG_WM, "Focus not requested for window=%" + focus + + " because it has no surface or is not focusable."); + mInputFocus = null; + return; + } + + if (focusToken == mInputFocus) { + return; } + + mInputFocus = focusToken; + mInputTransaction.setFocusedWindow(mInputFocus, mDisplayId); + EventLog.writeEvent(LOGTAG_INPUT_FOCUS, "Focus request " + focus, + "reason=UpdateInputWindows"); + ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Focus requested for window=%s", focus); } - public void setFocusedAppLw(ActivityRecord newApp) { + void setFocusedAppLw(ActivityRecord newApp) { // Focused app has changed. mService.mInputManager.setFocusedApplication(mDisplayId, newApp != null ? newApp.getInputApplicationHandle(true /* update */) : null); @@ -482,30 +513,6 @@ final class InputMonitor { Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } - private void updateInputFocusRequest() { - if (mDisplayContent.mLastRequestedFocus == mDisplayContent.mCurrentFocus) { - return; - } - - final WindowState focus = mDisplayContent.mCurrentFocus; - if (focus == null || focus.mInputChannelToken == null) { - mDisplayContent.mLastRequestedFocus = focus; - return; - } - - if (!focus.mWinAnimator.hasSurface()) { - Slog.v(TAG_WM, "Focus not requested for window=%" + focus - + " because it has no surface."); - return; - } - - mInputTransaction.setFocusedWindow(focus.mInputChannelToken, mDisplayId); - EventLog.writeEvent(LOGTAG_INPUT_FOCUS, - "Focus request " + focus, "reason=UpdateInputWindows"); - mDisplayContent.mLastRequestedFocus = focus; - ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Focus requested for window=%s", focus); - } - @Override public void accept(WindowState w) { final InputWindowHandleWrapper inputWindowHandle = w.mInputWindowHandle; @@ -516,9 +523,9 @@ final class InputMonitor { if (w.mInputChannelToken == null || w.mRemoved || (!w.canReceiveTouchInput() && !shouldApplyRecentsInputConsumer)) { if (w.mWinAnimator.hasSurface()) { - // Assign an InputInfo with type to the overlay window which can't receive input - // event. This is used to omit Surfaces from occlusion detection. - populateOverlayInputInfo(inputWindowHandle, w.isVisible()); + // Make sure the input info can't receive input event. It may be omitted from + // occlusion detection depending on the type or if it's a trusted overlay. + populateOverlayInputInfo(inputWindowHandle, w); setInputWindowInfoIfNeeded(mInputTransaction, w.mWinAnimator.mSurfaceController.mSurfaceControl, inputWindowHandle); return; @@ -596,6 +603,12 @@ final class InputMonitor { } } + static void populateOverlayInputInfo(InputWindowHandleWrapper inputWindowHandle, + WindowState w) { + populateOverlayInputInfo(inputWindowHandle, w.isVisible()); + inputWindowHandle.setTouchOcclusionMode(w.getTouchOcclusionMode()); + } + // This would reset InputWindowHandle fields to prevent it could be found by input event. // We need to check if any new field of InputWindowHandle could impact the result. @VisibleForTesting diff --git a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java index 9339f3475684..7a4d13c2d697 100644 --- a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java +++ b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java @@ -63,6 +63,10 @@ class InputWindowHandleWrapper { return mHandle.displayId; } + boolean isFocusable() { + return mHandle.focusable; + } + InputApplicationHandle getInputApplicationHandle() { return mHandle.inputApplicationHandle; } diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java index c3b6149482e2..ebd91a093326 100644 --- a/services/core/java/com/android/server/wm/KeyguardController.java +++ b/services/core/java/com/android/server/wm/KeyguardController.java @@ -206,9 +206,8 @@ class KeyguardController { mAodShowing ? 1 : 0, 1 /* keyguardGoingAway */, "keyguardGoingAway"); - mRootWindowContainer.getDefaultDisplay() - .prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, - convertTransitFlags(flags)); + mRootWindowContainer.getDefaultDisplay().requestTransitionAndLegacyPrepare( + TRANSIT_KEYGUARD_GOING_AWAY, convertTransitFlags(flags)); updateKeyguardSleepToken(); // Some stack visibility might change (e.g. docked stack) diff --git a/services/core/java/com/android/server/wm/LaunchParamsController.java b/services/core/java/com/android/server/wm/LaunchParamsController.java index f1ae921c87f5..b6b172eeae5b 100644 --- a/services/core/java/com/android/server/wm/LaunchParamsController.java +++ b/services/core/java/com/android/server/wm/LaunchParamsController.java @@ -19,6 +19,7 @@ package com.android.server.wm; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.view.Display.INVALID_DISPLAY; +import static com.android.server.wm.ActivityStarter.Request; import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS; import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE; import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_DONE; @@ -73,9 +74,10 @@ class LaunchParamsController { * @param source The {@link ActivityRecord} from which activity was started from. * @param options The {@link ActivityOptions} specified for the activity. * @param result The resulting params. + * @param request The optional request from the activity starter. */ - void calculate(Task task, WindowLayout layout, ActivityRecord activity, - ActivityRecord source, ActivityOptions options, int phase, LaunchParams result) { + void calculate(Task task, WindowLayout layout, ActivityRecord activity, ActivityRecord source, + ActivityOptions options, int phase, LaunchParams result, @Nullable Request request) { result.reset(); if (task != null || activity != null) { @@ -91,7 +93,7 @@ class LaunchParamsController { final LaunchParamsModifier modifier = mModifiers.get(i); switch(modifier.onCalculate(task, layout, activity, source, options, phase, mTmpCurrent, - mTmpResult)) { + mTmpResult, request)) { case RESULT_SKIP: // Do not apply any results when we are told to skip continue; @@ -128,7 +130,8 @@ class LaunchParamsController { boolean layoutTask(Task task, WindowLayout layout, ActivityRecord activity, ActivityRecord source, ActivityOptions options) { - calculate(task, layout, activity, source, options, PHASE_BOUNDS, mTmpParams); + calculate(task, layout, activity, source, options, PHASE_BOUNDS, mTmpParams, + null /* request */); // No changes, return. if (mTmpParams.isEmpty()) { @@ -305,15 +308,17 @@ class LaunchParamsController { * launched should have this be non-null. * @param source the Activity that launched a new task. Could be {@code null}. * @param options {@link ActivityOptions} used to start the activity with. - * @param phase the calculation phase, see {@link LaunchParamsModifier.Phase} + * @param phase the calculation phase, see {@link Phase} * @param currentParams launching params after the process of last {@link * LaunchParamsModifier}. * @param outParams the result params to be set. + * @param request Optional data to give more context on the launch * @return see {@link LaunchParamsModifier.Result} */ @Result - int onCalculate(Task task, WindowLayout layout, ActivityRecord activity, + int onCalculate(@Nullable Task task, WindowLayout layout, ActivityRecord activity, ActivityRecord source, ActivityOptions options, @Phase int phase, - LaunchParams currentParams, LaunchParams outParams); + LaunchParams currentParams, LaunchParams outParams, + @Nullable Request request); } } diff --git a/services/core/java/com/android/server/wm/MirrorActiveUids.java b/services/core/java/com/android/server/wm/MirrorActiveUids.java index 00479428525a..4e7f1d4cca18 100644 --- a/services/core/java/com/android/server/wm/MirrorActiveUids.java +++ b/services/core/java/com/android/server/wm/MirrorActiveUids.java @@ -18,7 +18,10 @@ package com.android.server.wm; import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT; -import android.util.SparseIntArray; +import android.app.ActivityManager.ProcessState; +import android.util.SparseArray; + +import java.io.PrintWriter; /** * This is a partial mirror of {@link @com.android.server.am.ActiveUids}. It is already thread @@ -26,28 +29,64 @@ import android.util.SparseIntArray; * adjustment) or getting state from window manager (background start check). */ class MirrorActiveUids { - private SparseIntArray mUidStates = new SparseIntArray(); + private final SparseArray<UidRecord> mUidStates = new SparseArray<>(); synchronized void onUidActive(int uid, int procState) { - mUidStates.put(uid, procState); + UidRecord r = mUidStates.get(uid); + if (r == null) { + r = new UidRecord(); + mUidStates.put(uid, r); + } + r.mProcState = procState; } synchronized void onUidInactive(int uid) { mUidStates.delete(uid); } - synchronized void onActiveUidsCleared() { - mUidStates.clear(); + synchronized void onUidProcStateChanged(int uid, int procState) { + final UidRecord r = mUidStates.get(uid); + if (r != null) { + r.mProcState = procState; + } } - synchronized void onUidProcStateChanged(int uid, int procState) { - final int index = mUidStates.indexOfKey(uid); - if (index >= 0) { - mUidStates.setValueAt(index, procState); + synchronized @ProcessState int getUidState(int uid) { + final UidRecord r = mUidStates.get(uid); + return r != null ? r.mProcState : PROCESS_STATE_NONEXISTENT; + } + + /** Called when the surface of non-application (exclude toast) window is shown or hidden. */ + synchronized void onNonAppSurfaceVisibilityChanged(int uid, boolean visible) { + final UidRecord r = mUidStates.get(uid); + if (r != null) { + r.mNumNonAppVisibleWindow += visible ? 1 : -1; + } + } + + /** + * Returns {@code true} if the uid has any non-application (exclude toast) window currently + * visible to the user. The application window visibility of a uid can be found from + * {@link VisibleActivityProcessTracker}. + */ + synchronized boolean hasNonAppVisibleWindow(int uid) { + final UidRecord r = mUidStates.get(uid); + return r != null && r.mNumNonAppVisibleWindow > 0; + } + + synchronized void dump(PrintWriter pw, String prefix) { + pw.print(prefix + "NumNonAppVisibleWindowByUid:["); + for (int i = mUidStates.size() - 1; i >= 0; i--) { + final UidRecord r = mUidStates.valueAt(i); + if (r.mNumNonAppVisibleWindow > 0) { + pw.print(" " + mUidStates.keyAt(i) + ":" + r.mNumNonAppVisibleWindow); + } } + pw.println("]"); } - synchronized int getUidState(int uid) { - return mUidStates.get(uid, PROCESS_STATE_NONEXISTENT); + private static final class UidRecord { + @ProcessState int mProcState; + int mNumNonAppVisibleWindow; } } diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java index a750e2c7c685..ba6b27ac3252 100644 --- a/services/core/java/com/android/server/wm/RecentTasks.java +++ b/services/core/java/com/android/server/wm/RecentTasks.java @@ -181,6 +181,9 @@ class RecentTasks { /** The non-empty tasks that are removed from recent tasks (see {@link #removeForAddTask}). */ private final ArrayList<Task> mHiddenTasks = new ArrayList<>(); + /** Whether to trim inactive tasks when activities are idle. */ + private boolean mCheckTrimmableTasksOnIdle; + // These values are generally loaded from resources, but can be set dynamically in the tests private boolean mHasVisibleRecentTasks; private int mGlobalMaxNumTasks; @@ -1045,16 +1048,6 @@ class RecentTasks { void add(Task task) { if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "add: task=" + task); - // Only allow trimming task if it is not updating visibility for activities, so the caller - // doesn't need to handle unexpected size and index when looping task containers. - final boolean canTrimTask = !mSupervisor.inActivityVisibilityUpdate(); - - // Clean up the hidden tasks when going to home because the user may not be unable to return - // to the task from recents. - if (canTrimTask && !mHiddenTasks.isEmpty() && task.isActivityTypeHome()) { - removeUnreachableHiddenTasks(task.getWindowingMode()); - } - final boolean isAffiliated = task.mAffiliatedTaskId != task.mTaskId || task.mNextAffiliateTaskId != INVALID_TASK_ID || task.mPrevAffiliateTaskId != INVALID_TASK_ID; @@ -1183,10 +1176,7 @@ class RecentTasks { cleanupLocked(task.mUserId); } - // Trim the set of tasks to the active set - if (canTrimTask) { - trimInactiveRecentTasks(); - } + mCheckTrimmableTasksOnIdle = true; notifyTaskPersisterLocked(task, false /* flush */); } @@ -1213,6 +1203,22 @@ class RecentTasks { } /** + * Called when an activity reports idle. The caller should not be in any loop that iterates + * window hierarchy. so it is safe (e.g. index out of bound) to remove inactive tasks. + */ + void onActivityIdle(ActivityRecord r) { + // Clean up the hidden tasks when going to home because the user may not be unable to return + // to the task from recents. + if (!mHiddenTasks.isEmpty() && r.isActivityTypeHome()) { + removeUnreachableHiddenTasks(r.getWindowingMode()); + } + if (mCheckTrimmableTasksOnIdle) { + mCheckTrimmableTasksOnIdle = false; + trimInactiveRecentTasks(); + } + } + + /** * Trims the recents task list to the global max number of recents. */ private void trimInactiveRecentTasks() { diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java index b117699a67bd..823dc51e939f 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimation.java +++ b/services/core/java/com/android/server/wm/RecentsAnimation.java @@ -385,12 +385,9 @@ class RecentsAnimation implements RecentsAnimationCallbacks, mWindowManager.executeAppTransition(); final Task rootTask = targetStack.getRootTask(); - if (rootTask.isOrganized()) { - // Client state may have changed during the recents animation, so force - // send task info so the client can synchronize its state. - mService.mTaskOrganizerController.dispatchTaskInfoChanged( - rootTask, true /* force */); - } + // Client state may have changed during the recents animation, so force + // send task info so the client can synchronize its state. + rootTask.dispatchTaskInfoChangedIfNeeded(true /* force */); } catch (Exception e) { Slog.e(TAG, "Failed to clean up recents activity", e); throw e; diff --git a/services/core/java/com/android/server/wm/RootDisplayArea.java b/services/core/java/com/android/server/wm/RootDisplayArea.java index 1e5d045e8680..da04f438a496 100644 --- a/services/core/java/com/android/server/wm/RootDisplayArea.java +++ b/services/core/java/com/android/server/wm/RootDisplayArea.java @@ -57,6 +57,11 @@ class RootDisplayArea extends DisplayArea<DisplayArea> { return this; } + @Override + RootDisplayArea asRootDisplayArea() { + return this; + } + /** Whether the orientation (based on dimensions) of this root is different from the Display. */ boolean isOrientationDifferentFromDisplay() { return false; diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index a550e150a7a0..707354823817 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -276,7 +276,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // Whether tasks have moved and we need to rank the tasks before next OOM scoring private boolean mTaskLayersChanged = true; private int mTmpTaskLayerRank; - private final ArraySet<WindowProcessController> mTmpTaskLayerChangedProcs = new ArraySet<>(); private final LockedScheduler mRankTaskLayersScheduler; private boolean mTmpBoolean; @@ -578,23 +577,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } /** - * Returns {@code true} if the callingUid has any non-toast window currently visible to the - * user. Also ignores {@link android.view.WindowManager.LayoutParams#TYPE_APPLICATION_STARTING}, - * since those windows don't belong to apps. - * - * @see WindowState#isNonToastOrStarting() - */ - boolean isAnyNonToastWindowVisibleForUid(int callingUid) { - final PooledPredicate p = PooledLambda.obtainPredicate( - WindowState::isNonToastWindowVisibleForUid, - PooledLambda.__(WindowState.class), callingUid); - - final WindowState w = getWindow(p); - p.recycle(); - return w != null; - } - - /** * Returns the app window token for the input binder if it exist in the system. * NOTE: Only one AppWindowToken is allowed to exist in the system for a binder token, since * AppWindowToken represents an activity which can only exist on one display. @@ -1016,9 +998,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } } - // Remove all deferred displays stacks, tasks, and activities. - handleCompleteDeferredRemoval(); - forAllDisplays(dc -> { dc.getInputMonitor().updateInputWindowsLw(true /*force*/); dc.updateSystemGestureExclusion(); @@ -1367,7 +1346,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } for (int i = mChildren.size() - 1; i >= 0; --i) { DisplayContent dc = mChildren.get(i); - if (dc.isAnyNonToastWindowVisibleForPid(pid)) { + if (dc.getWindow(w -> pid == w.mSession.mPid && w.isVisibleNow() + && w.mAttrs.type != WindowManager.LayoutParams.TYPE_TOAST) != null) { outContexts.add(dc.getDisplayUiContext()); } } @@ -2720,16 +2700,16 @@ class RootWindowContainer extends WindowContainer<DisplayContent> if (task.mLayerRank != oldRank) { task.forAllActivities(activity -> { if (activity.hasProcess()) { - mTmpTaskLayerChangedProcs.add(activity.app); + mTaskSupervisor.onProcessActivityStateChanged(activity.app, + true /* forceBatch */); } }); } }, true /* traverseTopToBottom */); - for (int i = mTmpTaskLayerChangedProcs.size() - 1; i >= 0; i--) { - mTmpTaskLayerChangedProcs.valueAt(i).computeProcessActivityState(); + if (!mTaskSupervisor.inActivityVisibilityUpdate()) { + mTaskSupervisor.computeProcessActivityStateBatch(); } - mTmpTaskLayerChangedProcs.clear(); } void clearOtherAppTimeTrackers(AppTimeTracker except) { diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 3caf86c24380..ebf5989d6b55 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -2041,9 +2041,7 @@ class Task extends WindowContainer<WindowContainer> { } } - if (isOrganized()) { - mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(this, false /* force */); - } + dispatchTaskInfoChangedIfNeeded(false /* force */); } private static boolean setTaskDescriptionFromActivityAboveRoot( @@ -2272,8 +2270,8 @@ class Task extends WindowContainer<WindowContainer> { } // If the task organizer has changed, then it will already be receiving taskAppeared with // the latest task-info thus the task-info won't have changed. - if (!taskOrgChanged && isOrganized()) { - mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(this, false /* force */); + if (!taskOrgChanged) { + dispatchTaskInfoChangedIfNeeded(false /* force */); } } @@ -4100,6 +4098,7 @@ class Task extends WindowContainer<WindowContainer> { info.parentTaskId = rootTask == getParent() && rootTask.mCreatedByOrganizer ? rootTask.mTaskId : INVALID_TASK_ID; + info.isFocused = isFocused(); } @Nullable PictureInPictureParams getPictureInPictureParams() { @@ -4120,8 +4119,7 @@ class Task extends WindowContainer<WindowContainer> { letterboxActivityBounds)) { mLetterboxActivityBounds = Rect.copyOrNull(letterboxActivityBounds); // Forcing update to reduce visual jank during the transition. - mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged( - this, /* force= */ true); + dispatchTaskInfoChangedIfNeeded(true /* force */); } } @@ -5067,12 +5065,11 @@ class Task extends WindowContainer<WindowContainer> { */ void onWindowFocusChanged(boolean hasFocus) { updateShadowsRadius(hasFocus, getSyncTransaction()); + dispatchTaskInfoChangedIfNeeded(false /* force */); } void onPictureInPictureParamsChanged() { - if (isOrganized()) { - mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(this, true /* force */); - } + dispatchTaskInfoChangedIfNeeded(true /* force */); } /** @@ -7447,9 +7444,7 @@ class Task extends WindowContainer<WindowContainer> { @Override void onChildPositionChanged(WindowContainer child) { - if (isOrganized()) { - mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(this, false /* force */); - } + dispatchTaskInfoChangedIfNeeded(false /* force */); if (!mChildren.contains(child)) { return; @@ -7604,6 +7599,12 @@ class Task extends WindowContainer<WindowContainer> { return super.getBounds(); } + void dispatchTaskInfoChangedIfNeeded(boolean force) { + if (isOrganized()) { + mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(this, force); + } + } + @Override public void dumpDebug(ProtoOutputStream proto, long fieldId, @WindowTraceLogLevel int logLevel) { diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index c02e7ad5b7c6..81b8200aa2b4 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -157,12 +157,24 @@ final class TaskDisplayArea extends DisplayArea<Task> { */ private int mLastLeafTaskToFrontId; + /** + * Whether this TaskDisplayArea was created by a {@link android.window.DisplayAreaOrganizer}. + * If {@code true}, this will be removed when the organizer is unregistered. + */ + final boolean mCreatedByOrganizer; + TaskDisplayArea(DisplayContent displayContent, WindowManagerService service, String name, int displayAreaFeature) { + this(displayContent, service, name, displayAreaFeature, false /* createdByOrganizer */); + } + + TaskDisplayArea(DisplayContent displayContent, WindowManagerService service, String name, + int displayAreaFeature, boolean createdByOrganizer) { super(service, Type.ANY, name, displayAreaFeature); mDisplayContent = displayContent; mRootWindowContainer = service.mRoot; mAtmService = service.mAtmService; + mCreatedByOrganizer = createdByOrganizer; } /** @@ -1914,6 +1926,11 @@ final class TaskDisplayArea extends DisplayArea<Task> { } @Override + TaskDisplayArea asTaskDisplayArea() { + return this; + } + + @Override void dump(PrintWriter pw, String prefix, boolean dumpAll) { pw.println(prefix + "TaskDisplayArea " + getName()); final String doublePrefix = prefix + " "; diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java index 6dc957c1d118..8b2fa52afd22 100644 --- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java +++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java @@ -35,6 +35,7 @@ import static android.util.DisplayMetrics.DENSITY_DEFAULT; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; +import static com.android.server.wm.ActivityStarter.Request; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; @@ -99,23 +100,25 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { ActivityRecord source, ActivityOptions options, LaunchParams currentParams, LaunchParams outParams) { return onCalculate(task, layout, activity, source, options, PHASE_BOUNDS, currentParams, - outParams); + outParams, null); } @Override - public int onCalculate(Task task, ActivityInfo.WindowLayout layout, - ActivityRecord activity, ActivityRecord source, ActivityOptions options, - int phase, LaunchParams currentParams, LaunchParams outParams) { + public int onCalculate(@Nullable Task task, @NonNull ActivityInfo.WindowLayout layout, + @NonNull ActivityRecord activity, @Nullable ActivityRecord source, + ActivityOptions options, int phase, LaunchParams currentParams, LaunchParams outParams, + @Nullable Request request) { initLogBuilder(task, activity); final int result = calculate(task, layout, activity, source, options, phase, currentParams, - outParams); + outParams, request); outputLog(); return result; } - private int calculate(Task task, ActivityInfo.WindowLayout layout, - ActivityRecord activity, ActivityRecord source, ActivityOptions options, int phase, - LaunchParams currentParams, LaunchParams outParams) { + private int calculate(@Nullable Task task, @NonNull ActivityInfo.WindowLayout layout, + @NonNull ActivityRecord activity, @Nullable ActivityRecord source, + ActivityOptions options, int phase, LaunchParams currentParams, LaunchParams outParams, + @Nullable Request request) { final ActivityRecord root; if (task != null) { root = task.getRootActivity() == null ? activity : task.getRootActivity(); @@ -138,7 +141,7 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { // STEP 1: Determine the display area to launch the activity/task. final TaskDisplayArea taskDisplayArea = getPreferredLaunchTaskDisplayArea(task, - options, source, currentParams); + options, source, currentParams, activity, request); outParams.mPreferredTaskDisplayArea = taskDisplayArea; // TODO(b/152116619): Update the usages of display to use taskDisplayArea below. final DisplayContent display = taskDisplayArea.mDisplayContent; @@ -298,7 +301,8 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { } private TaskDisplayArea getPreferredLaunchTaskDisplayArea(@Nullable Task task, - @Nullable ActivityOptions options, ActivityRecord source, LaunchParams currentParams) { + @Nullable ActivityOptions options, ActivityRecord source, LaunchParams currentParams, + @NonNull ActivityRecord activityRecord, @Nullable Request request) { TaskDisplayArea taskDisplayArea = null; final WindowContainerToken optionLaunchTaskDisplayAreaToken = options != null @@ -369,7 +373,7 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { taskDisplayArea = currentParams.mPreferredTaskDisplayArea; } - // Fallback to default display if the device didn't declare support for multi-display + // Re-route to default display if the device didn't declare support for multi-display if (taskDisplayArea != null && !mSupervisor.mService.mSupportsMultiDisplay && taskDisplayArea.getDisplayId() != DEFAULT_DISPLAY) { taskDisplayArea = mSupervisor.mRootWindowContainer.getDefaultTaskDisplayArea(); @@ -377,7 +381,53 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { return (taskDisplayArea != null) ? taskDisplayArea - : mSupervisor.mRootWindowContainer.getDefaultTaskDisplayArea(); + : getFallbackDisplayAreaForActivity(activityRecord, request); + } + + /** + * Calculates the default {@link TaskDisplayArea} for a task. We attempt to put the activity + * within the same display area if possible. The strategy is to find the display in the + * following order: + * + * <ol> + * <li>The display area of the top activity from the launching process will be used</li> + * <li>The display area of the top activity from the real launching process will be used + * </li> + * <li>Default display area from the associated root window container.</li> + * </ol> + * @param activityRecord the activity being started + * @param request optional {@link Request} made to start the activity record + * @return {@link TaskDisplayArea} to house the task + */ + private TaskDisplayArea getFallbackDisplayAreaForActivity( + @NonNull ActivityRecord activityRecord, @Nullable Request request) { + + WindowProcessController controllerFromLaunchingRecord = mSupervisor.mService + .getProcessController(activityRecord.launchedFromPid, + activityRecord.launchedFromUid); + final TaskDisplayArea displayAreaForLaunchingRecord = controllerFromLaunchingRecord == null + ? null : controllerFromLaunchingRecord.getTopActivityDisplayArea(); + if (displayAreaForLaunchingRecord != null) { + return displayAreaForLaunchingRecord; + } + + WindowProcessController controllerFromProcess = mSupervisor.mService.getProcessController( + activityRecord.getProcessName(), activityRecord.getUid()); + final TaskDisplayArea displayAreaForRecord = controllerFromProcess == null ? null + : controllerFromProcess.getTopActivityDisplayArea(); + if (displayAreaForRecord != null) { + return displayAreaForRecord; + } + + WindowProcessController controllerFromRequest = request == null ? null : mSupervisor + .mService.getProcessController(request.realCallingPid, request.realCallingUid); + final TaskDisplayArea displayAreaFromSourceProcess = controllerFromRequest == null ? null + : controllerFromRequest.getTopActivityDisplayArea(); + if (displayAreaFromSourceProcess != null) { + return displayAreaFromSourceProcess; + } + + return mSupervisor.mRootWindowContainer.getDefaultTaskDisplayArea(); } private boolean canInheritWindowingModeFromSource(@NonNull DisplayContent display, diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index e64c0476db1d..009a7ef9e4f2 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -27,7 +27,6 @@ import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_WINDO import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityManager.RunningTaskInfo; -import android.app.ActivityManager.TaskDescription; import android.app.WindowConfiguration; import android.content.Intent; import android.content.pm.ActivityInfo; @@ -52,7 +51,6 @@ import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; -import java.util.Objects; import java.util.WeakHashMap; import java.util.function.Consumer; @@ -538,16 +536,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } mTmpTaskInfo.configuration.unset(); task.fillTaskInfo(mTmpTaskInfo); - boolean changed = lastInfo == null - || mTmpTaskInfo.topActivityType != lastInfo.topActivityType - || mTmpTaskInfo.isResizeable != lastInfo.isResizeable - || !Objects.equals( - mTmpTaskInfo.positionInParent, - lastInfo.positionInParent) - || isLetterboxInfoChanged(lastInfo, mTmpTaskInfo) - || mTmpTaskInfo.pictureInPictureParams != lastInfo.pictureInPictureParams - || mTmpTaskInfo.getWindowingMode() != lastInfo.getWindowingMode() - || !TaskDescription.equals(mTmpTaskInfo.taskDescription, lastInfo.taskDescription); + boolean changed = !mTmpTaskInfo.equalsForTaskOrganizer(lastInfo); if (!changed) { int cfgChanges = mTmpTaskInfo.configuration.diff(lastInfo.configuration); final int winCfgChanges = (cfgChanges & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0 @@ -582,20 +571,6 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } } - private boolean isLetterboxInfoChanged( - final RunningTaskInfo lastInfo, final RunningTaskInfo currentInfo) { - return !Objects.equals( - currentInfo.letterboxActivityBounds, - lastInfo.letterboxActivityBounds) - || !Objects.equals( - currentInfo.getConfiguration().windowConfiguration.getBounds(), - lastInfo.getConfiguration().windowConfiguration.getBounds()) - || !Objects.equals( - currentInfo.getConfiguration().windowConfiguration.getMaxBounds(), - lastInfo.getConfiguration().windowConfiguration.getMaxBounds()) - || !Objects.equals(currentInfo.parentBounds, lastInfo.parentBounds); - } - @Override public WindowContainerToken getImeTarget(int displayId) { enforceTaskPermission("getImeTarget()"); diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index 341694d8ecb2..0cdd055d4052 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -26,6 +26,7 @@ import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY; import android.annotation.IntDef; import android.annotation.NonNull; +import android.graphics.Point; import android.graphics.Rect; import android.os.Binder; import android.os.IBinder; @@ -46,7 +47,6 @@ import com.android.internal.protolog.common.ProtoLog; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; -import java.util.Set; /** * Represents a logical transition. @@ -91,13 +91,24 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe private final BLASTSyncEngine mSyncEngine; /** + * This is a leash to put animating surfaces into flatly without clipping/ordering issues. It + * is a child of all the targets' shared ancestor. + */ + private SurfaceControl mRootLeash = null; + + /** * Contains change infos for both participants and all ancestors. We have to track ancestors * because they are all promotion candidates and thus we need their start-states * to be captured. */ final ArrayMap<WindowContainer, ChangeInfo> mChanges = new ArrayMap<>(); + /** The collected participants in the transition. */ final ArraySet<WindowContainer> mParticipants = new ArraySet<>(); + + /** The final animation targets derived from participants after promotion. */ + private ArraySet<WindowContainer> mTargets = null; + private @TransitionState int mState = STATE_COLLECTING; private boolean mReadyCalled = false; @@ -190,6 +201,42 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe if (mState < STATE_PLAYING) { throw new IllegalStateException("Can't finish a non-playing transition " + mSyncId); } + final Point tmpPos = new Point(); + // usually only size 1 + final ArraySet<DisplayContent> displays = new ArraySet<>(); + // Immediately apply all surface reparents, don't wait for pending/sync/etc. + SurfaceControl.Transaction t = mController.mAtm.mWindowManager.mTransactionFactory.get(); + for (int i = mTargets.size() - 1; i >= 0; --i) { + final WindowContainer target = mTargets.valueAt(i); + if (target.getParent() != null) { + // Ensure surfaceControls are re-parented back into the hierarchy. + t.reparent(target.getSurfaceControl(), target.getParent().getSurfaceControl()); + target.getRelativePosition(tmpPos); + t.setPosition(target.getSurfaceControl(), tmpPos.x, tmpPos.y); + displays.add(target.getDisplayContent()); + } + } + // Need to update layers on ALL displays (for now) since they were all paused while + // the animation played. + for (int i = displays.size() - 1; i >= 0; --i) { + if (displays.valueAt(i) == null) continue; + displays.valueAt(i).assignChildLayers(t); + } + // Also pro-actively hide going-invisible activity surfaces in same transaction to + // prevent flickers due to reparenting and animation z-order mismatch. + for (int i = mParticipants.size() - 1; i >= 0; --i) { + final ActivityRecord ar = mParticipants.valueAt(i).asActivityRecord(); + if (ar == null || ar.mVisibleRequested || !ar.isVisible()) continue; + t.hide(ar.getSurfaceControl()); + } + if (mRootLeash.isValid()) { + t.remove(mRootLeash); + } + mRootLeash = null; + t.apply(); + t.close(); + + // Commit all going-invisible containers for (int i = 0; i < mParticipants.size(); ++i) { final ActivityRecord ar = mParticipants.valueAt(i).asActivityRecord(); if (ar == null || ar.mVisibleRequested) { @@ -234,7 +281,11 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe mState = STATE_PLAYING; mController.moveToPlaying(this); - final TransitionInfo info = calculateTransitionInfo(mType, mParticipants, mChanges); + + // Resolve the animating targets from the participants + mTargets = calculateTargets(mParticipants, mChanges); + final TransitionInfo info = calculateTransitionInfo(mType, mTargets, mChanges); + mRootLeash = info.getRootLeash(); handleNonAppWindowsInTransition(displayId, mType, mFlags); @@ -245,11 +296,14 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe mController.getTransitionPlayer().onTransitionReady(this, info, transaction); } catch (RemoteException e) { // If there's an exception when trying to send the mergedTransaction to the - // client, we should immediately apply it here so the transactions aren't lost. + // client, we should finish and apply it here so the transactions aren't lost. transaction.apply(); + finishTransition(); } } else { + // No player registered, so just finish/apply immediately transaction.apply(); + finishTransition(); } mSyncId = -1; } @@ -325,10 +379,12 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe * * @return {@code true} if transition in target can be promoted to its parent. */ - private static boolean canPromote( - WindowContainer target, ArraySet<WindowContainer> topTargets) { + private static boolean canPromote(WindowContainer target, ArraySet<WindowContainer> topTargets, + ArrayMap<WindowContainer, ChangeInfo> changes) { final WindowContainer parent = target.getParent(); - if (parent == null || !parent.canCreateRemoteAnimationTarget()) { + final ChangeInfo parentChanges = parent != null ? changes.get(parent) : null; + if (parent == null || !parent.canCreateRemoteAnimationTarget() + || parentChanges == null || !parentChanges.hasChanged(parent)) { ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " SKIP: %s", parent == null ? "no parent" : ("parent can't be target " + parent)); return false; @@ -394,14 +450,14 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe // Go through each target until we find one that can be promoted. for (WindowContainer targ : topTargets) { ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " checking %s", targ); - if (!canPromote(targ, topTargets)) { + if (!canPromote(targ, topTargets, changes)) { continue; } - final WindowContainer parent = targ.getParent(); // No obstructions found to promotion, so promote + final WindowContainer parent = targ.getParent(); + final ChangeInfo parentInfo = changes.get(parent); ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " CAN PROMOTE: promoting to parent %s", parent); - final ChangeInfo parentInfo = changes.get(parent); targets.add(parent); // Go through all children of newly-promoted container and remove them from the @@ -443,10 +499,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe * animation targets to higher level in the window hierarchy if possible. */ @VisibleForTesting - static TransitionInfo calculateTransitionInfo(int type, Set<WindowContainer> participants, + @NonNull + static ArraySet<WindowContainer> calculateTargets(ArraySet<WindowContainer> participants, ArrayMap<WindowContainer, ChangeInfo> changes) { - final TransitionInfo out = new TransitionInfo(type); - ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Start calculating TransitionInfo based on participants: %s", participants); @@ -470,6 +525,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe // Search through ancestors to find the top-most participant (if one exists) WindowContainer topParent = null; tmpList.clear(); + if (reportIfNotTop(wc)) { + tmpList.add(wc); + } for (WindowContainer p = wc.getParent(); p != null; p = p.getParent()) { if (participants.contains(p)) { topParent = p; @@ -479,8 +537,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe } } if (topParent != null) { - // There was an ancestor participant, so don't add wc to targets. However, continue - // to add any always-report parents along the way. + // There was an ancestor participant, so don't add wc to targets unless always- + // report. Similarly, add any always-report parents along the way. for (int i = 0; i < tmpList.size(); ++i) { targets.add(tmpList.get(i)); final ChangeInfo info = changes.get(tmpList.get(i)); @@ -508,10 +566,70 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe while (tryPromote(topTargets, targets, changes)) { // Empty on purpose } + return targets; + } - // Convert all the resolved ChangeInfos into a TransactionInfo object. - for (int i = targets.size() - 1; i >= 0; --i) { - final WindowContainer target = targets.valueAt(i); + /** Add any of `members` within `root` to `out` in top-to-bottom z-order. */ + private static void addMembersInOrder(WindowContainer root, ArraySet<WindowContainer> members, + ArrayList<WindowContainer> out) { + for (int i = root.getChildCount() - 1; i >= 0; --i) { + final WindowContainer child = root.getChildAt(i); + addMembersInOrder(child, members, out); + if (members.contains(child)) { + out.add(child); + } + } + } + + /** + * Construct a TransitionInfo object from a set of targets and changes. Also populates the + * root surface. + */ + @VisibleForTesting + @NonNull + static TransitionInfo calculateTransitionInfo(int type, ArraySet<WindowContainer> targets, + ArrayMap<WindowContainer, ChangeInfo> changes) { + final TransitionInfo out = new TransitionInfo(type); + if (targets.isEmpty()) { + out.setRootLeash(new SurfaceControl(), 0, 0); + return out; + } + + // Find the top-most shared ancestor + WindowContainer ancestor = targets.valueAt(0).getParent(); + // Go up ancestor parent chain until all topTargets are descendants. + ancestorLoop: + while (ancestor != null) { + for (int i = 1; i < targets.size(); ++i) { + if (!targets.valueAt(i).isDescendantOf(ancestor)) { + ancestor = ancestor.getParent(); + continue ancestorLoop; + } + } + break; + } + + // Sort targets top-to-bottom in Z. + ArrayList<WindowContainer> sortedTargets = new ArrayList<>(); + addMembersInOrder(ancestor, targets, sortedTargets); + + // make leash based on highest (z-order) direct child of ancestor with a participant. + WindowContainer leashReference = sortedTargets.get(0); + while (leashReference.getParent() != ancestor) { + leashReference = leashReference.getParent(); + } + final SurfaceControl rootLeash = leashReference.makeAnimationLeash().setName( + "Transition Root: " + leashReference.getName()).build(); + SurfaceControl.Transaction t = ancestor.mWmService.mTransactionFactory.get(); + t.setLayer(rootLeash, leashReference.getLastLayer()); + t.apply(); + t.close(); + out.setRootLeash(rootLeash, ancestor.getBounds().left, ancestor.getBounds().top); + + // Convert all the resolved ChangeInfos into TransactionInfo.Change objects in order. + final int count = sortedTargets.size(); + for (int i = 0; i < count; ++i) { + final WindowContainer target = sortedTargets.get(i); final ChangeInfo info = changes.get(target); final TransitionInfo.Change change = new TransitionInfo.Change( target.mRemoteToken != null ? target.mRemoteToken.toWindowContainerToken() @@ -520,8 +638,10 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe change.setParent(info.mParent.mRemoteToken.toWindowContainerToken()); } change.setMode(info.getTransitMode(target)); - change.setStartBounds(info.mAbsoluteBounds); - change.setEndBounds(target.getBounds()); + change.setStartAbsBounds(info.mAbsoluteBounds); + change.setEndAbsBounds(target.getBounds()); + change.setEndRelOffset(target.getBounds().left - target.getParent().getBounds().left, + target.getBounds().top - target.getParent().getBounds().top); out.addChange(change); } @@ -543,23 +663,26 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe // before change state boolean mVisible; int mWindowingMode; - Rect mAbsoluteBounds; + final Rect mAbsoluteBounds = new Rect(); ChangeInfo(@NonNull WindowContainer origState) { mVisible = origState.isVisibleRequested(); mWindowingMode = origState.getWindowingMode(); - mAbsoluteBounds = origState.getBounds(); + mAbsoluteBounds.set(origState.getBounds()); } @VisibleForTesting ChangeInfo(boolean visible, boolean existChange) { mVisible = visible; - mAbsoluteBounds = new Rect(); mExistenceChanged = existChange; } boolean hasChanged(@NonNull WindowContainer newState) { - return newState.isVisibleRequested() != mVisible + // If it's invisible and hasn't changed visibility, always return false since even if + // something changed, it wouldn't be a visible change. + final boolean currVisible = newState.isVisibleRequested(); + if (currVisible == mVisible && !mVisible) return false; + return currVisible != mVisible // if mWindowingMode is 0, this container wasn't attached at collect time, so // assume no change in windowing-mode. || (mWindowingMode != 0 && newState.getWindowingMode() != mWindowingMode) diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index 890ae8f2a434..2f5d10afe3da 100644 --- a/services/core/java/com/android/server/wm/TransitionController.java +++ b/services/core/java/com/android/server/wm/TransitionController.java @@ -109,10 +109,18 @@ class TransitionController { return mCollectingTransition != null; } + /** + * @return {@code true} if transition is actively playing. This is not necessarily {@code true} + * during collection. + */ + boolean isPlaying() { + return !mPlayingTransitions.isEmpty(); + } + /** @return {@code true} if a transition is running */ boolean inTransition() { // TODO(shell-transitions): eventually properly support multiple - return mCollectingTransition != null || !mPlayingTransitions.isEmpty(); + return isCollecting() || isPlaying(); } /** @return {@code true} if wc is in a participant subtree */ diff --git a/services/core/java/com/android/server/wm/VisibleActivityProcessTracker.java b/services/core/java/com/android/server/wm/VisibleActivityProcessTracker.java index 7f626308976b..0e2b71c02637 100644 --- a/services/core/java/com/android/server/wm/VisibleActivityProcessTracker.java +++ b/services/core/java/com/android/server/wm/VisibleActivityProcessTracker.java @@ -21,7 +21,9 @@ import android.util.ArrayMap; import com.android.internal.annotations.GuardedBy; import com.android.internal.os.BackgroundThread; +import java.io.PrintWriter; import java.util.concurrent.Executor; +import java.util.function.Predicate; /** * A quick lookup for all processes with visible activities. It also tracks the CPU usage of @@ -70,10 +72,22 @@ class VisibleActivityProcessTracker { } boolean hasResumedActivity(int uid) { + return match(uid, WindowProcessController::hasResumedActivity); + } + + /** + * Returns {@code true} if the uid has a process that contains an activity with + * {@link ActivityRecord#mVisibleRequested} or {@link ActivityRecord#isVisible()} is true. + */ + boolean hasVisibleActivity(int uid) { + return match(uid, null /* predicate */); + } + + private boolean match(int uid, Predicate<WindowProcessController> predicate) { synchronized (mProcMap) { for (int i = mProcMap.size() - 1; i >= 0; i--) { final WindowProcessController wpc = mProcMap.keyAt(i); - if (wpc.mUid == uid && wpc.hasResumedActivity()) { + if (wpc.mUid == uid && (predicate == null || predicate.test(wpc))) { return true; } } @@ -87,6 +101,16 @@ class VisibleActivityProcessTracker { } } + void dump(PrintWriter pw, String prefix) { + pw.print(prefix + "VisibleActivityProcess:["); + synchronized (mProcMap) { + for (int i = mProcMap.size() - 1; i >= 0; i--) { + pw.print(" " + mProcMap.keyAt(i)); + } + } + pw.println("]"); + } + /** * Get CPU time in background thread because it will access proc files or the lock of cpu * tracker is held by a background thread. diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java index ff5c17487413..f627ca620bc3 100644 --- a/services/core/java/com/android/server/wm/WindowAnimator.java +++ b/services/core/java/com/android/server/wm/WindowAnimator.java @@ -145,6 +145,9 @@ public class WindowAnimator { ProtoLog.i(WM_SHOW_TRANSACTIONS, ">>> OPEN TRANSACTION animate"); mService.openSurfaceTransaction(); try { + // Remove all deferred displays, tasks, and activities. + mService.mRoot.handleCompleteDeferredRemoval(); + final AccessibilityController accessibilityController = mService.mAccessibilityController; final int numDisplays = mDisplayContentsAnimators.size(); diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index b25fbc0e18a3..a1bb89d26f2f 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -1112,7 +1112,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< // descendant. E.g. if a display is pending to be removed because it contains an // activity with {@link ActivityRecord#mIsExiting} is true, the display may be // removed when completing the removal of the last activity from - // {@link ActivityRecord#checkCompleteDeferredRemoval}. + // {@link ActivityRecord#handleCompleteDeferredRemoval}. return false; } } @@ -1814,7 +1814,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< /** * For all {@link TaskDisplayArea} at or below this container call the callback. * @param callback Applies on each {@link TaskDisplayArea} found and stops the search if it - * returns {@code true}. + * returns {@code true}. * @param traverseTopToBottom If {@code true}, traverses the hierarchy from top-to-bottom in * terms of z-order, else from bottom-to-top. * @return {@code true} if the search ended before we reached the end of the hierarchy due to @@ -1837,7 +1837,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< * For all {@link TaskDisplayArea} at or below this container call the callback. Traverses from * top to bottom in terms of z-order. * @param callback Applies on each {@link TaskDisplayArea} found and stops the search if it - * returns {@code true}. + * returns {@code true}. * @return {@code true} if the search ended before we reached the end of the hierarchy due to * callback returning {@code true}. */ @@ -1873,7 +1873,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< * Performs a reduction on all {@link TaskDisplayArea} at or below this container, using the * provided initial value and an accumulation function, and returns the reduced value. * @param accumulator Applies on each {@link TaskDisplayArea} found with the accumulative result - * from the previous call. + * from the previous call. * @param initValue The initial value to pass to the accumulating function with the first * {@link TaskDisplayArea}. * @param traverseTopToBottom If {@code true}, traverses the hierarchy from top-to-bottom in @@ -1899,7 +1899,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< * provided initial value and an accumulation function, and returns the reduced value. Traverses * from top to bottom in terms of z-order. * @param accumulator Applies on each {@link TaskDisplayArea} found with the accumulative result - * from the previous call. + * from the previous call. * @param initValue The initial value to pass to the accumulating function with the first * {@link TaskDisplayArea}. * @return the accumulative result. @@ -1912,9 +1912,29 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< /** * Finds the first non {@code null} return value from calling the callback on all + * {@link DisplayArea} at or below this container. Traverses from top to bottom in terms of + * z-order. + * @param callback Applies on each {@link DisplayArea} found and stops the search if it + * returns non {@code null}. + * @return the first returned object that is not {@code null}. Returns {@code null} if not + * found. + */ + @Nullable + <R> R getItemFromDisplayAreas(Function<DisplayArea, R> callback) { + for (int i = mChildren.size() - 1; i >= 0; --i) { + R result = (R) mChildren.get(i).getItemFromDisplayAreas(callback); + if (result != null) { + return result; + } + } + return null; + } + + /** + * Finds the first non {@code null} return value from calling the callback on all * {@link TaskDisplayArea} at or below this container. * @param callback Applies on each {@link TaskDisplayArea} found and stops the search if it - * returns non {@code null}. + * returns non {@code null}. * @param traverseTopToBottom If {@code true}, traverses the hierarchy from top-to-bottom in * terms of z-order, else from bottom-to-top. * @return the first returned object that is not {@code null}. Returns {@code null} if not @@ -1941,7 +1961,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< * {@link TaskDisplayArea} at or below this container. Traverses from top to bottom in terms of * z-order. * @param callback Applies on each {@link TaskDisplayArea} found and stops the search if it - * returns non {@code null}. + * returns non {@code null}. * @return the first returned object that is not {@code null}. Returns {@code null} if not * found. */ @@ -2069,6 +2089,9 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } void assignLayer(Transaction t, int layer) { + // Don't assign layers while a transition animation is playing + // TODO(b/173528115): establish robust best-practices around z-order fighting. + if (mWmService.mAtmService.getTransitionController().isPlaying()) return; final boolean changed = layer != mLastLayer || mLastRelativeToLayer != null; if (mSurfaceControl != null && changed) { setLayer(t, layer); @@ -2093,6 +2116,10 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< mSurfaceAnimator.setLayer(t, layer); } + int getLastLayer() { + return mLastLayer; + } + protected void setRelativeLayer(Transaction t, SurfaceControl relativeTo, int layer) { // Route through surface animator to accommodate that our surface control might be @@ -2884,6 +2911,16 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< return null; } + /** Cheap way of doing cast and instanceof. */ + RootDisplayArea asRootDisplayArea() { + return null; + } + + /** Cheap way of doing cast and instanceof. */ + TaskDisplayArea asTaskDisplayArea() { + return null; + } + /** * @return {@code true} if window container is manage by a * {@link android.window.WindowOrganizer} diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index d082778f50ba..a3a9eb773abf 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -30,6 +30,7 @@ import android.view.IWindow; import android.view.InputChannel; import android.view.MagnificationSpec; import android.view.WindowInfo; +import android.view.WindowManager.DisplayImePolicy; import com.android.internal.policy.KeyInterceptionInfo; import com.android.server.input.InputManagerService; @@ -506,12 +507,12 @@ public abstract class WindowManagerInternal { public abstract boolean shouldShowSystemDecorOnDisplay(int displayId); /** - * Indicates that the display should show IME. + * Indicates the policy for how the display should show IME. * * @param displayId The id of the display. - * @return {@code true} if the display should show IME when an input field become focused on it. + * @return The policy for how the display should show IME. */ - public abstract boolean shouldShowIme(int displayId); + public abstract @DisplayImePolicy int getDisplayImePolicy(int displayId); /** * Show IME on imeTargetWindow once IME has finished layout. diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 7c7dd6ff268c..d62df85e3cc0 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -48,6 +48,7 @@ import static android.provider.Settings.Global.DEVELOPMENT_IGNORE_VENDOR_DISPLAY import static android.provider.Settings.Global.DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; +import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; @@ -255,6 +256,7 @@ import android.view.View; import android.view.WindowContentFrameStats; import android.view.WindowInsets; import android.view.WindowManager; +import android.view.WindowManager.DisplayImePolicy; import android.view.WindowManager.LayoutParams; import android.view.WindowManager.RemoveContentMode; import android.view.WindowManagerGlobal; @@ -1625,7 +1627,7 @@ public class WindowManagerService extends IWindowManager.Stub } final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy(); - displayPolicy.adjustWindowParamsLw(win, win.mAttrs, callingPid, callingUid); + displayPolicy.adjustWindowParamsLw(win, win.mAttrs); win.updateRequestedVisibility(requestedVisibility); res = displayPolicy.validateAddingWindowLw(attrs, callingPid, callingUid); @@ -2195,7 +2197,7 @@ public class WindowManagerService extends IWindowManager.Stub int flagChanges = 0; int privateFlagChanges = 0; if (attrs != null) { - displayPolicy.adjustWindowParamsLw(win, attrs, pid, uid); + displayPolicy.adjustWindowParamsLw(win, attrs); win.mToken.adjustWindowParams(win, attrs); int disableFlags = (attrs.systemUiVisibility | attrs.subtreeSystemUiVisibility) & DISABLE_MASK; @@ -2978,7 +2980,7 @@ public class WindowManagerService extends IWindowManager.Stub displayContent, true /* includingParents */); } } - syncInputTransactions(); + syncInputTransactions(true /* waitForAnimations */); } /** @@ -7245,28 +7247,25 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public boolean shouldShowIme(int displayId) { - if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "shouldShowIme()")) { + public @DisplayImePolicy int getDisplayImePolicy(int displayId) { + if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "getDisplayImePolicy()")) { throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission"); } - boolean show; final DisplayContent dc = mRoot.getDisplayContent(displayId); if (dc == null) { ProtoLog.w(WM_ERROR, - "Attempted to get IME flag of a display that does not exist: %d", + "Attempted to get IME policy of a display that does not exist: %d", displayId); - return false; + return DISPLAY_IME_POLICY_FALLBACK_DISPLAY; } synchronized (mGlobalLock) { - show = dc.canShowIme(); + return dc.getImePolicy(); } - - return show; } @Override - public void setShouldShowIme(int displayId, boolean shouldShow) { - if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "setShouldShowIme()")) { + public void setDisplayImePolicy(int displayId, @DisplayImePolicy int imePolicy) { + if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "setDisplayImePolicy()")) { throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission"); } final long origId = Binder.clearCallingIdentity(); @@ -7274,16 +7273,16 @@ public class WindowManagerService extends IWindowManager.Stub synchronized (mGlobalLock) { final DisplayContent displayContent = getDisplayContentOrCreate(displayId, null); if (displayContent == null) { - ProtoLog.w(WM_ERROR, "Attempted to set IME flag to a display that does not " - + "exist: %d", displayId); + ProtoLog.w(WM_ERROR, "Attempted to set IME policy to a display" + + " that does not exist: %d", displayId); return; } if (!displayContent.isTrusted()) { - throw new SecurityException("Attempted to set IME flag to an untrusted " + throw new SecurityException("Attempted to set IME policy to an untrusted " + "virtual display: " + displayId); } - mDisplayWindowSettings.setShouldShowImeLocked(displayContent, shouldShow); + mDisplayWindowSettings.setDisplayImePolicy(displayContent, imePolicy); displayContent.reconfigureDisplayLocked(); } @@ -7765,9 +7764,9 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public boolean shouldShowIme(int displayId) { + public @DisplayImePolicy int getDisplayImePolicy(int displayId) { synchronized (mGlobalLock) { - return WindowManagerService.this.shouldShowIme(displayId); + return WindowManagerService.this.getDisplayImePolicy(displayId); } } @@ -7979,7 +7978,8 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public boolean injectInputAfterTransactionsApplied(InputEvent ev, int mode) { + public boolean injectInputAfterTransactionsApplied(InputEvent ev, int mode, + boolean waitForAnimations) { boolean isDown; boolean isUp; @@ -7998,21 +7998,23 @@ public class WindowManagerService extends IWindowManager.Stub // For all mouse events, also sync before injecting. // For ACTION_UP, sync after injecting. if (isDown || isMouseEvent) { - syncInputTransactions(); + syncInputTransactions(waitForAnimations); } final boolean result = LocalServices.getService(InputManagerInternal.class).injectInputEvent(ev, mode); if (isUp) { - syncInputTransactions(); + syncInputTransactions(waitForAnimations); } return result; } @Override - public void syncInputTransactions() { + public void syncInputTransactions(boolean waitForAnimations) { final long token = Binder.clearCallingIdentity(); try { - waitForAnimationsToComplete(); + if (waitForAnimations) { + waitForAnimationsToComplete(); + } // Collect all input transactions from all displays to make sure we could sync all input // windows at same time. diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index 88fd36114ed3..8f8fea34e620 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -607,7 +607,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio private boolean isBoundByForegroundUid() { for (int i = mBoundClientUids.size() - 1; i >= 0; --i) { - if (mAtm.isUidForeground(mBoundClientUids.valueAt(i))) { + if (mAtm.hasActiveVisibleWindow(mBoundClientUids.valueAt(i))) { return true; } } @@ -730,6 +730,28 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio return mHasActivities || mHasRecentTasks; } + @Nullable + TaskDisplayArea getTopActivityDisplayArea() { + if (mActivities.isEmpty()) { + return null; + } + + final int lastIndex = mActivities.size() - 1; + ActivityRecord topRecord = mActivities.get(lastIndex); + TaskDisplayArea displayArea = topRecord.getDisplayArea(); + + for (int index = lastIndex - 1; index >= 0; --index) { + ActivityRecord nextRecord = mActivities.get(index); + TaskDisplayArea nextDisplayArea = nextRecord.getDisplayArea(); + if (nextRecord.compareTo(topRecord) > 0 && nextDisplayArea != null) { + topRecord = nextRecord; + displayArea = nextDisplayArea; + } + } + + return displayArea; + } + private boolean hasActivityInVisibleTask() { for (int i = mActivities.size() - 1; i >= 0; --i) { Task task = mActivities.get(i).getTask(); @@ -1049,16 +1071,6 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio & (ACTIVITY_STATE_FLAG_IS_VISIBLE | ACTIVITY_STATE_FLAG_IS_WINDOW_VISIBLE)) != 0; for (int i = mActivities.size() - 1; i >= 0; i--) { final ActivityRecord r = mActivities.get(i); - if (r.app != this) { - Slog.e(TAG, "Found activity " + r + " in proc activity list using " + r.app - + " instead of expected " + this); - if (r.app == null || (r.app.mUid == mUid)) { - // Only fix things up when they look valid. - r.setProcess(this); - } else { - continue; - } - } if (r.isVisible()) { stateFlags |= ACTIVITY_STATE_FLAG_IS_WINDOW_VISIBLE; } @@ -1115,6 +1127,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio /** Called when the process has some oom related changes and it is going to update oom-adj. */ private void prepareOomAdjustment() { mAtm.mRootWindowContainer.rankTaskLayersIfNeeded(); + mAtm.mTaskSupervisor.computeProcessActivityStateBatch(); } public int computeRelaunchReason() { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index b5509f62f92a..c318fad4d0a0 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -127,6 +127,7 @@ import static com.android.server.wm.IdentifierProto.USER_ID; import static com.android.server.wm.MoveAnimationSpecProto.DURATION_MS; import static com.android.server.wm.MoveAnimationSpecProto.FROM; import static com.android.server.wm.MoveAnimationSpecProto.TO; +import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_ALL; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION; import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS; import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; @@ -972,6 +973,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (WindowManager.LayoutParams.isSystemAlertWindowType(mAttrs.type)) { return TouchOcclusionMode.USE_OPACITY; } + if (isAnimating(PARENTS | TRANSITION, ANIMATION_TYPE_ALL)) { + return TouchOcclusionMode.USE_OPACITY; + } return TouchOcclusionMode.BLOCK_UNTRUSTED; } @@ -3286,6 +3290,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP logExclusionRestrictions(EXCLUSION_LEFT); logExclusionRestrictions(EXCLUSION_RIGHT); } + // Exclude toast because legacy apps may show toast window by themselves, so the misused + // apps won't always be considered as foreground state. + if (mAttrs.type >= FIRST_SYSTEM_WINDOW && mAttrs.type != TYPE_TOAST) { + mWmService.mAtmService.mActiveUids.onNonAppSurfaceVisibilityChanged(mOwnerUid, shown); + } } private void logExclusionRestrictions(int side) { @@ -5262,6 +5271,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (mSurfaceControl == null) { return; } + if (mWmService.mWindowPlacerLocked.isLayoutDeferred() || isGoneForLayout()) { + // Since this relies on mWindowFrames, changes made while layout is deferred are + // likely to be invalid. Similarly, if it's goneForLayout, mWindowFrames may not be + // up-to-date and thus can't be relied on. + return; + } transformFrameToSurfacePosition(mWindowFrames.mFrame.left, mWindowFrames.mFrame.top, mSurfacePosition); @@ -5624,23 +5639,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP true /* ignoreVisibility */)); } - /** - * Returns {@code true} if this window is not {@link WindowManager.LayoutParams#TYPE_TOAST} - * or {@link WindowManager.LayoutParams#TYPE_APPLICATION_STARTING}, - * since this window doesn't belong to apps. - */ - boolean isNonToastOrStarting() { - return mAttrs.type != TYPE_TOAST && mAttrs.type != TYPE_APPLICATION_STARTING; - } - - boolean isNonToastWindowVisibleForUid(int callingUid) { - return getOwningUid() == callingUid && isNonToastOrStarting() && isVisibleNow(); - } - - boolean isNonToastWindowVisibleForPid(int pid) { - return mSession.mPid == pid && isNonToastOrStarting() && isVisibleNow(); - } - void setViewVisibility(int viewVisibility) { mViewVisibility = viewVisibility; } diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index da2c005d7332..6da9517743d2 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -53,8 +53,8 @@ cc_library_static { "com_android_server_UsbDescriptorParser.cpp", "com_android_server_UsbMidiDevice.cpp", "com_android_server_UsbHostManager.cpp", + "com_android_server_vibrator_VibratorController.cpp", "com_android_server_VibratorManagerService.cpp", - "com_android_server_VibratorService.cpp", "com_android_server_PersistentDataBlockService.cpp", "com_android_server_am_CachedAppOptimizer.cpp", "com_android_server_am_LowMemDetector.cpp", diff --git a/services/core/jni/OWNERS b/services/core/jni/OWNERS index 4c017f5513a2..6f74885b98e5 100644 --- a/services/core/jni/OWNERS +++ b/services/core/jni/OWNERS @@ -2,8 +2,8 @@ per-file com_android_server_lights_LightsService.cpp = michaelwr@google.com, santoscordon@google.com # Haptics +per-file com_android_server_vibrator_VibratorController.cpp = michaelwr@google.com per-file com_android_server_VibratorManagerService.cpp = michaelwr@google.com -per-file com_android_server_VibratorService.cpp = michaelwr@google.com # Input per-file com_android_server_input_InputManagerService.cpp = michaelwr@google.com, svv@google.com diff --git a/services/core/jni/com_android_server_am_BatteryStatsService.cpp b/services/core/jni/com_android_server_am_BatteryStatsService.cpp index 5d78f127f77f..c44cea38df1c 100644 --- a/services/core/jni/com_android_server_am_BatteryStatsService.cpp +++ b/services/core/jni/com_android_server_am_BatteryStatsService.cpp @@ -122,7 +122,7 @@ public: { std::unique_lock<std::mutex> reasonsLock(mReasonsMutex, std::defer_lock); if (reasonsLock.try_lock() && mWakeupReasons.empty()) { - mWakeupReasons = std::move(wakeupReasons); + mWakeupReasons = wakeupReasons; reasonsCaptured = true; } } diff --git a/services/core/jni/com_android_server_VibratorService.cpp b/services/core/jni/com_android_server_vibrator_VibratorController.cpp index 9aca84849fc6..afce5379febb 100644 --- a/services/core/jni/com_android_server_VibratorService.cpp +++ b/services/core/jni/com_android_server_vibrator_VibratorController.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,20 +14,18 @@ * limitations under the License. */ -#define LOG_TAG "VibratorService" +#define LOG_TAG "VibratorController" #include <android/hardware/vibrator/1.3/IVibrator.h> #include <android/hardware/vibrator/IVibrator.h> -#include "jni.h" #include <nativehelper/JNIHelp.h> #include "android_runtime/AndroidRuntime.h" #include "core_jni_helpers.h" +#include "jni.h" -#include <utils/misc.h> #include <utils/Log.h> - -#include <inttypes.h> +#include <utils/misc.h> #include <vibratorservice/VibratorHalController.h> @@ -48,58 +46,59 @@ static struct { } sPrimitiveClassInfo; static_assert(static_cast<uint8_t>(V1_0::EffectStrength::LIGHT) == - static_cast<uint8_t>(aidl::EffectStrength::LIGHT)); + static_cast<uint8_t>(aidl::EffectStrength::LIGHT)); static_assert(static_cast<uint8_t>(V1_0::EffectStrength::MEDIUM) == - static_cast<uint8_t>(aidl::EffectStrength::MEDIUM)); + static_cast<uint8_t>(aidl::EffectStrength::MEDIUM)); static_assert(static_cast<uint8_t>(V1_0::EffectStrength::STRONG) == - static_cast<uint8_t>(aidl::EffectStrength::STRONG)); + static_cast<uint8_t>(aidl::EffectStrength::STRONG)); static_assert(static_cast<uint8_t>(V1_3::Effect::CLICK) == - static_cast<uint8_t>(aidl::Effect::CLICK)); + static_cast<uint8_t>(aidl::Effect::CLICK)); static_assert(static_cast<uint8_t>(V1_3::Effect::DOUBLE_CLICK) == - static_cast<uint8_t>(aidl::Effect::DOUBLE_CLICK)); -static_assert(static_cast<uint8_t>(V1_3::Effect::TICK) == - static_cast<uint8_t>(aidl::Effect::TICK)); -static_assert(static_cast<uint8_t>(V1_3::Effect::THUD) == - static_cast<uint8_t>(aidl::Effect::THUD)); -static_assert(static_cast<uint8_t>(V1_3::Effect::POP) == - static_cast<uint8_t>(aidl::Effect::POP)); + static_cast<uint8_t>(aidl::Effect::DOUBLE_CLICK)); +static_assert(static_cast<uint8_t>(V1_3::Effect::TICK) == static_cast<uint8_t>(aidl::Effect::TICK)); +static_assert(static_cast<uint8_t>(V1_3::Effect::THUD) == static_cast<uint8_t>(aidl::Effect::THUD)); +static_assert(static_cast<uint8_t>(V1_3::Effect::POP) == static_cast<uint8_t>(aidl::Effect::POP)); static_assert(static_cast<uint8_t>(V1_3::Effect::HEAVY_CLICK) == - static_cast<uint8_t>(aidl::Effect::HEAVY_CLICK)); + static_cast<uint8_t>(aidl::Effect::HEAVY_CLICK)); static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_1) == - static_cast<uint8_t>(aidl::Effect::RINGTONE_1)); + static_cast<uint8_t>(aidl::Effect::RINGTONE_1)); static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_2) == - static_cast<uint8_t>(aidl::Effect::RINGTONE_2)); + static_cast<uint8_t>(aidl::Effect::RINGTONE_2)); static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_15) == - static_cast<uint8_t>(aidl::Effect::RINGTONE_15)); + static_cast<uint8_t>(aidl::Effect::RINGTONE_15)); static_assert(static_cast<uint8_t>(V1_3::Effect::TEXTURE_TICK) == - static_cast<uint8_t>(aidl::Effect::TEXTURE_TICK)); + static_cast<uint8_t>(aidl::Effect::TEXTURE_TICK)); -class NativeVibratorService { +class VibratorControllerWrapper { public: - NativeVibratorService(JNIEnv* env, jobject callbackListener) - : mController(std::make_unique<vibrator::HalController>()), + VibratorControllerWrapper(JNIEnv* env, int32_t vibratorId, jobject callbackListener) + // TODO(b/167946816): use ManagerHalController to get vibrator by id + : mHal(std::make_unique<vibrator::HalController>()), + mVibratorId(vibratorId), mCallbackListener(env->NewGlobalRef(callbackListener)) { LOG_ALWAYS_FATAL_IF(mCallbackListener == nullptr, "Unable to create global reference to vibration callback handler"); } - ~NativeVibratorService() { + ~VibratorControllerWrapper() { auto jniEnv = GetOrAttachJNIEnvironment(sJvm); jniEnv->DeleteGlobalRef(mCallbackListener); } - vibrator::HalController* controller() const { return mController.get(); } + vibrator::HalController* hal() const { return mHal.get(); } std::function<void()> createCallback(jlong vibrationId) { return [vibrationId, this]() { auto jniEnv = GetOrAttachJNIEnvironment(sJvm); - jniEnv->CallVoidMethod(mCallbackListener, sMethodIdOnComplete, vibrationId); + jniEnv->CallVoidMethod(mCallbackListener, sMethodIdOnComplete, mVibratorId, + vibrationId); }; } private: - const std::unique_ptr<vibrator::HalController> mController; + const std::unique_ptr<vibrator::HalController> mHal; + const int32_t mVibratorId; const jobject mCallbackListener; }; @@ -112,80 +111,80 @@ static aidl::CompositeEffect effectFromJavaPrimitive(JNIEnv* env, jobject primit return effect; } -static void destroyNativeService(void* servicePtr) { - NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr); - if (service) { - delete service; +static void destroyNativeWrapper(void* ptr) { + VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr); + if (wrapper) { + delete wrapper; } } -static jlong vibratorInit(JNIEnv* env, jclass /* clazz */, jobject callbackListener) { - std::unique_ptr<NativeVibratorService> service = - std::make_unique<NativeVibratorService>(env, callbackListener); - service->controller()->init(); - return reinterpret_cast<jlong>(service.release()); +static jlong vibratorInit(JNIEnv* env, jclass /* clazz */, jint vibratorId, + jobject callbackListener) { + std::unique_ptr<VibratorControllerWrapper> wrapper = + std::make_unique<VibratorControllerWrapper>(env, vibratorId, callbackListener); + wrapper->hal()->init(); + return reinterpret_cast<jlong>(wrapper.release()); } static jlong vibratorGetFinalizer(JNIEnv* /* env */, jclass /* clazz */) { - return static_cast<jlong>(reinterpret_cast<uintptr_t>(&destroyNativeService)); + return static_cast<jlong>(reinterpret_cast<uintptr_t>(&destroyNativeWrapper)); } -static jboolean vibratorExists(JNIEnv* env, jclass /* clazz */, jlong servicePtr) { - NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr); - if (service == nullptr) { - ALOGE("vibratorExists failed because native service was not initialized"); +static jboolean vibratorIsAvailable(JNIEnv* env, jclass /* clazz */, jlong ptr) { + VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr); + if (wrapper == nullptr) { + ALOGE("vibratorIsAvailable failed because native wrapper was not initialized"); return JNI_FALSE; } - return service->controller()->ping().isOk() ? JNI_TRUE : JNI_FALSE; + return wrapper->hal()->ping().isOk() ? JNI_TRUE : JNI_FALSE; } -static void vibratorOn(JNIEnv* env, jclass /* clazz */, jlong servicePtr, jlong timeoutMs, +static void vibratorOn(JNIEnv* env, jclass /* clazz */, jlong ptr, jlong timeoutMs, jlong vibrationId) { - NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr); - if (service == nullptr) { - ALOGE("vibratorOn failed because native service was not initialized"); + VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr); + if (wrapper == nullptr) { + ALOGE("vibratorOn failed because native wrapper was not initialized"); return; } - auto callback = service->createCallback(vibrationId); - service->controller()->on(std::chrono::milliseconds(timeoutMs), callback); + auto callback = wrapper->createCallback(vibrationId); + wrapper->hal()->on(std::chrono::milliseconds(timeoutMs), callback); } -static void vibratorOff(JNIEnv* env, jclass /* clazz */, jlong servicePtr) { - NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr); - if (service == nullptr) { - ALOGE("vibratorOff failed because native service was not initialized"); +static void vibratorOff(JNIEnv* env, jclass /* clazz */, jlong ptr) { + VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr); + if (wrapper == nullptr) { + ALOGE("vibratorOff failed because native wrapper was not initialized"); return; } - service->controller()->off(); + wrapper->hal()->off(); } -static void vibratorSetAmplitude(JNIEnv* env, jclass /* clazz */, jlong servicePtr, - jint amplitude) { - NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr); - if (service == nullptr) { - ALOGE("vibratorSetAmplitude failed because native service was not initialized"); +static void vibratorSetAmplitude(JNIEnv* env, jclass /* clazz */, jlong ptr, jint amplitude) { + VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr); + if (wrapper == nullptr) { + ALOGE("vibratorSetAmplitude failed because native wrapper was not initialized"); return; } - service->controller()->setAmplitude(static_cast<int32_t>(amplitude)); + wrapper->hal()->setAmplitude(static_cast<int32_t>(amplitude)); } -static void vibratorSetExternalControl(JNIEnv* env, jclass /* clazz */, jlong servicePtr, +static void vibratorSetExternalControl(JNIEnv* env, jclass /* clazz */, jlong ptr, jboolean enabled) { - NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr); - if (service == nullptr) { - ALOGE("vibratorSetExternalControl failed because native service was not initialized"); + VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr); + if (wrapper == nullptr) { + ALOGE("vibratorSetExternalControl failed because native wrapper was not initialized"); return; } - service->controller()->setExternalControl(enabled); + wrapper->hal()->setExternalControl(enabled); } -static jintArray vibratorGetSupportedEffects(JNIEnv* env, jclass /* clazz */, jlong servicePtr) { - NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr); - if (service == nullptr) { - ALOGE("vibratorGetSupportedEffects failed because native service was not initialized"); +static jintArray vibratorGetSupportedEffects(JNIEnv* env, jclass /* clazz */, jlong ptr) { + VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr); + if (wrapper == nullptr) { + ALOGE("vibratorGetSupportedEffects failed because native wrapper was not initialized"); return nullptr; } - auto result = service->controller()->getSupportedEffects(); + auto result = wrapper->hal()->getSupportedEffects(); if (!result.isOk()) { return nullptr; } @@ -196,13 +195,13 @@ static jintArray vibratorGetSupportedEffects(JNIEnv* env, jclass /* clazz */, jl return effects; } -static jintArray vibratorGetSupportedPrimitives(JNIEnv* env, jclass /* clazz */, jlong servicePtr) { - NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr); - if (service == nullptr) { - ALOGE("vibratorGetSupportedPrimitives failed because native service was not initialized"); +static jintArray vibratorGetSupportedPrimitives(JNIEnv* env, jclass /* clazz */, jlong ptr) { + VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr); + if (wrapper == nullptr) { + ALOGE("vibratorGetSupportedPrimitives failed because native wrapper was not initialized"); return nullptr; } - auto result = service->controller()->getSupportedPrimitives(); + auto result = wrapper->hal()->getSupportedPrimitives(); if (!result.isOk()) { return nullptr; } @@ -213,25 +212,25 @@ static jintArray vibratorGetSupportedPrimitives(JNIEnv* env, jclass /* clazz */, return primitives; } -static jlong vibratorPerformEffect(JNIEnv* env, jclass /* clazz */, jlong servicePtr, jlong effect, +static jlong vibratorPerformEffect(JNIEnv* env, jclass /* clazz */, jlong ptr, jlong effect, jlong strength, jlong vibrationId) { - NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr); - if (service == nullptr) { - ALOGE("vibratorPerformEffect failed because native service was not initialized"); + VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr); + if (wrapper == nullptr) { + ALOGE("vibratorPerformEffect failed because native wrapper was not initialized"); return -1; } aidl::Effect effectType = static_cast<aidl::Effect>(effect); aidl::EffectStrength effectStrength = static_cast<aidl::EffectStrength>(strength); - auto callback = service->createCallback(vibrationId); - auto result = service->controller()->performEffect(effectType, effectStrength, callback); + auto callback = wrapper->createCallback(vibrationId); + auto result = wrapper->hal()->performEffect(effectType, effectStrength, callback); return result.isOk() ? result.value().count() : -1; } -static void vibratorPerformComposedEffect(JNIEnv* env, jclass /* clazz */, jlong servicePtr, +static void vibratorPerformComposedEffect(JNIEnv* env, jclass /* clazz */, jlong ptr, jobjectArray composition, jlong vibrationId) { - NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr); - if (service == nullptr) { - ALOGE("vibratorPerformComposedEffect failed because native service was not initialized"); + VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr); + if (wrapper == nullptr) { + ALOGE("vibratorPerformComposedEffect failed because native wrapper was not initialized"); return; } size_t size = env->GetArrayLength(composition); @@ -240,46 +239,46 @@ static void vibratorPerformComposedEffect(JNIEnv* env, jclass /* clazz */, jlong jobject element = env->GetObjectArrayElement(composition, i); effects.push_back(effectFromJavaPrimitive(env, element)); } - auto callback = service->createCallback(vibrationId); - service->controller()->performComposedEffect(effects, callback); + auto callback = wrapper->createCallback(vibrationId); + wrapper->hal()->performComposedEffect(effects, callback); } -static jlong vibratorGetCapabilities(JNIEnv* env, jclass /* clazz */, jlong servicePtr) { - NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr); - if (service == nullptr) { - ALOGE("vibratorGetCapabilities failed because native service was not initialized"); +static jlong vibratorGetCapabilities(JNIEnv* env, jclass /* clazz */, jlong ptr) { + VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr); + if (wrapper == nullptr) { + ALOGE("vibratorGetCapabilities failed because native wrapper was not initialized"); return 0; } - auto result = service->controller()->getCapabilities(); + auto result = wrapper->hal()->getCapabilities(); return result.isOk() ? static_cast<jlong>(result.value()) : 0; } -static void vibratorAlwaysOnEnable(JNIEnv* env, jclass /* clazz */, jlong servicePtr, jlong id, +static void vibratorAlwaysOnEnable(JNIEnv* env, jclass /* clazz */, jlong ptr, jlong id, jlong effect, jlong strength) { - NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr); - if (service == nullptr) { - ALOGE("vibratorAlwaysOnEnable failed because native service was not initialized"); + VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr); + if (wrapper == nullptr) { + ALOGE("vibratorAlwaysOnEnable failed because native wrapper was not initialized"); return; } - service->controller()->alwaysOnEnable(static_cast<int32_t>(id), - static_cast<aidl::Effect>(effect), - static_cast<aidl::EffectStrength>(strength)); + wrapper->hal()->alwaysOnEnable(static_cast<int32_t>(id), static_cast<aidl::Effect>(effect), + static_cast<aidl::EffectStrength>(strength)); } -static void vibratorAlwaysOnDisable(JNIEnv* env, jclass /* clazz */, jlong servicePtr, jlong id) { - NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr); - if (service == nullptr) { - ALOGE("vibratorAlwaysOnDisable failed because native service was not initialized"); +static void vibratorAlwaysOnDisable(JNIEnv* env, jclass /* clazz */, jlong ptr, jlong id) { + VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr); + if (wrapper == nullptr) { + ALOGE("vibratorAlwaysOnDisable failed because native wrapper was not initialized"); return; } - service->controller()->alwaysOnDisable(static_cast<int32_t>(id)); + wrapper->hal()->alwaysOnDisable(static_cast<int32_t>(id)); } static const JNINativeMethod method_table[] = { - {"vibratorInit", "(Lcom/android/server/VibratorService$OnCompleteListener;)J", + {"vibratorInit", + "(ILcom/android/server/vibrator/VibratorController$OnVibrationCompleteListener;)J", (void*)vibratorInit}, {"vibratorGetFinalizer", "()J", (void*)vibratorGetFinalizer}, - {"vibratorExists", "(J)Z", (void*)vibratorExists}, + {"vibratorIsAvailable", "(J)Z", (void*)vibratorIsAvailable}, {"vibratorOn", "(JJJ)V", (void*)vibratorOn}, {"vibratorOff", "(J)V", (void*)vibratorOff}, {"vibratorSetAmplitude", "(JI)V", (void*)vibratorSetAmplitude}, @@ -295,11 +294,12 @@ static const JNINativeMethod method_table[] = { {"vibratorAlwaysOnDisable", "(JJ)V", (void*)vibratorAlwaysOnDisable}, }; -int register_android_server_VibratorService(JavaVM* jvm, JNIEnv* env) { +int register_android_server_vibrator_VibratorController(JavaVM* jvm, JNIEnv* env) { sJvm = jvm; - jclass listenerClass = - FindClassOrDie(env, "com/android/server/VibratorService$OnCompleteListener"); - sMethodIdOnComplete = GetMethodIDOrDie(env, listenerClass, "onComplete", "(J)V"); + auto listenerClassName = + "com/android/server/vibrator/VibratorController$OnVibrationCompleteListener"; + jclass listenerClass = FindClassOrDie(env, listenerClassName); + sMethodIdOnComplete = GetMethodIDOrDie(env, listenerClass, "onComplete", "(IJ)V"); jclass primitiveClass = FindClassOrDie(env, "android/os/VibrationEffect$Composition$PrimitiveEffect"); @@ -307,8 +307,8 @@ int register_android_server_VibratorService(JavaVM* jvm, JNIEnv* env) { sPrimitiveClassInfo.scale = GetFieldIDOrDie(env, primitiveClass, "scale", "F"); sPrimitiveClassInfo.delay = GetFieldIDOrDie(env, primitiveClass, "delay", "I"); - return jniRegisterNativeMethods(env, "com/android/server/VibratorService", method_table, - NELEM(method_table)); + return jniRegisterNativeMethods(env, "com/android/server/vibrator/VibratorController", + method_table, NELEM(method_table)); } }; // namespace android diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp index 79b5fed3e448..5a0d08aeb6b3 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -37,8 +37,8 @@ int register_android_server_UsbDeviceManager(JNIEnv* env); int register_android_server_UsbMidiDevice(JNIEnv* env); int register_android_server_UsbHostManager(JNIEnv* env); int register_android_server_vr_VrManagerService(JNIEnv* env); +int register_android_server_vibrator_VibratorController(JavaVM* vm, JNIEnv* env); int register_android_server_VibratorManagerService(JNIEnv* env); -int register_android_server_VibratorService(JavaVM* vm, JNIEnv* env); int register_android_server_location_GnssLocationProvider(JNIEnv* env); int register_android_server_connectivity_Vpn(JNIEnv* env); int register_android_server_TestNetworkService(JNIEnv* env); @@ -90,8 +90,8 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) register_android_server_UsbAlsaJackDetector(env); register_android_server_UsbHostManager(env); register_android_server_vr_VrManagerService(env); + register_android_server_vibrator_VibratorController(vm, env); register_android_server_VibratorManagerService(env); - register_android_server_VibratorService(vm, env); register_android_server_SystemServer(env); register_android_server_location_GnssLocationProvider(env); register_android_server_connectivity_Vpn(env); diff --git a/services/core/xsd/Android.bp b/services/core/xsd/Android.bp index fb55e75b9ac4..d1918d8dbe14 100644 --- a/services/core/xsd/Android.bp +++ b/services/core/xsd/Android.bp @@ -28,3 +28,10 @@ xsd_config { api_dir: "cec-config/schema", package_name: "com.android.server.hdmi.cec.config", } + +xsd_config { + name: "device-state-config", + srcs: ["device-state-config/device-state-config.xsd"], + api_dir: "device-state-config/schema", + package_name: "com.android.server.policy.devicestate.config", +} diff --git a/services/core/xsd/device-state-config/device-state-config.xsd b/services/core/xsd/device-state-config/device-state-config.xsd new file mode 100644 index 000000000000..0d8c08c93ff2 --- /dev/null +++ b/services/core/xsd/device-state-config/device-state-config.xsd @@ -0,0 +1,78 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<xs:schema version="2.0" + elementFormDefault="qualified" + xmlns:xs="http://www.w3.org/2001/XMLSchema"> + + <xs:element name="device-state-config"> + <xs:complexType> + <xs:sequence> + <xs:element name="device-state" type="deviceState" maxOccurs="256" /> + </xs:sequence> + </xs:complexType> + </xs:element> + + <xs:complexType name="deviceState"> + <xs:sequence> + <xs:element name="identifier"> + <xs:simpleType> + <xs:restriction base="xs:integer"> + <xs:minInclusive value="0" /> + <xs:maxInclusive value="255" /> + </xs:restriction> + </xs:simpleType> + </xs:element> + <xs:element name="name" type="xs:string" minOccurs="0" /> + <xs:element name="conditions" type="conditions" /> + </xs:sequence> + </xs:complexType> + + <xs:complexType name="conditions"> + <xs:sequence> + <xs:element name="lid-switch" type="lidSwitchCondition" minOccurs="0" /> + <xs:element name="sensor" type="sensorCondition" minOccurs="0" maxOccurs="unbounded" /> + </xs:sequence> + </xs:complexType> + + <xs:complexType name="lidSwitchCondition"> + <xs:sequence> + <xs:element name="open" type="xs:boolean" /> + </xs:sequence> + </xs:complexType> + + <xs:complexType name="sensorCondition"> + <xs:sequence> + <xs:element name="name" type="xs:string" /> + <xs:element name="type" type="xs:positiveInteger" /> + <xs:element name="value" type="numericRange" maxOccurs="unbounded" /> + </xs:sequence> + </xs:complexType> + + <xs:complexType name="numericRange"> + <xs:sequence> + <xs:choice minOccurs="0"> + <xs:element name="min" type="xs:decimal" /> + <xs:element name="min-inclusive" type="xs:decimal" /> + </xs:choice> + <xs:choice minOccurs="0"> + <xs:element name="max" type="xs:decimal" /> + <xs:element name="max-inclusive" type="xs:decimal"/> + </xs:choice> + </xs:sequence> + </xs:complexType> +</xs:schema> diff --git a/services/core/xsd/device-state-config/schema/current.txt b/services/core/xsd/device-state-config/schema/current.txt new file mode 100644 index 000000000000..667d1add5a98 --- /dev/null +++ b/services/core/xsd/device-state-config/schema/current.txt @@ -0,0 +1,61 @@ +// Signature format: 2.0 +package com.android.server.policy.devicestate.config { + + public class Conditions { + ctor public Conditions(); + method public com.android.server.policy.devicestate.config.LidSwitchCondition getLidSwitch(); + method public java.util.List<com.android.server.policy.devicestate.config.SensorCondition> getSensor(); + method public void setLidSwitch(com.android.server.policy.devicestate.config.LidSwitchCondition); + } + + public class DeviceState { + ctor public DeviceState(); + method public com.android.server.policy.devicestate.config.Conditions getConditions(); + method public java.math.BigInteger getIdentifier(); + method public String getName(); + method public void setConditions(com.android.server.policy.devicestate.config.Conditions); + method public void setIdentifier(java.math.BigInteger); + method public void setName(String); + } + + public class DeviceStateConfig { + ctor public DeviceStateConfig(); + method public java.util.List<com.android.server.policy.devicestate.config.DeviceState> getDeviceState(); + } + + public class LidSwitchCondition { + ctor public LidSwitchCondition(); + method public boolean getOpen(); + method public void setOpen(boolean); + } + + public class NumericRange { + ctor public NumericRange(); + method public java.math.BigDecimal getMaxInclusive_optional(); + method public java.math.BigDecimal getMax_optional(); + method public java.math.BigDecimal getMinInclusive_optional(); + method public java.math.BigDecimal getMin_optional(); + method public void setMaxInclusive_optional(java.math.BigDecimal); + method public void setMax_optional(java.math.BigDecimal); + method public void setMinInclusive_optional(java.math.BigDecimal); + method public void setMin_optional(java.math.BigDecimal); + } + + public class SensorCondition { + ctor public SensorCondition(); + method public String getName(); + method public java.math.BigInteger getType(); + method public java.util.List<com.android.server.policy.devicestate.config.NumericRange> getValue(); + method public void setName(String); + method public void setType(java.math.BigInteger); + } + + public class XmlParser { + ctor public XmlParser(); + method public static com.android.server.policy.devicestate.config.DeviceStateConfig read(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException; + method public static String readText(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; + method public static void skip(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; + } + +} + diff --git a/services/core/xsd/device-state-config/schema/last_current.txt b/services/core/xsd/device-state-config/schema/last_current.txt new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/services/core/xsd/device-state-config/schema/last_current.txt diff --git a/services/core/xsd/device-state-config/schema/last_removed.txt b/services/core/xsd/device-state-config/schema/last_removed.txt new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/services/core/xsd/device-state-config/schema/last_removed.txt diff --git a/services/core/xsd/device-state-config/schema/removed.txt b/services/core/xsd/device-state-config/schema/removed.txt new file mode 100644 index 000000000000..d802177e249b --- /dev/null +++ b/services/core/xsd/device-state-config/schema/removed.txt @@ -0,0 +1 @@ +// Signature format: 2.0 diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index a6466821ff38..6ca27b597987 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -1576,172 +1576,65 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { * Creates a new {@link CallerIdentity} object to represent the caller's identity. */ private CallerIdentity getCallerIdentity() { - final int callerUid = mInjector.binderGetCallingUid(); - return new CallerIdentity(callerUid, null, null); + return getCallerIdentity(null, null); } /** * Creates a new {@link CallerIdentity} object to represent the caller's identity. */ - private CallerIdentity getCallerIdentity(@NonNull String callerPackage) { - final int callerUid = mInjector.binderGetCallingUid(); - - if (!isCallingFromPackage(callerPackage, callerUid)) { - throw new SecurityException( - String.format("Caller with uid %d is not %s", callerUid, callerPackage)); - } + private CallerIdentity getCallerIdentity(@Nullable String callerPackage) { - return new CallerIdentity(callerUid, callerPackage, null); + return getCallerIdentity(null, callerPackage); } /** * Creates a new {@link CallerIdentity} object to represent the caller's identity. + * The component name should be an active admin for the calling user. */ @VisibleForTesting - protected CallerIdentity getCallerIdentity(@Nullable ComponentName adminComponent, - @NonNull String callerPackage) { - return adminComponent == null - ? getCallerIdentity(callerPackage) - : getCallerIdentity(adminComponent); + CallerIdentity getCallerIdentity(@Nullable ComponentName adminComponent) { + return getCallerIdentity(adminComponent, null); } /** * Creates a new {@link CallerIdentity} object to represent the caller's identity. - * The component name should be an active admin for the calling user. + * If {@code adminComponent} is provided, it's validated against the list of known + * active admins and caller uid. If {@code callerPackage} is provided, it's validated + * against the caller uid. If a valid {@code adminComponent} is provided but not + * {@code callerPackage}, the package name of the {@code adminComponent} is used instead. */ @VisibleForTesting - protected CallerIdentity getCallerIdentity(@NonNull ComponentName adminComponent) { + CallerIdentity getCallerIdentity(@Nullable ComponentName adminComponent, + @Nullable String callerPackage) { final int callerUid = mInjector.binderGetCallingUid(); - final DevicePolicyData policy = getUserData(UserHandle.getUserId(callerUid)); - ActiveAdmin admin = policy.mAdminMap.get(adminComponent); - - if (admin == null) { - throw new SecurityException(String.format("No active admin for %s", adminComponent)); - } - if (admin.getUid() != callerUid) { - throw new SecurityException( - String.format("Admin %s is not owned by uid %d", adminComponent, callerUid)); - } - - return new CallerIdentity(callerUid, adminComponent.getPackageName(), adminComponent); - } - - /** - * Creates a new {@link CallerIdentity} object to represent the caller's identity, which should - * be an admin of a profile on the device. If no component name is provided, look up the - * component name and fill it in for the caller. - * - * Note: this method should only be called when the expected caller is an admin. - * - * @throws SecurityException if the caller is not an active admin. - */ - private CallerIdentity getAdminCallerIdentity(@Nullable ComponentName adminComponent) { - if (adminComponent == null) { - ActiveAdmin admin = getActiveAdminOfCaller(); - if (admin != null) { - return getCallerIdentity(admin.info.getComponent()); - } - throw new SecurityException("Caller is not an active admin"); - } else { - return getCallerIdentity(adminComponent); - } - } - /** - * Creates a new {@link CallerIdentity} object to represent the caller's identity. If no - * component name is provided, look up the component name and fill it in for the caller. - * - * Note: this method should only be called when the caller may not be an admin. If the caller - * is not an admin, the ComponentName in the returned identity will be null. - */ - private CallerIdentity getNonPrivilegedOrAdminCallerIdentity( - @Nullable ComponentName adminComponent) { - if (adminComponent == null) { - ActiveAdmin admin = getActiveAdminOfCaller(); - if (admin != null) { - adminComponent = admin.info.getComponent(); - } else { - return getCallerIdentity(); + if (callerPackage != null) { + if (!isCallingFromPackage(callerPackage, callerUid)) { + throw new SecurityException( + String.format("Caller with uid %d is not %s", callerUid, callerPackage)); } } - return getCallerIdentity(adminComponent); - } + if (adminComponent != null) { + final DevicePolicyData policy = getUserData(UserHandle.getUserId(callerUid)); + ActiveAdmin admin = policy.mAdminMap.get(adminComponent); - /** - * Creates a new {@link CallerIdentity} object to represent the caller's identity. If no - * package name is provided, look up the package name and fill it in for the caller. - * - * Note: this method should only be called when the expected caller is an admin. - * - * @throws SecurityException if the caller is not an active admin. - */ - private CallerIdentity getAdminCallerIdentityUsingPackage(@Nullable String callerPackage) { - if (callerPackage == null) { - ActiveAdmin admin = getActiveAdminOfCaller(); - if (admin != null) { - return getCallerIdentity(admin.info.getPackageName()); + if (admin == null) { + throw new SecurityException(String.format( + "No active admin for %s", adminComponent)); } - throw new SecurityException("Caller is not an active admin"); - } - return getCallerIdentity(callerPackage); - } - - /** - * Creates a new {@link CallerIdentity} object to represent the caller's identity. If no - * package name is provided, look up the package name and fill it in for the caller. - */ - private CallerIdentity getNonPrivilegedOrAdminCallerIdentityUsingPackage( - @Nullable String callerPackage) { - if (callerPackage == null) { - ActiveAdmin admin = getActiveAdminOfCaller(); - if (admin != null) { - callerPackage = admin.info.getPackageName(); + if (admin.getUid() != callerUid) { + throw new SecurityException(String.format( + "Admin %s is not owned by uid %d", adminComponent, callerUid)); + } + if (callerPackage != null) { + Preconditions.checkArgument(callerPackage.equals(adminComponent.getPackageName())); } else { - return getCallerIdentity(); + callerPackage = adminComponent.getPackageName(); } } - return getCallerIdentity(callerPackage); - } - - /** - * Creates a new {@link CallerIdentity} object to represent the caller's identity. - * If an {@code adminComponent} is specified, then the caller must be an admin and - * the provided component name must match the caller's UID. - * - * If a package name is provided, then the caller doesn't have to be an admin, and the - * provided package must belong to the caller's UID. - * - * If neither is provided, the caller identity is returned as-is. - * - * Note: this method should only be called when the caller may not be an admin. If the caller - * is not an admin, the ComponentName in the returned identity will be null. - */ - private CallerIdentity getNonPrivilegedOrAdminCallerIdentity( - @Nullable ComponentName adminComponent, - @Nullable String callerPackage) { - if (adminComponent != null) { - return getCallerIdentity(adminComponent); - } - return getNonPrivilegedOrAdminCallerIdentityUsingPackage(callerPackage); - } - - /** - * Retrieves the active admin of the caller. This method should not be called directly and - * should only be called by {@link #getAdminCallerIdentity}, - * {@link #getNonPrivilegedOrAdminCallerIdentity}, {@link #getAdminCallerIdentityUsingPackage} - * or {@link #getNonPrivilegedOrAdminCallerIdentityUsingPackage}. - */ - private ActiveAdmin getActiveAdminOfCaller() { - final int callerUid = mInjector.binderGetCallingUid(); - final DevicePolicyData policy = getUserData(UserHandle.getUserId(callerUid)); - for (ActiveAdmin admin : policy.mAdminList) { - if (admin.getUid() == callerUid) { - return admin; - } - } - return null; + return new CallerIdentity(callerUid, callerPackage, adminComponent); } /** @@ -3192,6 +3085,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } + private void checkAllUsersAreAffiliatedWithDevice() { + Preconditions.checkCallAuthorization(areAllUsersAffiliatedWithDeviceLocked(), + "operation not allowed when device has unaffiliated users"); + } + @Override public boolean isAdminActive(ComponentName adminReceiver, int userHandle) { if (!mHasFeature) { @@ -4307,7 +4205,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override @PasswordComplexity public int getPasswordComplexity(boolean parent) { - final CallerIdentity caller = getNonPrivilegedOrAdminCallerIdentity(null); + final CallerIdentity caller = getCallerIdentity(); DevicePolicyEventLogger .createEvent(DevicePolicyEnums.GET_USER_PASSWORD_COMPLEXITY_LEVEL) .setStrings(parent ? CALLED_FROM_PARENT : NOT_CALLED_FROM_PARENT, @@ -4315,15 +4213,18 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { mInjector.binderGetCallingUid())) .write(); - Preconditions.checkCallAuthorization(!parent || (isDeviceOwner(caller) - || isProfileOwner(caller) || isSystemUid(caller)), - "Only profile owner, device owner and system may call this method."); enforceUserUnlocked(caller.getUserId()); - Preconditions.checkCallAuthorization( - hasCallingOrSelfPermission(REQUEST_PASSWORD_COMPLEXITY) - || isDeviceOwner(caller) || isProfileOwner(caller), - "Must have " + REQUEST_PASSWORD_COMPLEXITY - + " permission, or be a profile owner or device owner."); + if (parent) { + Preconditions.checkCallAuthorization( + isDeviceOwner(caller) || isProfileOwner(caller) || isSystemUid(caller), + "Only profile owner, device owner and system may call this method on parent."); + } else { + Preconditions.checkCallAuthorization( + hasCallingOrSelfPermission(REQUEST_PASSWORD_COMPLEXITY) + || isDeviceOwner(caller) || isProfileOwner(caller), + "Must have " + REQUEST_PASSWORD_COMPLEXITY + + " permission, or be a profile owner or device owner."); + } synchronized (getLockObject()) { final int credentialOwner = getCredentialOwner(caller.getUserId(), parent); @@ -4342,7 +4243,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Preconditions.checkArgument(allowedModes.contains(passwordComplexity), "Provided complexity is not one of the allowed values."); - final CallerIdentity caller = getAdminCallerIdentity(null); + final CallerIdentity caller = getCallerIdentity(); Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)); Preconditions.checkArgument(!calledOnParent || isProfileOwner(caller)); @@ -4386,11 +4287,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return PASSWORD_COMPLEXITY_NONE; } - final CallerIdentity caller = getAdminCallerIdentity(null); + final CallerIdentity caller = getCallerIdentity(); Preconditions.checkCallAuthorization( isDeviceOwner(caller) || isProfileOwner(caller)); - Preconditions.checkArgument(!calledOnParent || hasProfileOwner(caller.getUserId())); + Preconditions.checkArgument(!calledOnParent || isProfileOwner(caller)); synchronized (getLockObject()) { final ActiveAdmin requiredAdmin = getParentOfAdminIfRequired( @@ -5626,15 +5527,18 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public List<String> getDelegatedScopes(ComponentName who, String delegatePackage) throws SecurityException { Objects.requireNonNull(delegatePackage, "Delegate package is null"); - final CallerIdentity caller = getNonPrivilegedOrAdminCallerIdentity(who, delegatePackage); + final CallerIdentity caller = getCallerIdentity(who); // Ensure the caller may call this method: - // * Either it's an admin - // * Or it's an app identified by its calling package name (the - // getNonPrivilegedOrAdminCallerIdentity method validated the UID and package match). - Preconditions.checkCallAuthorization( - (caller.hasAdminComponent() && (isProfileOwner(caller) || isDeviceOwner(caller))) - || delegatePackage != null); + // * Either it's a profile owner / device owner, if componentName is provided + // * Or it's an app querying its own delegation scopes + if (caller.hasAdminComponent()) { + Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); + } else { + Preconditions.checkCallAuthorization(isPackage(caller, delegatePackage), + String.format("Caller with uid %d is not %s", caller.getUid(), + delegatePackage)); + } synchronized (getLockObject()) { final DevicePolicyData policy = getUserData(caller.getUserId()); // Retrieve the scopes assigned to delegatePackage, or null if no scope was given. @@ -5924,12 +5828,16 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public boolean isAlwaysOnVpnLockdownEnabled(ComponentName admin) throws SecurityException { - Objects.requireNonNull(admin, "ComponentName is null"); - - final CallerIdentity caller = getNonPrivilegedOrAdminCallerIdentity(admin); - Preconditions.checkCallAuthorization((caller.hasAdminComponent() - && (isDeviceOwner(caller) || isProfileOwner(caller))) - || hasCallingPermission(PERMISSION_MAINLINE_NETWORK_STACK)); + final CallerIdentity caller; + if (hasCallingPermission(PERMISSION_MAINLINE_NETWORK_STACK)) { + // TODO: CaptivePortalLoginActivity erroneously calls this method with a non-admin + // ComponentName, so we have to use a separate code path for it: + // getCallerIdentity(admin) will throw if the admin is not in the known admin list. + caller = getCallerIdentity(); + } else { + caller = getCallerIdentity(admin); + Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)); + } return mInjector.binderWithCleanCallingIdentity( () -> mInjector.getConnectivityManager().isVpnLockdownEnabled(caller.getUserId())); @@ -6209,6 +6117,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return getFrpManagementAgentUid() != -1; } + /** + * Called by a privileged caller holding {@code BIND_DEVICE_ADMIN} permission to retrieve + * the remove warning for the given device admin. + */ @Override public void getRemoveWarning(ComponentName comp, final RemoteCallback result, int userHandle) { if (!mHasFeature) { @@ -6649,13 +6561,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { * active admins. */ @Override - public boolean getStorageEncryption(ComponentName who, int userHandle) { + public boolean getStorageEncryption(@Nullable ComponentName who, int userHandle) { if (!mHasFeature) { return false; } Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); - final CallerIdentity caller = getAdminCallerIdentity(who); + final CallerIdentity caller = getCallerIdentity(who); Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle)); synchronized (getLockObject()) { @@ -6689,12 +6601,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); - final CallerIdentity caller = getAdminCallerIdentityUsingPackage(callerPackage); + final CallerIdentity caller = getCallerIdentity(callerPackage); Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle)); - // It's not critical here, but let's make sure the package name is correct, in case - // we start using it for different purposes. - ensureCallerPackage(callerPackage); final ApplicationInfo ai; try { @@ -7034,7 +6943,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // next boot? Might not be needed given that this still requires user consent. final CallerIdentity caller = getCallerIdentity(who); Preconditions.checkCallAuthorization(isDeviceOwner(caller)); - Preconditions.checkCallAuthorization(areAllUsersAffiliatedWithDeviceLocked()); + checkAllUsersAreAffiliatedWithDevice(); if (mBugreportCollectionManager.requestBugreport()) { DevicePolicyEventLogger @@ -7571,6 +7480,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return isProfileOwner(caller) && caller.getUserHandle().isSystem(); } + private boolean isPackage(CallerIdentity caller, String packageName) { + return isCallingFromPackage(packageName, caller.getUid()); + } + @Override public ComponentName getDeviceOwnerComponent(boolean callingUserOnly) { if (!mHasFeature) { @@ -8521,8 +8434,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return true; } - private void enforceCanCallLockTaskLocked(ComponentName who) { - final CallerIdentity caller = getAdminCallerIdentity(who); + private void enforceCanCallLockTaskLocked(CallerIdentity caller) { Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); final int userId = caller.getUserId(); @@ -8531,22 +8443,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } - private void ensureCallerPackage(@Nullable String packageName) { - if (packageName == null) { - enforceSystemCaller("omit package name"); - } else { - final int callingUid = mInjector.binderGetCallingUid(); - final int userId = mInjector.userHandleGetCallingUserId(); - try { - final ApplicationInfo ai = mIPackageManager.getApplicationInfo( - packageName, 0, userId); - Preconditions.checkState(ai.uid == callingUid, "Unmatching package name"); - } catch (RemoteException e) { - // Shouldn't happen - } - } - } - private boolean isCallerWithSystemUid() { return UserHandle.isSameApp(mInjector.binderGetCallingUid(), Process.SYSTEM_UID); } @@ -8643,6 +8539,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { pw.println(policy.mUserControlDisabledPackages); pw.print("mAppsSuspended="); pw.println(policy.mAppsSuspended); pw.print("mUserSetupComplete="); pw.println(policy.mUserSetupComplete); + pw.print("mAffiliationIds="); pw.println(policy.mAffiliationIds); pw.decreaseIndent(); } } @@ -8842,7 +8739,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(agent, "agent null"); Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); - final CallerIdentity caller = getAdminCallerIdentity(admin); + final CallerIdentity caller = getCallerIdentity(); Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle)); synchronized (getLockObject()) { @@ -10551,10 +10448,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { throws SecurityException { Objects.requireNonNull(who, "ComponentName is null"); Objects.requireNonNull(packages, "packages is null"); + final CallerIdentity caller = getCallerIdentity(who); synchronized (getLockObject()) { - enforceCanCallLockTaskLocked(who); - final int userHandle = mInjector.userHandleGetCallingUserId(); + enforceCanCallLockTaskLocked(caller); + final int userHandle = caller.getUserId(); setLockTaskPackagesLocked(userHandle, new ArrayList<>(Arrays.asList(packages))); } } @@ -10571,10 +10469,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public String[] getLockTaskPackages(ComponentName who) { Objects.requireNonNull(who, "ComponentName is null"); + final CallerIdentity caller = getCallerIdentity(who); + final int userHandle = caller.getUserId(); - final int userHandle = mInjector.binderGetCallingUserHandle().getIdentifier(); synchronized (getLockObject()) { - enforceCanCallLockTaskLocked(who); + enforceCanCallLockTaskLocked(caller); final List<String> packages = getUserData(userHandle).mLockTaskPackages; return packages.toArray(new String[packages.size()]); } @@ -10601,9 +10500,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Preconditions.checkArgument(hasHome || !hasNotification, "Cannot use LOCK_TASK_FEATURE_NOTIFICATIONS without LOCK_TASK_FEATURE_HOME"); - final int userHandle = mInjector.userHandleGetCallingUserId(); + final CallerIdentity caller = getCallerIdentity(who); + final int userHandle = caller.getUserId(); synchronized (getLockObject()) { - enforceCanCallLockTaskLocked(who); + enforceCanCallLockTaskLocked(caller); setLockTaskFeaturesLocked(userHandle, flags); } } @@ -10618,9 +10518,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public int getLockTaskFeatures(ComponentName who) { Objects.requireNonNull(who, "ComponentName is null"); - final int userHandle = mInjector.userHandleGetCallingUserId(); + final CallerIdentity caller = getCallerIdentity(who); + final int userHandle = caller.getUserId(); synchronized (getLockObject()) { - enforceCanCallLockTaskLocked(who); + enforceCanCallLockTaskLocked(caller); return getUserData(userHandle).mLockTaskFeatures; } } @@ -11069,7 +10970,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public boolean setStatusBarDisabled(ComponentName who, boolean disabled) { - final CallerIdentity caller = getAdminCallerIdentity(who); + final CallerIdentity caller = getCallerIdentity(who); Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); int userId = caller.getUserId(); @@ -12832,7 +12733,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } final Set<String> affiliationIds = new ArraySet<>(ids); - final CallerIdentity caller = getAdminCallerIdentity(admin); + final CallerIdentity caller = getCallerIdentity(admin); Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); final int callingUserId = caller.getUserId(); @@ -12925,13 +12826,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { }); } - private boolean canStartSecurityLogging() { - synchronized (getLockObject()) { - return isOrganizationOwnedDeviceWithManagedProfile() - || areAllUsersAffiliatedWithDeviceLocked(); - } - } - private @UserIdInt int getSecurityLoggingEnabledUser() { synchronized (getLockObject()) { if (mOwners.hasDeviceOwner()) { @@ -13651,7 +13545,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final CallerIdentity caller = getCallerIdentity(admin, packageName); Preconditions.checkCallAuthorization((caller.hasAdminComponent() && isDeviceOwner(caller)) || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_NETWORK_LOGGING))); - Preconditions.checkCallAuthorization(areAllUsersAffiliatedWithDeviceLocked()); + checkAllUsersAreAffiliatedWithDevice(); synchronized (getLockObject()) { if (mNetworkLogger == null || !isNetworkLoggingEnabledInternalLocked()) { @@ -13787,7 +13681,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (token == null || token.length < 32) { throw new IllegalArgumentException("token must be at least 32-byte long"); } - final CallerIdentity caller = getAdminCallerIdentity(admin); + final CallerIdentity caller = getCallerIdentity(admin); Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); synchronized (getLockObject()) { @@ -13811,7 +13705,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) { return false; } - final CallerIdentity caller = getAdminCallerIdentity(admin); + final CallerIdentity caller = getCallerIdentity(admin); Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); synchronized (getLockObject()) { @@ -13836,7 +13730,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) { return false; } - final CallerIdentity caller = getAdminCallerIdentity(admin); + final CallerIdentity caller = getCallerIdentity(admin); Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); synchronized (getLockObject()) { @@ -13861,7 +13755,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } Objects.requireNonNull(token); - final CallerIdentity caller = getAdminCallerIdentity(admin); + final CallerIdentity caller = getCallerIdentity(admin); Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); synchronized (getLockObject()) { diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp index 91478a594ad2..eb6b325050eb 100644 --- a/services/incremental/IncrementalService.cpp +++ b/services/incremental/IncrementalService.cpp @@ -481,6 +481,9 @@ StorageId IncrementalService::createStorage(std::string_view mountPoint, if (!mkdirOrLog(path::join(backing, ".index"), 0777)) { return kInvalidStorageId; } + if (!mkdirOrLog(path::join(backing, ".incomplete"), 0777)) { + return kInvalidStorageId; + } auto status = mVold->mountIncFs(backing, mountTarget, 0, &controlParcel); if (!status.isOk()) { LOG(ERROR) << "Vold::mountIncFs() failed: " << status.toString8(); diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp index a2e6698963a4..8fc5c085999e 100644 --- a/services/tests/mockingservicestests/Android.bp +++ b/services/tests/mockingservicestests/Android.bp @@ -20,6 +20,7 @@ android_test { static_libs: [ "services.core", "services.net", + "services.usage", "service-jobscheduler", "service-permission.impl", "service-blobstore", diff --git a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java index ebd4a4c5378f..f375421043fd 100644 --- a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java @@ -16,6 +16,7 @@ package com.android.server; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean; import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt; import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyLong; @@ -44,6 +45,7 @@ import android.os.SystemProperties; import android.os.UserHandle; import android.provider.DeviceConfig; import android.provider.Settings; +import android.util.ArraySet; import com.android.dx.mockito.inline.extended.ExtendedMockito; import com.android.server.PackageWatchdog.PackageHealthObserverImpact; @@ -63,6 +65,7 @@ import org.mockito.stubbing.Answer; import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; import java.util.List; /** @@ -79,9 +82,11 @@ public class RescuePartyTest { private static final String PROP_DISABLE_RESCUE = "persist.sys.disable_rescue"; private static final String CALLING_PACKAGE1 = "com.package.name1"; private static final String CALLING_PACKAGE2 = "com.package.name2"; + private static final String CALLING_PACKAGE3 = "com.package.name3"; private static final String NAMESPACE1 = "namespace1"; private static final String NAMESPACE2 = "namespace2"; private static final String NAMESPACE3 = "namespace3"; + private static final String NAMESPACE4 = "namespace4"; private static final String PROP_DEVICE_CONFIG_DISABLE_FLAG = "persist.device_config.configuration.disable_rescue_party"; private static final String PROP_DISABLE_FACTORY_RESET_FLAG = @@ -89,6 +94,8 @@ public class RescuePartyTest { private MockitoSession mSession; private HashMap<String, String> mSystemSettingsMap; + //Records the namespaces wiped by setProperties(). + private HashSet<String> mNamespacesWiped; @Mock(answer = Answers.RETURNS_DEEP_STUBS) private Context mMockContext; @@ -119,6 +126,7 @@ public class RescuePartyTest { .spyStatic(PackageWatchdog.class) .startMocking(); mSystemSettingsMap = new HashMap<>(); + mNamespacesWiped = new HashSet<>(); when(mMockContext.getContentResolver()).thenReturn(mMockContentResolver); // Reset observer instance to get new mock context on every run @@ -167,6 +175,16 @@ public class RescuePartyTest { anyBoolean())); doAnswer((Answer<Void>) invocationOnMock -> null) .when(() -> DeviceConfig.resetToDefaults(anyInt(), anyString())); + doAnswer((Answer<Boolean>) invocationOnMock -> { + DeviceConfig.Properties properties = invocationOnMock.getArgument(0); + String namespace = properties.getNamespace(); + // record a wipe + if (properties.getKeyset().isEmpty()) { + mNamespacesWiped.add(namespace); + } + return true; + } + ).when(() -> DeviceConfig.setProperties(any(DeviceConfig.Properties.class))); // Mock PackageWatchdog doAnswer((Answer<PackageWatchdog>) invocationOnMock -> mMockPackageWatchdog) @@ -174,8 +192,6 @@ public class RescuePartyTest { doReturn(CURRENT_NETWORK_TIME_MILLIS).when(() -> RescueParty.getElapsedRealtime()); - SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, - Integer.toString(RescueParty.LEVEL_NONE)); SystemProperties.set(RescueParty.PROP_RESCUE_BOOT_COUNT, Integer.toString(0)); SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(true)); SystemProperties.set(PROP_DEVICE_CONFIG_DISABLE_FLAG, Boolean.toString(false)); @@ -193,12 +209,10 @@ public class RescuePartyTest { mMonitorCallbackCaptor.capture())); HashMap<String, Integer> verifiedTimesMap = new HashMap<String, Integer>(); - noteBoot(); + noteBoot(1); verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS, /*resetNamespaces=*/ null, verifiedTimesMap); - assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS, - SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE)); // Record DeviceConfig accesses RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext); @@ -208,24 +222,19 @@ public class RescuePartyTest { final String[] expectedAllResetNamespaces = new String[]{NAMESPACE1, NAMESPACE2}; - noteBoot(); + noteBoot(2); verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES, expectedAllResetNamespaces, verifiedTimesMap); - assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES, - SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE)); - noteBoot(); + noteBoot(3); verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, expectedAllResetNamespaces, verifiedTimesMap); - assertEquals(RescueParty.LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS, - SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE)); - noteBoot(); + noteBoot(4); - assertEquals(LEVEL_FACTORY_RESET, - SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE)); + assertTrue(RescueParty.isAttemptingFactoryReset()); } @Test @@ -364,24 +373,12 @@ public class RescuePartyTest { @Test public void testIsAttemptingFactoryReset() { for (int i = 0; i < LEVEL_FACTORY_RESET; i++) { - noteBoot(); + noteBoot(i + 1); } assertTrue(RescueParty.isAttemptingFactoryReset()); } @Test - public void testOnSettingsProviderPublishedExecutesRescueLevels() { - SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString(1)); - - RescueParty.onSettingsProviderPublished(mMockContext); - - verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS, /*resetNamespaces=*/ null, - /*configResetVerifiedTimesMap=*/ null); - assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS, - SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE)); - } - - @Test public void testNativeRescuePartyResets() { doReturn(true).when(() -> SettingsToPropertiesMapper.isNativeFlagsResetPerformed()); doReturn(FAKE_RESET_NATIVE_NAMESPACES).when( @@ -425,7 +422,7 @@ public class RescuePartyTest { SystemProperties.set(PROP_DISABLE_FACTORY_RESET_FLAG, Boolean.toString(true)); for (int i = 0; i < LEVEL_FACTORY_RESET; i++) { - noteBoot(); + noteBoot(i + 1); } assertFalse(RescueParty.isAttemptingFactoryReset()); @@ -463,29 +460,128 @@ public class RescuePartyTest { public void testBootLoopLevels() { RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext); - /* - Ensure that the returned user impact corresponds with the user impact of the next available - rescue level, not the current one. - */ - SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString( - RescueParty.LEVEL_NONE)); - assertEquals(observer.onBootLoop(), PackageHealthObserverImpact.USER_IMPACT_LOW); - - SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString( - RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS)); - assertEquals(observer.onBootLoop(), PackageHealthObserverImpact.USER_IMPACT_LOW); - - SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString( - RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES)); - assertEquals(observer.onBootLoop(), PackageHealthObserverImpact.USER_IMPACT_HIGH); - - SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString( - RescueParty.LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS)); - assertEquals(observer.onBootLoop(), PackageHealthObserverImpact.USER_IMPACT_HIGH); - - SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString( - LEVEL_FACTORY_RESET)); - assertEquals(observer.onBootLoop(), PackageHealthObserverImpact.USER_IMPACT_HIGH); + assertEquals(observer.onBootLoop(0), PackageHealthObserverImpact.USER_IMPACT_NONE); + assertEquals(observer.onBootLoop(1), PackageHealthObserverImpact.USER_IMPACT_LOW); + assertEquals(observer.onBootLoop(2), PackageHealthObserverImpact.USER_IMPACT_LOW); + assertEquals(observer.onBootLoop(3), PackageHealthObserverImpact.USER_IMPACT_HIGH); + assertEquals(observer.onBootLoop(4), PackageHealthObserverImpact.USER_IMPACT_HIGH); + assertEquals(observer.onBootLoop(5), PackageHealthObserverImpact.USER_IMPACT_HIGH); + } + + @Test + public void testResetDeviceConfigForPackagesOnlyRuntimeMap() { + RescueParty.onSettingsProviderPublished(mMockContext); + verify(() -> Settings.Config.registerMonitorCallback(eq(mMockContentResolver), + mMonitorCallbackCaptor.capture())); + + // Record DeviceConfig accesses + RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext); + RemoteCallback monitorCallback = mMonitorCallbackCaptor.getValue(); + monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE1, NAMESPACE1)); + monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE1, NAMESPACE2)); + monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE2, NAMESPACE2)); + monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE2, NAMESPACE3)); + // Fake DeviceConfig value changes + monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE1)); + monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE2)); + monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE3)); + + doReturn("").when(() -> DeviceConfig.getString( + eq(RescueParty.NAMESPACE_CONFIGURATION), + eq(RescueParty.NAMESPACE_TO_PACKAGE_MAPPING_FLAG), + eq(""))); + + RescueParty.resetDeviceConfigForPackages(Arrays.asList(new String[]{CALLING_PACKAGE1})); + ArraySet<String> expectedNamespacesWiped = new ArraySet<String>( + Arrays.asList(new String[]{NAMESPACE1, NAMESPACE2})); + assertEquals(mNamespacesWiped, expectedNamespacesWiped); + } + + @Test + public void testResetDeviceConfigForPackagesOnlyPresetMap() { + RescueParty.onSettingsProviderPublished(mMockContext); + verify(() -> Settings.Config.registerMonitorCallback(eq(mMockContentResolver), + mMonitorCallbackCaptor.capture())); + + String presetMapping = NAMESPACE1 + ":" + CALLING_PACKAGE1 + "," + + NAMESPACE2 + ":" + CALLING_PACKAGE2 + "," + + NAMESPACE3 + ":" + CALLING_PACKAGE1; + doReturn(presetMapping).when(() -> DeviceConfig.getString( + eq(RescueParty.NAMESPACE_CONFIGURATION), + eq(RescueParty.NAMESPACE_TO_PACKAGE_MAPPING_FLAG), + eq(""))); + + RescueParty.resetDeviceConfigForPackages(Arrays.asList(new String[]{CALLING_PACKAGE1})); + ArraySet<String> expectedNamespacesWiped = new ArraySet<String>( + Arrays.asList(new String[]{NAMESPACE1, NAMESPACE3})); + assertEquals(mNamespacesWiped, expectedNamespacesWiped); + } + + @Test + public void testResetDeviceConfigForPackagesBothMaps() { + RescueParty.onSettingsProviderPublished(mMockContext); + verify(() -> Settings.Config.registerMonitorCallback(eq(mMockContentResolver), + mMonitorCallbackCaptor.capture())); + + // Record DeviceConfig accesses + RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext); + RemoteCallback monitorCallback = mMonitorCallbackCaptor.getValue(); + monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE1, NAMESPACE1)); + monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE1, NAMESPACE2)); + monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE2, NAMESPACE2)); + monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE2, NAMESPACE3)); + monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE3, NAMESPACE4)); + // Fake DeviceConfig value changes + monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE1)); + monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE2)); + monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE3)); + monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE4)); + + String presetMapping = NAMESPACE1 + ":" + CALLING_PACKAGE1 + "," + + NAMESPACE2 + ":" + CALLING_PACKAGE2 + "," + + NAMESPACE4 + ":" + CALLING_PACKAGE3; + doReturn(presetMapping).when(() -> DeviceConfig.getString( + eq(RescueParty.NAMESPACE_CONFIGURATION), + eq(RescueParty.NAMESPACE_TO_PACKAGE_MAPPING_FLAG), + eq(""))); + + RescueParty.resetDeviceConfigForPackages( + Arrays.asList(new String[]{CALLING_PACKAGE1, CALLING_PACKAGE2})); + ArraySet<String> expectedNamespacesWiped = new ArraySet<String>( + Arrays.asList(new String[]{NAMESPACE1, NAMESPACE2, NAMESPACE3})); + assertEquals(mNamespacesWiped, expectedNamespacesWiped); + } + + @Test + public void testResetDeviceConfigNoExceptionWhenFlagMalformed() { + RescueParty.onSettingsProviderPublished(mMockContext); + verify(() -> Settings.Config.registerMonitorCallback(eq(mMockContentResolver), + mMonitorCallbackCaptor.capture())); + + // Record DeviceConfig accesses + RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext); + RemoteCallback monitorCallback = mMonitorCallbackCaptor.getValue(); + monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE1, NAMESPACE1)); + monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE2, NAMESPACE3)); + monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE3, NAMESPACE4)); + // Fake DeviceConfig value changes + monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE1)); + monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE2)); + monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE3)); + monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE4)); + + String invalidPresetMapping = NAMESPACE2 + ":" + CALLING_PACKAGE2 + "," + + NAMESPACE1 + "." + CALLING_PACKAGE2; + doReturn(invalidPresetMapping).when(() -> DeviceConfig.getString( + eq(RescueParty.NAMESPACE_CONFIGURATION), + eq(RescueParty.NAMESPACE_TO_PACKAGE_MAPPING_FLAG), + eq(""))); + + RescueParty.resetDeviceConfigForPackages( + Arrays.asList(new String[]{CALLING_PACKAGE1, CALLING_PACKAGE2})); + ArraySet<String> expectedNamespacesWiped = new ArraySet<String>( + Arrays.asList(new String[]{NAMESPACE1, NAMESPACE3})); + assertEquals(mNamespacesWiped, expectedNamespacesWiped); } private void verifySettingsResets(int resetMode, String[] resetNamespaces, @@ -513,8 +609,8 @@ public class RescuePartyTest { } } - private void noteBoot() { - RescuePartyObserver.getInstance(mMockContext).executeBootLoopMitigation(); + private void noteBoot(int mitigationCount) { + RescuePartyObserver.getInstance(mMockContext).executeBootLoopMitigation(mitigationCount); } private void notePersistentAppCrash(int mitigationCount) { diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java index 77fef1274415..8795d7711c63 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java @@ -284,6 +284,8 @@ public class QuotaControllerTest { return UsageStatsManager.STANDBY_BUCKET_FREQUENT; case RARE_INDEX: return UsageStatsManager.STANDBY_BUCKET_RARE; + case RESTRICTED_INDEX: + return UsageStatsManager.STANDBY_BUCKET_RESTRICTED; default: return UsageStatsManager.STANDBY_BUCKET_NEVER; } @@ -292,6 +294,7 @@ public class QuotaControllerTest { private void setStandbyBucket(int bucketIndex) { when(mUsageStatsManager.getAppStandbyBucket(eq(SOURCE_PACKAGE), eq(SOURCE_USER_ID), anyLong())).thenReturn(bucketIndexToUsageStatsBucket(bucketIndex)); + mQuotaController.updateStandbyBucket(SOURCE_USER_ID, SOURCE_PACKAGE, bucketIndex); } private void setStandbyBucket(int bucketIndex, JobStatus... jobs) { diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeUserInfoHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeUserInfoHelper.java index be6bc9958140..ac23d4e427c4 100644 --- a/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeUserInfoHelper.java +++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeUserInfoHelper.java @@ -100,6 +100,11 @@ public class FakeUserInfoHelper extends UserInfoHelper { } @Override + public int getCurrentUserId() { + return mCurrentUserId; + } + + @Override protected int[] getProfileIds(int userId) { IntArray profiles = mProfiles.get(userId); if (profiles != null) { diff --git a/services/tests/mockingservicestests/src/com/android/server/usage/UsageStatsServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/usage/UsageStatsServiceTest.java new file mode 100644 index 000000000000..c9fcd0233bef --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/usage/UsageStatsServiceTest.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.usage; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; + +import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.mock; + +import android.app.ActivityManager; +import android.app.IActivityManager; +import android.app.usage.UsageEvents; +import android.app.usage.UsageStatsManagerInternal; +import android.content.Context; +import android.os.RemoteException; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.server.LocalServices; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoSession; +import org.mockito.quality.Strictness; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +@RunWith(AndroidJUnit4.class) +public class UsageStatsServiceTest { + private static final long TIMEOUT = 5000; + + private UsageStatsService mService; + + private MockitoSession mMockingSession; + @Mock + private Context mContext; + + private static class TestInjector extends UsageStatsService.Injector { + AppStandbyInternal getAppStandbyController(Context context) { + return mock(AppStandbyInternal.class); + } + } + + @Before + public void setUp() { + mMockingSession = mockitoSession() + .initMocks(this) + .strictness(Strictness.LENIENT) + .startMocking(); + IActivityManager activityManager = ActivityManager.getService(); + spyOn(activityManager); + try { + doNothing().when(activityManager).registerUidObserver(any(), anyInt(), anyInt(), any()); + } catch (RemoteException e) { + fail("registerUidObserver threw exception: " + e.getMessage()); + } + mService = new UsageStatsService(mContext, new TestInjector()); + spyOn(mService); + doNothing().when(mService).publishBinderServices(); + mService.onStart(); + } + + @Test + public void testUsageEventListener() throws Exception { + TestUsageEventListener listener = new TestUsageEventListener(); + UsageStatsManagerInternal usmi = LocalServices.getService(UsageStatsManagerInternal.class); + usmi.registerListener(listener); + + UsageEvents.Event event = new UsageEvents.Event(UsageEvents.Event.CONFIGURATION_CHANGE, 10); + usmi.reportEvent("com.android.test", 10, event.getEventType()); + listener.setExpectation(10, event); + listener.mCountDownLatch.await(TIMEOUT, TimeUnit.MILLISECONDS); + + usmi.unregisterListener(listener); + listener.reset(); + + usmi.reportEvent("com.android.test", 0, UsageEvents.Event.CHOOSER_ACTION); + Thread.sleep(TIMEOUT); + assertNull(listener.mLastReceivedEvent); + } + + private static class TestUsageEventListener implements + UsageStatsManagerInternal.UsageEventListener { + UsageEvents.Event mLastReceivedEvent; + int mLastReceivedUserId; + UsageEvents.Event mExpectedEvent; + int mExpectedUserId; + CountDownLatch mCountDownLatch; + + @Override + public void onUsageEvent(int userId, UsageEvents.Event event) { + mLastReceivedUserId = userId; + mLastReceivedEvent = event; + if (mCountDownLatch != null && userId == mExpectedUserId + && event.getEventType() == mExpectedEvent.getEventType()) { + mCountDownLatch.countDown(); + } + } + + private void setExpectation(int userId, UsageEvents.Event event) { + mExpectedUserId = userId; + mExpectedEvent = event; + mCountDownLatch = new CountDownLatch(1); + } + + private void reset() { + mLastReceivedUserId = mExpectedUserId = -1; + mLastReceivedEvent = mExpectedEvent = null; + mCountDownLatch = null; + } + } +} diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp index 1f723749856f..0c2fab83ee66 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -49,6 +49,8 @@ android_test { // TODO: remove once Android migrates to JUnit 4.12, // which provides assertThrows "testng", + "junit", + "platform-compat-test-rules", ], @@ -103,6 +105,7 @@ android_test { ":PackageParserTestApp1", ":PackageParserTestApp2", ":PackageParserTestApp3", + ":PackageParserTestApp4", ":apex.test", ], resource_zips: [":FrameworksServicesTests_apks_as_resources"], diff --git a/services/tests/servicestests/res/xml/usertypes_test_full.xml b/services/tests/servicestests/res/xml/usertypes_test_full.xml index a281dcaafb3d..099ccbe5b5f6 100644 --- a/services/tests/servicestests/res/xml/usertypes_test_full.xml +++ b/services/tests/servicestests/res/xml/usertypes_test_full.xml @@ -22,4 +22,7 @@ <item res='@*android:color/profile_badge_1' /> </badge-colors> </full-type> + + <change-user-type from="android.old.name" to="android.test.1" whenVersionLeq="1" /> + </user-types> diff --git a/services/tests/servicestests/res/xml/usertypes_test_profile.xml b/services/tests/servicestests/res/xml/usertypes_test_profile.xml index b6c8fbdcdd20..daa7d7b7341a 100644 --- a/services/tests/servicestests/res/xml/usertypes_test_profile.xml +++ b/services/tests/servicestests/res/xml/usertypes_test_profile.xml @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<user-types> +<user-types version="1234"> <profile-type name='android.test.2' max-allowed-per-parent='12' @@ -32,4 +32,7 @@ <default-restrictions no_remove_user='true' no_bluetooth='true' /> </profile-type> <profile-type name='custom.test.1' max-allowed-per-parent='14' /> + + <change-user-type from="android.test.1" to="android.test.2" whenVersionLeq="1233" /> + </user-types> diff --git a/services/tests/servicestests/src/android/location/timezone/LocationTimeZoneEventTest.java b/services/tests/servicestests/src/com/android/internal/location/timezone/LocationTimeZoneEventTest.java index 80373ac66109..84b886eb4b3d 100644 --- a/services/tests/servicestests/src/android/location/timezone/LocationTimeZoneEventTest.java +++ b/services/tests/servicestests/src/com/android/internal/location/timezone/LocationTimeZoneEventTest.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package android.location.timezone; +package com.android.internal.location.timezone; -import static android.location.timezone.ParcelableTestSupport.assertRoundTripParcelable; +import static com.android.internal.location.timezone.ParcelableTestSupport.assertRoundTripParcelable; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; @@ -29,7 +29,7 @@ import java.util.List; public class LocationTimeZoneEventTest { - private static final long ARBITRARY_ELAPSED_REALTIME_NANOS = 9999; + private static final long ARBITRARY_ELAPSED_REALTIME_MILLIS = 9999; private static final List<String> ARBITRARY_TIME_ZONE_IDS = singletonList("Europe/London"); @@ -42,7 +42,7 @@ public class LocationTimeZoneEventTest { public void testBuildUnsetEventType() { new LocationTimeZoneEvent.Builder() .setTimeZoneIds(ARBITRARY_TIME_ZONE_IDS) - .setElapsedRealtimeNanos(ARBITRARY_ELAPSED_REALTIME_NANOS) + .setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS) .build(); } @@ -51,7 +51,7 @@ public class LocationTimeZoneEventTest { new LocationTimeZoneEvent.Builder() .setEventType(LocationTimeZoneEvent.EVENT_TYPE_UNCERTAIN) .setTimeZoneIds(ARBITRARY_TIME_ZONE_IDS) - .setElapsedRealtimeNanos(ARBITRARY_ELAPSED_REALTIME_NANOS) + .setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS) .build(); } @@ -59,7 +59,7 @@ public class LocationTimeZoneEventTest { public void testEquals() { LocationTimeZoneEvent.Builder builder1 = new LocationTimeZoneEvent.Builder() .setEventType(LocationTimeZoneEvent.EVENT_TYPE_UNCERTAIN) - .setElapsedRealtimeNanos(ARBITRARY_ELAPSED_REALTIME_NANOS); + .setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS); { LocationTimeZoneEvent one = builder1.build(); assertEquals(one, one); @@ -67,7 +67,7 @@ public class LocationTimeZoneEventTest { LocationTimeZoneEvent.Builder builder2 = new LocationTimeZoneEvent.Builder() .setEventType(LocationTimeZoneEvent.EVENT_TYPE_UNCERTAIN) - .setElapsedRealtimeNanos(ARBITRARY_ELAPSED_REALTIME_NANOS); + .setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS); { LocationTimeZoneEvent one = builder1.build(); LocationTimeZoneEvent two = builder2.build(); @@ -75,7 +75,7 @@ public class LocationTimeZoneEventTest { assertEquals(two, one); } - builder1.setElapsedRealtimeNanos(ARBITRARY_ELAPSED_REALTIME_NANOS + 1); + builder1.setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS + 1); { LocationTimeZoneEvent one = builder1.build(); LocationTimeZoneEvent two = builder2.build(); @@ -83,7 +83,7 @@ public class LocationTimeZoneEventTest { assertNotEquals(two, one); } - builder2.setElapsedRealtimeNanos(ARBITRARY_ELAPSED_REALTIME_NANOS + 1); + builder2.setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS + 1); { LocationTimeZoneEvent one = builder1.build(); LocationTimeZoneEvent two = builder2.build(); @@ -128,7 +128,7 @@ public class LocationTimeZoneEventTest { public void testParcelable() { LocationTimeZoneEvent.Builder builder = new LocationTimeZoneEvent.Builder() .setEventType(LocationTimeZoneEvent.EVENT_TYPE_PERMANENT_FAILURE) - .setElapsedRealtimeNanos(ARBITRARY_ELAPSED_REALTIME_NANOS); + .setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS); assertRoundTripParcelable(builder.build()); builder.setEventType(LocationTimeZoneEvent.EVENT_TYPE_SUCCESS) diff --git a/services/tests/servicestests/src/com/android/internal/location/timezone/LocationTimeZoneProviderRequestTest.java b/services/tests/servicestests/src/com/android/internal/location/timezone/LocationTimeZoneProviderRequestTest.java index 75696dac3b44..95daa36f2c40 100644 --- a/services/tests/servicestests/src/com/android/internal/location/timezone/LocationTimeZoneProviderRequestTest.java +++ b/services/tests/servicestests/src/com/android/internal/location/timezone/LocationTimeZoneProviderRequestTest.java @@ -16,7 +16,7 @@ package com.android.internal.location.timezone; -import static android.location.timezone.ParcelableTestSupport.assertRoundTripParcelable; +import static com.android.internal.location.timezone.ParcelableTestSupport.assertRoundTripParcelable; import org.junit.Test; diff --git a/services/tests/servicestests/src/android/location/timezone/ParcelableTestSupport.java b/services/tests/servicestests/src/com/android/internal/location/timezone/ParcelableTestSupport.java index 316a2e65fb30..ece5d004975d 100644 --- a/services/tests/servicestests/src/android/location/timezone/ParcelableTestSupport.java +++ b/services/tests/servicestests/src/com/android/internal/location/timezone/ParcelableTestSupport.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.location.timezone; +package com.android.internal.location.timezone; import static org.junit.Assert.assertEquals; @@ -24,7 +24,7 @@ import android.os.Parcelable; import java.lang.reflect.Field; /** Utility methods related to {@link Parcelable} objects used in several tests. */ -public final class ParcelableTestSupport { +final class ParcelableTestSupport { private ParcelableTestSupport() {} diff --git a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java index e76c5a476c48..a02c53336da0 100644 --- a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java @@ -19,6 +19,7 @@ package com.android.server; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.never; @@ -36,6 +37,7 @@ import android.platform.test.annotations.Presubmit; import android.provider.Settings; import android.telecom.TelecomManager; import android.test.mock.MockContentResolver; +import android.testing.TestableLooper; import android.util.MutableBoolean; import android.view.KeyEvent; @@ -44,6 +46,7 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.test.FakeSettingsProvider; import com.android.server.statusbar.StatusBarManagerInternal; @@ -65,6 +68,7 @@ import java.util.List; @Presubmit @SmallTest @RunWith(AndroidJUnit4.class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) public class GestureLauncherServiceTest { private static final int FAKE_USER_ID = 1337; @@ -83,6 +87,7 @@ public class GestureLauncherServiceTest { private @Mock StatusBarManagerInternal mStatusBarManagerInternal; private @Mock TelecomManager mTelecomManager; private @Mock MetricsLogger mMetricsLogger; + @Mock private UiEventLogger mUiEventLogger; private MockContentResolver mContentResolver; private GestureLauncherService mGestureLauncherService; @@ -109,7 +114,8 @@ public class GestureLauncherServiceTest { when(mContext.getSystemService(Context.TELECOM_SERVICE)).thenReturn(mTelecomManager); when(mTelecomManager.createLaunchEmergencyDialerIntent(null)).thenReturn(new Intent()); - mGestureLauncherService = new GestureLauncherService(mContext, mMetricsLogger); + mGestureLauncherService = new GestureLauncherService(mContext, mMetricsLogger, + mUiEventLogger); } @Test @@ -268,6 +274,7 @@ public class GestureLauncherServiceTest { verify(mMetricsLogger, never()) .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt()); + verify(mUiEventLogger, never()).log(any()); final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class); verify(mMetricsLogger, times(2)).histogram( @@ -312,6 +319,7 @@ public class GestureLauncherServiceTest { verify(mMetricsLogger, never()) .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt()); + verify(mUiEventLogger, never()).log(any()); final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class); verify(mMetricsLogger, times(2)).histogram( @@ -358,6 +366,7 @@ public class GestureLauncherServiceTest { verify(mMetricsLogger, never()) .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt()); + verify(mUiEventLogger, never()).log(any()); final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class); verify(mMetricsLogger, times(2)).histogram( @@ -406,6 +415,8 @@ public class GestureLauncherServiceTest { StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP); verify(mMetricsLogger) .action(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE, (int) interval); + verify(mUiEventLogger, times(1)) + .log(GestureLauncherService.GestureLauncherEvent.GESTURE_CAMERA_DOUBLE_TAP_POWER); final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class); verify(mMetricsLogger, times(2)).histogram( @@ -460,6 +471,8 @@ public class GestureLauncherServiceTest { StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP); verify(mMetricsLogger) .action(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE, (int) interval); + verify(mUiEventLogger, times(1)) + .log(GestureLauncherService.GestureLauncherEvent.GESTURE_CAMERA_DOUBLE_TAP_POWER); final ArgumentCaptor<Integer> cameraIntervalCaptor = ArgumentCaptor.forClass(Integer.class); verify(mMetricsLogger, times(2)).histogram( @@ -499,7 +512,8 @@ public class GestureLauncherServiceTest { assertTrue(intercepted); assertTrue(outLaunched.value); - // TODO (b/169960245) Verify metric event equiv. to ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE + verify(mUiEventLogger, times(1)) + .log(GestureLauncherService.GestureLauncherEvent.GESTURE_PANIC_TAP_POWER); verify(mStatusBarManagerInternal).onEmergencyActionLaunchGestureDetected(); final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class); @@ -551,7 +565,8 @@ public class GestureLauncherServiceTest { assertTrue(outLaunched.value); assertTrue(intercepted); - // TODO (b/169960245) Verify metric event equiv. to ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE + verify(mUiEventLogger, times(1)) + .log(GestureLauncherService.GestureLauncherEvent.GESTURE_PANIC_TAP_POWER); verify(mStatusBarManagerInternal).onEmergencyActionLaunchGestureDetected(); final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class); @@ -646,6 +661,7 @@ public class GestureLauncherServiceTest { verify(mMetricsLogger, never()) .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt()); + verify(mUiEventLogger, never()).log(any()); final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class); verify(mMetricsLogger, times(1)).histogram( @@ -690,6 +706,7 @@ public class GestureLauncherServiceTest { verify(mMetricsLogger, never()) .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt()); + verify(mUiEventLogger, never()).log(any()); final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class); verify(mMetricsLogger, times(2)).histogram( @@ -736,6 +753,7 @@ public class GestureLauncherServiceTest { verify(mMetricsLogger, never()) .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt()); + verify(mUiEventLogger, never()).log(any()); final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class); verify(mMetricsLogger, times(2)).histogram( @@ -782,6 +800,7 @@ public class GestureLauncherServiceTest { verify(mMetricsLogger, never()) .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt()); + verify(mUiEventLogger, never()).log(any()); final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class); verify(mMetricsLogger, times(2)).histogram( @@ -826,6 +845,7 @@ public class GestureLauncherServiceTest { verify(mMetricsLogger, never()) .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt()); + verify(mUiEventLogger, never()).log(any()); final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class); verify(mMetricsLogger, times(2)).histogram( @@ -869,6 +889,7 @@ public class GestureLauncherServiceTest { assertFalse(outLaunched.value); verify(mMetricsLogger, never()) .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt()); + verify(mUiEventLogger, never()).log(any()); final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class); verify(mMetricsLogger, times(2)).histogram( @@ -914,6 +935,7 @@ public class GestureLauncherServiceTest { assertFalse(outLaunched.value); verify(mMetricsLogger, never()) .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt()); + verify(mUiEventLogger, never()).log(any()); final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class); verify(mMetricsLogger, times(2)).histogram( @@ -961,6 +983,8 @@ public class GestureLauncherServiceTest { StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP); verify(mMetricsLogger) .action(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE, (int) interval); + verify(mUiEventLogger, times(1)) + .log(GestureLauncherService.GestureLauncherEvent.GESTURE_CAMERA_DOUBLE_TAP_POWER); final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class); verify(mMetricsLogger, times(2)).histogram( @@ -1007,6 +1031,7 @@ public class GestureLauncherServiceTest { verify(mMetricsLogger, never()) .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt()); + verify(mUiEventLogger, never()).log(any()); final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class); verify(mMetricsLogger, times(2)).histogram( @@ -1051,6 +1076,7 @@ public class GestureLauncherServiceTest { verify(mMetricsLogger, never()) .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt()); + verify(mUiEventLogger, never()).log(any()); final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class); verify(mMetricsLogger, times(2)).histogram( @@ -1097,6 +1123,7 @@ public class GestureLauncherServiceTest { verify(mMetricsLogger, never()) .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt()); + verify(mUiEventLogger, never()).log(any()); final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class); verify(mMetricsLogger, times(2)).histogram( diff --git a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java index eae4a0804971..64f31358ccb3 100644 --- a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java @@ -43,6 +43,8 @@ import android.content.ContentResolver; import android.content.Context; import android.content.ContextWrapper; import android.content.pm.PackageManagerInternal; +import android.hardware.input.IInputManager; +import android.hardware.input.InputManager; import android.hardware.vibrator.IVibrator; import android.media.AudioAttributes; import android.media.AudioManager; @@ -63,11 +65,13 @@ import android.os.Vibrator; import android.os.test.TestLooper; import android.platform.test.annotations.Presubmit; import android.provider.Settings; +import android.view.InputDevice; import androidx.test.InstrumentationRegistry; import com.android.internal.util.test.FakeSettingsProvider; import com.android.internal.util.test.FakeSettingsProviderRule; +import com.android.server.vibrator.VibratorController; import org.junit.After; import org.junit.Before; @@ -118,8 +122,9 @@ public class VibratorServiceTest { // TODO(b/131311651): replace with a FakeVibrator instead. @Mock private Vibrator mVibratorMock; @Mock private AppOpsManager mAppOpsManagerMock; - @Mock private VibratorService.NativeWrapper mNativeWrapperMock; + @Mock private VibratorController.NativeWrapper mNativeWrapperMock; @Mock private IVibratorStateListener mVibratorStateListenerMock; + @Mock private IInputManager mIInputManagerMock; @Mock private IBinder mVibratorStateListenerBinderMock; private TestLooper mTestLooper; @@ -129,10 +134,12 @@ public class VibratorServiceTest { public void setUp() throws Exception { mTestLooper = new TestLooper(); mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext())); + InputManager inputManager = InputManager.resetInstance(mIInputManagerMock); ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy); when(mContextSpy.getContentResolver()).thenReturn(contentResolver); when(mContextSpy.getSystemService(eq(Context.VIBRATOR_SERVICE))).thenReturn(mVibratorMock); + when(mContextSpy.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(inputManager); when(mContextSpy.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManagerMock); when(mVibratorMock.getDefaultHapticFeedbackIntensity()) .thenReturn(Vibrator.VIBRATION_INTENSITY_MEDIUM); @@ -145,6 +152,7 @@ public class VibratorServiceTest { .thenReturn(new ComponentName("", "")); when(mPowerManagerInternalMock.getLowPowerState(PowerManager.ServiceType.VIBRATION)) .thenReturn(mPowerSaveStateMock); + when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[0]); setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1); setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, @@ -168,8 +176,9 @@ public class VibratorServiceTest { VibratorService service = new VibratorService(mContextSpy, new VibratorService.Injector() { @Override - VibratorService.NativeWrapper getNativeWrapper() { - return mNativeWrapperMock; + VibratorController createVibratorController( + VibratorController.OnVibrationCompleteListener listener) { + return new VibratorController(0, listener, mNativeWrapperMock); } @Override @@ -189,19 +198,19 @@ public class VibratorServiceTest { @Test public void createService_initializesNativeService() { createService(); - verify(mNativeWrapperMock).vibratorInit(notNull()); - verify(mNativeWrapperMock).vibratorOff(); + verify(mNativeWrapperMock).init(eq(0), notNull()); + verify(mNativeWrapperMock).off(); } @Test public void hasVibrator_withVibratorHalPresent_returnsTrue() { - when(mNativeWrapperMock.vibratorExists()).thenReturn(true); + when(mNativeWrapperMock.isAvailable()).thenReturn(true); assertTrue(createService().hasVibrator()); } @Test public void hasVibrator_withNoVibratorHalPresent_returnsFalse() { - when(mNativeWrapperMock.vibratorExists()).thenReturn(false); + when(mNativeWrapperMock.isAvailable()).thenReturn(false); assertFalse(createService().hasVibrator()); } @@ -217,8 +226,17 @@ public class VibratorServiceTest { } @Test + public void hasAmplitudeControl_withInputDevices_returnsTrue() throws Exception { + when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1}); + when(mIInputManagerMock.getInputDevice(1)).thenReturn(createInputDeviceWithVibrator(1)); + mockVibratorCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); + setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1); + assertTrue(createService().hasAmplitudeControl()); + } + + @Test public void areEffectsSupported_withNullResultFromNative_returnsSupportUnknown() { - when(mNativeWrapperMock.vibratorGetSupportedEffects()).thenReturn(null); + when(mNativeWrapperMock.getSupportedEffects()).thenReturn(null); assertArrayEquals(new int[]{Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN}, createService().areEffectsSupported(new int[]{VibrationEffect.EFFECT_CLICK})); } @@ -227,7 +245,7 @@ public class VibratorServiceTest { public void areEffectsSupported_withSomeEffectsSupported_returnsSupportYesAndNoForEffects() { int[] effects = new int[]{VibrationEffect.EFFECT_CLICK, VibrationEffect.EFFECT_TICK}; - when(mNativeWrapperMock.vibratorGetSupportedEffects()) + when(mNativeWrapperMock.getSupportedEffects()) .thenReturn(new int[]{VibrationEffect.EFFECT_CLICK}); assertArrayEquals( new int[]{Vibrator.VIBRATION_EFFECT_SUPPORT_YES, @@ -247,7 +265,7 @@ public class VibratorServiceTest { @Test public void arePrimitivesSupported_withNullResultFromNative_returnsAlwaysFalse() { mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); - when(mNativeWrapperMock.vibratorGetSupportedPrimitives()).thenReturn(null); + when(mNativeWrapperMock.getSupportedPrimitives()).thenReturn(null); assertArrayEquals(new boolean[]{false, false}, createService().arePrimitivesSupported(new int[]{ @@ -259,7 +277,7 @@ public class VibratorServiceTest { @Test public void arePrimitivesSupported_withSomeSupportedPrimitives_returnsBasedOnNativeResult() { mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); - when(mNativeWrapperMock.vibratorGetSupportedPrimitives()) + when(mNativeWrapperMock.getSupportedPrimitives()) .thenReturn(new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK}); assertArrayEquals(new boolean[]{true, false}, @@ -275,7 +293,7 @@ public class VibratorServiceTest { assertTrue(createService().setAlwaysOnEffect(UID, PACKAGE_NAME, 1, VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK), ALARM_ATTRS)); - verify(mNativeWrapperMock).vibratorAlwaysOnEnable( + verify(mNativeWrapperMock).alwaysOnEnable( eq(1L), eq((long) VibrationEffect.EFFECT_CLICK), eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG)); } @@ -286,8 +304,8 @@ public class VibratorServiceTest { assertFalse(createService().setAlwaysOnEffect(UID, PACKAGE_NAME, 1, VibrationEffect.createOneShot(100, 255), ALARM_ATTRS)); - verify(mNativeWrapperMock, never()).vibratorAlwaysOnDisable(anyLong()); - verify(mNativeWrapperMock, never()).vibratorAlwaysOnEnable(anyLong(), anyLong(), anyLong()); + verify(mNativeWrapperMock, never()).alwaysOnDisable(anyLong()); + verify(mNativeWrapperMock, never()).alwaysOnEnable(anyLong(), anyLong(), anyLong()); } @Test @@ -295,15 +313,15 @@ public class VibratorServiceTest { mockVibratorCapabilities(IVibrator.CAP_ALWAYS_ON_CONTROL); assertTrue(createService().setAlwaysOnEffect(UID, PACKAGE_NAME, 1, null, ALARM_ATTRS)); - verify(mNativeWrapperMock).vibratorAlwaysOnDisable(eq(1L)); + verify(mNativeWrapperMock).alwaysOnDisable(eq(1L)); } @Test public void setAlwaysOnEffect_withoutCapability_ignoresEffect() { assertFalse(createService().setAlwaysOnEffect(UID, PACKAGE_NAME, 1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), ALARM_ATTRS)); - verify(mNativeWrapperMock, never()).vibratorAlwaysOnDisable(anyLong()); - verify(mNativeWrapperMock, never()).vibratorAlwaysOnEnable(anyLong(), anyLong(), anyLong()); + verify(mNativeWrapperMock, never()).alwaysOnDisable(anyLong()); + verify(mNativeWrapperMock, never()).alwaysOnEnable(anyLong(), anyLong(), anyLong()); } @Test @@ -322,9 +340,9 @@ public class VibratorServiceTest { vibrate(createService(), VibrationEffect.createOneShot(100, 100), RINGTONE_ATTRS); InOrder inOrderVerifier = inOrder(mNativeWrapperMock); - inOrderVerifier.verify(mNativeWrapperMock, never()).vibratorOn(eq(1L), anyLong()); - inOrderVerifier.verify(mNativeWrapperMock).vibratorOn(eq(10L), anyLong()); - inOrderVerifier.verify(mNativeWrapperMock).vibratorOn(eq(100L), anyLong()); + inOrderVerifier.verify(mNativeWrapperMock, never()).on(eq(1L), anyLong()); + inOrderVerifier.verify(mNativeWrapperMock).on(eq(10L), anyLong()); + inOrderVerifier.verify(mNativeWrapperMock).on(eq(100L), anyLong()); } @Test @@ -375,6 +393,22 @@ public class VibratorServiceTest { } @Test + public void vibrate_withOneShotAndInputDevices_vibratesInputDevices() throws Exception { + when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1}); + when(mIInputManagerMock.getInputDevice(1)).thenReturn(createInputDeviceWithVibrator(1)); + setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1); + VibratorService service = createService(); + Mockito.clearInvocations(mNativeWrapperMock); + + VibrationEffect effect = VibrationEffect.createOneShot(100, 128); + vibrate(service, effect); + assertFalse(service.isVibrating()); + + verify(mIInputManagerMock).vibrate(eq(1), eq(effect), any()); + verify(mNativeWrapperMock, never()).on(anyLong(), anyLong()); + } + + @Test public void vibrate_withOneShotAndAmplitudeControl_turnsVibratorOnAndSetsAmplitude() { mockVibratorCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); VibratorService service = createService(); @@ -383,9 +417,9 @@ public class VibratorServiceTest { vibrate(service, VibrationEffect.createOneShot(100, 128)); assertTrue(service.isVibrating()); - verify(mNativeWrapperMock).vibratorOff(); - verify(mNativeWrapperMock).vibratorOn(eq(100L), gt(0L)); - verify(mNativeWrapperMock).vibratorSetAmplitude(eq(128)); + verify(mNativeWrapperMock).off(); + verify(mNativeWrapperMock).on(eq(100L), gt(0L)); + verify(mNativeWrapperMock).setAmplitude(eq(128)); } @Test @@ -396,26 +430,45 @@ public class VibratorServiceTest { vibrate(service, VibrationEffect.createOneShot(100, 128)); assertTrue(service.isVibrating()); - verify(mNativeWrapperMock).vibratorOff(); - verify(mNativeWrapperMock).vibratorOn(eq(100L), gt(0L)); - verify(mNativeWrapperMock, never()).vibratorSetAmplitude(anyInt()); + verify(mNativeWrapperMock).off(); + verify(mNativeWrapperMock).on(eq(100L), gt(0L)); + verify(mNativeWrapperMock, never()).setAmplitude(anyInt()); } @Test public void vibrate_withPrebaked_performsEffect() { - when(mNativeWrapperMock.vibratorGetSupportedEffects()) + when(mNativeWrapperMock.getSupportedEffects()) .thenReturn(new int[]{VibrationEffect.EFFECT_CLICK}); VibratorService service = createService(); Mockito.clearInvocations(mNativeWrapperMock); vibrate(service, VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)); - verify(mNativeWrapperMock).vibratorOff(); - verify(mNativeWrapperMock).vibratorPerformEffect(eq((long) VibrationEffect.EFFECT_CLICK), + verify(mNativeWrapperMock).off(); + verify(mNativeWrapperMock).perform(eq((long) VibrationEffect.EFFECT_CLICK), eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG), gt(0L)); } @Test + public void vibrate_withPrebakedAndInputDevices_vibratesFallbackWaveformOnInputDevices() + throws Exception { + when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1}); + when(mIInputManagerMock.getInputDevice(1)).thenReturn(createInputDeviceWithVibrator(1)); + setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1); + VibratorService service = createService(); + Mockito.clearInvocations(mNativeWrapperMock); + + vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK)); + assertFalse(service.isVibrating()); + + // Wait for VibrateThread to turn input device vibrator ON. + Thread.sleep(5); + verify(mIInputManagerMock).vibrate(eq(1), any(), any()); + verify(mNativeWrapperMock, never()).on(anyLong(), anyLong()); + verify(mNativeWrapperMock, never()).perform(anyLong(), anyLong(), anyLong()); + } + + @Test public void vibrate_withComposed_performsEffect() { mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); VibratorService service = createService(); @@ -429,9 +482,8 @@ public class VibratorServiceTest { ArgumentCaptor<VibrationEffect.Composition.PrimitiveEffect[]> primitivesCaptor = ArgumentCaptor.forClass(VibrationEffect.Composition.PrimitiveEffect[].class); - verify(mNativeWrapperMock).vibratorOff(); - verify(mNativeWrapperMock).vibratorPerformComposedEffect( - primitivesCaptor.capture(), gt(0L)); + verify(mNativeWrapperMock).off(); + verify(mNativeWrapperMock).compose(primitivesCaptor.capture(), gt(0L)); // Check all primitive effect fields are passed down to the HAL. assertEquals(1, primitivesCaptor.getValue().length); @@ -442,6 +494,27 @@ public class VibratorServiceTest { } @Test + public void vibrate_withComposedAndInputDevices_vibratesInputDevices() + throws Exception { + when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1, 2}); + when(mIInputManagerMock.getInputDevice(1)).thenReturn(createInputDeviceWithVibrator(1)); + when(mIInputManagerMock.getInputDevice(2)).thenReturn(createInputDeviceWithVibrator(2)); + setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1); + VibratorService service = createService(); + Mockito.clearInvocations(mNativeWrapperMock); + + VibrationEffect effect = VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f, 10) + .compose(); + vibrate(service, effect); + assertFalse(service.isVibrating()); + + verify(mIInputManagerMock).vibrate(eq(1), eq(effect), any()); + verify(mIInputManagerMock).vibrate(eq(2), eq(effect), any()); + verify(mNativeWrapperMock, never()).compose(any(), anyLong()); + } + + @Test public void vibrate_withWaveform_controlsVibratorAmplitudeDuringTotalVibrationTime() throws Exception { mockVibratorCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); @@ -455,12 +528,12 @@ public class VibratorServiceTest { // Wait for VibrateThread to finish: 10ms 100, 10ms 200, 10ms 50. Thread.sleep(40); InOrder inOrderVerifier = inOrder(mNativeWrapperMock); - inOrderVerifier.verify(mNativeWrapperMock).vibratorOff(); - inOrderVerifier.verify(mNativeWrapperMock).vibratorOn(eq(30L), anyLong()); - inOrderVerifier.verify(mNativeWrapperMock).vibratorSetAmplitude(eq(100)); - inOrderVerifier.verify(mNativeWrapperMock).vibratorSetAmplitude(eq(200)); - inOrderVerifier.verify(mNativeWrapperMock).vibratorSetAmplitude(eq(50)); - inOrderVerifier.verify(mNativeWrapperMock).vibratorOff(); + inOrderVerifier.verify(mNativeWrapperMock).off(); + inOrderVerifier.verify(mNativeWrapperMock).on(eq(30L), anyLong()); + inOrderVerifier.verify(mNativeWrapperMock).setAmplitude(eq(100)); + inOrderVerifier.verify(mNativeWrapperMock).setAmplitude(eq(200)); + inOrderVerifier.verify(mNativeWrapperMock).setAmplitude(eq(50)); + inOrderVerifier.verify(mNativeWrapperMock).off(); } @Test @@ -473,12 +546,12 @@ public class VibratorServiceTest { doAnswer(invocation -> { Thread.currentThread().sleep(stepDuration / 4); return null; - }).when(mNativeWrapperMock).vibratorOn(anyLong(), anyLong()); + }).when(mNativeWrapperMock).on(anyLong(), anyLong()); // 25% of each waveform step will be spent on the native setAmplitude() call.. doAnswer(invocation -> { Thread.currentThread().sleep(stepDuration / 4); return null; - }).when(mNativeWrapperMock).vibratorSetAmplitude(anyInt()); + }).when(mNativeWrapperMock).setAmplitude(anyInt()); VibratorService service = createService(); @@ -500,49 +573,71 @@ public class VibratorServiceTest { } @Test + public void vibrate_withWaveformAndInputDevices_vibratesInputDevices() throws Exception { + when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1}); + when(mIInputManagerMock.getInputDevice(1)).thenReturn(createInputDeviceWithVibrator(1)); + setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1); + VibratorService service = createService(); + Mockito.clearInvocations(mNativeWrapperMock); + + VibrationEffect effect = VibrationEffect.createWaveform( + new long[]{10, 10, 10}, new int[]{100, 200, 50}, -1); + vibrate(service, effect); + assertFalse(service.isVibrating()); + + // Wait for VibrateThread to turn input device vibrator ON. + Thread.sleep(5); + verify(mIInputManagerMock).vibrate(eq(1), eq(effect), any()); + verify(mNativeWrapperMock, never()).on(anyLong(), anyLong()); + } + + @Test public void vibrate_withOneShotAndNativeCallbackTriggered_finishesVibration() { VibratorService service = createService(); doAnswer(invocation -> { service.onVibrationComplete(invocation.getArgument(1)); return null; - }).when(mNativeWrapperMock).vibratorOn(anyLong(), anyLong()); + }).when(mNativeWrapperMock).on(anyLong(), anyLong()); Mockito.clearInvocations(mNativeWrapperMock); vibrate(service, VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE)); InOrder inOrderVerifier = inOrder(mNativeWrapperMock); - inOrderVerifier.verify(mNativeWrapperMock).vibratorOff(); - inOrderVerifier.verify(mNativeWrapperMock).vibratorOn(eq(100L), - gt(0L)); - inOrderVerifier.verify(mNativeWrapperMock).vibratorOff(); + inOrderVerifier.verify(mNativeWrapperMock).off(); + inOrderVerifier.verify(mNativeWrapperMock).on(eq(100L), gt(0L)); + inOrderVerifier.verify(mNativeWrapperMock).off(); } @Test public void vibrate_withPrebakedAndNativeCallbackTriggered_finishesVibration() { - when(mNativeWrapperMock.vibratorGetSupportedEffects()) + when(mNativeWrapperMock.getSupportedEffects()) .thenReturn(new int[]{VibrationEffect.EFFECT_CLICK}); VibratorService service = createService(); doAnswer(invocation -> { service.onVibrationComplete(invocation.getArgument(2)); return 10_000L; // 10s - }).when(mNativeWrapperMock).vibratorPerformEffect(anyLong(), anyLong(), anyLong()); + }).when(mNativeWrapperMock).perform(anyLong(), anyLong(), anyLong()); Mockito.clearInvocations(mNativeWrapperMock); vibrate(service, VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)); InOrder inOrderVerifier = inOrder(mNativeWrapperMock); - inOrderVerifier.verify(mNativeWrapperMock).vibratorOff(); - inOrderVerifier.verify(mNativeWrapperMock).vibratorPerformEffect( + inOrderVerifier.verify(mNativeWrapperMock).off(); + inOrderVerifier.verify(mNativeWrapperMock).perform( eq((long) VibrationEffect.EFFECT_CLICK), eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG), gt(0L)); - inOrderVerifier.verify(mNativeWrapperMock).vibratorOff(); + inOrderVerifier.verify(mNativeWrapperMock).off(); } @Test - public void vibrate_withWaveformAndNativeCallback_callbackCannotBeTriggeredByNative() + public void vibrate_withWaveformAndNativeCallback_callbackIgnoredAndWaveformPlaysCompletely() throws Exception { VibratorService service = createService(); + doAnswer(invocation -> { + service.onVibrationComplete(invocation.getArgument(1)); + return null; + }).when(mNativeWrapperMock).on(anyLong(), anyLong()); Mockito.clearInvocations(mNativeWrapperMock); VibrationEffect effect = VibrationEffect.createWaveform(new long[]{1, 3, 1, 2}, -1); @@ -551,10 +646,11 @@ public class VibratorServiceTest { // Wait for VibrateThread to finish: 1ms OFF, 3ms ON, 1ms OFF, 2ms ON. Thread.sleep(15); InOrder inOrderVerifier = inOrder(mNativeWrapperMock); - inOrderVerifier.verify(mNativeWrapperMock).vibratorOff(); - inOrderVerifier.verify(mNativeWrapperMock).vibratorOn(eq(3L), anyLong()); - inOrderVerifier.verify(mNativeWrapperMock).vibratorOn(eq(2L), anyLong()); - inOrderVerifier.verify(mNativeWrapperMock).vibratorOff(); + inOrderVerifier.verify(mNativeWrapperMock, times(2)).off(); + inOrderVerifier.verify(mNativeWrapperMock).on(eq(3L), anyLong()); + inOrderVerifier.verify(mNativeWrapperMock).off(); + inOrderVerifier.verify(mNativeWrapperMock).on(eq(2L), anyLong()); + inOrderVerifier.verify(mNativeWrapperMock).off(); } @Test @@ -564,7 +660,7 @@ public class VibratorServiceTest { doAnswer(invocation -> { service.onVibrationComplete(invocation.getArgument(1)); return null; - }).when(mNativeWrapperMock).vibratorPerformComposedEffect(any(), anyLong()); + }).when(mNativeWrapperMock).compose(any(), anyLong()); Mockito.clearInvocations(mNativeWrapperMock); VibrationEffect effect = VibrationEffect.startComposition() @@ -573,14 +669,14 @@ public class VibratorServiceTest { vibrate(service, effect); InOrder inOrderVerifier = inOrder(mNativeWrapperMock); - inOrderVerifier.verify(mNativeWrapperMock).vibratorOff(); - inOrderVerifier.verify(mNativeWrapperMock).vibratorPerformComposedEffect( + inOrderVerifier.verify(mNativeWrapperMock).off(); + inOrderVerifier.verify(mNativeWrapperMock).compose( any(VibrationEffect.Composition.PrimitiveEffect[].class), gt(0L)); - inOrderVerifier.verify(mNativeWrapperMock).vibratorOff(); + inOrderVerifier.verify(mNativeWrapperMock).off(); } @Test - public void cancelVibrate_withDeviceVibrating_callsVibratorOff() { + public void cancelVibrate_withDeviceVibrating_callsoff() { VibratorService service = createService(); vibrate(service, VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE)); assertTrue(service.isVibrating()); @@ -588,7 +684,7 @@ public class VibratorServiceTest { service.cancelVibrate(service); assertFalse(service.isVibrating()); - verify(mNativeWrapperMock).vibratorOff(); + verify(mNativeWrapperMock).off(); } @Test @@ -598,24 +694,23 @@ public class VibratorServiceTest { service.cancelVibrate(service); assertFalse(service.isVibrating()); - verify(mNativeWrapperMock, never()).vibratorOff(); + verify(mNativeWrapperMock, never()).off(); } @Test public void registerVibratorStateListener_callbacksAreTriggered() throws Exception { VibratorService service = createService(); - doAnswer(invocation -> { - service.onVibrationComplete(invocation.getArgument(1)); - return null; - }).when(mNativeWrapperMock).vibratorOn(anyLong(), anyLong()); service.registerVibratorStateListener(mVibratorStateListenerMock); - verify(mVibratorStateListenerMock).onVibrating(false); - Mockito.clearInvocations(mVibratorStateListenerMock); vibrate(service, VibrationEffect.createOneShot(10, VibrationEffect.DEFAULT_AMPLITUDE)); + service.cancelVibrate(service); + InOrder inOrderVerifier = inOrder(mVibratorStateListenerMock); + // First notification done when listener is registered. + inOrderVerifier.verify(mVibratorStateListenerMock).onVibrating(false); inOrderVerifier.verify(mVibratorStateListenerMock).onVibrating(eq(true)); inOrderVerifier.verify(mVibratorStateListenerMock).onVibrating(eq(false)); + inOrderVerifier.verifyNoMoreInteractions(); } @Test @@ -655,16 +750,16 @@ public class VibratorServiceTest { vibrate(service, VibrationEffect.createPredefined(VibrationEffect.EFFECT_HEAVY_CLICK), RINGTONE_ATTRS); - verify(mNativeWrapperMock).vibratorPerformEffect( + verify(mNativeWrapperMock).perform( eq((long) VibrationEffect.EFFECT_CLICK), eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG), anyLong()); - verify(mNativeWrapperMock).vibratorPerformEffect( + verify(mNativeWrapperMock).perform( eq((long) VibrationEffect.EFFECT_TICK), eq((long) VibrationEffect.EFFECT_STRENGTH_MEDIUM), anyLong()); - verify(mNativeWrapperMock).vibratorPerformEffect( + verify(mNativeWrapperMock).perform( eq((long) VibrationEffect.EFFECT_DOUBLE_CLICK), eq((long) VibrationEffect.EFFECT_STRENGTH_LIGHT), anyLong()); - verify(mNativeWrapperMock, never()).vibratorPerformEffect( + verify(mNativeWrapperMock, never()).perform( eq((long) VibrationEffect.EFFECT_HEAVY_CLICK), anyLong(), anyLong()); } @@ -692,14 +787,14 @@ public class VibratorServiceTest { Thread.sleep(15); // Alarm vibration is never scaled. - verify(mNativeWrapperMock).vibratorSetAmplitude(eq(100)); + verify(mNativeWrapperMock).setAmplitude(eq(100)); // Notification vibrations will be scaled with SCALE_VERY_HIGH. - verify(mNativeWrapperMock).vibratorSetAmplitude(intThat(amplitude -> amplitude > 150)); + verify(mNativeWrapperMock).setAmplitude(intThat(amplitude -> amplitude > 150)); // Haptic feedback vibrations will be scaled with SCALE_LOW. - verify(mNativeWrapperMock).vibratorSetAmplitude( + verify(mNativeWrapperMock).setAmplitude( intThat(amplitude -> amplitude < 100 && amplitude > 50)); // Ringtone vibration is off. - verify(mNativeWrapperMock, never()).vibratorSetAmplitude(eq(255)); + verify(mNativeWrapperMock, never()).setAmplitude(eq(255)); } @Test @@ -729,7 +824,7 @@ public class VibratorServiceTest { vibrate(service, effect, RINGTONE_ATTRS); // Ringtone vibration is off, so only the other 3 are propagated to native. - verify(mNativeWrapperMock, times(3)).vibratorPerformComposedEffect( + verify(mNativeWrapperMock, times(3)).compose( primitivesCaptor.capture(), anyLong()); List<VibrationEffect.Composition.PrimitiveEffect[]> values = @@ -788,7 +883,12 @@ public class VibratorServiceTest { } private void mockVibratorCapabilities(int capabilities) { - when(mNativeWrapperMock.vibratorGetCapabilities()).thenReturn((long) capabilities); + when(mNativeWrapperMock.getCapabilities()).thenReturn((long) capabilities); + } + + private InputDevice createInputDeviceWithVibrator(int id) { + return new InputDevice(id, 0, 0, "name", 0, 0, "description", false, 0, 0, + null, /* hasVibrator= */ true, false, false); } private static <T> void addLocalServiceMock(Class<T> clazz, T mock) { diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java index 1cdd873860b8..e43a002806ee 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java @@ -66,7 +66,6 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; - import java.util.ArrayList; import java.util.List; import java.util.function.IntConsumer; @@ -129,6 +128,8 @@ public class FullScreenMagnificationGestureHandlerTest { ScaleChangedListener mMockScaleChangedListener; @Mock MagnificationRequestObserver mMagnificationRequestObserver; + @Mock + WindowMagnificationPromptController mWindowMagnificationPromptController; private OffsettableClock mClock; private FullScreenMagnificationGestureHandler mMgh; @@ -170,7 +171,9 @@ public class FullScreenMagnificationGestureHandlerTest { @After public void tearDown() { + mMgh.onDestroy(); mFullScreenMagnificationController.unregister(DISPLAY_0); + verify(mWindowMagnificationPromptController).onDestroy(); } @NonNull @@ -178,7 +181,8 @@ public class FullScreenMagnificationGestureHandlerTest { boolean detectShortcutTrigger) { FullScreenMagnificationGestureHandler h = new FullScreenMagnificationGestureHandler( mContext, mFullScreenMagnificationController, mMockScaleChangedListener, - detectTripleTap, detectShortcutTrigger, DISPLAY_0); + detectTripleTap, detectShortcutTrigger, mWindowMagnificationPromptController, + DISPLAY_0); mHandler = new TestHandler(h.mDetectingState, mClock) { @Override protected String messageToString(Message m) { @@ -434,6 +438,20 @@ public class FullScreenMagnificationGestureHandlerTest { returnToNormalFrom(STATE_PANNING); } + @Test + public void testZoomedWithTripleTap_invokeShowWindowPromptAction() { + goFromStateIdleTo(STATE_ZOOMED); + + verify(mWindowMagnificationPromptController).showNotificationIfNeeded(); + } + + @Test + public void testShortcutTriggered_invokeShowWindowPromptAction() { + goFromStateIdleTo(STATE_SHORTCUT_TRIGGERED); + + verify(mWindowMagnificationPromptController).showNotificationIfNeeded(); + } + private void assertActionsInOrder(List<MotionEvent> actualEvents, List<Integer> expectedActions) { assertTrue(actualEvents.size() == expectedActions.size()); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationPromptControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationPromptControllerTest.java new file mode 100644 index 000000000000..5fd28f57c7c3 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationPromptControllerTest.java @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.accessibility.magnification; + +import static android.provider.Settings.Secure.ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT; + +import static com.android.internal.messages.nano.SystemMessageProto.SystemMessage.NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE; +import static com.android.server.accessibility.magnification.WindowMagnificationPromptController.ACTION_TURN_ON_IN_SETTINGS; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.StatusBarManager; +import android.content.BroadcastReceiver; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Bundle; +import android.os.Handler; +import android.os.UserHandle; +import android.provider.Settings; +import android.testing.TestableContext; + +import androidx.test.InstrumentationRegistry; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +/** + * Tests for {@link WindowMagnificationPromptController}. + */ +public class WindowMagnificationPromptControllerTest { + + private static final int TEST_USER = 0; + + @Mock + private NotificationManager mNotificationManager; + @Mock + private StatusBarManager mStatusBarManager; + @Rule + public A11yTestableContext mTestableContext = new A11yTestableContext( + InstrumentationRegistry.getContext()); + private ContentResolver mResolver = mTestableContext.getContentResolver(); + private WindowMagnificationPromptController mWindowMagnificationPromptController; + private BroadcastReceiver mReceiver; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mTestableContext.addMockSystemService(NotificationManager.class, mNotificationManager); + mTestableContext.addMockSystemService(StatusBarManager.class, mStatusBarManager); + setWindowMagnificationPromptSettings(true); + mWindowMagnificationPromptController = new WindowMagnificationPromptController( + mTestableContext, TEST_USER); + } + + @After + public void tearDown() throws Exception { + mWindowMagnificationPromptController.onDestroy(); + } + + @Test + public void showNotificationIfNeeded_promptSettingsIsOn_showNotification() { + mWindowMagnificationPromptController.showNotificationIfNeeded(); + + verify(mNotificationManager).notify(eq(NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE), any( + Notification.class)); + } + + @Test + public void tapTurnOnAction_isShown_cancelNotificationAndLaunchMagnificationSettings() { + showNotificationAndAssert(); + + final Intent intent = new Intent(ACTION_TURN_ON_IN_SETTINGS); + mReceiver.onReceive(mTestableContext, intent); + + verify(mNotificationManager).cancel(NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE); + verifyLaunchMagnificationSettings(); + } + + @Test + public void tapTurnOnAction_isShown_settingsValueIsFalseAndUnregisterReceiver() { + showNotificationAndAssert(); + + final Intent intent = new Intent(ACTION_TURN_ON_IN_SETTINGS); + mReceiver.onReceive(mTestableContext, intent); + + assertThat(Settings.Secure.getInt(mResolver, ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT, + -1)).isEqualTo(0); + verify(mTestableContext.getSpyContext()).unregisterReceiver(mReceiver); + } + + @Test + public void tapDismissAction_isShown_cancelNotificationAndUnregisterReceiver() { + showNotificationAndAssert(); + + final Intent intent = new Intent(WindowMagnificationPromptController.ACTION_DISMISS); + mReceiver.onReceive(mTestableContext, intent); + + verify(mNotificationManager).cancel(NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE); + verify(mTestableContext.getSpyContext()).unregisterReceiver(mReceiver); + } + + @Test + public void promptSettingsChangeToFalse_isShown_cancelNotificationAndUnregisterReceiver() { + showNotificationAndAssert(); + + setWindowMagnificationPromptSettings(false); + + verify(mNotificationManager).cancel(NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE); + verify(mTestableContext.getSpyContext()).unregisterReceiver(mReceiver); + } + + @Test + public void onDestroy_isShown_cancelNotificationAndUnregisterReceiver() { + showNotificationAndAssert(); + + mWindowMagnificationPromptController.onDestroy(); + + verify(mNotificationManager).cancel(NOTE_A11Y_WINDOW_MAGNIFICATION_FEATURE); + verify(mTestableContext.getSpyContext()).unregisterReceiver(mReceiver); + } + + private void verifyLaunchMagnificationSettings() { + final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); + final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); + final ArgumentCaptor<UserHandle> userHandleCaptor = ArgumentCaptor.forClass( + UserHandle.class); + verify(mTestableContext.getSpyContext()).startActivityAsUser(intentCaptor.capture(), + bundleCaptor.capture(), userHandleCaptor.capture()); + assertThat(intentCaptor.getValue().getAction()).isEqualTo( + Settings.ACTION_ACCESSIBILITY_DETAILS_SETTINGS); + assertThat(userHandleCaptor.getValue().getIdentifier()).isEqualTo(TEST_USER); + verify(mStatusBarManager).collapsePanels(); + } + + private void showNotificationAndAssert() { + mWindowMagnificationPromptController.showNotificationIfNeeded(); + mReceiver = mWindowMagnificationPromptController.mNotificationActionReceiver; + assertThat(mReceiver).isNotNull(); + } + + private void setWindowMagnificationPromptSettings(boolean enable) { + Settings.Secure.putIntForUser(mResolver, ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT, + enable ? 1 : 0, TEST_USER); + if (mWindowMagnificationPromptController != null) { + mWindowMagnificationPromptController.onPromptSettingsValueChanged(); + } + } + + private class A11yTestableContext extends TestableContext { + + private Context mSpyContext; + + A11yTestableContext(Context base) { + super(base); + mSpyContext = Mockito.mock(Context.class); + } + + @Override + public void startActivityAsUser(Intent intent, Bundle options, UserHandle user) { + mSpyContext.startActivityAsUser(intent, options, user); + } + + @Override + public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, + String broadcastPermission, Handler scheduler) { + return mSpyContext.registerReceiver(receiver, filter, broadcastPermission, scheduler); + } + + @Override + public void unregisterReceiver(BroadcastReceiver receiver) { + mSpyContext.unregisterReceiver(receiver); + } + + Context getSpyContext() { + return mSpyContext; + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java index 726e48a001d7..b929061a967e 100644 --- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java +++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java @@ -26,15 +26,17 @@ import android.app.appsearch.SearchResultPage; import android.app.appsearch.SearchSpec; import android.app.appsearch.exceptions.AppSearchException; +import com.android.server.appsearch.external.localstorage.converter.SchemaToProtoConverter; import com.android.server.appsearch.proto.DocumentProto; import com.android.server.appsearch.proto.GetOptimizeInfoResultProto; -import com.android.server.appsearch.proto.IndexingConfig; import com.android.server.appsearch.proto.PropertyConfigProto; import com.android.server.appsearch.proto.PropertyProto; import com.android.server.appsearch.proto.SchemaProto; import com.android.server.appsearch.proto.SchemaTypeConfigProto; import com.android.server.appsearch.proto.SearchSpecProto; +import com.android.server.appsearch.proto.StringIndexingConfig; import com.android.server.appsearch.proto.TermMatchType; + import com.google.common.collect.ImmutableSet; import org.junit.Before; @@ -42,18 +44,36 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; +import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; +import java.util.List; import java.util.Set; public class AppSearchImplTest { - @Rule - public TemporaryFolder mTemporaryFolder = new TemporaryFolder(); + @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder(); private AppSearchImpl mAppSearchImpl; + private SchemaTypeConfigProto mVisibilitySchemaProto; @Before public void setUp() throws Exception { mAppSearchImpl = AppSearchImpl.create(mTemporaryFolder.newFolder()); + + AppSearchSchema visibilityAppSearchSchema = + new AppSearchSchema.Builder( + VisibilityStore.DATABASE_NAME + + AppSearchImpl.DATABASE_DELIMITER + + VisibilityStore.SCHEMA_TYPE) + .addProperty( + new AppSearchSchema.PropertyConfig.Builder( + VisibilityStore.PLATFORM_HIDDEN_PROPERTY) + .setDataType( + AppSearchSchema.PropertyConfig.DATA_TYPE_STRING) + .setCardinality( + AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED) + .build()) + .build(); + mVisibilitySchemaProto = SchemaToProtoConverter.convert(visibilityAppSearchSchema); } /** @@ -62,91 +82,217 @@ public class AppSearchImplTest { * schema. */ @Test - public void testRewriteSchema() throws Exception { - SchemaProto.Builder existingSchemaBuilder = mAppSearchImpl.getSchemaProto().toBuilder(); - - SchemaProto newSchema = SchemaProto.newBuilder() - .addTypes(SchemaTypeConfigProto.newBuilder() - .setSchemaType("Foo").build()) - .addTypes(SchemaTypeConfigProto.newBuilder() - .setSchemaType("TestType") - .addProperties(PropertyConfigProto.newBuilder() - .setPropertyName("subject") - .setDataType(PropertyConfigProto.DataType.Code.STRING) - .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL) - .setIndexingConfig( - IndexingConfig.newBuilder() - .setTokenizerType( - IndexingConfig.TokenizerType.Code.PLAIN) - .setTermMatchType(TermMatchType.Code.PREFIX) - .build() - ).build() - ).addProperties(PropertyConfigProto.newBuilder() - .setPropertyName("link") - .setDataType(PropertyConfigProto.DataType.Code.DOCUMENT) - .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL) - .setSchemaType("RefType") - .build() - ).build() - ).build(); - - Set<String> newTypes = mAppSearchImpl.rewriteSchema("databaseName", existingSchemaBuilder, - newSchema); - assertThat(newTypes).containsExactly("databaseName/Foo", "databaseName/TestType"); - - SchemaProto expectedSchema = SchemaProto.newBuilder() - .addTypes(SchemaTypeConfigProto.newBuilder() - .setSchemaType("databaseName/Foo").build()) - .addTypes(SchemaTypeConfigProto.newBuilder() - .setSchemaType("databaseName/TestType") - .addProperties(PropertyConfigProto.newBuilder() - .setPropertyName("subject") - .setDataType(PropertyConfigProto.DataType.Code.STRING) - .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL) - .setIndexingConfig( - IndexingConfig.newBuilder() - .setTokenizerType( - IndexingConfig.TokenizerType.Code.PLAIN) - .setTermMatchType(TermMatchType.Code.PREFIX) - .build() - ).build() - ).addProperties(PropertyConfigProto.newBuilder() - .setPropertyName("link") - .setDataType(PropertyConfigProto.DataType.Code.DOCUMENT) - .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL) - .setSchemaType("databaseName/RefType") - .build() - ).build()) - .build(); + public void testRewriteSchema_addType() throws Exception { + SchemaProto.Builder existingSchemaBuilder = + SchemaProto.newBuilder() + .addTypes( + SchemaTypeConfigProto.newBuilder() + .setSchemaType("existingDatabase/Foo") + .build()); + + // Create a copy so we can modify it. + List<SchemaTypeConfigProto> existingTypes = + new ArrayList<>(existingSchemaBuilder.getTypesList()); + + SchemaProto newSchema = + SchemaProto.newBuilder() + .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("Foo").build()) + .addTypes( + SchemaTypeConfigProto.newBuilder() + .setSchemaType("TestType") + .addProperties( + PropertyConfigProto.newBuilder() + .setPropertyName("subject") + .setDataType( + PropertyConfigProto.DataType.Code + .STRING) + .setCardinality( + PropertyConfigProto.Cardinality.Code + .OPTIONAL) + .setStringIndexingConfig( + StringIndexingConfig.newBuilder() + .setTokenizerType( + StringIndexingConfig + .TokenizerType + .Code.PLAIN) + .setTermMatchType( + TermMatchType.Code + .PREFIX) + .build()) + .build()) + .addProperties( + PropertyConfigProto.newBuilder() + .setPropertyName("link") + .setDataType( + PropertyConfigProto.DataType.Code + .DOCUMENT) + .setCardinality( + PropertyConfigProto.Cardinality.Code + .OPTIONAL) + .setSchemaType("RefType") + .build()) + .build()) + .build(); + + AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults = + mAppSearchImpl.rewriteSchema("newDatabase", existingSchemaBuilder, newSchema); + + // We rewrote all the new types that were added. And nothing was removed. + assertThat(rewrittenSchemaResults.mRewrittenQualifiedTypes) + .containsExactly("newDatabase/Foo", "newDatabase/TestType"); + assertThat(rewrittenSchemaResults.mDeletedQualifiedTypes).isEmpty(); + + SchemaProto expectedSchema = + SchemaProto.newBuilder() + .addTypes( + SchemaTypeConfigProto.newBuilder() + .setSchemaType("newDatabase/Foo") + .build()) + .addTypes( + SchemaTypeConfigProto.newBuilder() + .setSchemaType("newDatabase/TestType") + .addProperties( + PropertyConfigProto.newBuilder() + .setPropertyName("subject") + .setDataType( + PropertyConfigProto.DataType.Code + .STRING) + .setCardinality( + PropertyConfigProto.Cardinality.Code + .OPTIONAL) + .setStringIndexingConfig( + StringIndexingConfig.newBuilder() + .setTokenizerType( + StringIndexingConfig + .TokenizerType + .Code.PLAIN) + .setTermMatchType( + TermMatchType.Code + .PREFIX) + .build()) + .build()) + .addProperties( + PropertyConfigProto.newBuilder() + .setPropertyName("link") + .setDataType( + PropertyConfigProto.DataType.Code + .DOCUMENT) + .setCardinality( + PropertyConfigProto.Cardinality.Code + .OPTIONAL) + .setSchemaType("newDatabase/RefType") + .build()) + .build()) + .build(); + + existingTypes.addAll(expectedSchema.getTypesList()); + assertThat(existingSchemaBuilder.getTypesList()).containsExactlyElementsIn(existingTypes); + } + + /** + * Ensure that we track all types that were rewritten in the input schema. Even if they were not + * technically "added" to the existing schema. + */ + @Test + public void testRewriteSchema_rewriteType() throws Exception { + SchemaProto.Builder existingSchemaBuilder = + SchemaProto.newBuilder() + .addTypes( + SchemaTypeConfigProto.newBuilder() + .setSchemaType("existingDatabase/Foo") + .build()); + + SchemaProto newSchema = + SchemaProto.newBuilder() + .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("Foo").build()) + .build(); + + AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults = + mAppSearchImpl.rewriteSchema("existingDatabase", existingSchemaBuilder, newSchema); + + // Nothing was removed, but the method did rewrite the type name. + assertThat(rewrittenSchemaResults.mRewrittenQualifiedTypes) + .containsExactly("existingDatabase/Foo"); + assertThat(rewrittenSchemaResults.mDeletedQualifiedTypes).isEmpty(); + + // Same schema since nothing was added. + SchemaProto expectedSchema = existingSchemaBuilder.build(); + assertThat(existingSchemaBuilder.getTypesList()) + .containsExactlyElementsIn(expectedSchema.getTypesList()); + } + + /** + * Ensure that we track which types from the existing schema are deleted when a new schema is + * set. + */ + @Test + public void testRewriteSchema_deleteType() throws Exception { + SchemaProto.Builder existingSchemaBuilder = + SchemaProto.newBuilder() + .addTypes( + SchemaTypeConfigProto.newBuilder() + .setSchemaType("existingDatabase/Foo") + .build()); + + SchemaProto newSchema = + SchemaProto.newBuilder() + .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("Bar").build()) + .build(); + + AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults = + mAppSearchImpl.rewriteSchema("existingDatabase", existingSchemaBuilder, newSchema); + + // Bar type was rewritten, but Foo ended up being deleted since it wasn't included in the + // new schema. + assertThat(rewrittenSchemaResults.mRewrittenQualifiedTypes) + .containsExactly("existingDatabase/Bar"); + assertThat(rewrittenSchemaResults.mDeletedQualifiedTypes) + .containsExactly("existingDatabase/Foo"); + + // Same schema since nothing was added. + SchemaProto expectedSchema = + SchemaProto.newBuilder() + .addTypes( + SchemaTypeConfigProto.newBuilder() + .setSchemaType("existingDatabase/Bar") + .build()) + .build(); + assertThat(existingSchemaBuilder.getTypesList()) .containsExactlyElementsIn(expectedSchema.getTypesList()); } @Test public void testAddDocumentTypePrefix() { - DocumentProto insideDocument = DocumentProto.newBuilder() - .setUri("inside-uri") - .setSchema("type") - .setNamespace("namespace") - .build(); - DocumentProto documentProto = DocumentProto.newBuilder() - .setUri("uri") - .setSchema("type") - .setNamespace("namespace") - .addProperties(PropertyProto.newBuilder().addDocumentValues(insideDocument)) - .build(); - - DocumentProto expectedInsideDocument = DocumentProto.newBuilder() - .setUri("inside-uri") - .setSchema("databaseName/type") - .setNamespace("databaseName/namespace") - .build(); - DocumentProto expectedDocumentProto = DocumentProto.newBuilder() - .setUri("uri") - .setSchema("databaseName/type") - .setNamespace("databaseName/namespace") - .addProperties(PropertyProto.newBuilder().addDocumentValues(expectedInsideDocument)) - .build(); + DocumentProto insideDocument = + DocumentProto.newBuilder() + .setUri("inside-uri") + .setSchema("type") + .setNamespace("namespace") + .build(); + DocumentProto documentProto = + DocumentProto.newBuilder() + .setUri("uri") + .setSchema("type") + .setNamespace("namespace") + .addProperties(PropertyProto.newBuilder().addDocumentValues(insideDocument)) + .build(); + + DocumentProto expectedInsideDocument = + DocumentProto.newBuilder() + .setUri("inside-uri") + .setSchema("databaseName/type") + .setNamespace("databaseName/namespace") + .build(); + DocumentProto expectedDocumentProto = + DocumentProto.newBuilder() + .setUri("uri") + .setSchema("databaseName/type") + .setNamespace("databaseName/namespace") + .addProperties( + PropertyProto.newBuilder() + .addDocumentValues(expectedInsideDocument)) + .build(); DocumentProto.Builder actualDocument = documentProto.toBuilder(); mAppSearchImpl.addPrefixToDocument(actualDocument, "databaseName/"); @@ -154,31 +300,37 @@ public class AppSearchImplTest { } @Test - public void testRemoveDocumentTypePrefixes() { - DocumentProto insideDocument = DocumentProto.newBuilder() - .setUri("inside-uri") - .setSchema("databaseName1/type") - .setNamespace("databaseName2/namespace") - .build(); - DocumentProto documentProto = DocumentProto.newBuilder() - .setUri("uri") - .setSchema("databaseName2/type") - .setNamespace("databaseName3/namespace") - .addProperties(PropertyProto.newBuilder().addDocumentValues(insideDocument)) - .build(); - - DocumentProto expectedInsideDocument = DocumentProto.newBuilder() - .setUri("inside-uri") - .setSchema("type") - .setNamespace("namespace") - .build(); + public void testRemoveDocumentTypePrefixes() throws Exception { + DocumentProto insideDocument = + DocumentProto.newBuilder() + .setUri("inside-uri") + .setSchema("databaseName1/type") + .setNamespace("databaseName2/namespace") + .build(); + DocumentProto documentProto = + DocumentProto.newBuilder() + .setUri("uri") + .setSchema("databaseName2/type") + .setNamespace("databaseName3/namespace") + .addProperties(PropertyProto.newBuilder().addDocumentValues(insideDocument)) + .build(); + + DocumentProto expectedInsideDocument = + DocumentProto.newBuilder() + .setUri("inside-uri") + .setSchema("type") + .setNamespace("namespace") + .build(); // Since we don't pass in "databaseName3/" as a prefix to remove, it stays on the Document. - DocumentProto expectedDocumentProto = DocumentProto.newBuilder() - .setUri("uri") - .setSchema("type") - .setNamespace("namespace") - .addProperties(PropertyProto.newBuilder().addDocumentValues(expectedInsideDocument)) - .build(); + DocumentProto expectedDocumentProto = + DocumentProto.newBuilder() + .setUri("uri") + .setSchema("type") + .setNamespace("namespace") + .addProperties( + PropertyProto.newBuilder() + .addDocumentValues(expectedInsideDocument)) + .build(); DocumentProto.Builder actualDocument = documentProto.toBuilder(); mAppSearchImpl.removeDatabasesFromDocument(actualDocument); @@ -190,19 +342,23 @@ public class AppSearchImplTest { // Insert schema Set<AppSearchSchema> schemas = Collections.singleton(new AppSearchSchema.Builder("type").build()); - mAppSearchImpl.setSchema("database", schemas, /*forceOverride=*/false); + mAppSearchImpl.setSchema("database", schemas, /*forceOverride=*/ false); // Insert enough documents. - for (int i = 0; i < AppSearchImpl.OPTIMIZE_THRESHOLD_DOC_COUNT - + AppSearchImpl.CHECK_OPTIMIZE_INTERVAL; i++) { + for (int i = 0; + i + < AppSearchImpl.OPTIMIZE_THRESHOLD_DOC_COUNT + + AppSearchImpl.CHECK_OPTIMIZE_INTERVAL; + i++) { GenericDocument document = - new GenericDocument.Builder("uri" + i, "type").setNamespace( - "namespace").build(); + new GenericDocument.Builder("uri" + i, "type") + .setNamespace("namespace") + .build(); mAppSearchImpl.putDocument("database", document); } // Check optimize() will release 0 docs since there is no deletion. - GetOptimizeInfoResultProto optimizeInfo = mAppSearchImpl.getOptimizeInfoResult(); + GetOptimizeInfoResultProto optimizeInfo = mAppSearchImpl.getOptimizeInfoResultLocked(); assertThat(optimizeInfo.getOptimizableDocs()).isEqualTo(0); // delete 999 documents , we will reach the threshold to trigger optimize() in next @@ -212,82 +368,82 @@ public class AppSearchImplTest { } // optimize() still not be triggered since we are in the interval to call getOptimizeInfo() - optimizeInfo = mAppSearchImpl.getOptimizeInfoResult(); + optimizeInfo = mAppSearchImpl.getOptimizeInfoResultLocked(); assertThat(optimizeInfo.getOptimizableDocs()) .isEqualTo(AppSearchImpl.OPTIMIZE_THRESHOLD_DOC_COUNT - 1); // Keep delete docs, will reach the interval this time and trigger optimize(). for (int i = AppSearchImpl.OPTIMIZE_THRESHOLD_DOC_COUNT; - i < AppSearchImpl.OPTIMIZE_THRESHOLD_DOC_COUNT - + AppSearchImpl.CHECK_OPTIMIZE_INTERVAL; i++) { + i + < AppSearchImpl.OPTIMIZE_THRESHOLD_DOC_COUNT + + AppSearchImpl.CHECK_OPTIMIZE_INTERVAL; + i++) { mAppSearchImpl.remove("database", "namespace", "uri" + i); } // Verify optimize() is triggered - optimizeInfo = mAppSearchImpl.getOptimizeInfoResult(); + optimizeInfo = mAppSearchImpl.getOptimizeInfoResultLocked(); assertThat(optimizeInfo.getOptimizableDocs()) .isLessThan(AppSearchImpl.CHECK_OPTIMIZE_INTERVAL); } @Test - public void testRewriteSearchSpec_OneInstance() throws Exception { - SearchSpecProto.Builder searchSpecProto = - SearchSpecProto.newBuilder().setQuery(""); + public void testRewriteSearchSpec_oneInstance() throws Exception { + SearchSpecProto.Builder searchSpecProto = SearchSpecProto.newBuilder().setQuery(""); // Insert schema Set<AppSearchSchema> schemas = Collections.singleton(new AppSearchSchema.Builder("type").build()); - mAppSearchImpl.setSchema("database", schemas, /*forceOverride=*/false); + mAppSearchImpl.setSchema("database", schemas, /*forceOverride=*/ false); // Insert document - GenericDocument document = new GenericDocument.Builder("uri", "type").setNamespace( - "namespace").build(); + GenericDocument document = + new GenericDocument.Builder("uri", "type").setNamespace("namespace").build(); mAppSearchImpl.putDocument("database", document); // Rewrite SearchSpec - mAppSearchImpl.rewriteSearchSpecForDatabases(searchSpecProto, Collections.singleton( - "database")); + mAppSearchImpl.rewriteSearchSpecForDatabasesLocked( + searchSpecProto, Collections.singleton("database")); assertThat(searchSpecProto.getSchemaTypeFiltersList()).containsExactly("database/type"); assertThat(searchSpecProto.getNamespaceFiltersList()).containsExactly("database/namespace"); } @Test - public void testRewriteSearchSpec_TwoInstances() throws Exception { - SearchSpecProto.Builder searchSpecProto = - SearchSpecProto.newBuilder().setQuery(""); + public void testRewriteSearchSpec_twoInstances() throws Exception { + SearchSpecProto.Builder searchSpecProto = SearchSpecProto.newBuilder().setQuery(""); // Insert schema - Set<AppSearchSchema> schemas = Set.of( - new AppSearchSchema.Builder("typeA").build(), - new AppSearchSchema.Builder("typeB").build()); - mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/false); - mAppSearchImpl.setSchema("database2", schemas, /*forceOverride=*/false); + Set<AppSearchSchema> schemas = + Set.of( + new AppSearchSchema.Builder("typeA").build(), + new AppSearchSchema.Builder("typeB").build()); + mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/ false); + mAppSearchImpl.setSchema("database2", schemas, /*forceOverride=*/ false); // Insert documents - GenericDocument document1 = new GenericDocument.Builder("uri", "typeA").setNamespace( - "namespace").build(); + GenericDocument document1 = + new GenericDocument.Builder("uri", "typeA").setNamespace("namespace").build(); mAppSearchImpl.putDocument("database1", document1); - GenericDocument document2 = new GenericDocument.Builder("uri", "typeB").setNamespace( - "namespace").build(); + GenericDocument document2 = + new GenericDocument.Builder("uri", "typeB").setNamespace("namespace").build(); mAppSearchImpl.putDocument("database2", document2); // Rewrite SearchSpec - mAppSearchImpl.rewriteSearchSpecForDatabases(searchSpecProto, - ImmutableSet.of("database1", "database2")); - assertThat(searchSpecProto.getSchemaTypeFiltersList()).containsExactly( - "database1/typeA", "database1/typeB", "database2/typeA", "database2/typeB"); - assertThat(searchSpecProto.getNamespaceFiltersList()).containsExactly( - "database1/namespace", "database2/namespace"); + mAppSearchImpl.rewriteSearchSpecForDatabasesLocked( + searchSpecProto, ImmutableSet.of("database1", "database2")); + assertThat(searchSpecProto.getSchemaTypeFiltersList()) + .containsExactly( + "database1/typeA", "database1/typeB", "database2/typeA", "database2/typeB"); + assertThat(searchSpecProto.getNamespaceFiltersList()) + .containsExactly("database1/namespace", "database2/namespace"); } @Test public void testQueryEmptyDatabase() throws Exception { SearchSpec searchSpec = new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build(); - SearchResultPage searchResultPage = mAppSearchImpl.query( - "EmptyDatabase", - "", searchSpec); + SearchResultPage searchResultPage = mAppSearchImpl.query("EmptyDatabase", "", searchSpec); assertThat(searchResultPage.getResults()).isEmpty(); } @@ -295,25 +451,25 @@ public class AppSearchImplTest { public void testGlobalQueryEmptyDatabase() throws Exception { SearchSpec searchSpec = new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build(); - SearchResultPage searchResultPage = mAppSearchImpl.query( - "EmptyDatabase", - "", searchSpec); + SearchResultPage searchResultPage = mAppSearchImpl.query("EmptyDatabase", "", searchSpec); assertThat(searchResultPage.getResults()).isEmpty(); } @Test - public void testRemoveEmptyDatabase_NoExceptionThrown() throws Exception { + public void testRemoveEmptyDatabase_noExceptionThrown() throws Exception { SearchSpec searchSpec = - new SearchSpec.Builder().addSchema("FakeType").setTermMatch( - TermMatchType.Code.PREFIX_VALUE).build(); - mAppSearchImpl.removeByQuery("EmptyDatabase", - "", searchSpec); + new SearchSpec.Builder() + .addSchemaType("FakeType") + .setTermMatch(TermMatchType.Code.PREFIX_VALUE) + .build(); + mAppSearchImpl.removeByQuery("EmptyDatabase", "", searchSpec); searchSpec = - new SearchSpec.Builder().addNamespace("FakeNamespace").setTermMatch( - TermMatchType.Code.PREFIX_VALUE).build(); - mAppSearchImpl.removeByQuery("EmptyDatabase", - "", searchSpec); + new SearchSpec.Builder() + .addNamespace("FakeNamespace") + .setTermMatch(TermMatchType.Code.PREFIX_VALUE) + .build(); + mAppSearchImpl.removeByQuery("EmptyDatabase", "", searchSpec); searchSpec = new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build(); mAppSearchImpl.removeByQuery("EmptyDatabase", "", searchSpec); @@ -324,14 +480,46 @@ public class AppSearchImplTest { Set<AppSearchSchema> schemas = Collections.singleton(new AppSearchSchema.Builder("Email").build()); // Set schema Email to AppSearch database1 - mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/false); - - // Create excepted schemaType proto. - SchemaProto exceptedProto = SchemaProto.newBuilder() - .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email")) - .build(); - assertThat(mAppSearchImpl.getSchemaProto().getTypesList()) - .containsExactlyElementsIn(exceptedProto.getTypesList()); + mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/ false); + + // Create expected schemaType proto. + SchemaProto expectedProto = + SchemaProto.newBuilder() + .addTypes( + SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email")) + .build(); + + List<SchemaTypeConfigProto> expectedTypes = new ArrayList<>(); + expectedTypes.add(mVisibilitySchemaProto); + expectedTypes.addAll(expectedProto.getTypesList()); + assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList()) + .containsExactlyElementsIn(expectedTypes); + } + + @Test + public void testSetSchema_existingSchemaRetainsVisibilitySetting() throws Exception { + mAppSearchImpl.setSchema( + "database", + Collections.singleton(new AppSearchSchema.Builder("schema1").build()), + /*forceOverride=*/ false); + mAppSearchImpl.setVisibility("database", Set.of("schema1")); + + // "schema1" is platform hidden now + assertThat(mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas("database")) + .containsExactly("database/schema1"); + + // Add a new schema, and include the already-existing "schema1" + mAppSearchImpl.setSchema( + "database", + Set.of( + new AppSearchSchema.Builder("schema1").build(), + new AppSearchSchema.Builder("schema2").build()), + /*forceOverride=*/ false); + + // Check that "schema1" is still platform hidden, but "schema2" is the default platform + // visible. + assertThat(mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas("database")) + .containsExactly("database/schema1"); } @Test @@ -340,35 +528,52 @@ public class AppSearchImplTest { schemas.add(new AppSearchSchema.Builder("Email").build()); schemas.add(new AppSearchSchema.Builder("Document").build()); // Set schema Email and Document to AppSearch database1 - mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/false); - - // Create excepted schemaType proto. - SchemaProto exceptedProto = SchemaProto.newBuilder() - .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email")) - .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Document")) - .build(); + mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/ false); + + // Create expected schemaType proto. + SchemaProto expectedProto = + SchemaProto.newBuilder() + .addTypes( + SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email")) + .addTypes( + SchemaTypeConfigProto.newBuilder() + .setSchemaType("database1/Document")) + .build(); // Check both schema Email and Document saved correctly. - assertThat(mAppSearchImpl.getSchemaProto().getTypesList()) - .containsExactlyElementsIn(exceptedProto.getTypesList()); + List<SchemaTypeConfigProto> expectedTypes = new ArrayList<>(); + expectedTypes.add(mVisibilitySchemaProto); + expectedTypes.addAll(expectedProto.getTypesList()); + assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList()) + .containsExactlyElementsIn(expectedTypes); - final Set<AppSearchSchema> finalSchemas = Collections.singleton(new AppSearchSchema.Builder( - "Email").build()); + final Set<AppSearchSchema> finalSchemas = + Collections.singleton(new AppSearchSchema.Builder("Email").build()); // Check the incompatible error has been thrown. - AppSearchException e = expectThrows(AppSearchException.class, () -> - mAppSearchImpl.setSchema("database1", finalSchemas, /*forceOverride=*/false)); + AppSearchException e = + expectThrows( + AppSearchException.class, + () -> + mAppSearchImpl.setSchema( + "database1", finalSchemas, /*forceOverride=*/ false)); assertThat(e).hasMessageThat().contains("Schema is incompatible"); assertThat(e).hasMessageThat().contains("Deleted types: [database1/Document]"); // ForceOverride to delete. - mAppSearchImpl.setSchema("database1", finalSchemas, /*forceOverride=*/true); + mAppSearchImpl.setSchema("database1", finalSchemas, /*forceOverride=*/ true); // Check Document schema is removed. - exceptedProto = SchemaProto.newBuilder() - .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email")) - .build(); - assertThat(mAppSearchImpl.getSchemaProto().getTypesList()) - .containsExactlyElementsIn(exceptedProto.getTypesList()); + expectedProto = + SchemaProto.newBuilder() + .addTypes( + SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email")) + .build(); + + expectedTypes = new ArrayList<>(); + expectedTypes.add(mVisibilitySchemaProto); + expectedTypes.addAll(expectedProto.getTypesList()); + assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList()) + .containsExactlyElementsIn(expectedTypes); } @Test @@ -379,35 +584,156 @@ public class AppSearchImplTest { schemas.add(new AppSearchSchema.Builder("Document").build()); // Set schema Email and Document to AppSearch database1 and 2 - mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/false); - mAppSearchImpl.setSchema("database2", schemas, /*forceOverride=*/false); - - // Create excepted schemaType proto. - SchemaProto exceptedProto = SchemaProto.newBuilder() - .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email")) - .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Document")) - .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database2/Email")) - .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database2/Document")) - .build(); + mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/ false); + mAppSearchImpl.setSchema("database2", schemas, /*forceOverride=*/ false); + + // Create expected schemaType proto. + SchemaProto expectedProto = + SchemaProto.newBuilder() + .addTypes( + SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email")) + .addTypes( + SchemaTypeConfigProto.newBuilder() + .setSchemaType("database1/Document")) + .addTypes( + SchemaTypeConfigProto.newBuilder().setSchemaType("database2/Email")) + .addTypes( + SchemaTypeConfigProto.newBuilder() + .setSchemaType("database2/Document")) + .build(); // Check Email and Document is saved in database 1 and 2 correctly. - assertThat(mAppSearchImpl.getSchemaProto().getTypesList()) - .containsExactlyElementsIn(exceptedProto.getTypesList()); + List<SchemaTypeConfigProto> expectedTypes = new ArrayList<>(); + expectedTypes.add(mVisibilitySchemaProto); + expectedTypes.addAll(expectedProto.getTypesList()); + assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList()) + .containsExactlyElementsIn(expectedTypes); // Save only Email to database1 this time. schemas = Collections.singleton(new AppSearchSchema.Builder("Email").build()); - mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/true); + mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/ true); - // Create excepted schemaType list, database 1 should only contain Email but database 2 + // Create expected schemaType list, database 1 should only contain Email but database 2 // remains in same. - exceptedProto = SchemaProto.newBuilder() - .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email")) - .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database2/Email")) - .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database2/Document")) - .build(); + expectedProto = + SchemaProto.newBuilder() + .addTypes( + SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email")) + .addTypes( + SchemaTypeConfigProto.newBuilder().setSchemaType("database2/Email")) + .addTypes( + SchemaTypeConfigProto.newBuilder() + .setSchemaType("database2/Document")) + .build(); // Check nothing changed in database2. - assertThat(mAppSearchImpl.getSchemaProto().getTypesList()) - .containsExactlyElementsIn(exceptedProto.getTypesList()); + expectedTypes = new ArrayList<>(); + expectedTypes.add(mVisibilitySchemaProto); + expectedTypes.addAll(expectedProto.getTypesList()); + assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList()) + .containsExactlyElementsIn(expectedTypes); + } + + @Test + public void testRemoveSchema_removedFromVisibilityStore() throws Exception { + mAppSearchImpl.setSchema( + "database", + Collections.singleton(new AppSearchSchema.Builder("schema1").build()), + /*forceOverride=*/ false); + mAppSearchImpl.setVisibility("database", Set.of("schema1")); + + // "schema1" is platform hidden now + assertThat(mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas("database")) + .containsExactly("database/schema1"); + + // Remove "schema1" by force overriding + mAppSearchImpl.setSchema("database", Collections.emptySet(), /*forceOverride=*/ true); + + // Check that "schema1" is no longer considered platform hidden + assertThat(mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas("database")) + .isEmpty(); + + // Add "schema1" back, it gets default visibility settings which means it's not platform + // hidden. + mAppSearchImpl.setSchema( + "database", + Collections.singleton(new AppSearchSchema.Builder("schema1").build()), + /*forceOverride=*/ false); + assertThat(mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas("database")) + .isEmpty(); + } + + @Test + public void testSetVisibility_defaultPlatformVisible() throws Exception { + mAppSearchImpl.setSchema( + "database", + Collections.singleton(new AppSearchSchema.Builder("Schema").build()), + /*forceOverride=*/ false); + assertThat(mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas("database")) + .isEmpty(); + } + + @Test + public void testSetVisibility_platformHidden() throws Exception { + mAppSearchImpl.setSchema( + "database", + Collections.singleton(new AppSearchSchema.Builder("Schema").build()), + /*forceOverride=*/ false); + mAppSearchImpl.setVisibility("database", Set.of("Schema")); + assertThat(mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas("database")) + .containsExactly("database/Schema"); + } + + @Test + public void testSetVisibility_unknownSchema() throws Exception { + mAppSearchImpl.setSchema( + "database", + Collections.singleton(new AppSearchSchema.Builder("Schema").build()), + /*forceOverride=*/ false); + + // We'll throw an exception if a client tries to set visibility on a schema we don't know + // about. + AppSearchException e = + expectThrows( + AppSearchException.class, + () -> mAppSearchImpl.setVisibility("database", Set.of("UnknownSchema"))); + assertThat(e).hasMessageThat().contains("Unknown schema(s)"); + } + + @Test + public void testHasSchemaType() throws Exception { + // Nothing exists yet + assertThat(mAppSearchImpl.hasSchemaTypeLocked("database", "Schema")).isFalse(); + + mAppSearchImpl.setSchema( + "database", + Collections.singleton(new AppSearchSchema.Builder("Schema").build()), + /*forceOverride=*/ false); + assertThat(mAppSearchImpl.hasSchemaTypeLocked("database", "Schema")).isTrue(); + + assertThat(mAppSearchImpl.hasSchemaTypeLocked("database", "UnknownSchema")).isFalse(); + } + + @Test + public void testGetDatabases() throws Exception { + // No client databases exist yet, but the VisibilityStore's does + assertThat(mAppSearchImpl.getDatabasesLocked()) + .containsExactly(VisibilityStore.DATABASE_NAME); + + // Has database1 + mAppSearchImpl.setSchema( + "database1", + Collections.singleton(new AppSearchSchema.Builder("schema").build()), + /*forceOverride=*/ false); + assertThat(mAppSearchImpl.getDatabasesLocked()) + .containsExactly(VisibilityStore.DATABASE_NAME, "database1"); + + // Has both databases + mAppSearchImpl.setSchema( + "database2", + Collections.singleton(new AppSearchSchema.Builder("schema").build()), + /*forceOverride=*/ false); + assertThat(mAppSearchImpl.getDatabasesLocked()) + .containsExactly(VisibilityStore.DATABASE_NAME, "database1", "database2"); } } diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/VisibilityStoreTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/VisibilityStoreTest.java new file mode 100644 index 000000000000..dfe2de6538a4 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/VisibilityStoreTest.java @@ -0,0 +1,74 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.appsearch.external.localstorage; + +import static com.google.common.truth.Truth.assertThat; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.util.Collections; +import java.util.Set; + +public class VisibilityStoreTest { + + @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder(); + private AppSearchImpl mAppSearchImpl; + private VisibilityStore mVisibilityStore; + + @Before + public void setUp() throws Exception { + mAppSearchImpl = AppSearchImpl.create(mTemporaryFolder.newFolder()); + mVisibilityStore = mAppSearchImpl.getVisibilityStoreLocked(); + } + + @Test + public void testSetVisibility() throws Exception { + mVisibilityStore.setVisibility( + "database", /*platformHiddenSchemas=*/ Set.of("schema1", "schema2")); + assertThat(mVisibilityStore.getPlatformHiddenSchemas("database")) + .containsExactly("schema1", "schema2"); + + // New .setVisibility() call completely overrides previous visibility settings. So + // "schema1" isn't preserved. + mVisibilityStore.setVisibility( + "database", /*platformHiddenSchemas=*/ Set.of("schema1", "schema3")); + assertThat(mVisibilityStore.getPlatformHiddenSchemas("database")) + .containsExactly("schema1", "schema3"); + + mVisibilityStore.setVisibility( + "database", /*platformHiddenSchemas=*/ Collections.emptySet()); + assertThat(mVisibilityStore.getPlatformHiddenSchemas("database")).isEmpty(); + } + + @Test + public void testRemoveSchemas() throws Exception { + mVisibilityStore.setVisibility( + "database", /*platformHiddenSchemas=*/ Set.of("schema1", "schema2")); + + // Removed just schema1 + mVisibilityStore.updateSchemas("database", /*schemasToRemove=*/ Set.of("schema1")); + assertThat(mVisibilityStore.getPlatformHiddenSchemas("database")) + .containsExactly("schema2"); + + // Removed everything now + mVisibilityStore.updateSchemas("database", /*schemasToRemove=*/ Set.of("schema2")); + assertThat(mVisibilityStore.getPlatformHiddenSchemas("database")).isEmpty(); + } +} diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java index 85d4f01f8d41..98392a71be58 100644 --- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java +++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java @@ -32,18 +32,18 @@ import java.util.HashMap; import java.util.List; public class GenericDocumentToProtoConverterTest { - private static final byte[] BYTE_ARRAY_1 = new byte[]{(byte) 1, (byte) 2, (byte) 3}; - private static final byte[] BYTE_ARRAY_2 = new byte[]{(byte) 4, (byte) 5, (byte) 6, (byte) 7}; + private static final byte[] BYTE_ARRAY_1 = new byte[] {(byte) 1, (byte) 2, (byte) 3}; + private static final byte[] BYTE_ARRAY_2 = new byte[] {(byte) 4, (byte) 5, (byte) 6, (byte) 7}; private static final GenericDocument DOCUMENT_PROPERTIES_1 = new GenericDocument.Builder<GenericDocument.Builder<?>>( - "sDocumentProperties1", "sDocumentPropertiesSchemaType1") - .setCreationTimestampMillis(12345L) - .build(); + "sDocumentProperties1", "sDocumentPropertiesSchemaType1") + .setCreationTimestampMillis(12345L) + .build(); private static final GenericDocument DOCUMENT_PROPERTIES_2 = new GenericDocument.Builder<GenericDocument.Builder<?>>( - "sDocumentProperties2", "sDocumentPropertiesSchemaType2") - .setCreationTimestampMillis(6789L) - .build(); + "sDocumentProperties2", "sDocumentPropertiesSchemaType2") + .setCreationTimestampMillis(6789L) + .build(); @Test public void testDocumentProtoConvert() { @@ -63,32 +63,42 @@ public class GenericDocumentToProtoConverterTest { .build(); // Create the Document proto. Need to sort the property order by key. - DocumentProto.Builder documentProtoBuilder = DocumentProto.newBuilder() - .setUri("uri1") - .setSchema("schemaType1") - .setCreationTimestampMs(5L) - .setScore(1) - .setTtlMs(1L) - .setNamespace("namespace"); + DocumentProto.Builder documentProtoBuilder = + DocumentProto.newBuilder() + .setUri("uri1") + .setSchema("schemaType1") + .setCreationTimestampMs(5L) + .setScore(1) + .setTtlMs(1L) + .setNamespace("namespace"); HashMap<String, PropertyProto.Builder> propertyProtoMap = new HashMap<>(); - propertyProtoMap.put("longKey1", - PropertyProto.newBuilder().setName("longKey1").addInt64Values(1L)); - propertyProtoMap.put("doubleKey1", + propertyProtoMap.put( + "longKey1", PropertyProto.newBuilder().setName("longKey1").addInt64Values(1L)); + propertyProtoMap.put( + "doubleKey1", PropertyProto.newBuilder().setName("doubleKey1").addDoubleValues(1.0)); - propertyProtoMap.put("booleanKey1", + propertyProtoMap.put( + "booleanKey1", PropertyProto.newBuilder().setName("booleanKey1").addBooleanValues(true)); - propertyProtoMap.put("stringKey1", + propertyProtoMap.put( + "stringKey1", PropertyProto.newBuilder().setName("stringKey1").addStringValues("test-value1")); - propertyProtoMap.put("byteKey1", - PropertyProto.newBuilder().setName("byteKey1") + propertyProtoMap.put( + "byteKey1", + PropertyProto.newBuilder() + .setName("byteKey1") .addBytesValues(ByteString.copyFrom(BYTE_ARRAY_1)) .addBytesValues(ByteString.copyFrom(BYTE_ARRAY_2))); - propertyProtoMap.put("documentKey1", - PropertyProto.newBuilder().setName("documentKey1") + propertyProtoMap.put( + "documentKey1", + PropertyProto.newBuilder() + .setName("documentKey1") .addDocumentValues( GenericDocumentToProtoConverter.convert(DOCUMENT_PROPERTIES_1))); - propertyProtoMap.put("documentKey2", - PropertyProto.newBuilder().setName("documentKey2") + propertyProtoMap.put( + "documentKey2", + PropertyProto.newBuilder() + .setName("documentKey2") .addDocumentValues( GenericDocumentToProtoConverter.convert(DOCUMENT_PROPERTIES_2))); List<String> sortedKey = new ArrayList<>(propertyProtoMap.keySet()); @@ -97,8 +107,7 @@ public class GenericDocumentToProtoConverterTest { documentProtoBuilder.addProperties(propertyProtoMap.get(key)); } DocumentProto documentProto = documentProtoBuilder.build(); - assertThat(GenericDocumentToProtoConverter.convert(document)) - .isEqualTo(documentProto); + assertThat(GenericDocumentToProtoConverter.convert(document)).isEqualTo(documentProto); assertThat(document).isEqualTo(GenericDocumentToProtoConverter.convert(documentProto)); } } diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java index 7336c3c36417..dedfca42ff90 100644 --- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java +++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java @@ -20,9 +20,9 @@ import static com.google.common.truth.Truth.assertThat; import android.app.appsearch.AppSearchSchema; -import com.android.server.appsearch.proto.IndexingConfig; import com.android.server.appsearch.proto.PropertyConfigProto; import com.android.server.appsearch.proto.SchemaTypeConfigProto; +import com.android.server.appsearch.proto.StringIndexingConfig; import com.android.server.appsearch.proto.TermMatchType; import org.junit.Test; @@ -30,84 +30,126 @@ import org.junit.Test; public class SchemaToProtoConverterTest { @Test public void testGetProto_Email() { - AppSearchSchema emailSchema = new AppSearchSchema.Builder("Email") - .addProperty(new AppSearchSchema.PropertyConfig.Builder("subject") - .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING) - .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) - .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_PREFIXES) - .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN) - .build() - ).addProperty(new AppSearchSchema.PropertyConfig.Builder("body") - .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING) - .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) - .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_PREFIXES) - .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN) - .build() - ).build(); + AppSearchSchema emailSchema = + new AppSearchSchema.Builder("Email") + .addProperty( + new AppSearchSchema.PropertyConfig.Builder("subject") + .setDataType( + AppSearchSchema.PropertyConfig.DATA_TYPE_STRING) + .setCardinality( + AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType( + AppSearchSchema.PropertyConfig + .INDEXING_TYPE_PREFIXES) + .setTokenizerType( + AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN) + .build()) + .addProperty( + new AppSearchSchema.PropertyConfig.Builder("body") + .setDataType( + AppSearchSchema.PropertyConfig.DATA_TYPE_STRING) + .setCardinality( + AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType( + AppSearchSchema.PropertyConfig + .INDEXING_TYPE_PREFIXES) + .setTokenizerType( + AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN) + .build()) + .build(); - SchemaTypeConfigProto expectedEmailProto = SchemaTypeConfigProto.newBuilder() - .setSchemaType("Email") - .addProperties(PropertyConfigProto.newBuilder() - .setPropertyName("subject") - .setDataType(PropertyConfigProto.DataType.Code.STRING) - .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL) - .setIndexingConfig( - com.android.server.appsearch.proto.IndexingConfig.newBuilder() - .setTokenizerType(IndexingConfig.TokenizerType.Code.PLAIN) - .setTermMatchType(TermMatchType.Code.PREFIX) - ) - ).addProperties(PropertyConfigProto.newBuilder() - .setPropertyName("body") - .setDataType(PropertyConfigProto.DataType.Code.STRING) - .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL) - .setIndexingConfig( - com.android.server.appsearch.proto.IndexingConfig.newBuilder() - .setTokenizerType(IndexingConfig.TokenizerType.Code.PLAIN) - .setTermMatchType(TermMatchType.Code.PREFIX) - ) - ).build(); + SchemaTypeConfigProto expectedEmailProto = + SchemaTypeConfigProto.newBuilder() + .setSchemaType("Email") + .addProperties( + PropertyConfigProto.newBuilder() + .setPropertyName("subject") + .setDataType(PropertyConfigProto.DataType.Code.STRING) + .setCardinality( + PropertyConfigProto.Cardinality.Code.OPTIONAL) + .setStringIndexingConfig( + StringIndexingConfig.newBuilder() + .setTokenizerType( + StringIndexingConfig.TokenizerType + .Code.PLAIN) + .setTermMatchType( + TermMatchType.Code.PREFIX))) + .addProperties( + PropertyConfigProto.newBuilder() + .setPropertyName("body") + .setDataType(PropertyConfigProto.DataType.Code.STRING) + .setCardinality( + PropertyConfigProto.Cardinality.Code.OPTIONAL) + .setStringIndexingConfig( + StringIndexingConfig.newBuilder() + .setTokenizerType( + StringIndexingConfig.TokenizerType + .Code.PLAIN) + .setTermMatchType( + TermMatchType.Code.PREFIX))) + .build(); assertThat(SchemaToProtoConverter.convert(emailSchema)).isEqualTo(expectedEmailProto); } @Test public void testGetProto_MusicRecording() { - AppSearchSchema musicRecordingSchema = new AppSearchSchema.Builder("MusicRecording") - .addProperty(new AppSearchSchema.PropertyConfig.Builder("artist") - .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING) - .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED) - .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_PREFIXES) - .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN) - .build() - ).addProperty(new AppSearchSchema.PropertyConfig.Builder("pubDate") - .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64) - .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) - .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE) - .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE) - .build() - ).build(); + AppSearchSchema musicRecordingSchema = + new AppSearchSchema.Builder("MusicRecording") + .addProperty( + new AppSearchSchema.PropertyConfig.Builder("artist") + .setDataType( + AppSearchSchema.PropertyConfig.DATA_TYPE_STRING) + .setCardinality( + AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED) + .setIndexingType( + AppSearchSchema.PropertyConfig + .INDEXING_TYPE_PREFIXES) + .setTokenizerType( + AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN) + .build()) + .addProperty( + new AppSearchSchema.PropertyConfig.Builder("pubDate") + .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64) + .setCardinality( + AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType( + AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE) + .setTokenizerType( + AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE) + .build()) + .build(); - SchemaTypeConfigProto expectedMusicRecordingProto = SchemaTypeConfigProto.newBuilder() - .setSchemaType("MusicRecording") - .addProperties(PropertyConfigProto.newBuilder() - .setPropertyName("artist") - .setDataType(PropertyConfigProto.DataType.Code.STRING) - .setCardinality(PropertyConfigProto.Cardinality.Code.REPEATED) - .setIndexingConfig( - com.android.server.appsearch.proto.IndexingConfig.newBuilder() - .setTokenizerType(IndexingConfig.TokenizerType.Code.PLAIN) - .setTermMatchType(TermMatchType.Code.PREFIX) - ) - ).addProperties(PropertyConfigProto.newBuilder() - .setPropertyName("pubDate") - .setDataType(PropertyConfigProto.DataType.Code.INT64) - .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL) - .setIndexingConfig( - com.android.server.appsearch.proto.IndexingConfig.newBuilder() - .setTokenizerType(IndexingConfig.TokenizerType.Code.NONE) - .setTermMatchType(TermMatchType.Code.UNKNOWN) - ) - ).build(); + SchemaTypeConfigProto expectedMusicRecordingProto = + SchemaTypeConfigProto.newBuilder() + .setSchemaType("MusicRecording") + .addProperties( + PropertyConfigProto.newBuilder() + .setPropertyName("artist") + .setDataType(PropertyConfigProto.DataType.Code.STRING) + .setCardinality( + PropertyConfigProto.Cardinality.Code.REPEATED) + .setStringIndexingConfig( + StringIndexingConfig.newBuilder() + .setTokenizerType( + StringIndexingConfig.TokenizerType + .Code.PLAIN) + .setTermMatchType( + TermMatchType.Code.PREFIX))) + .addProperties( + PropertyConfigProto.newBuilder() + .setPropertyName("pubDate") + .setDataType(PropertyConfigProto.DataType.Code.INT64) + .setCardinality( + PropertyConfigProto.Cardinality.Code.OPTIONAL) + .setStringIndexingConfig( + StringIndexingConfig.newBuilder() + .setTokenizerType( + StringIndexingConfig.TokenizerType + .Code.NONE) + .setTermMatchType( + TermMatchType.Code.UNKNOWN))) + .build(); assertThat(SchemaToProtoConverter.convert(musicRecordingSchema)) .isEqualTo(expectedMusicRecordingProto); diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java index 2e9286c6b2ad..518f53205588 100644 --- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java +++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java @@ -36,9 +36,10 @@ public class SnippetTest { public void testSingleStringSnippet() { final String propertyKeyString = "content"; - final String propertyValueString = "A commonly used fake word is foo.\n" - + " Another nonsense word that’s used a lot\n" - + " is bar.\n"; + final String propertyValueString = + "A commonly used fake word is foo.\n" + + " Another nonsense word that’s used a lot\n" + + " is bar.\n"; final String uri = "uri1"; final String schemaType = "schema1"; final String searchWord = "foo"; @@ -46,34 +47,39 @@ public class SnippetTest { final String window = "is foo"; // Building the SearchResult received from query. - PropertyProto property = PropertyProto.newBuilder() - .setName(propertyKeyString) - .addStringValues(propertyValueString) - .build(); - DocumentProto documentProto = DocumentProto.newBuilder() - .setUri(uri) - .setSchema(schemaType) - .addProperties(property) - .build(); - SnippetProto snippetProto = SnippetProto.newBuilder() - .addEntries(SnippetProto.EntryProto.newBuilder() - .setPropertyName(propertyKeyString) - .addSnippetMatches(SnippetMatchProto.newBuilder() - .setValuesIndex(0) - .setExactMatchPosition(29) - .setExactMatchBytes(3) - .setWindowPosition(26) - .setWindowBytes(6) - .build()) - .build()) - .build(); - SearchResultProto.ResultProto resultProto = SearchResultProto.ResultProto.newBuilder() - .setDocument(documentProto) - .setSnippet(snippetProto) - .build(); - SearchResultProto searchResultProto = SearchResultProto.newBuilder() - .addResults(resultProto) - .build(); + PropertyProto property = + PropertyProto.newBuilder() + .setName(propertyKeyString) + .addStringValues(propertyValueString) + .build(); + DocumentProto documentProto = + DocumentProto.newBuilder() + .setUri(uri) + .setSchema(schemaType) + .addProperties(property) + .build(); + SnippetProto snippetProto = + SnippetProto.newBuilder() + .addEntries( + SnippetProto.EntryProto.newBuilder() + .setPropertyName(propertyKeyString) + .addSnippetMatches( + SnippetMatchProto.newBuilder() + .setValuesIndex(0) + .setExactMatchPosition(29) + .setExactMatchBytes(3) + .setWindowPosition(26) + .setWindowBytes(6) + .build()) + .build()) + .build(); + SearchResultProto.ResultProto resultProto = + SearchResultProto.ResultProto.newBuilder() + .setDocument(documentProto) + .setSnippet(snippetProto) + .build(); + SearchResultProto searchResultProto = + SearchResultProto.newBuilder().addResults(resultProto).build(); // Making ResultReader and getting Snippet values. SearchResultPage searchResultPage = @@ -83,11 +89,11 @@ public class SnippetTest { assertThat(match.getPropertyPath()).isEqualTo(propertyKeyString); assertThat(match.getFullText()).isEqualTo(propertyValueString); assertThat(match.getExactMatch()).isEqualTo(exactMatch); - assertThat(match.getExactMatchPosition()).isEqualTo( - new SearchResult.MatchRange(/*lower=*/29, /*upper=*/32)); + assertThat(match.getExactMatchPosition()) + .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 29, /*upper=*/ 32)); assertThat(match.getFullText()).isEqualTo(propertyValueString); - assertThat(match.getSnippetPosition()).isEqualTo( - new SearchResult.MatchRange(/*lower=*/26, /*upper=*/32)); + assertThat(match.getSnippetPosition()) + .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 26, /*upper=*/ 32)); assertThat(match.getSnippet()).isEqualTo(window); } } @@ -97,9 +103,10 @@ public class SnippetTest { public void testNoSnippets() throws Exception { final String propertyKeyString = "content"; - final String propertyValueString = "A commonly used fake word is foo.\n" - + " Another nonsense word that’s used a lot\n" - + " is bar.\n"; + final String propertyValueString = + "A commonly used fake word is foo.\n" + + " Another nonsense word that’s used a lot\n" + + " is bar.\n"; final String uri = "uri1"; final String schemaType = "schema1"; final String searchWord = "foo"; @@ -107,21 +114,21 @@ public class SnippetTest { final String window = "is foo"; // Building the SearchResult received from query. - PropertyProto property = PropertyProto.newBuilder() - .setName(propertyKeyString) - .addStringValues(propertyValueString) - .build(); - DocumentProto documentProto = DocumentProto.newBuilder() - .setUri(uri) - .setSchema(schemaType) - .addProperties(property) - .build(); - SearchResultProto.ResultProto resultProto = SearchResultProto.ResultProto.newBuilder() - .setDocument(documentProto) - .build(); - SearchResultProto searchResultProto = SearchResultProto.newBuilder() - .addResults(resultProto) - .build(); + PropertyProto property = + PropertyProto.newBuilder() + .setName(propertyKeyString) + .addStringValues(propertyValueString) + .build(); + DocumentProto documentProto = + DocumentProto.newBuilder() + .setUri(uri) + .setSchema(schemaType) + .addProperties(property) + .build(); + SearchResultProto.ResultProto resultProto = + SearchResultProto.ResultProto.newBuilder().setDocument(documentProto).build(); + SearchResultProto searchResultProto = + SearchResultProto.newBuilder().addResults(resultProto).build(); SearchResultPage searchResultPage = SearchResultToProtoConverter.convertToSearchResultPage(searchResultProto); @@ -135,54 +142,57 @@ public class SnippetTest { final String searchWord = "Test"; // Building the SearchResult received from query. - PropertyProto property1 = PropertyProto.newBuilder() - .setName("sender.name") - .addStringValues("Test Name Jr.") - .build(); - PropertyProto property2 = PropertyProto.newBuilder() - .setName("sender.email") - .addStringValues("TestNameJr@gmail.com") - .build(); - DocumentProto documentProto = DocumentProto.newBuilder() - .setUri("uri1") - .setSchema("schema1") - .addProperties(property1) - .addProperties(property2) - .build(); - SnippetProto snippetProto = SnippetProto.newBuilder() - .addEntries( - SnippetProto.EntryProto.newBuilder() - .setPropertyName("sender.name") - .addSnippetMatches( - SnippetMatchProto.newBuilder() - .setValuesIndex(0) - .setExactMatchPosition(0) - .setExactMatchBytes(4) - .setWindowPosition(0) - .setWindowBytes(9) - .build()) - .build()) - .addEntries( - SnippetProto.EntryProto.newBuilder() - .setPropertyName("sender.email") - .addSnippetMatches( - SnippetMatchProto.newBuilder() - .setValuesIndex(0) - .setExactMatchPosition(0) - .setExactMatchBytes(20) - .setWindowPosition(0) - .setWindowBytes(20) - .build()) - .build() - ) - .build(); - SearchResultProto.ResultProto resultProto = SearchResultProto.ResultProto.newBuilder() - .setDocument(documentProto) - .setSnippet(snippetProto) - .build(); - SearchResultProto searchResultProto = SearchResultProto.newBuilder() - .addResults(resultProto) - .build(); + PropertyProto property1 = + PropertyProto.newBuilder() + .setName("sender.name") + .addStringValues("Test Name Jr.") + .build(); + PropertyProto property2 = + PropertyProto.newBuilder() + .setName("sender.email") + .addStringValues("TestNameJr@gmail.com") + .build(); + DocumentProto documentProto = + DocumentProto.newBuilder() + .setUri("uri1") + .setSchema("schema1") + .addProperties(property1) + .addProperties(property2) + .build(); + SnippetProto snippetProto = + SnippetProto.newBuilder() + .addEntries( + SnippetProto.EntryProto.newBuilder() + .setPropertyName("sender.name") + .addSnippetMatches( + SnippetMatchProto.newBuilder() + .setValuesIndex(0) + .setExactMatchPosition(0) + .setExactMatchBytes(4) + .setWindowPosition(0) + .setWindowBytes(9) + .build()) + .build()) + .addEntries( + SnippetProto.EntryProto.newBuilder() + .setPropertyName("sender.email") + .addSnippetMatches( + SnippetMatchProto.newBuilder() + .setValuesIndex(0) + .setExactMatchPosition(0) + .setExactMatchBytes(20) + .setWindowPosition(0) + .setWindowBytes(20) + .build()) + .build()) + .build(); + SearchResultProto.ResultProto resultProto = + SearchResultProto.ResultProto.newBuilder() + .setDocument(documentProto) + .setSnippet(snippetProto) + .build(); + SearchResultProto searchResultProto = + SearchResultProto.newBuilder().addResults(resultProto).build(); // Making ResultReader and getting Snippet values. SearchResultPage searchResultPage = @@ -192,21 +202,21 @@ public class SnippetTest { SearchResult.MatchInfo match1 = result.getMatches().get(0); assertThat(match1.getPropertyPath()).isEqualTo("sender.name"); assertThat(match1.getFullText()).isEqualTo("Test Name Jr."); - assertThat(match1.getExactMatchPosition()).isEqualTo( - new SearchResult.MatchRange(/*lower=*/0, /*upper=*/4)); + assertThat(match1.getExactMatchPosition()) + .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 4)); assertThat(match1.getExactMatch()).isEqualTo("Test"); - assertThat(match1.getSnippetPosition()).isEqualTo( - new SearchResult.MatchRange(/*lower=*/0, /*upper=*/9)); + assertThat(match1.getSnippetPosition()) + .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 9)); assertThat(match1.getSnippet()).isEqualTo("Test Name"); SearchResult.MatchInfo match2 = result.getMatches().get(1); assertThat(match2.getPropertyPath()).isEqualTo("sender.email"); assertThat(match2.getFullText()).isEqualTo("TestNameJr@gmail.com"); - assertThat(match2.getExactMatchPosition()).isEqualTo( - new SearchResult.MatchRange(/*lower=*/0, /*upper=*/20)); + assertThat(match2.getExactMatchPosition()) + .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 20)); assertThat(match2.getExactMatch()).isEqualTo("TestNameJr@gmail.com"); - assertThat(match2.getSnippetPosition()).isEqualTo( - new SearchResult.MatchRange(/*lower=*/0, /*upper=*/20)); + assertThat(match2.getSnippetPosition()) + .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 20)); assertThat(match2.getSnippet()).isEqualTo("TestNameJr@gmail.com"); } } diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java index 026db42d4d7a..640d6e599736 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java @@ -28,6 +28,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.PropertyInvalidatedCache; +import android.compat.testing.PlatformCompatChangeRule; import android.content.Context; import android.graphics.Insets; import android.graphics.Rect; @@ -44,8 +45,10 @@ import android.hardware.display.VirtualDisplayConfig; import android.hardware.input.InputManagerInternal; import android.os.Handler; import android.os.IBinder; +import android.os.Process; import android.view.Display; import android.view.DisplayCutout; +import android.view.DisplayEventReceiver; import android.view.DisplayInfo; import android.view.Surface; import android.view.SurfaceControl; @@ -57,13 +60,17 @@ import androidx.test.runner.AndroidJUnit4; import com.android.server.LocalServices; import com.android.server.SystemService; -import com.android.server.display.DisplayDeviceInfo; import com.android.server.display.DisplayManagerService.SyncRoot; import com.android.server.lights.LightsManager; import com.android.server.wm.WindowManagerInternal; +import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges; +import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges; + import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TestRule; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; @@ -80,6 +87,9 @@ public class DisplayManagerServiceTest { private static final String VIRTUAL_DISPLAY_NAME = "Test Virtual Display"; private static final String PACKAGE_NAME = "com.android.frameworks.servicestests"; + @Rule + public TestRule compatChangeRule = new PlatformCompatChangeRule(); + private Context mContext; private final DisplayManagerService.Injector mShortMockedInjector = @@ -95,15 +105,31 @@ public class DisplayManagerServiceTest { return SHORT_DEFAULT_DISPLAY_TIMEOUT_MILLIS; } }; - private final DisplayManagerService.Injector mBasicInjector = - new DisplayManagerService.Injector() { + + class BasicInjector extends DisplayManagerService.Injector { + @Override + VirtualDisplayAdapter getVirtualDisplayAdapter(SyncRoot syncRoot, Context context, + Handler handler, DisplayAdapter.Listener displayAdapterListener) { + return new VirtualDisplayAdapter(syncRoot, context, handler, displayAdapterListener, + (String name, boolean secure) -> mMockDisplayToken); + } + } + + private final DisplayManagerService.Injector mBasicInjector = new BasicInjector(); + + private final DisplayManagerService.Injector mAllowNonNativeRefreshRateOverrideInjector = + new BasicInjector() { @Override - VirtualDisplayAdapter getVirtualDisplayAdapter(SyncRoot syncRoot, - Context context, Handler handler, - DisplayAdapter.Listener displayAdapterListener) { - return new VirtualDisplayAdapter(syncRoot, context, handler, - displayAdapterListener, - (String name, boolean secure) -> mMockDisplayToken); + boolean getAllowNonNativeRefreshRateOverride() { + return true; + } + }; + + private final DisplayManagerService.Injector mDenyNonNativeRefreshRateOverrideInjector = + new BasicInjector() { + @Override + boolean getAllowNonNativeRefreshRateOverride() { + return false; } }; @@ -575,6 +601,337 @@ public class DisplayManagerServiceTest { assertEquals(displayManager.getVirtualDisplaySurfaceInternal(mMockAppToken), surface); } + /** + * Tests that there should be a display change notification if the frame rate overrides + * list is updated. + */ + @Test + public void testShouldNotifyChangeWhenDisplayInfoFrameRateOverrideChanged() throws Exception { + DisplayManagerService displayManager = + new DisplayManagerService(mContext, mShortMockedInjector); + DisplayManagerService.BinderService displayManagerBinderService = + displayManager.new BinderService(); + registerDefaultDisplays(displayManager); + displayManager.onBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY); + + FakeDisplayDevice displayDevice = createFakeDisplayDevice(displayManager, new float[]{60f}); + FakeDisplayManagerCallback callback = registerDisplayListenerCallback(displayManager, + displayManagerBinderService, displayDevice); + + int myUid = Process.myUid(); + updateFrameRateOverride(displayManager, displayDevice, + new DisplayEventReceiver.FrameRateOverride[]{ + new DisplayEventReceiver.FrameRateOverride(myUid, 30f), + }); + assertTrue(callback.mCalled); + callback.clear(); + + updateFrameRateOverride(displayManager, displayDevice, + new DisplayEventReceiver.FrameRateOverride[]{ + new DisplayEventReceiver.FrameRateOverride(myUid, 30f), + new DisplayEventReceiver.FrameRateOverride(1234, 30f), + }); + assertFalse(callback.mCalled); + + updateFrameRateOverride(displayManager, displayDevice, + new DisplayEventReceiver.FrameRateOverride[]{ + new DisplayEventReceiver.FrameRateOverride(myUid, 20f), + new DisplayEventReceiver.FrameRateOverride(1234, 30f), + new DisplayEventReceiver.FrameRateOverride(5678, 30f), + }); + assertTrue(callback.mCalled); + callback.clear(); + + updateFrameRateOverride(displayManager, displayDevice, + new DisplayEventReceiver.FrameRateOverride[]{ + new DisplayEventReceiver.FrameRateOverride(1234, 30f), + new DisplayEventReceiver.FrameRateOverride(5678, 30f), + }); + assertTrue(callback.mCalled); + callback.clear(); + + updateFrameRateOverride(displayManager, displayDevice, + new DisplayEventReceiver.FrameRateOverride[]{ + new DisplayEventReceiver.FrameRateOverride(5678, 30f), + }); + assertFalse(callback.mCalled); + } + + /** + * Tests that the DisplayInfo is updated correctly with a frame rate override + */ + @Test + public void testDisplayInfoFrameRateOverride() throws Exception { + DisplayManagerService displayManager = + new DisplayManagerService(mContext, mShortMockedInjector); + DisplayManagerService.BinderService displayManagerBinderService = + displayManager.new BinderService(); + registerDefaultDisplays(displayManager); + displayManager.onBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY); + + FakeDisplayDevice displayDevice = createFakeDisplayDevice(displayManager, + new float[]{60f, 30f, 20f}); + int displayId = getDisplayIdForDisplayDevice(displayManager, displayManagerBinderService, + displayDevice); + DisplayInfo displayInfo = displayManagerBinderService.getDisplayInfo(displayId); + assertEquals(60f, displayInfo.getRefreshRate(), 0.01f); + + updateFrameRateOverride(displayManager, displayDevice, + new DisplayEventReceiver.FrameRateOverride[]{ + new DisplayEventReceiver.FrameRateOverride( + Process.myUid(), 20f), + new DisplayEventReceiver.FrameRateOverride( + Process.myUid() + 1, 30f) + }); + displayInfo = displayManagerBinderService.getDisplayInfo(displayId); + assertEquals(20f, displayInfo.getRefreshRate(), 0.01f); + + // Changing the mode to 30Hz should not override the refresh rate to 20Hz anymore + // as 20 is not a divider of 30. + updateModeId(displayManager, displayDevice, 2); + displayInfo = displayManagerBinderService.getDisplayInfo(displayId); + assertEquals(30f, displayInfo.getRefreshRate(), 0.01f); + } + + /** + * Tests that the frame rate override is updated accordingly to the + * allowNonNativeRefreshRateOverride policy. + */ + @Test + public void testDisplayInfoNonNativeFrameRateOverride() throws Exception { + testDisplayInfoNonNativeFrameRateOverride(mDenyNonNativeRefreshRateOverrideInjector); + testDisplayInfoNonNativeFrameRateOverride(mAllowNonNativeRefreshRateOverrideInjector); + } + + /** + * Tests that the mode reflects the frame rate override is in compat mode + */ + @Test + @DisableCompatChanges({DisplayManagerService.DISPLAY_MODE_RETURNS_PHYSICAL_REFRESH_RATE}) + public void testDisplayInfoFrameRateOverrideModeCompat() throws Exception { + testDisplayInfoFrameRateOverrideModeCompat(/*compatChangeEnabled*/ false); + } + + /** + * Tests that the mode reflects the physical display refresh rate when not in compat mode. + */ + @Test + @EnableCompatChanges({DisplayManagerService.DISPLAY_MODE_RETURNS_PHYSICAL_REFRESH_RATE}) + public void testDisplayInfoFrameRateOverrideMode() throws Exception { + testDisplayInfoFrameRateOverrideModeCompat(/*compatChangeEnabled*/ true); + } + + /** + * Tests that the mode reflects the frame rate override is in compat mode and accordingly to the + * allowNonNativeRefreshRateOverride policy. + */ + @Test + @DisableCompatChanges({DisplayManagerService.DISPLAY_MODE_RETURNS_PHYSICAL_REFRESH_RATE}) + public void testDisplayInfoNonNativeFrameRateOverrideModeCompat() throws Exception { + testDisplayInfoNonNativeFrameRateOverrideMode(mDenyNonNativeRefreshRateOverrideInjector, + /*compatChangeEnabled*/ false); + testDisplayInfoNonNativeFrameRateOverrideMode(mAllowNonNativeRefreshRateOverrideInjector, + /*compatChangeEnabled*/ false); + } + + /** + * Tests that the mode reflects the physical display refresh rate when not in compat mode. + */ + @Test + @EnableCompatChanges({DisplayManagerService.DISPLAY_MODE_RETURNS_PHYSICAL_REFRESH_RATE}) + public void testDisplayInfoNonNativeFrameRateOverrideMode() throws Exception { + testDisplayInfoNonNativeFrameRateOverrideMode(mDenyNonNativeRefreshRateOverrideInjector, + /*compatChangeEnabled*/ true); + testDisplayInfoNonNativeFrameRateOverrideMode(mAllowNonNativeRefreshRateOverrideInjector, + /*compatChangeEnabled*/ true); + } + + private void testDisplayInfoFrameRateOverrideModeCompat(boolean compatChangeEnabled) + throws Exception { + DisplayManagerService displayManager = + new DisplayManagerService(mContext, mShortMockedInjector); + DisplayManagerService.BinderService displayManagerBinderService = + displayManager.new BinderService(); + registerDefaultDisplays(displayManager); + displayManager.onBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY); + + FakeDisplayDevice displayDevice = createFakeDisplayDevice(displayManager, + new float[]{60f, 30f, 20f}); + int displayId = getDisplayIdForDisplayDevice(displayManager, displayManagerBinderService, + displayDevice); + DisplayInfo displayInfo = displayManagerBinderService.getDisplayInfo(displayId); + assertEquals(60f, displayInfo.getRefreshRate(), 0.01f); + + updateFrameRateOverride(displayManager, displayDevice, + new DisplayEventReceiver.FrameRateOverride[]{ + new DisplayEventReceiver.FrameRateOverride( + Process.myUid(), 20f), + new DisplayEventReceiver.FrameRateOverride( + Process.myUid() + 1, 30f) + }); + displayInfo = displayManagerBinderService.getDisplayInfo(displayId); + assertEquals(20f, displayInfo.getRefreshRate(), 0.01f); + Display.Mode expectedMode; + if (compatChangeEnabled) { + expectedMode = new Display.Mode(1, 100, 200, 60f); + } else { + expectedMode = new Display.Mode(3, 100, 200, 20f); + } + assertEquals(expectedMode, displayInfo.getMode()); + } + + private void testDisplayInfoNonNativeFrameRateOverrideMode( + DisplayManagerService.Injector injector, boolean compatChangeEnabled) { + DisplayManagerService displayManager = + new DisplayManagerService(mContext, injector); + DisplayManagerService.BinderService displayManagerBinderService = + displayManager.new BinderService(); + registerDefaultDisplays(displayManager); + displayManager.onBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY); + + FakeDisplayDevice displayDevice = createFakeDisplayDevice(displayManager, + new float[]{60f}); + int displayId = getDisplayIdForDisplayDevice(displayManager, displayManagerBinderService, + displayDevice); + DisplayInfo displayInfo = displayManagerBinderService.getDisplayInfo(displayId); + assertEquals(60f, displayInfo.getRefreshRate(), 0.01f); + + updateFrameRateOverride(displayManager, displayDevice, + new DisplayEventReceiver.FrameRateOverride[]{ + new DisplayEventReceiver.FrameRateOverride( + Process.myUid(), 20f) + }); + displayInfo = displayManagerBinderService.getDisplayInfo(displayId); + Display.Mode expectedMode; + if (compatChangeEnabled) { + expectedMode = new Display.Mode(1, 100, 200, 60f); + } else if (injector.getAllowNonNativeRefreshRateOverride()) { + expectedMode = new Display.Mode(255, 100, 200, 20f); + } else { + expectedMode = new Display.Mode(1, 100, 200, 60f); + } + assertEquals(expectedMode, displayInfo.getMode()); + } + + private void testDisplayInfoNonNativeFrameRateOverride( + DisplayManagerService.Injector injector) { + DisplayManagerService displayManager = + new DisplayManagerService(mContext, injector); + DisplayManagerService.BinderService displayManagerBinderService = + displayManager.new BinderService(); + registerDefaultDisplays(displayManager); + displayManager.onBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY); + + FakeDisplayDevice displayDevice = createFakeDisplayDevice(displayManager, + new float[]{60f}); + int displayId = getDisplayIdForDisplayDevice(displayManager, displayManagerBinderService, + displayDevice); + DisplayInfo displayInfo = displayManagerBinderService.getDisplayInfo(displayId); + assertEquals(60f, displayInfo.getRefreshRate(), 0.01f); + + updateFrameRateOverride(displayManager, displayDevice, + new DisplayEventReceiver.FrameRateOverride[]{ + new DisplayEventReceiver.FrameRateOverride( + Process.myUid(), 20f) + }); + displayInfo = displayManagerBinderService.getDisplayInfo(displayId); + float expectedRefreshRate = injector.getAllowNonNativeRefreshRateOverride() ? 20f : 60f; + assertEquals(expectedRefreshRate, displayInfo.getRefreshRate(), 0.01f); + } + + private int getDisplayIdForDisplayDevice( + DisplayManagerService displayManager, + DisplayManagerService.BinderService displayManagerBinderService, + FakeDisplayDevice displayDevice) { + + final int[] displayIds = displayManagerBinderService.getDisplayIds(); + assertTrue(displayIds.length > 0); + int displayId = Display.INVALID_DISPLAY; + for (int i = 0; i < displayIds.length; i++) { + DisplayDeviceInfo ddi = displayManager.getDisplayDeviceInfoInternal(displayIds[i]); + if (displayDevice.getDisplayDeviceInfoLocked().equals(ddi)) { + displayId = displayIds[i]; + break; + } + } + assertFalse(displayId == Display.INVALID_DISPLAY); + return displayId; + } + + private void updateDisplayDeviceInfo(DisplayManagerService displayManager, + FakeDisplayDevice displayDevice, + DisplayDeviceInfo displayDeviceInfo) { + displayDevice.setDisplayDeviceInfo(displayDeviceInfo); + displayManager.getDisplayDeviceRepository() + .onDisplayDeviceEvent(displayDevice, DisplayAdapter.DISPLAY_DEVICE_EVENT_CHANGED); + Handler handler = displayManager.getDisplayHandler(); + handler.runWithScissors(() -> { + }, 0 /* now */); + } + + private void updateFrameRateOverride(DisplayManagerService displayManager, + FakeDisplayDevice displayDevice, + DisplayEventReceiver.FrameRateOverride[] frameRateOverrides) { + DisplayDeviceInfo displayDeviceInfo = new DisplayDeviceInfo(); + displayDeviceInfo.copyFrom(displayDevice.getDisplayDeviceInfoLocked()); + displayDeviceInfo.frameRateOverrides = frameRateOverrides; + updateDisplayDeviceInfo(displayManager, displayDevice, displayDeviceInfo); + } + + private void updateModeId(DisplayManagerService displayManager, + FakeDisplayDevice displayDevice, + int modeId) { + DisplayDeviceInfo displayDeviceInfo = new DisplayDeviceInfo(); + displayDeviceInfo.copyFrom(displayDevice.getDisplayDeviceInfoLocked()); + displayDeviceInfo.modeId = modeId; + updateDisplayDeviceInfo(displayManager, displayDevice, displayDeviceInfo); + } + + private FakeDisplayManagerCallback registerDisplayListenerCallback( + DisplayManagerService displayManager, + DisplayManagerService.BinderService displayManagerBinderService, + FakeDisplayDevice displayDevice) { + // Find the display id of the added FakeDisplayDevice + DisplayDeviceInfo displayDeviceInfo = displayDevice.getDisplayDeviceInfoLocked(); + + int displayId = getDisplayIdForDisplayDevice(displayManager, displayManagerBinderService, + displayDevice); + + Handler handler = displayManager.getDisplayHandler(); + handler.runWithScissors(() -> { + }, 0 /* now */); + + // register display listener callback + FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback(displayId); + displayManagerBinderService.registerCallback(callback); + return callback; + } + + private FakeDisplayDevice createFakeDisplayDevice(DisplayManagerService displayManager, + float[] refreshRates) { + FakeDisplayDevice displayDevice = new FakeDisplayDevice(); + DisplayDeviceInfo displayDeviceInfo = new DisplayDeviceInfo(); + int width = 100; + int height = 200; + displayDeviceInfo.supportedModes = new Display.Mode[refreshRates.length]; + for (int i = 0; i < refreshRates.length; i++) { + displayDeviceInfo.supportedModes[i] = + new Display.Mode(i + 1, width, height, refreshRates[i]); + } + displayDeviceInfo.modeId = 1; + displayDeviceInfo.width = width; + displayDeviceInfo.height = height; + final Rect zeroRect = new Rect(); + displayDeviceInfo.displayCutout = new DisplayCutout( + Insets.of(0, 10, 0, 0), + zeroRect, new Rect(0, 0, 10, 10), zeroRect, zeroRect); + displayDeviceInfo.flags = DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY; + displayDevice.setDisplayDeviceInfo(displayDeviceInfo); + displayManager.getDisplayDeviceRepository() + .onDisplayDeviceEvent(displayDevice, DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED); + return displayDevice; + } + private void registerDefaultDisplays(DisplayManagerService displayManager) { Handler handler = displayManager.getDisplayHandler(); // Would prefer to call displayManager.onStart() directly here but it performs binderService @@ -598,6 +955,10 @@ public class DisplayManagerServiceTest { mCalled = true; } } + + public void clear() { + mCalled = false; + } } private class FakeDisplayDevice extends DisplayDevice { diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java index d10e075c5136..de9a5e4f0fe7 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java @@ -18,13 +18,17 @@ package com.android.server.hdmi; import static com.google.common.truth.Truth.assertThat; import static junit.framework.Assert.assertTrue; +import static junit.framework.Assert.fail; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.testng.Assert.assertThrows; +import android.annotation.NonNull; import android.content.Context; import android.hardware.hdmi.HdmiControlManager; +import android.os.Looper; import android.platform.test.annotations.Presubmit; import android.provider.Settings.Global; @@ -38,15 +42,21 @@ import org.junit.runners.JUnit4; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + @SmallTest @Presubmit @RunWith(JUnit4.class) public final class HdmiCecConfigTest { private static final String TAG = "HdmiCecConfigTest"; + private static final int TIMEOUT_CONTENT_CHANGE_SEC = 4; + private Context mContext; @Mock private HdmiCecConfig.StorageAdapter mStorageAdapter; + @Mock private HdmiCecConfig.SettingChangeListener mSettingChangeListener; @Before public void setUp() throws Exception { @@ -1019,4 +1029,105 @@ public final class HdmiCecConfigTest { HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, Integer.toString(HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_DISABLED)); } + + @Test + public void registerChangeListener_SharedPref_BasicSanity() { + HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( + mContext, mStorageAdapter, + "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" + + "<cec-settings>" + + " <setting name=\"system_audio_mode_muting\"" + + " value-type=\"int\"" + + " user-configurable=\"true\">" + + " <allowed-values>" + + " <value int-value=\"0\" />" + + " <value int-value=\"1\" />" + + " </allowed-values>" + + " <default-value int-value=\"1\" />" + + " </setting>" + + "</cec-settings>", null); + hdmiCecConfig.registerChangeListener( + HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, + mSettingChangeListener); + hdmiCecConfig.setIntValue( + HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, + HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_DISABLED); + verify(mSettingChangeListener).onChange( + HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING); + } + + @Test + public void removeChangeListener_SharedPref_BasicSanity() { + HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( + mContext, mStorageAdapter, + "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" + + "<cec-settings>" + + " <setting name=\"system_audio_mode_muting\"" + + " value-type=\"int\"" + + " user-configurable=\"true\">" + + " <allowed-values>" + + " <value int-value=\"0\" />" + + " <value int-value=\"1\" />" + + " </allowed-values>" + + " <default-value int-value=\"1\" />" + + " </setting>" + + "</cec-settings>", null); + hdmiCecConfig.registerChangeListener( + HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, + mSettingChangeListener); + hdmiCecConfig.removeChangeListener( + HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, + mSettingChangeListener); + hdmiCecConfig.setIntValue( + HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, + HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_DISABLED); + verify(mSettingChangeListener, never()).onChange( + HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING); + } + + /** + * Externally modified Global Settings still need to be supported. This test verifies that + * setting change notification is being forwarded to listeners registered via HdmiCecConfig. + */ + @Test + public void globalSettingObserver_BasicSanity() throws Exception { + CountDownLatch notifyLatch = new CountDownLatch(1); + // Get current value of the setting in the system. + String val = Global.getString(mContext.getContentResolver(), Global.HDMI_CONTROL_ENABLED); + HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( + mContext, mStorageAdapter, + "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" + + "<cec-settings>" + + " <setting name=\"hdmi_cec_enabled\"" + + " value-type=\"int\"" + + " user-configurable=\"true\">" + + " <allowed-values>" + + " <value int-value=\"0\" />" + + " <value int-value=\"1\" />" + + " </allowed-values>" + + " <default-value int-value=\"1\" />" + + " </setting>" + + "</cec-settings>", null); + hdmiCecConfig.registerGlobalSettingsObserver(Looper.getMainLooper()); + HdmiCecConfig.SettingChangeListener latchUpdateListener = + new HdmiCecConfig.SettingChangeListener() { + @Override + public void onChange(@NonNull @HdmiControlManager.CecSettingName String setting) { + notifyLatch.countDown(); + assertThat(setting).isEqualTo(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED); + } + }; + hdmiCecConfig.registerChangeListener( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED, + latchUpdateListener); + // Flip the value of the setting. + Global.putString(mContext.getContentResolver(), Global.HDMI_CONTROL_ENABLED, + ((val == null || val.equals("1")) ? "0" : "1")); + if (!notifyLatch.await(TIMEOUT_CONTENT_CHANGE_SEC, TimeUnit.SECONDS)) { + fail("Timed out waiting for the notify callback"); + } + hdmiCecConfig.unregisterGlobalSettingsObserver(); + // Restore the previous value of the setting in the system. + Global.putString(mContext.getContentResolver(), Global.HDMI_CONTROL_ENABLED, val); + } } diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java index ce3b8d618747..dfeed1362b81 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java @@ -1248,4 +1248,91 @@ public class HdmiCecLocalDevicePlaybackTest { assertThat(mHdmiControlService.getActiveSource().getPhysicalAddress()).isEqualTo( externalDevice.getPhysicalAddress()); } + + @Test + public void queryDisplayStatus() { + mHdmiControlService.queryDisplayStatus(new IHdmiControlCallback.Stub() { + @Override + public void onComplete(int result) { + } + }); + mTestLooper.dispatchAll(); + + HdmiCecMessage expectedMessage = HdmiCecMessageBuilder.buildGiveDevicePowerStatus( + mPlaybackLogicalAddress, Constants.ADDR_TV); + assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage); + } + + @Test + public void toggleAndFollowTvPower_ToTv_TvStatusOn() { + mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue( + HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP, + HdmiControlManager.SEND_STANDBY_ON_SLEEP_TO_TV); + mStandby = false; + mHdmiControlService.toggleAndFollowTvPower(); + HdmiCecMessage tvPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(ADDR_TV, + mPlaybackLogicalAddress, HdmiControlManager.POWER_STATUS_ON); + assertThat(mHdmiCecLocalDevicePlayback.dispatchMessage(tvPowerStatus)).isTrue(); + mTestLooper.dispatchAll(); + + HdmiCecMessage expectedMessage = HdmiCecMessageBuilder.buildStandby( + mPlaybackLogicalAddress, ADDR_TV); + assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage); + assertThat(mStandby).isTrue(); + } + + @Test + public void toggleAndFollowTvPower_Broadcast_TvStatusOn() { + mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue( + HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP, + HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST); + mStandby = false; + mHdmiControlService.toggleAndFollowTvPower(); + HdmiCecMessage tvPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(ADDR_TV, + mPlaybackLogicalAddress, HdmiControlManager.POWER_STATUS_ON); + assertThat(mHdmiCecLocalDevicePlayback.dispatchMessage(tvPowerStatus)).isTrue(); + mTestLooper.dispatchAll(); + + HdmiCecMessage expectedMessage = HdmiCecMessageBuilder.buildStandby( + mPlaybackLogicalAddress, ADDR_BROADCAST); + assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage); + assertThat(mStandby).isTrue(); + } + + @Test + public void toggleAndFollowTvPower_TvStatusStandby() { + mStandby = false; + mHdmiControlService.toggleAndFollowTvPower(); + HdmiCecMessage tvPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(ADDR_TV, + mPlaybackLogicalAddress, HdmiControlManager.POWER_STATUS_STANDBY); + assertThat(mHdmiCecLocalDevicePlayback.dispatchMessage(tvPowerStatus)).isTrue(); + mTestLooper.dispatchAll(); + + HdmiCecMessage textViewOn = HdmiCecMessageBuilder.buildTextViewOn(mPlaybackLogicalAddress, + ADDR_TV); + HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource( + mPlaybackLogicalAddress, mPlaybackPhysicalAddress); + assertThat(mNativeWrapper.getResultMessages()).contains(textViewOn); + assertThat(mNativeWrapper.getResultMessages()).contains(activeSource); + assertThat(mStandby).isFalse(); + } + + @Test + public void toggleAndFollowTvPower_TvStatusUnknown() { + mStandby = false; + mHdmiControlService.toggleAndFollowTvPower(); + HdmiCecMessage tvPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(ADDR_TV, + mPlaybackLogicalAddress, HdmiControlManager.POWER_STATUS_UNKNOWN); + assertThat(mHdmiCecLocalDevicePlayback.dispatchMessage(tvPowerStatus)).isTrue(); + mTestLooper.dispatchAll(); + + HdmiCecMessage userControlPressed = HdmiCecMessageBuilder.buildUserControlPressed( + mPlaybackLogicalAddress, Constants.ADDR_TV, + HdmiCecKeycode.CEC_KEYCODE_POWER_TOGGLE_FUNCTION); + HdmiCecMessage userControlReleased = HdmiCecMessageBuilder.buildUserControlReleased( + mPlaybackLogicalAddress, Constants.ADDR_TV); + assertThat(mNativeWrapper.getResultMessages()).contains(userControlPressed); + assertThat(mNativeWrapper.getResultMessages()).contains(userControlReleased); + assertThat(mStandby).isFalse(); + } } diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java index 6f62014f0141..649626492448 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java @@ -22,6 +22,7 @@ import static com.android.server.hdmi.HdmiUtils.buildMessage; import static com.google.common.truth.Truth.assertThat; +import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; import android.platform.test.annotations.Presubmit; @@ -103,7 +104,7 @@ public class HdmiCecMessageBuilderTest { @Test public void buildReportFeatures_basicTv_1_4() { HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_TV, - Constants.VERSION_1_4, + HdmiControlManager.HDMI_CEC_VERSION_1_4_b, Lists.newArrayList(HdmiDeviceInfo.DEVICE_TV), Constants.RC_PROFILE_TV, Lists.newArrayList(Constants.RC_PROFILE_TV_NONE), Collections.emptyList()); @@ -113,7 +114,7 @@ public class HdmiCecMessageBuilderTest { @Test public void buildReportFeatures_basicPlayback_1_4() { HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_PLAYBACK_1, - Constants.VERSION_1_4, + HdmiControlManager.HDMI_CEC_VERSION_1_4_b, Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK), Constants.RC_PROFILE_TV, Lists.newArrayList(Constants.RC_PROFILE_TV_NONE), Collections.emptyList()); @@ -123,7 +124,7 @@ public class HdmiCecMessageBuilderTest { @Test public void buildReportFeatures_basicPlaybackAudioSystem_1_4() { HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_PLAYBACK_1, - Constants.VERSION_1_4, + HdmiControlManager.HDMI_CEC_VERSION_1_4_b, Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM), Constants.RC_PROFILE_TV, Lists.newArrayList(Constants.RC_PROFILE_TV_NONE), Collections.emptyList()); @@ -134,7 +135,7 @@ public class HdmiCecMessageBuilderTest { @Test public void buildReportFeatures_basicTv_2_0() { HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_TV, - Constants.VERSION_2_0, + HdmiControlManager.HDMI_CEC_VERSION_2_0, Lists.newArrayList(HdmiDeviceInfo.DEVICE_TV), Constants.RC_PROFILE_TV, Lists.newArrayList(Constants.RC_PROFILE_TV_NONE), Collections.emptyList()); @@ -144,7 +145,7 @@ public class HdmiCecMessageBuilderTest { @Test public void buildReportFeatures_remoteControlTv_2_0() { HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_TV, - Constants.VERSION_2_0, + HdmiControlManager.HDMI_CEC_VERSION_2_0, Lists.newArrayList(HdmiDeviceInfo.DEVICE_TV), Constants.RC_PROFILE_TV, Lists.newArrayList(Constants.RC_PROFILE_TV_ONE), Collections.emptyList()); @@ -154,7 +155,7 @@ public class HdmiCecMessageBuilderTest { @Test public void buildReportFeatures_remoteControlPlayback_2_0() { HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_TV, - Constants.VERSION_2_0, + HdmiControlManager.HDMI_CEC_VERSION_2_0, Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK), Constants.RC_PROFILE_SOURCE, Lists.newArrayList(Constants.RC_PROFILE_SOURCE_HANDLES_TOP_MENU, Constants.RC_PROFILE_SOURCE_HANDLES_SETUP_MENU), Collections.emptyList()); @@ -165,7 +166,7 @@ public class HdmiCecMessageBuilderTest { @Test public void buildReportFeatures_deviceFeaturesTv_2_0() { HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_TV, - Constants.VERSION_2_0, + HdmiControlManager.HDMI_CEC_VERSION_2_0, Lists.newArrayList(HdmiDeviceInfo.DEVICE_TV), Constants.RC_PROFILE_TV, Lists.newArrayList(Constants.RC_PROFILE_TV_NONE), Lists.newArrayList(Constants.DEVICE_FEATURE_TV_SUPPORTS_RECORD_TV_SCREEN)); @@ -176,7 +177,7 @@ public class HdmiCecMessageBuilderTest { @Test public void buildReportFeatures_deviceFeaturesPlayback_2_0() { HdmiCecMessage message = HdmiCecMessageBuilder.buildReportFeatures(ADDR_TV, - Constants.VERSION_2_0, + HdmiControlManager.HDMI_CEC_VERSION_2_0, Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK), Constants.RC_PROFILE_SOURCE, Lists.newArrayList(Constants.RC_PROFILE_SOURCE_HANDLES_TOP_MENU, Constants.RC_PROFILE_SOURCE_HANDLES_SETUP_MENU), diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java index f3a4366eaaec..a05cbb48a3f7 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java @@ -67,8 +67,8 @@ public class HdmiCecMessageValidatorTest { public void isValid_reportPowerStatus() { assertMessageValidity("04:90:00").isEqualTo(OK); assertMessageValidity("04:90:03:05").isEqualTo(OK); + assertMessageValidity("0F:90:00").isEqualTo(OK); - assertMessageValidity("0F:90:00").isEqualTo(ERROR_DESTINATION); assertMessageValidity("F0:90").isEqualTo(ERROR_SOURCE); assertMessageValidity("04:90").isEqualTo(ERROR_PARAMETER_SHORT); assertMessageValidity("04:90:04").isEqualTo(ERROR_PARAMETER); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java new file mode 100644 index 000000000000..3cc7c6b88a0d --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java @@ -0,0 +1,264 @@ +/* + * 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 com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.ContextWrapper; +import android.hardware.hdmi.HdmiControlManager; +import android.hardware.hdmi.HdmiPortInfo; +import android.os.Handler; +import android.os.IPowerManager; +import android.os.IThermalService; +import android.os.Looper; +import android.os.PowerManager; +import android.os.test.TestLooper; +import android.platform.test.annotations.Presubmit; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; + +import com.android.server.SystemService; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; + +@SmallTest +@Presubmit +@RunWith(JUnit4.class) +/** Tests for {@link HdmiCecPowerStatusController} class. */ +public class HdmiCecPowerStatusControllerTest { + + public static final int[] ARRAY_POWER_STATUS = new int[]{HdmiControlManager.POWER_STATUS_ON, + HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON, + HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY, + HdmiControlManager.POWER_STATUS_STANDBY}; + private HdmiCecPowerStatusController mHdmiCecPowerStatusController; + private FakeNativeWrapper mNativeWrapper; + private TestLooper mTestLooper = new TestLooper(); + private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>(); + private int mHdmiCecVersion = HdmiControlManager.HDMI_CEC_VERSION_1_4_b; + @Mock + private IPowerManager mIPowerManagerMock; + @Mock + private IThermalService mIThermalServiceMock; + private HdmiControlService mHdmiControlService; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + Context contextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext())); + Looper myLooper = mTestLooper.getLooper(); + PowerManager powerManager = new PowerManager(contextSpy, mIPowerManagerMock, + mIThermalServiceMock, new Handler(myLooper)); + when(contextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager); + when(contextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager); + when(mIPowerManagerMock.isInteractive()).thenReturn(true); + + HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(contextSpy); + + mHdmiControlService = new HdmiControlService(contextSpy) { + @Override + boolean isControlEnabled() { + return true; + } + + @Override + boolean isPlaybackDevice() { + return true; + } + + @Override + void writeStringSystemProperty(String key, String value) { + // do nothing + } + + @Override + int getCecVersion() { + return mHdmiCecVersion; + } + + @Override + boolean isPowerStandby() { + return false; + } + + @Override + HdmiCecConfig getHdmiCecConfig() { + return hdmiCecConfig; + } + }; + mHdmiControlService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY); + + HdmiCecLocalDevicePlayback hdmiCecLocalDevicePlayback = new HdmiCecLocalDevicePlayback( + mHdmiControlService); + hdmiCecLocalDevicePlayback.init(); + mHdmiControlService.setIoLooper(myLooper); + mNativeWrapper = new FakeNativeWrapper(); + HdmiCecController hdmiCecController = HdmiCecController.createWithNativeWrapper( + mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter()); + mHdmiControlService.setCecController(hdmiCecController); + mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService)); + mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService)); + mLocalDevices.add(hdmiCecLocalDevicePlayback); + HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[1]; + hdmiPortInfos[0] = + new HdmiPortInfo(1, HdmiPortInfo.PORT_OUTPUT, 0x0000, true, false, false); + mNativeWrapper.setPortInfo(hdmiPortInfos); + mNativeWrapper.setPortConnectionStatus(1, true); + mHdmiControlService.initService(); + mHdmiControlService.getHdmiCecNetwork().initPortInfo(); + mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); + mNativeWrapper.setPhysicalAddress(0x2000); + mTestLooper.dispatchAll(); + + mHdmiCecPowerStatusController = new HdmiCecPowerStatusController(mHdmiControlService); + mNativeWrapper.clearResultMessages(); + } + + @Test + public void setPowerStatus() { + for (int status : ARRAY_POWER_STATUS) { + mHdmiCecPowerStatusController.setPowerStatus(status); + assertThat(mHdmiCecPowerStatusController.getPowerStatus()).isEqualTo(status); + } + } + + @Test + public void isPowerStatusOn() { + for (int status : ARRAY_POWER_STATUS) { + mHdmiCecPowerStatusController.setPowerStatus(status); + assertThat(mHdmiCecPowerStatusController.isPowerStatusOn()).isEqualTo( + HdmiControlManager.POWER_STATUS_ON == status); + } + } + + @Test + public void isPowerStatusStandby() { + for (int status : ARRAY_POWER_STATUS) { + mHdmiCecPowerStatusController.setPowerStatus(status); + assertThat(mHdmiCecPowerStatusController.isPowerStatusStandby()).isEqualTo( + HdmiControlManager.POWER_STATUS_STANDBY == status); + } + } + + @Test + public void isPowerStatusTransientToOn() { + for (int status : ARRAY_POWER_STATUS) { + mHdmiCecPowerStatusController.setPowerStatus(status); + assertThat(mHdmiCecPowerStatusController.isPowerStatusTransientToOn()).isEqualTo( + HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON == status); + } + } + + @Test + public void isPowerStatusTransientToStandby() { + for (int status : ARRAY_POWER_STATUS) { + mHdmiCecPowerStatusController.setPowerStatus(status); + assertThat(mHdmiCecPowerStatusController.isPowerStatusTransientToStandby()).isEqualTo( + HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY == status); + } + } + + @Test + public void setPowerStatus_doesntSendBroadcast_1_4() { + mHdmiCecPowerStatusController.setPowerStatus(HdmiControlManager.POWER_STATUS_ON); + mTestLooper.dispatchAll(); + + HdmiCecMessage reportPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus( + Constants.ADDR_PLAYBACK_1, Constants.ADDR_BROADCAST, + HdmiControlManager.POWER_STATUS_ON); + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(reportPowerStatus); + } + + @Test + public void setPowerStatus_transient_doesntSendBroadcast_1_4() { + mHdmiCecPowerStatusController.setPowerStatus( + HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON); + mTestLooper.dispatchAll(); + + HdmiCecMessage reportPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus( + Constants.ADDR_PLAYBACK_1, Constants.ADDR_BROADCAST, + HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON); + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(reportPowerStatus); + } + + @Test + public void setPowerStatus_fast_transient_doesntSendBroadcast_1_4() { + mHdmiCecPowerStatusController.setPowerStatus( + HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON, false); + mTestLooper.dispatchAll(); + + HdmiCecMessage reportPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus( + Constants.ADDR_PLAYBACK_1, Constants.ADDR_BROADCAST, + HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON); + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(reportPowerStatus); + } + + @Test + public void setPowerStatus_sendsBroadcast_2_0() { + mHdmiCecVersion = HdmiControlManager.HDMI_CEC_VERSION_2_0; + + mHdmiCecPowerStatusController.setPowerStatus(HdmiControlManager.POWER_STATUS_ON); + mTestLooper.dispatchAll(); + + HdmiCecMessage reportPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus( + Constants.ADDR_PLAYBACK_1, Constants.ADDR_BROADCAST, + HdmiControlManager.POWER_STATUS_ON); + assertThat(mNativeWrapper.getResultMessages()).contains(reportPowerStatus); + } + + @Test + public void setPowerStatus_transient_sendsBroadcast_2_0() { + mHdmiCecVersion = HdmiControlManager.HDMI_CEC_VERSION_2_0; + + mHdmiCecPowerStatusController.setPowerStatus( + HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON); + mTestLooper.dispatchAll(); + + HdmiCecMessage reportPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus( + Constants.ADDR_PLAYBACK_1, Constants.ADDR_BROADCAST, + HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON); + assertThat(mNativeWrapper.getResultMessages()).contains(reportPowerStatus); + } + + @Test + public void setPowerStatus_fast_transient_doesntSendBroadcast_2_0() { + mHdmiCecVersion = HdmiControlManager.HDMI_CEC_VERSION_2_0; + + mHdmiCecPowerStatusController.setPowerStatus( + HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON, false); + mTestLooper.dispatchAll(); + + HdmiCecMessage reportPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus( + Constants.ADDR_PLAYBACK_1, Constants.ADDR_BROADCAST, + HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON); + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(reportPowerStatus); + } +} diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java index 2e4bed97dbec..819bd01992cb 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java @@ -243,6 +243,50 @@ public class HdmiControlServiceTest { } @Test + public void initialPowerStatus_normalBoot_goToStandby_doesNotBroadcastsPowerStatus_1_4() { + mHdmiControlService.getHdmiCecConfig().setIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION, + HdmiControlManager.HDMI_CEC_VERSION_1_4_b); + + mHdmiControlService.setControlEnabled(true); + mNativeWrapper.clearResultMessages(); + + assertThat(mHdmiControlService.getInitialPowerStatus()).isEqualTo( + HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY); + + mHdmiControlService.onStandby(HdmiControlService.STANDBY_SCREEN_OFF); + + HdmiCecMessage reportPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus( + Constants.ADDR_PLAYBACK_1, Constants.ADDR_BROADCAST, + HdmiControlManager.POWER_STATUS_STANDBY); + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(reportPowerStatus); + } + + @Test + public void initialPowerStatus_normalBoot_goToStandby_broadcastsPowerStatus_2_0() { + mHdmiControlService.getHdmiCecConfig().setIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION, + HdmiControlManager.HDMI_CEC_VERSION_2_0); + + mHdmiControlService.setControlEnabled(true); + mNativeWrapper.clearResultMessages(); + + mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); + mTestLooper.dispatchAll(); + + assertThat(mHdmiControlService.getInitialPowerStatus()).isEqualTo( + HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY); + + mHdmiControlService.onStandby(HdmiControlService.STANDBY_SCREEN_OFF); + mTestLooper.dispatchAll(); + + HdmiCecMessage reportPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus( + Constants.ADDR_PLAYBACK_1, Constants.ADDR_BROADCAST, + HdmiControlManager.POWER_STATUS_STANDBY); + assertThat(mNativeWrapper.getResultMessages()).contains(reportPowerStatus); + } + + @Test public void setAndGetCecVolumeControlEnabled_isApi() { mHdmiControlService.setHdmiCecVolumeControlEnabled(false); assertThat(mHdmiControlService.isHdmiCecVolumeControlEnabled()).isFalse(); @@ -470,7 +514,42 @@ public class HdmiControlServiceTest { mTestLooper.dispatchAll(); HdmiCecMessage reportFeatures = HdmiCecMessageBuilder.buildReportFeatures( - Constants.ADDR_PLAYBACK_1, Constants.VERSION_2_0, + Constants.ADDR_PLAYBACK_1, HdmiControlManager.HDMI_CEC_VERSION_2_0, + Arrays.asList(DEVICE_PLAYBACK, DEVICE_AUDIO_SYSTEM), + mMyPlaybackDevice.getRcProfile(), mMyPlaybackDevice.getRcFeatures(), + mMyPlaybackDevice.getDeviceFeatures()); + assertThat(mNativeWrapper.getResultMessages()).contains(reportFeatures); + } + + @Test + public void initializeCec_14_doesNotBroadcastReportFeatures() { + mNativeWrapper.clearResultMessages(); + mHdmiControlService.getHdmiCecConfig().setIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION, + HdmiControlManager.HDMI_CEC_VERSION_1_4_b); + mHdmiControlService.setControlEnabled(true); + mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); + mTestLooper.dispatchAll(); + + HdmiCecMessage reportFeatures = HdmiCecMessageBuilder.buildReportFeatures( + Constants.ADDR_PLAYBACK_1, HdmiControlManager.HDMI_CEC_VERSION_2_0, + Arrays.asList(DEVICE_PLAYBACK, DEVICE_AUDIO_SYSTEM), + mMyPlaybackDevice.getRcProfile(), mMyPlaybackDevice.getRcFeatures(), + mMyPlaybackDevice.getDeviceFeatures()); + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(reportFeatures); + } + + @Test + public void initializeCec_20_reportsFeaturesBroadcast() { + mHdmiControlService.getHdmiCecConfig().setIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION, + HdmiControlManager.HDMI_CEC_VERSION_2_0); + mHdmiControlService.setControlEnabled(true); + mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); + mTestLooper.dispatchAll(); + + HdmiCecMessage reportFeatures = HdmiCecMessageBuilder.buildReportFeatures( + Constants.ADDR_PLAYBACK_1, HdmiControlManager.HDMI_CEC_VERSION_2_0, Arrays.asList(DEVICE_PLAYBACK, DEVICE_AUDIO_SYSTEM), mMyPlaybackDevice.getRcProfile(), mMyPlaybackDevice.getRcFeatures(), mMyPlaybackDevice.getDeviceFeatures()); diff --git a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodManagerServiceTests.java b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodManagerServiceTests.java index 8afc3d30efa3..1db5544871bf 100644 --- a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodManagerServiceTests.java +++ b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodManagerServiceTests.java @@ -18,6 +18,8 @@ package com.android.server.inputmethod; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; +import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL; +import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; @@ -38,9 +40,9 @@ public class InputMethodManagerServiceTests { (displayId) -> { switch (displayId) { case SYSTEM_DECORATION_SUPPORT_DISPLAY_ID: - return true; + return DISPLAY_IME_POLICY_LOCAL; case NO_SYSTEM_DECORATION_SUPPORT_DISPLAY_ID: - return false; + return DISPLAY_IME_POLICY_FALLBACK_DISPLAY; default: throw new IllegalArgumentException("Unknown displayId=" + displayId); } @@ -49,7 +51,7 @@ public class InputMethodManagerServiceTests { static InputMethodManagerService.ImeDisplayValidator sMustNotBeCalledChecker = (displayId) -> { fail("Should not pass to display config check for this test case."); - return false; + return DISPLAY_IME_POLICY_FALLBACK_DISPLAY; }; @Test diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java b/services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java index c4b19e84bccb..00cef8fb8481 100644 --- a/services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java +++ b/services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java @@ -15,10 +15,9 @@ */ package com.android.server.location.timezone; -import static android.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_PERMANENT_FAILURE; -import static android.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_SUCCESS; -import static android.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_UNCERTAIN; - +import static com.android.internal.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_PERMANENT_FAILURE; +import static com.android.internal.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_SUCCESS; +import static com.android.internal.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_UNCERTAIN; import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DISABLED; import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_ENABLED_CERTAIN; import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_ENABLED_INITIALIZING; @@ -37,10 +36,10 @@ import static java.util.Arrays.asList; import android.annotation.NonNull; import android.annotation.Nullable; -import android.location.timezone.LocationTimeZoneEvent; import android.platform.test.annotations.Presubmit; import android.util.IndentingPrintWriter; +import com.android.internal.location.timezone.LocationTimeZoneEvent; import com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.ProviderStateEnum; import com.android.server.timezonedetector.ConfigurationInternal; import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion; @@ -60,7 +59,7 @@ import java.util.Objects; @Presubmit public class ControllerImplTest { - private static final long ARBITRARY_TIME = 12345L; + private static final long ARBITRARY_TIME_MILLIS = 12345L; private static final LocationTimeZoneEvent USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1 = createLocationTimeZoneEvent(EVENT_TYPE_SUCCESS, asList("Europe/London")); @@ -936,7 +935,7 @@ public class ControllerImplTest { private static LocationTimeZoneEvent createLocationTimeZoneEvent( int eventType, @Nullable List<String> timeZoneIds) { LocationTimeZoneEvent.Builder builder = new LocationTimeZoneEvent.Builder() - .setElapsedRealtimeNanos(ARBITRARY_TIME) + .setElapsedRealtimeMillis(ARBITRARY_TIME_MILLIS) .setEventType(eventType); if (timeZoneIds != null) { builder.setTimeZoneIds(timeZoneIds); diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java index 7694d096917f..90c29824409f 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java @@ -21,7 +21,9 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static java.lang.Boolean.TRUE; import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; import android.annotation.NonNull; @@ -32,6 +34,7 @@ import android.content.pm.ConfigurationInfo; import android.content.pm.FeatureGroupInfo; import android.content.pm.FeatureInfo; import android.content.pm.PackageInfo; +import android.content.pm.PackageManager.Property; import android.content.pm.PackageParser; import android.content.pm.PackageUserState; import android.content.pm.ServiceInfo; @@ -81,7 +84,9 @@ import java.nio.file.Files; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; +import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Set; @Presubmit @@ -99,6 +104,8 @@ public class PackageParserTest { private static final String TEST_APP1_APK = "PackageParserTestApp1.apk"; private static final String TEST_APP2_APK = "PackageParserTestApp2.apk"; private static final String TEST_APP3_APK = "PackageParserTestApp3.apk"; + private static final String TEST_APP4_APK = "PackageParserTestApp4.apk"; + private static final String PACKAGE_NAME = "com.android.servicestests.apps.packageparserapp"; @Before public void setUp() throws IOException { @@ -270,6 +277,234 @@ public class PackageParserTest { } } + private static final int PROPERTY_TYPE_BOOLEAN = 1; + private static final int PROPERTY_TYPE_FLOAT = 2; + private static final int PROPERTY_TYPE_INTEGER = 3; + private static final int PROPERTY_TYPE_RESOURCE = 4; + private static final int PROPERTY_TYPE_STRING = 5; + public void assertProperty(Map<String, Property> properties, String propertyName, + int propertyType, Object propertyValue) { + assertTrue(properties.containsKey(propertyName)); + + final Property testProperty = properties.get(propertyName); + assertEquals(propertyType, testProperty.getType()); + + if (propertyType == PROPERTY_TYPE_BOOLEAN) { + assertTrue(testProperty.isBoolean()); + assertFalse(testProperty.isFloat()); + assertFalse(testProperty.isInteger()); + assertFalse(testProperty.isResourceId()); + assertFalse(testProperty.isString()); + + // assert the property's type is set correctly + final Boolean boolValue = (Boolean) propertyValue; + if (boolValue.booleanValue()) { + assertTrue(testProperty.getBoolean()); + } else { + assertFalse(testProperty.getBoolean()); + } + // assert the other values have an appropriate default + assertEquals(0.0f, testProperty.getFloat(), 0.0f); + assertEquals(0, testProperty.getInteger()); + assertEquals(0, testProperty.getResourceId()); + assertEquals(null, testProperty.getString()); + } else if (propertyType == PROPERTY_TYPE_FLOAT) { + assertFalse(testProperty.isBoolean()); + assertTrue(testProperty.isFloat()); + assertFalse(testProperty.isInteger()); + assertFalse(testProperty.isResourceId()); + assertFalse(testProperty.isString()); + + // assert the property's type is set correctly + final Float floatValue = (Float) propertyValue; + assertEquals(floatValue.floatValue(), testProperty.getFloat(), 0.0f); + // assert the other values have an appropriate default + assertFalse(testProperty.getBoolean()); + assertEquals(0, testProperty.getInteger()); + assertEquals(0, testProperty.getResourceId()); + assertEquals(null, testProperty.getString()); + } else if (propertyType == PROPERTY_TYPE_INTEGER) { + assertFalse(testProperty.isBoolean()); + assertFalse(testProperty.isFloat()); + assertTrue(testProperty.isInteger()); + assertFalse(testProperty.isResourceId()); + assertFalse(testProperty.isString()); + + // assert the property's type is set correctly + final Integer integerValue = (Integer) propertyValue; + assertEquals(integerValue.intValue(), testProperty.getInteger()); + // assert the other values have an appropriate default + assertFalse(testProperty.getBoolean()); + assertEquals(0.0f, testProperty.getFloat(), 0.0f); + assertEquals(0, testProperty.getResourceId()); + assertEquals(null, testProperty.getString()); + } else if (propertyType == PROPERTY_TYPE_RESOURCE) { + assertFalse(testProperty.isBoolean()); + assertFalse(testProperty.isFloat()); + assertFalse(testProperty.isInteger()); + assertTrue(testProperty.isResourceId()); + assertFalse(testProperty.isString()); + + // assert the property's type is set correctly + final Integer resourceValue = (Integer) propertyValue; + assertEquals(resourceValue.intValue(), testProperty.getResourceId()); + // assert the other values have an appropriate default + assertFalse(testProperty.getBoolean()); + assertEquals(0.0f, testProperty.getFloat(), 0.0f); + assertEquals(0, testProperty.getInteger()); + assertEquals(null, testProperty.getString()); + } else if (propertyType == PROPERTY_TYPE_STRING) { + assertFalse(testProperty.isBoolean()); + assertFalse(testProperty.isFloat()); + assertFalse(testProperty.isInteger()); + assertFalse(testProperty.isResourceId()); + assertTrue(testProperty.isString()); + + // assert the property's type is set correctly + final String stringValue = (String) propertyValue; + assertEquals(stringValue, testProperty.getString()); + // assert the other values have an appropriate default + assertFalse(testProperty.getBoolean()); + assertEquals(0.0f, testProperty.getFloat(), 0.0f); + assertEquals(0, testProperty.getInteger()); + assertEquals(0, testProperty.getResourceId()); + } else { + fail("Unknown property type"); + } + } + + @Test + public void testParseApplicationProperties() throws Exception { + final File testFile = extractFile(TEST_APP4_APK); + try { + final ParsedPackage pkg = new TestPackageParser2().parsePackage(testFile, 0, false); + final Map<String, Property> properties = pkg.getProperties(); + assertEquals(10, properties.size()); + assertProperty(properties, + "android.cts.PROPERTY_RESOURCE_XML", PROPERTY_TYPE_RESOURCE, 0x7f060000); + assertProperty(properties, + "android.cts.PROPERTY_RESOURCE_INTEGER", PROPERTY_TYPE_RESOURCE, 0x7f040000); + assertProperty(properties, + "android.cts.PROPERTY_BOOLEAN", PROPERTY_TYPE_BOOLEAN, TRUE); + assertProperty(properties, + "android.cts.PROPERTY_BOOLEAN_VIA_RESOURCE", PROPERTY_TYPE_BOOLEAN, TRUE); + assertProperty(properties, + "android.cts.PROPERTY_FLOAT", PROPERTY_TYPE_FLOAT, 3.14f); + assertProperty(properties, + "android.cts.PROPERTY_FLOAT_VIA_RESOURCE", PROPERTY_TYPE_FLOAT, 2.718f); + assertProperty(properties, + "android.cts.PROPERTY_INTEGER", PROPERTY_TYPE_INTEGER, 42); + assertProperty(properties, + "android.cts.PROPERTY_INTEGER_VIA_RESOURCE", PROPERTY_TYPE_INTEGER, 123); + assertProperty(properties, + "android.cts.PROPERTY_STRING", PROPERTY_TYPE_STRING, "koala"); + assertProperty(properties, + "android.cts.PROPERTY_STRING_VIA_RESOURCE", PROPERTY_TYPE_STRING, "giraffe"); + } finally { + testFile.delete(); + } + } + + @Test + public void testParseActivityProperties() throws Exception { + final File testFile = extractFile(TEST_APP4_APK); + try { + final ParsedPackage pkg = new TestPackageParser2().parsePackage(testFile, 0, false); + final List<ParsedActivity> activities = pkg.getActivities(); + for (ParsedActivity activity : activities) { + final Map<String, Property> properties = activity.getProperties(); + if ((PACKAGE_NAME + ".MyActivityAlias").equals(activity.getName())) { + assertEquals(2, properties.size()); + assertProperty(properties, + "android.cts.PROPERTY_ACTIVITY_ALIAS", PROPERTY_TYPE_INTEGER, 123); + assertProperty(properties, + "android.cts.PROPERTY_COMPONENT", PROPERTY_TYPE_INTEGER, 123); + } else if ((PACKAGE_NAME + ".MyActivity").equals(activity.getName())) { + assertEquals(3, properties.size()); + assertProperty(properties, + "android.cts.PROPERTY_ACTIVITY", PROPERTY_TYPE_INTEGER, 123); + assertProperty(properties, + "android.cts.PROPERTY_COMPONENT", PROPERTY_TYPE_INTEGER, 123); + assertProperty(properties, + "android.cts.PROPERTY_STRING", PROPERTY_TYPE_STRING, "koala activity"); + } else if ("android.app.AppDetailsActivity".equals(activity.getName())) { + // ignore default added activity + } else { + fail("Found unknown activity; name = " + activity.getName()); + } + } + } finally { + testFile.delete(); + } + } + + @Test + public void testParseProviderProperties() throws Exception { + final File testFile = extractFile(TEST_APP4_APK); + try { + final ParsedPackage pkg = new TestPackageParser2().parsePackage(testFile, 0, false); + final List<ParsedProvider> providers = pkg.getProviders(); + for (ParsedProvider provider : providers) { + final Map<String, Property> properties = provider.getProperties(); + if ((PACKAGE_NAME + ".MyProvider").equals(provider.getName())) { + assertEquals(1, properties.size()); + assertProperty(properties, + "android.cts.PROPERTY_PROVIDER", PROPERTY_TYPE_INTEGER, 123); + } else { + fail("Found unknown provider; name = " + provider.getName()); + } + } + } finally { + testFile.delete(); + } + } + + @Test + public void testParseReceiverProperties() throws Exception { + final File testFile = extractFile(TEST_APP4_APK); + try { + final ParsedPackage pkg = new TestPackageParser2().parsePackage(testFile, 0, false); + final List<ParsedActivity> receivers = pkg.getReceivers(); + for (ParsedActivity receiver : receivers) { + final Map<String, Property> properties = receiver.getProperties(); + if ((PACKAGE_NAME + ".MyReceiver").equals(receiver.getName())) { + assertEquals(2, properties.size()); + assertProperty(properties, + "android.cts.PROPERTY_RECEIVER", PROPERTY_TYPE_INTEGER, 123); + assertProperty(properties, + "android.cts.PROPERTY_STRING", PROPERTY_TYPE_STRING, "koala receiver"); + } else { + fail("Found unknown receiver; name = " + receiver.getName()); + } + } + } finally { + testFile.delete(); + } + } + + @Test + public void testParseServiceProperties() throws Exception { + final File testFile = extractFile(TEST_APP4_APK); + try { + final ParsedPackage pkg = new TestPackageParser2().parsePackage(testFile, 0, false); + final List<ParsedService> services = pkg.getServices(); + for (ParsedService service : services) { + final Map<String, Property> properties = service.getProperties(); + if ((PACKAGE_NAME + ".MyService").equals(service.getName())) { + assertEquals(2, properties.size()); + assertProperty(properties, + "android.cts.PROPERTY_SERVICE", PROPERTY_TYPE_INTEGER, 123); + assertProperty(properties, + "android.cts.PROPERTY_COMPONENT", PROPERTY_TYPE_RESOURCE, 0x7f040000); + } else { + fail("Found unknown service; name = " + service.getName()); + } + } + } finally { + testFile.delete(); + } + } + /** * A trivial subclass of package parser that only caches the package name, and throws away * all other information. @@ -386,6 +621,13 @@ public class PackageParserTest { b.getInstrumentations().get(i)); } + assertEquals(a.getProperties().size(), b.getProperties().size()); + final Iterator<String> iter = a.getProperties().keySet().iterator(); + while (iter.hasNext()) { + final String key = iter.next(); + assertEquals(a.getProperties().get(key), b.getProperties().get(key)); + } + assertEquals(a.getRequestedPermissions(), b.getRequestedPermissions()); assertEquals(a.getProtectedBroadcasts(), b.getProtectedBroadcasts()); assertEquals(a.getLibraryNames(), b.getLibraryNames()); @@ -443,6 +685,13 @@ public class PackageParserTest { assertEquals(aIntent.getNonLocalizedLabel(), bIntent.getNonLocalizedLabel()); assertEquals(aIntent.getIcon(), bIntent.getIcon()); } + + assertEquals(a.getProperties().size(), b.getProperties().size()); + final Iterator<String> iter = a.getProperties().keySet().iterator(); + while (iter.hasNext()) { + final String key = iter.next(); + assertEquals(a.getProperties().get(key), b.getProperties().get(key)); + } } private static void assertPermissionsEqual(ParsedPermission a, ParsedPermission b) { diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java index 4fac9dc391e3..dfc25e0c7cb6 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java @@ -21,6 +21,7 @@ import static android.content.pm.UserInfo.FLAG_DISABLED; import static android.content.pm.UserInfo.FLAG_EPHEMERAL; import static android.content.pm.UserInfo.FLAG_FULL; import static android.content.pm.UserInfo.FLAG_GUEST; +import static android.content.pm.UserInfo.FLAG_INITIALIZED; import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE; import static android.content.pm.UserInfo.FLAG_PROFILE; import static android.content.pm.UserInfo.FLAG_RESTRICTED; @@ -44,6 +45,7 @@ import android.content.pm.UserInfo.UserInfoFlag; import android.os.Looper; import android.os.Parcel; import android.os.UserHandle; +import android.os.UserManager; import android.text.TextUtils; import androidx.test.InstrumentationRegistry; @@ -206,6 +208,8 @@ public class UserManagerServiceUserInfoTest { @Test public void testUpgradeIfNecessaryLP_9() { final int versionToTest = 9; + // do not trigger a user type upgrade + final int userTypeVersion = UserTypeFactory.getUserTypeVersion(); mUserManagerService.putUserInfo(createUser(100, FLAG_MANAGED_PROFILE, null)); mUserManagerService.putUserInfo(createUser(101, @@ -216,7 +220,7 @@ public class UserManagerServiceUserInfoTest { mUserManagerService.putUserInfo(createUser(105, FLAG_SYSTEM | FLAG_FULL, null)); mUserManagerService.putUserInfo(createUser(106, FLAG_DEMO | FLAG_FULL, null)); - mUserManagerService.upgradeIfNecessaryLP(null, versionToTest - 1); + mUserManagerService.upgradeIfNecessaryLP(null, versionToTest - 1, userTypeVersion); assertTrue(mUserManagerService.isUserOfType(100, USER_TYPE_PROFILE_MANAGED)); assertTrue((mUserManagerService.getUserInfo(100).flags & FLAG_PROFILE) != 0); @@ -278,4 +282,86 @@ public class UserManagerServiceUserInfoTest { two.convertedFromPreCreated); } } + + /** Tests upgrading profile types */ + @Test + public void testUpgradeProfileType_updateTypeAndFlags() { + final int userId = 42; + final String newUserTypeName = "new.user.type"; + final String oldUserTypeName = USER_TYPE_PROFILE_MANAGED; + + UserTypeDetails.Builder oldUserTypeBuilder = new UserTypeDetails.Builder() + .setName(oldUserTypeName) + .setBaseType(FLAG_PROFILE) + .setDefaultUserInfoPropertyFlags(FLAG_MANAGED_PROFILE) + .setMaxAllowedPerParent(32) + .setIconBadge(401) + .setBadgeColors(402, 403, 404) + .setBadgeLabels(23, 24, 25); + UserTypeDetails oldUserType = oldUserTypeBuilder.createUserTypeDetails(); + + UserInfo userInfo = createUser(userId, + oldUserType.getDefaultUserInfoFlags() | FLAG_INITIALIZED, oldUserTypeName); + mUserManagerService.putUserInfo(userInfo); + + UserTypeDetails.Builder newUserTypeBuilder = new UserTypeDetails.Builder() + .setName(newUserTypeName) + .setBaseType(FLAG_PROFILE) + .setMaxAllowedPerParent(32) + .setIconBadge(401) + .setBadgeColors(402, 403, 404) + .setBadgeLabels(23, 24, 25); + UserTypeDetails newUserType = newUserTypeBuilder.createUserTypeDetails(); + + mUserManagerService.upgradeProfileToTypeLU(userInfo, newUserType); + + assertTrue(mUserManagerService.isUserOfType(userId, newUserTypeName)); + assertTrue((mUserManagerService.getUserInfo(userId).flags & FLAG_PROFILE) != 0); + assertTrue((mUserManagerService.getUserInfo(userId).flags & FLAG_MANAGED_PROFILE) == 0); + assertTrue((mUserManagerService.getUserInfo(userId).flags & FLAG_INITIALIZED) != 0); + } + + @Test + public void testUpgradeProfileType_updateRestrictions() { + final int userId = 42; + final String newUserTypeName = "new.user.type"; + final String oldUserTypeName = USER_TYPE_PROFILE_MANAGED; + + UserTypeDetails.Builder oldUserTypeBuilder = new UserTypeDetails.Builder() + .setName(oldUserTypeName) + .setBaseType(FLAG_PROFILE) + .setDefaultUserInfoPropertyFlags(FLAG_MANAGED_PROFILE) + .setMaxAllowedPerParent(32) + .setIconBadge(401) + .setBadgeColors(402, 403, 404) + .setBadgeLabels(23, 24, 25); + UserTypeDetails oldUserType = oldUserTypeBuilder.createUserTypeDetails(); + + UserInfo userInfo = createUser(userId, oldUserType.getDefaultUserInfoFlags(), + oldUserTypeName); + mUserManagerService.putUserInfo(userInfo); + mUserManagerService.setUserRestriction(UserManager.DISALLOW_CAMERA, true, userId); + mUserManagerService.setUserRestriction(UserManager.DISALLOW_PRINTING, true, userId); + + UserTypeDetails.Builder newUserTypeBuilder = new UserTypeDetails.Builder() + .setName(newUserTypeName) + .setBaseType(FLAG_PROFILE) + .setMaxAllowedPerParent(32) + .setIconBadge(401) + .setBadgeColors(402, 403, 404) + .setBadgeLabels(23, 24, 25) + .setDefaultRestrictions( + UserManagerServiceUserTypeTest.makeRestrictionsBundle( + UserManager.DISALLOW_WALLPAPER)); + UserTypeDetails newUserType = newUserTypeBuilder.createUserTypeDetails(); + + mUserManagerService.upgradeProfileToTypeLU(userInfo, newUserType); + + assertTrue(mUserManagerService.getUserRestrictions(userId).getBoolean( + UserManager.DISALLOW_PRINTING)); + assertTrue(mUserManagerService.getUserRestrictions(userId).getBoolean( + UserManager.DISALLOW_CAMERA)); + assertTrue(mUserManagerService.getUserRestrictions(userId).getBoolean( + UserManager.DISALLOW_WALLPAPER)); + } } diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java index 8e74c903534f..ee30f68de7a0 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java @@ -51,6 +51,8 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.List; + /** * Tests for {@link UserTypeDetails} and {@link UserTypeFactory}. * @@ -358,13 +360,56 @@ public class UserManagerServiceUserTypeTest { () -> UserTypeFactory.customizeBuilders(builders, parser)); } + @Test + public void testUserTypeFactoryVersion_versionMissing() { + final XmlResourceParser parser = mResources.getXml(R.xml.usertypes_test_eraseArray); + assertEquals(0, UserTypeFactory.getUserTypeVersion(parser)); + } + + @Test + public void testUserTypeFactoryVersion_versionPresent() { + final XmlResourceParser parser = mResources.getXml(R.xml.usertypes_test_profile); + assertEquals(1234, UserTypeFactory.getUserTypeVersion(parser)); + } + + @Test + public void testUserTypeFactoryUpgrades_validUpgrades() { + final ArrayMap<String, UserTypeDetails.Builder> builders = new ArrayMap<>(); + builders.put("name", getMinimalBuilder()); + + final XmlResourceParser parser = mResources.getXml(R.xml.usertypes_test_profile); + List<UserTypeFactory.UserTypeUpgrade> upgrades = UserTypeFactory.parseUserUpgrades(builders, + parser); + + assertFalse(upgrades.isEmpty()); + UserTypeFactory.UserTypeUpgrade upgrade = upgrades.get(0); + assertEquals("android.test.1", upgrade.getFromType()); + assertEquals("android.test.2", upgrade.getToType()); + assertEquals(1233, upgrade.getUpToVersion()); + } + + @Test + public void testUserTypeFactoryUpgrades_illegalBaseTypeUpgrade() { + final String userTypeFull = "android.test.1"; + final ArrayMap<String, UserTypeDetails.Builder> builders = new ArrayMap<>(); + builders.put(userTypeFull, new UserTypeDetails.Builder() + .setName(userTypeFull) + .setBaseType(FLAG_FULL)); + + final XmlResourceParser parser = mResources.getXml(R.xml.usertypes_test_full); + + // parser is illegal because the "to" upgrade type is not a profile, but a full user + assertThrows(IllegalArgumentException.class, + () -> UserTypeFactory.parseUserUpgrades(builders, parser)); + } + /** Returns a minimal {@link UserTypeDetails.Builder} that can legitimately be created. */ private UserTypeDetails.Builder getMinimalBuilder() { return new UserTypeDetails.Builder().setName("name").setBaseType(FLAG_FULL); } /** Creates a Bundle of the given String restrictions, each set to true. */ - private Bundle makeRestrictionsBundle(String ... restrictions) { + public static Bundle makeRestrictionsBundle(String ... restrictions) { final Bundle bundle = new Bundle(); for (String restriction : restrictions) { bundle.putBoolean(restriction, true); diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java index 3846be09a6c1..34cefec4655b 100644 --- a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java @@ -134,7 +134,7 @@ public class DexoptUtilsTest { private List<SharedLibraryInfo> createMockSharedLibrary(String [] sharedLibrary) { SharedLibraryInfo info = new SharedLibraryInfo(null, null, Arrays.asList(sharedLibrary), - null, 0L, SharedLibraryInfo.TYPE_STATIC, null, null, null); + null, 0L, SharedLibraryInfo.TYPE_STATIC, null, null, null, false /* isNative */); ArrayList<SharedLibraryInfo> libraries = new ArrayList<>(); libraries.add(info); return libraries; diff --git a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java new file mode 100644 index 000000000000..92942bb91528 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.policy; + + +import static android.content.Context.SENSOR_SERVICE; + +import static com.android.server.policy.DeviceStateProviderImpl.DEFAULT_DEVICE_STATE; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.annotation.Nullable; +import android.content.Context; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorManager; +import android.hardware.input.InputManagerInternal; + +import androidx.annotation.NonNull; + +import com.android.server.LocalServices; +import com.android.server.devicestate.DeviceStateProvider; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; +import org.mockito.internal.util.reflection.FieldSetter; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Constructor; +import java.util.List; + +/** + * Unit tests for {@link DeviceStateProviderImpl}. + * <p/> + * Run with <code>atest DeviceStateProviderImplTest</code>. + */ +public final class DeviceStateProviderImplTest { + private final ArgumentCaptor<int[]> mIntArrayCaptor = ArgumentCaptor.forClass(int[].class); + private final ArgumentCaptor<Integer> mIntegerCaptor = ArgumentCaptor.forClass(Integer.class); + + private Context mContext; + private SensorManager mSensorManager; + + @Before + public void setup() { + LocalServices.addService(InputManagerInternal.class, mock(InputManagerInternal.class)); + mContext = mock(Context.class); + mSensorManager = mock(SensorManager.class); + when(mContext.getSystemServiceName(eq(SensorManager.class))).thenReturn(SENSOR_SERVICE); + when(mContext.getSystemService(eq(SENSOR_SERVICE))).thenReturn(mSensorManager); + } + + @After + public void tearDown() { + LocalServices.removeServiceForTest(InputManagerInternal.class); + } + + @Test + public void create_noConfig() { + assertDefaultProviderValues(null); + } + + @Test + public void create_emptyFile() { + String configString = ""; + DeviceStateProviderImpl.ReadableConfig config = new TestReadableConfig(configString); + + assertDefaultProviderValues(config); + } + + @Test + public void create_emptyConfig() { + String configString = "<device-state-config></device-state-config>"; + DeviceStateProviderImpl.ReadableConfig config = new TestReadableConfig(configString); + + assertDefaultProviderValues(config); + } + + @Test + public void create_invalidConfig() { + String configString = "<device-state-config>\n" + + " </device-state>\n" + + "</device-state-config>\n"; + DeviceStateProviderImpl.ReadableConfig config = new TestReadableConfig(configString); + + assertDefaultProviderValues(config); + } + + private void assertDefaultProviderValues( + @Nullable DeviceStateProviderImpl.ReadableConfig config) { + DeviceStateProviderImpl provider = DeviceStateProviderImpl.createFromConfig(mContext, + config); + + DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class); + provider.setListener(listener); + + verify(listener).onSupportedDeviceStatesChanged(mIntArrayCaptor.capture()); + assertArrayEquals(new int[] { DEFAULT_DEVICE_STATE }, mIntArrayCaptor.getValue()); + + verify(listener).onStateChanged(mIntegerCaptor.capture()); + assertEquals(DEFAULT_DEVICE_STATE, mIntegerCaptor.getValue().intValue()); + } + + @Test + public void create_lidSwitch() { + String configString = "<device-state-config>\n" + + " <device-state>\n" + + " <identifier>1</identifier>\n" + + " <conditions>\n" + + " <lid-switch>\n" + + " <open>true</open>\n" + + " </lid-switch>\n" + + " </conditions>\n" + + " </device-state>\n" + + " <device-state>\n" + + " <identifier>2</identifier>\n" + + " <conditions>\n" + + " <lid-switch>\n" + + " <open>false</open>\n" + + " </lid-switch>\n" + + " </conditions>\n" + + " </device-state>\n" + + "</device-state-config>\n"; + DeviceStateProviderImpl.ReadableConfig config = new TestReadableConfig(configString); + DeviceStateProviderImpl provider = DeviceStateProviderImpl.createFromConfig(mContext, + config); + + DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class); + provider.setListener(listener); + + verify(listener).onSupportedDeviceStatesChanged(mIntArrayCaptor.capture()); + assertArrayEquals(new int[] { 1, 2 }, mIntArrayCaptor.getValue()); + + verify(listener).onStateChanged(mIntegerCaptor.capture()); + assertEquals(2, mIntegerCaptor.getValue().intValue()); + + Mockito.clearInvocations(listener); + + provider.notifyLidSwitchChanged(0, true /* lidOpen */); + + verify(listener, never()).onSupportedDeviceStatesChanged(mIntArrayCaptor.capture()); + verify(listener).onStateChanged(mIntegerCaptor.capture()); + assertEquals(1, mIntegerCaptor.getValue().intValue()); + } + + @Test + public void create_sensor() throws Exception { + Sensor sensor = newSensor("sensor", Sensor.TYPE_HINGE_ANGLE); + when(mSensorManager.getSensorList(eq(sensor.getType()))).thenReturn(List.of(sensor)); + + String configString = "<device-state-config>\n" + + " <device-state>\n" + + " <identifier>1</identifier>\n" + + " <conditions>\n" + + " <sensor>\n" + + " <name>" + sensor.getName() + "</name>\n" + + " <type>" + sensor.getType() + "</type>\n" + + " <value>\n" + + " <max>90</max>\n" + + " </value>\n" + + " </sensor>\n" + + " </conditions>\n" + + " </device-state>\n" + + " <device-state>\n" + + " <identifier>2</identifier>\n" + + " <conditions>\n" + + " <sensor>\n" + + " <name>" + sensor.getName() + "</name>\n" + + " <type>" + sensor.getType() + "</type>\n" + + " <value>\n" + + " <min-inclusive>90</min-inclusive>\n" + + " <max>180</max>\n" + + " </value>\n" + + " </sensor>\n" + + " </conditions>\n" + + " </device-state>\n" + + " <device-state>\n" + + " <identifier>3</identifier>\n" + + " <conditions>\n" + + " <sensor>\n" + + " <name>" + sensor.getName() + "</name>\n" + + " <type>" + sensor.getType() + "</type>\n" + + " <value>\n" + + " <min-inclusive>180</min-inclusive>\n" + + " </value>\n" + + " </sensor>\n" + + " </conditions>\n" + + " </device-state>\n" + + "</device-state-config>\n"; + DeviceStateProviderImpl.ReadableConfig config = new TestReadableConfig(configString); + DeviceStateProviderImpl provider = DeviceStateProviderImpl.createFromConfig(mContext, + config); + + DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class); + provider.setListener(listener); + + verify(listener).onSupportedDeviceStatesChanged(mIntArrayCaptor.capture()); + assertArrayEquals(new int[] { 1, 2, 3 }, mIntArrayCaptor.getValue()); + + verify(listener).onStateChanged(mIntegerCaptor.capture()); + assertEquals(1, mIntegerCaptor.getValue().intValue()); + + Mockito.clearInvocations(listener); + + SensorEvent event0 = mock(SensorEvent.class); + event0.sensor = sensor; + FieldSetter.setField(event0, event0.getClass().getField("values"), new float[] { 180 }); + + provider.onSensorChanged(event0); + + verify(listener, never()).onSupportedDeviceStatesChanged(mIntArrayCaptor.capture()); + verify(listener).onStateChanged(mIntegerCaptor.capture()); + assertEquals(3, mIntegerCaptor.getValue().intValue()); + + Mockito.clearInvocations(listener); + + SensorEvent event1 = mock(SensorEvent.class); + event1.sensor = sensor; + FieldSetter.setField(event1, event1.getClass().getField("values"), new float[] { 90 }); + + provider.onSensorChanged(event1); + + verify(listener, never()).onSupportedDeviceStatesChanged(mIntArrayCaptor.capture()); + verify(listener).onStateChanged(mIntegerCaptor.capture()); + assertEquals(2, mIntegerCaptor.getValue().intValue()); + + Mockito.clearInvocations(listener); + + SensorEvent event2 = mock(SensorEvent.class); + event2.sensor = sensor; + FieldSetter.setField(event2, event2.getClass().getField("values"), new float[] { 0 }); + + provider.onSensorChanged(event2); + + verify(listener, never()).onSupportedDeviceStatesChanged(mIntArrayCaptor.capture()); + verify(listener).onStateChanged(mIntegerCaptor.capture()); + assertEquals(1, mIntegerCaptor.getValue().intValue()); + } + + private static Sensor newSensor(String name, int type) throws Exception { + Constructor<Sensor> constructor = Sensor.class.getDeclaredConstructor(); + constructor.setAccessible(true); + + Sensor sensor = constructor.newInstance(); + FieldSetter.setField(sensor, Sensor.class.getDeclaredField("mName"), name); + FieldSetter.setField(sensor, Sensor.class.getDeclaredField("mType"), type); + return sensor; + } + + private static final class TestReadableConfig implements + DeviceStateProviderImpl.ReadableConfig { + private final byte[] mData; + + TestReadableConfig(String configFileData) { + mData = configFileData.getBytes(); + } + + @NonNull + @Override + public InputStream openRead() throws IOException { + return new ByteArrayInputStream(mData); + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java index 59aff8d43755..b26d1efef2a8 100644 --- a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java @@ -23,6 +23,10 @@ import android.content.Context; import android.hardware.power.stats.ChannelInfo; import android.hardware.power.stats.EnergyConsumerResult; import android.hardware.power.stats.EnergyMeasurement; +import android.hardware.power.stats.PowerEntityInfo; +import android.hardware.power.stats.StateInfo; +import android.hardware.power.stats.StateResidency; +import android.hardware.power.stats.StateResidencyResult; import androidx.test.InstrumentationRegistry; @@ -56,8 +60,13 @@ public class PowerStatsServiceTest { private static final String MODEL_FILENAME = "modeltest"; private static final String PROTO_OUTPUT_FILENAME = "powerstats.proto"; private static final String CHANNEL_NAME = "channelname"; + private static final String POWER_ENTITY_NAME = "powerentityinfo"; + private static final String STATE_NAME = "stateinfo"; private static final int ENERGY_METER_COUNT = 8; private static final int ENERGY_CONSUMER_COUNT = 2; + private static final int POWER_ENTITY_COUNT = 3; + private static final int STATE_INFO_COUNT = 5; + private static final int STATE_RESIDENCY_COUNT = 4; private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext(); private PowerStatsService mService; @@ -118,6 +127,43 @@ public class PowerStatsServiceTest { public static final class TestPowerStatsHALWrapper implements IPowerStatsHALWrapper { @Override + public PowerEntityInfo[] getPowerEntityInfo() { + PowerEntityInfo[] powerEntityInfoList = new PowerEntityInfo[POWER_ENTITY_COUNT]; + for (int i = 0; i < powerEntityInfoList.length; i++) { + powerEntityInfoList[i] = new PowerEntityInfo(); + powerEntityInfoList[i].powerEntityId = i; + powerEntityInfoList[i].powerEntityName = new String(POWER_ENTITY_NAME + i); + powerEntityInfoList[i].states = new StateInfo[STATE_INFO_COUNT]; + for (int j = 0; j < powerEntityInfoList[i].states.length; j++) { + powerEntityInfoList[i].states[j] = new StateInfo(); + powerEntityInfoList[i].states[j].stateId = j; + powerEntityInfoList[i].states[j].stateName = new String(STATE_NAME + i); + } + } + return powerEntityInfoList; + } + + @Override + public StateResidencyResult[] getStateResidency(int[] powerEntityIds) { + StateResidencyResult[] stateResidencyResultList = + new StateResidencyResult[POWER_ENTITY_COUNT]; + for (int i = 0; i < stateResidencyResultList.length; i++) { + stateResidencyResultList[i] = new StateResidencyResult(); + stateResidencyResultList[i].powerEntityId = i; + stateResidencyResultList[i].stateResidencyData = + new StateResidency[STATE_RESIDENCY_COUNT]; + for (int j = 0; j < stateResidencyResultList[i].stateResidencyData.length; j++) { + stateResidencyResultList[i].stateResidencyData[j] = new StateResidency(); + stateResidencyResultList[i].stateResidencyData[j].totalTimeInStateMs = j; + stateResidencyResultList[i].stateResidencyData[j].totalStateEntryCount = j; + stateResidencyResultList[i].stateResidencyData[j].lastEntryTimestampMs = j; + } + } + + return stateResidencyResultList; + } + + @Override public int[] getEnergyConsumerInfo() { int[] energyConsumerInfoList = new int[ENERGY_CONSUMER_COUNT]; for (int i = 0; i < energyConsumerInfoList.length; i++) { @@ -127,7 +173,7 @@ public class PowerStatsServiceTest { } @Override - public EnergyConsumerResult[] getEnergyConsumed() { + public EnergyConsumerResult[] getEnergyConsumed(int[] energyConsumerIds) { EnergyConsumerResult[] energyConsumedList = new EnergyConsumerResult[ENERGY_CONSUMER_COUNT]; for (int i = 0; i < energyConsumedList.length; i++) { @@ -151,7 +197,7 @@ public class PowerStatsServiceTest { } @Override - public EnergyMeasurement[] readEnergyMeters() { + public EnergyMeasurement[] readEnergyMeters(int[] channelIds) { EnergyMeasurement[] energyMeasurementList = new EnergyMeasurement[ENERGY_METER_COUNT]; for (int i = 0; i < energyMeasurementList.length; i++) { energyMeasurementList[i] = new EnergyMeasurement(); diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java index eedc9781aa40..c42f936d3ab4 100644 --- a/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java +++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java @@ -110,6 +110,8 @@ public class RollbackStoreTest { @Rule public TemporaryFolder mFolder = new TemporaryFolder(); + @Rule + public TemporaryFolder mHistoryDir = new TemporaryFolder(); private File mRollbackDir; @@ -117,7 +119,7 @@ public class RollbackStoreTest { @Before public void setUp() throws Exception { - mRollbackStore = new RollbackStore(mFolder.getRoot()); + mRollbackStore = new RollbackStore(mFolder.getRoot(), mHistoryDir.getRoot()); mRollbackDir = mFolder.newFolder(ID + ""); mFolder.newFile("rollback.json"); } @@ -202,6 +204,8 @@ public class RollbackStoreTest { origRb.info.getPackages().add(pkgInfo1); origRb.info.getPackages().add(pkgInfo2); + origRb.setState(Rollback.ROLLBACK_STATE_AVAILABLE, "hello world"); + RollbackStore.saveRollback(origRb); List<Rollback> loadedRollbacks = mRollbackStore.loadRollbacks(); @@ -324,10 +328,26 @@ public class RollbackStoreTest { assertThat(expectedFile.exists()).isFalse(); } - private void assertRollbacksAreEquivalent(Rollback b, Rollback a) { - assertThat(b.info.getRollbackId()).isEqualTo(ID); + @Test + public void saveToHistoryAndLoad() { + Rollback origRb = mRollbackStore.createNonStagedRollback( + ID, USER, INSTALLER, null, new SparseIntArray(0)); + mRollbackStore.saveRollbackToHistory(origRb); + + List<Rollback> loadedRollbacks = mRollbackStore.loadHistorialRollbacks(); + assertThat(loadedRollbacks).hasSize(1); + Rollback loadedRb = loadedRollbacks.get(0); + + assertRollbacksAreEquivalentExcludingBackupDir(loadedRb, origRb); + } + private void assertRollbacksAreEquivalent(Rollback b, Rollback a) { assertThat(b.getBackupDir()).isEqualTo(a.getBackupDir()); + assertRollbacksAreEquivalentExcludingBackupDir(b, a); + } + + private void assertRollbacksAreEquivalentExcludingBackupDir(Rollback b, Rollback a) { + assertThat(b.info.getRollbackId()).isEqualTo(ID); assertThat(b.isRestoreUserDataInProgress()) .isEqualTo(a.isRestoreUserDataInProgress()); @@ -337,6 +357,7 @@ public class RollbackStoreTest { assertThat(b.isEnabling()).isEqualTo(a.isEnabling()); assertThat(b.isAvailable()).isEqualTo(a.isAvailable()); assertThat(b.isCommitted()).isEqualTo(a.isCommitted()); + assertThat(b.getStateDescription()).isEqualTo(a.getStateDescription()); assertThat(b.isStaged()).isEqualTo(a.isStaged()); diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java index cd2c9230221c..cf1ed4815a74 100644 --- a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java +++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java @@ -123,7 +123,7 @@ public class RollbackUnitTest { public void deletedRollbackCannotBeMadeAvailable() { Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER); - rollback.delete(mMockDataHelper); + rollback.delete(mMockDataHelper, "test"); assertThat(rollback.isDeleted()).isTrue(); @@ -221,7 +221,7 @@ public class RollbackUnitTest { PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true); rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2)); - rollback.delete(mMockDataHelper); + rollback.delete(mMockDataHelper, "test"); assertThat(rollback.isDeleted()).isTrue(); @@ -247,7 +247,7 @@ public class RollbackUnitTest { verify(mMockDataHelper).snapshotAppData(eq(123), pkgRollbackInfoFor(PKG_2), eq(userIds)); - rollback.delete(mMockDataHelper); + rollback.delete(mMockDataHelper, "test"); verify(mMockDataHelper).destroyAppDataSnapshot(eq(123), pkgRollbackInfoFor(PKG_2), eq(111)); verify(mMockDataHelper).destroyAppDataSnapshot(eq(123), pkgRollbackInfoFor(PKG_2), eq(222)); @@ -269,7 +269,7 @@ public class RollbackUnitTest { verify(mMockDataHelper).snapshotAppData(eq(123), pkgRollbackInfoFor(PKG_2), eq(userIds)); - rollback.delete(mMockDataHelper); + rollback.delete(mMockDataHelper, "test"); verify(mMockDataHelper, never()) .destroyAppDataSnapshot(anyInt(), pkgRollbackInfoFor(PKG_2), anyInt()); diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java index 9f59763cfa58..4a44005e9602 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java @@ -229,10 +229,6 @@ public class TimeDetectorServiceTest { private boolean mDumpCalled; @Override - public void initialize(Callback ignored) { - } - - @Override public void suggestTelephonyTime(TelephonyTimeSuggestion timeSuggestion) { mLastTelephonySuggestion = timeSuggestion; } @@ -249,7 +245,7 @@ public class TimeDetectorServiceTest { } @Override - public void handleAutoTimeDetectionChanged() { + public void handleAutoTimeConfigChanged() { mHandleAutoTimeDetectionChangedCalled = true; } diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java index 1d79d0d3b86e..c23fb8028224 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java @@ -16,6 +16,9 @@ package com.android.server.timedetector; +import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_NETWORK; +import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_TELEPHONY; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; @@ -25,32 +28,39 @@ import static org.junit.Assert.fail; import android.app.timedetector.ManualTimeSuggestion; import android.app.timedetector.NetworkTimeSuggestion; import android.app.timedetector.TelephonyTimeSuggestion; -import android.icu.util.Calendar; -import android.icu.util.GregorianCalendar; -import android.icu.util.TimeZone; import android.os.TimestampedValue; import androidx.test.runner.AndroidJUnit4; +import com.android.server.timedetector.TimeDetectorStrategy.Origin; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.time.Duration; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneOffset; @RunWith(AndroidJUnit4.class) public class TimeDetectorStrategyImplTest { - private static final TimestampedValue<Long> ARBITRARY_CLOCK_INITIALIZATION_INFO = + private static final Instant TIME_LOWER_BOUND = createUtcTime(2009, 1, 1, 12, 0, 0); + + private static final TimestampedValue<Instant> ARBITRARY_CLOCK_INITIALIZATION_INFO = new TimestampedValue<>( 123456789L /* realtimeClockMillis */, - createUtcTime(2008, 5, 23, 12, 0, 0)); + createUtcTime(2010, 5, 23, 12, 0, 0)); + + // This is the traditional ordering for time detection on Android. + private static final @Origin int [] PROVIDERS_PRIORITY = { ORIGIN_TELEPHONY, ORIGIN_NETWORK }; /** * An arbitrary time, very different from the {@link #ARBITRARY_CLOCK_INITIALIZATION_INFO} * time. Can be used as the basis for time suggestions. */ - private static final long ARBITRARY_TEST_TIME_MILLIS = createUtcTime(2018, 1, 1, 12, 0, 0); + private static final Instant ARBITRARY_TEST_TIME = createUtcTime(2018, 1, 1, 12, 0, 0); private static final int ARBITRARY_SLOT_INDEX = 123456; @@ -67,10 +77,10 @@ public class TimeDetectorStrategyImplTest { .pokeAutoTimeDetectionEnabled(true); int slotIndex = ARBITRARY_SLOT_INDEX; - long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS; + Instant testTime = ARBITRARY_TEST_TIME; TelephonyTimeSuggestion timeSuggestion = - mScript.generateTelephonyTimeSuggestion(slotIndex, testTimeMillis); + mScript.generateTelephonyTimeSuggestion(slotIndex, testTime); mScript.simulateTimePassing() .simulateTelephonyTimeSuggestion(timeSuggestion); @@ -106,9 +116,9 @@ public class TimeDetectorStrategyImplTest { // Send the first time signal. It should be used. { TelephonyTimeSuggestion timeSuggestion1 = - mScript.generateTelephonyTimeSuggestion(slotIndex, ARBITRARY_TEST_TIME_MILLIS); + mScript.generateTelephonyTimeSuggestion(slotIndex, ARBITRARY_TEST_TIME); - // Increment the the device clocks to simulate the passage of time. + // Increment the device clocks to simulate the passage of time. mScript.simulateTimePassing(clockIncrementMillis); long expectedSystemClockMillis1 = @@ -157,13 +167,13 @@ public class TimeDetectorStrategyImplTest { // uses the lowest slotIndex when multiple telephony suggestions are available. int slotIndex1 = ARBITRARY_SLOT_INDEX; int slotIndex2 = ARBITRARY_SLOT_INDEX + 1; - long slotIndex1TimeMillis = ARBITRARY_TEST_TIME_MILLIS; - long slotIndex2TimeMillis = ARBITRARY_TEST_TIME_MILLIS + Duration.ofDays(1).toMillis(); + Instant slotIndex1Time = ARBITRARY_TEST_TIME; + Instant slotIndex2Time = ARBITRARY_TEST_TIME.plus(Duration.ofDays(1)); // Make a suggestion with slotIndex2. { TelephonyTimeSuggestion slotIndex2TimeSuggestion = - mScript.generateTelephonyTimeSuggestion(slotIndex2, slotIndex2TimeMillis); + mScript.generateTelephonyTimeSuggestion(slotIndex2, slotIndex2Time); mScript.simulateTimePassing(); long expectedSystemClockMillis = @@ -180,7 +190,7 @@ public class TimeDetectorStrategyImplTest { // Now make a different suggestion with slotIndex1. { TelephonyTimeSuggestion slotIndex1TimeSuggestion = - mScript.generateTelephonyTimeSuggestion(slotIndex1, slotIndex1TimeMillis); + mScript.generateTelephonyTimeSuggestion(slotIndex1, slotIndex1Time); mScript.simulateTimePassing(); long expectedSystemClockMillis = @@ -198,7 +208,7 @@ public class TimeDetectorStrategyImplTest { // slotIndex1 suggestion will still "win". { TelephonyTimeSuggestion slotIndex2TimeSuggestion = - mScript.generateTelephonyTimeSuggestion(slotIndex2, slotIndex2TimeMillis); + mScript.generateTelephonyTimeSuggestion(slotIndex2, slotIndex2Time); mScript.simulateTimePassing(); mScript.simulateTelephonyTimeSuggestion(slotIndex2TimeSuggestion) @@ -213,7 +223,7 @@ public class TimeDetectorStrategyImplTest { // is in an older "bucket". { TelephonyTimeSuggestion slotIndex2TimeSuggestion = - mScript.generateTelephonyTimeSuggestion(slotIndex2, slotIndex2TimeMillis); + mScript.generateTelephonyTimeSuggestion(slotIndex2, slotIndex2Time); mScript.simulateTimePassing(); long expectedSystemClockMillis = @@ -232,7 +242,7 @@ public class TimeDetectorStrategyImplTest { int slotIndex = ARBITRARY_SLOT_INDEX; TelephonyTimeSuggestion timeSuggestion = - mScript.generateTelephonyTimeSuggestion(slotIndex, ARBITRARY_TEST_TIME_MILLIS); + mScript.generateTelephonyTimeSuggestion(slotIndex, ARBITRARY_TEST_TIME); mScript.simulateTimePassing() .simulateTelephonyTimeSuggestion(timeSuggestion) .verifySystemClockWasNotSetAndResetCallTracking() @@ -246,11 +256,11 @@ public class TimeDetectorStrategyImplTest { .pokeThresholds(systemClockUpdateThreshold) .pokeAutoTimeDetectionEnabled(true); - long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS; + Instant testTime = ARBITRARY_TEST_TIME; int slotIndex = ARBITRARY_SLOT_INDEX; TelephonyTimeSuggestion timeSuggestion1 = - mScript.generateTelephonyTimeSuggestion(slotIndex, testTimeMillis); + mScript.generateTelephonyTimeSuggestion(slotIndex, testTime); TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime(); // Initialize the strategy / device with a time set from a telephony suggestion. @@ -300,6 +310,23 @@ public class TimeDetectorStrategyImplTest { } @Test + public void telephonyTimeSuggestion_ignoredWhenReferencedTimeIsInThePast() { + mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) + .pokeAutoTimeDetectionEnabled(true); + + int slotIndex = ARBITRARY_SLOT_INDEX; + Instant suggestedTime = TIME_LOWER_BOUND.minus(Duration.ofDays(1)); + + TelephonyTimeSuggestion timeSuggestion = + mScript.generateTelephonyTimeSuggestion( + slotIndex, suggestedTime); + + mScript.simulateTelephonyTimeSuggestion(timeSuggestion) + .verifySystemClockWasNotSetAndResetCallTracking() + .assertLatestTelephonySuggestion(slotIndex, null); + } + + @Test public void testSuggestTelephonyTime_timeDetectionToggled() { final int clockIncrementMillis = 100; final int systemClockUpdateThreshold = 2000; @@ -308,9 +335,9 @@ public class TimeDetectorStrategyImplTest { .pokeAutoTimeDetectionEnabled(false); int slotIndex = ARBITRARY_SLOT_INDEX; - long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS; + Instant testTime = ARBITRARY_TEST_TIME; TelephonyTimeSuggestion timeSuggestion1 = - mScript.generateTelephonyTimeSuggestion(slotIndex, testTimeMillis); + mScript.generateTelephonyTimeSuggestion(slotIndex, testTime); TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime(); // Simulate time passing. @@ -366,9 +393,9 @@ public class TimeDetectorStrategyImplTest { .pokeAutoTimeDetectionEnabled(true); int slotIndex = ARBITRARY_SLOT_INDEX; - long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS; + Instant testTime = ARBITRARY_TEST_TIME; TelephonyTimeSuggestion telephonySuggestion = - mScript.generateTelephonyTimeSuggestion(slotIndex, testTimeMillis); + mScript.generateTelephonyTimeSuggestion(slotIndex, testTime); mScript.simulateTimePassing(); @@ -397,7 +424,7 @@ public class TimeDetectorStrategyImplTest { .pokeAutoTimeDetectionEnabled(false); ManualTimeSuggestion timeSuggestion = - mScript.generateManualTimeSuggestion(ARBITRARY_TEST_TIME_MILLIS); + mScript.generateManualTimeSuggestion(ARBITRARY_TEST_TIME); mScript.simulateTimePassing(); @@ -416,9 +443,9 @@ public class TimeDetectorStrategyImplTest { int slotIndex = ARBITRARY_SLOT_INDEX; // Simulate a telephony suggestion. - long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS; + Instant testTime = ARBITRARY_TEST_TIME; TelephonyTimeSuggestion telephonyTimeSuggestion = - mScript.generateTelephonyTimeSuggestion(slotIndex, testTimeMillis); + mScript.generateTelephonyTimeSuggestion(slotIndex, testTime); // Simulate the passage of time. mScript.simulateTimePassing(); @@ -441,9 +468,9 @@ public class TimeDetectorStrategyImplTest { mScript.simulateTimePassing(); // Simulate a manual suggestion 1 day different from the auto suggestion. - long manualTimeMillis = testTimeMillis + Duration.ofDays(1).toMillis(); + Instant manualTime = testTime.plus(Duration.ofDays(1)); ManualTimeSuggestion manualTimeSuggestion = - mScript.generateManualTimeSuggestion(manualTimeMillis); + mScript.generateManualTimeSuggestion(manualTime); mScript.simulateTimePassing(); long expectedManualClockMillis = @@ -469,16 +496,13 @@ public class TimeDetectorStrategyImplTest { .assertLatestTelephonySuggestion(slotIndex, telephonyTimeSuggestion); } - /** - * Manual suggestions should be ignored if auto time is enabled. - */ @Test - public void testSuggestManualTime_autoTimeEnabled() { + public void manualTimeSuggestion_isIgnored_whenAutoTimeEnabled() { mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) .pokeAutoTimeDetectionEnabled(true); ManualTimeSuggestion timeSuggestion = - mScript.generateManualTimeSuggestion(ARBITRARY_TEST_TIME_MILLIS); + mScript.generateManualTimeSuggestion(ARBITRARY_TEST_TIME); mScript.simulateTimePassing() .simulateManualTimeSuggestion(timeSuggestion, false /* expectedResult */) @@ -486,12 +510,25 @@ public class TimeDetectorStrategyImplTest { } @Test + public void manualTimeSuggestion_ignoresTimeLowerBound() { + mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) + .pokeAutoTimeDetectionEnabled(false); + Instant suggestedTime = TIME_LOWER_BOUND.minus(Duration.ofDays(1)); + + ManualTimeSuggestion timeSuggestion = + mScript.generateManualTimeSuggestion(suggestedTime); + + mScript.simulateManualTimeSuggestion(timeSuggestion, true /* expectedResult */) + .verifySystemClockWasSetAndResetCallTracking(suggestedTime.toEpochMilli()); + } + + @Test public void testSuggestNetworkTime_autoTimeEnabled() { mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) .pokeAutoTimeDetectionEnabled(true); NetworkTimeSuggestion timeSuggestion = - mScript.generateNetworkTimeSuggestion(ARBITRARY_TEST_TIME_MILLIS); + mScript.generateNetworkTimeSuggestion(ARBITRARY_TEST_TIME); mScript.simulateTimePassing(); @@ -507,7 +544,7 @@ public class TimeDetectorStrategyImplTest { .pokeAutoTimeDetectionEnabled(false); NetworkTimeSuggestion timeSuggestion = - mScript.generateNetworkTimeSuggestion(ARBITRARY_TEST_TIME_MILLIS); + mScript.generateNetworkTimeSuggestion(ARBITRARY_TEST_TIME); mScript.simulateTimePassing() .simulateNetworkTimeSuggestion(timeSuggestion) @@ -520,16 +557,16 @@ public class TimeDetectorStrategyImplTest { .pokeAutoTimeDetectionEnabled(true); // Three obviously different times that could not be mistaken for each other. - long networkTimeMillis1 = ARBITRARY_TEST_TIME_MILLIS; - long networkTimeMillis2 = ARBITRARY_TEST_TIME_MILLIS + Duration.ofDays(30).toMillis(); - long telephonyTimeMillis = ARBITRARY_TEST_TIME_MILLIS + Duration.ofDays(60).toMillis(); + Instant networkTime1 = ARBITRARY_TEST_TIME; + Instant networkTime2 = ARBITRARY_TEST_TIME.plus(Duration.ofDays(30)); + Instant telephonyTime = ARBITRARY_TEST_TIME.plus(Duration.ofDays(60)); // A small increment used to simulate the passage of time, but not enough to interfere with // macro-level time changes associated with suggestion age. final long smallTimeIncrementMillis = 101; // A network suggestion is made. It should be used because there is no telephony suggestion. NetworkTimeSuggestion networkTimeSuggestion1 = - mScript.generateNetworkTimeSuggestion(networkTimeMillis1); + mScript.generateNetworkTimeSuggestion(networkTime1); mScript.simulateTimePassing(smallTimeIncrementMillis) .simulateNetworkTimeSuggestion(networkTimeSuggestion1) .verifySystemClockWasSetAndResetCallTracking( @@ -548,7 +585,7 @@ public class TimeDetectorStrategyImplTest { // Now a telephony suggestion is made. Telephony suggestions are prioritized over network // suggestions so it should "win". TelephonyTimeSuggestion telephonyTimeSuggestion = - mScript.generateTelephonyTimeSuggestion(ARBITRARY_SLOT_INDEX, telephonyTimeMillis); + mScript.generateTelephonyTimeSuggestion(ARBITRARY_SLOT_INDEX, telephonyTime); mScript.simulateTimePassing(smallTimeIncrementMillis) .simulateTelephonyTimeSuggestion(telephonyTimeSuggestion) .verifySystemClockWasSetAndResetCallTracking( @@ -568,7 +605,7 @@ public class TimeDetectorStrategyImplTest { // Now another network suggestion is made. Telephony suggestions are prioritized over // network suggestions so the latest telephony suggestion should still "win". NetworkTimeSuggestion networkTimeSuggestion2 = - mScript.generateNetworkTimeSuggestion(networkTimeMillis2); + mScript.generateNetworkTimeSuggestion(networkTime2); mScript.simulateTimePassing(smallTimeIncrementMillis) .simulateNetworkTimeSuggestion(networkTimeSuggestion2) .verifySystemClockWasNotSetAndResetCallTracking(); @@ -612,16 +649,98 @@ public class TimeDetectorStrategyImplTest { assertNull(mScript.peekBestTelephonySuggestion()); } + @Test + public void networkTimeSuggestion_ignoredWhenReferencedTimeIsInThePast() { + mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) + .pokeAutoTimeDetectionEnabled(true); + + Instant suggestedTime = TIME_LOWER_BOUND.minus(Duration.ofDays(1)); + NetworkTimeSuggestion timeSuggestion = mScript + .generateNetworkTimeSuggestion(suggestedTime); + + mScript.simulateNetworkTimeSuggestion(timeSuggestion) + .verifySystemClockWasNotSetAndResetCallTracking() + .assertLatestNetworkSuggestion(null); + } + + @Test + public void whenAllTimeSuggestionsAreAvailable_higherPriorityWins_lowerPriorityComesFirst() { + mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) + .pokeAutoTimeDetectionEnabled(true); + + Instant networkTime = ARBITRARY_TEST_TIME; + Instant telephonyTime = ARBITRARY_TEST_TIME.plus(Duration.ofDays(30)); + + NetworkTimeSuggestion networkTimeSuggestion = + mScript.generateNetworkTimeSuggestion(networkTime); + TelephonyTimeSuggestion telephonyTimeSuggestion = + mScript.generateTelephonyTimeSuggestion(ARBITRARY_SLOT_INDEX, telephonyTime); + + mScript.simulateNetworkTimeSuggestion(networkTimeSuggestion) + .simulateTelephonyTimeSuggestion(telephonyTimeSuggestion) + .assertLatestNetworkSuggestion(networkTimeSuggestion) + .assertLatestTelephonySuggestion(ARBITRARY_SLOT_INDEX, telephonyTimeSuggestion) + .verifySystemClockWasSetAndResetCallTracking(telephonyTime.toEpochMilli()); + } + + @Test + public void whenAllTimeSuggestionsAreAvailable_higherPriorityWins_higherPriorityComesFirst() { + mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) + .pokeAutoTimeDetectionEnabled(true); + + Instant networkTime = ARBITRARY_TEST_TIME; + Instant telephonyTime = ARBITRARY_TEST_TIME.plus(Duration.ofDays(30)); + + NetworkTimeSuggestion networkTimeSuggestion = + mScript.generateNetworkTimeSuggestion(networkTime); + TelephonyTimeSuggestion telephonyTimeSuggestion = + mScript.generateTelephonyTimeSuggestion(ARBITRARY_SLOT_INDEX, telephonyTime); + + mScript.simulateTelephonyTimeSuggestion(telephonyTimeSuggestion) + .simulateNetworkTimeSuggestion(networkTimeSuggestion) + .assertLatestNetworkSuggestion(networkTimeSuggestion) + .assertLatestTelephonySuggestion(ARBITRARY_SLOT_INDEX, telephonyTimeSuggestion) + .verifySystemClockWasSetAndResetCallTracking(telephonyTime.toEpochMilli()); + } + + @Test + public void whenHighestPrioritySuggestionIsNotAvailable_fallbacksToNext() { + mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) + .pokeAutoTimeDetectionEnabled(true); + + NetworkTimeSuggestion timeSuggestion = + mScript.generateNetworkTimeSuggestion(ARBITRARY_TEST_TIME); + + mScript.simulateNetworkTimeSuggestion(timeSuggestion) + .assertLatestNetworkSuggestion(timeSuggestion) + .verifySystemClockWasSetAndResetCallTracking(ARBITRARY_TEST_TIME.toEpochMilli()); + } + + @Test + public void suggestionsFromSourceNotListedInPrioritiesList_areIgnored() { + mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) + .pokeAutoTimeDetectionEnabled(true) + .pokeAutoOriginPriorities(new int[]{ORIGIN_TELEPHONY}); + + NetworkTimeSuggestion timeSuggestion = mScript.generateNetworkTimeSuggestion( + ARBITRARY_TEST_TIME); + + mScript.simulateNetworkTimeSuggestion(timeSuggestion) + .assertLatestNetworkSuggestion(timeSuggestion) + .verifySystemClockWasNotSetAndResetCallTracking(); + } + /** * A fake implementation of TimeDetectorStrategy.Callback. Besides tracking changes and behaving * like the real thing should, it also asserts preconditions. */ - private static class FakeCallback implements TimeDetectorStrategy.Callback { + private static class FakeCallback implements TimeDetectorStrategyImpl.Callback { private boolean mAutoTimeDetectionEnabled; private boolean mWakeLockAcquired; private long mElapsedRealtimeMillis; private long mSystemClockMillis; private int mSystemClockUpdateThresholdMillis = 2000; + private int[] mAutoOriginPriorities = PROVIDERS_PRIORITY; // Tracking operations. private boolean mSystemClockWasSet; @@ -637,6 +756,16 @@ public class TimeDetectorStrategyImplTest { } @Override + public Instant autoTimeLowerBound() { + return TIME_LOWER_BOUND; + } + + @Override + public int[] getAutoOriginPriorities() { + return mAutoOriginPriorities; + } + + @Override public void acquireWakeLock() { if (mWakeLockAcquired) { fail("Wake lock already acquired"); @@ -685,6 +814,10 @@ public class TimeDetectorStrategyImplTest { mAutoTimeDetectionEnabled = enabled; } + void pokeAutoOriginPriorities(@Origin int[] autoOriginPriorities) { + mAutoOriginPriorities = autoOriginPriorities; + } + long peekElapsedRealtimeMillis() { return mElapsedRealtimeMillis; } @@ -703,7 +836,10 @@ public class TimeDetectorStrategyImplTest { } void verifySystemClockNotSet() { - assertFalse(mSystemClockWasSet); + assertFalse( + String.format("System clock was manipulated and set to %s(=%s)", + Instant.ofEpochMilli(mSystemClockMillis), mSystemClockMillis), + mSystemClockWasSet); } void verifySystemClockWasSet(long expectedSystemClockMillis) { @@ -731,9 +867,7 @@ public class TimeDetectorStrategyImplTest { Script() { mFakeCallback = new FakeCallback(); - mTimeDetectorStrategy = new TimeDetectorStrategyImpl(); - mTimeDetectorStrategy.initialize(mFakeCallback); - + mTimeDetectorStrategy = new TimeDetectorStrategyImpl(mFakeCallback); } Script pokeAutoTimeDetectionEnabled(boolean enabled) { @@ -741,9 +875,9 @@ public class TimeDetectorStrategyImplTest { return this; } - Script pokeFakeClocks(TimestampedValue<Long> timeInfo) { + Script pokeFakeClocks(TimestampedValue<Instant> timeInfo) { mFakeCallback.pokeElapsedRealtimeMillis(timeInfo.getReferenceTimeMillis()); - mFakeCallback.pokeSystemClockMillis(timeInfo.getValue()); + mFakeCallback.pokeSystemClockMillis(timeInfo.getValue().toEpochMilli()); return this; } @@ -752,6 +886,11 @@ public class TimeDetectorStrategyImplTest { return this; } + Script pokeAutoOriginPriorities(@Origin int[] autoOriginPriorites) { + mFakeCallback.pokeAutoOriginPriorities(autoOriginPriorites); + return this; + } + long peekElapsedRealtimeMillis() { return mFakeCallback.peekElapsedRealtimeMillis(); } @@ -767,7 +906,13 @@ public class TimeDetectorStrategyImplTest { Script simulateManualTimeSuggestion( ManualTimeSuggestion timeSuggestion, boolean expectedResult) { - assertEquals(expectedResult, mTimeDetectorStrategy.suggestManualTime(timeSuggestion)); + String errorMessage = expectedResult + ? "Manual time suggestion was ignored, but expected to be accepted." + : "Manual time suggestion was accepted, but expected to be ignored."; + assertEquals( + errorMessage, + expectedResult, + mTimeDetectorStrategy.suggestManualTime(timeSuggestion)); return this; } @@ -778,7 +923,7 @@ public class TimeDetectorStrategyImplTest { Script simulateAutoTimeDetectionToggle() { mFakeCallback.simulateAutoTimeZoneDetectionToggle(); - mTimeDetectorStrategy.handleAutoTimeDetectionChanged(); + mTimeDetectorStrategy.handleAutoTimeConfigChanged(); return this; } @@ -810,7 +955,10 @@ public class TimeDetectorStrategyImplTest { * White box test info: Asserts the latest suggestion for the slotIndex is as expected. */ Script assertLatestTelephonySuggestion(int slotIndex, TelephonyTimeSuggestion expected) { - assertEquals(expected, mTimeDetectorStrategy.getLatestTelephonySuggestion(slotIndex)); + assertEquals( + "Expected to see " + expected + " at slotIndex=" + slotIndex + ", but got " + + mTimeDetectorStrategy.getLatestTelephonySuggestion(slotIndex), + expected, mTimeDetectorStrategy.getLatestTelephonySuggestion(slotIndex)); return this; } @@ -842,9 +990,11 @@ public class TimeDetectorStrategyImplTest { * Generates a ManualTimeSuggestion using the current elapsed realtime clock for the * reference time. */ - ManualTimeSuggestion generateManualTimeSuggestion(long timeMillis) { + ManualTimeSuggestion generateManualTimeSuggestion(Instant suggestedTime) { TimestampedValue<Long> utcTime = - new TimestampedValue<>(mFakeCallback.peekElapsedRealtimeMillis(), timeMillis); + new TimestampedValue<>( + mFakeCallback.peekElapsedRealtimeMillis(), + suggestedTime.toEpochMilli()); return new ManualTimeSuggestion(utcTime); } @@ -852,21 +1002,33 @@ public class TimeDetectorStrategyImplTest { * Generates a {@link TelephonyTimeSuggestion} using the current elapsed realtime clock for * the reference time. */ - TelephonyTimeSuggestion generateTelephonyTimeSuggestion(int slotIndex, Long timeMillis) { - TimestampedValue<Long> time = null; - if (timeMillis != null) { - time = new TimestampedValue<>(peekElapsedRealtimeMillis(), timeMillis); - } + TelephonyTimeSuggestion generateTelephonyTimeSuggestion(int slotIndex, long timeMillis) { + TimestampedValue<Long> time = + new TimestampedValue<>(peekElapsedRealtimeMillis(), timeMillis); return createTelephonyTimeSuggestion(slotIndex, time); } /** + * Generates a {@link TelephonyTimeSuggestion} using the current elapsed realtime clock for + * the reference time. + */ + TelephonyTimeSuggestion generateTelephonyTimeSuggestion( + int slotIndex, Instant suggestedTime) { + if (suggestedTime == null) { + return createTelephonyTimeSuggestion(slotIndex, null); + } + return generateTelephonyTimeSuggestion(slotIndex, suggestedTime.toEpochMilli()); + } + + /** * Generates a NetworkTimeSuggestion using the current elapsed realtime clock for the * reference time. */ - NetworkTimeSuggestion generateNetworkTimeSuggestion(long timeMillis) { + NetworkTimeSuggestion generateNetworkTimeSuggestion(Instant suggestedTime) { TimestampedValue<Long> utcTime = - new TimestampedValue<>(mFakeCallback.peekElapsedRealtimeMillis(), timeMillis); + new TimestampedValue<>( + mFakeCallback.peekElapsedRealtimeMillis(), + suggestedTime.toEpochMilli()); return new NetworkTimeSuggestion(utcTime); } @@ -886,11 +1048,9 @@ public class TimeDetectorStrategyImplTest { .build(); } - private static long createUtcTime(int year, int monthInYear, int day, int hourOfDay, int minute, - int second) { - Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("Etc/UTC")); - cal.clear(); - cal.set(year, monthInYear - 1, day, hourOfDay, minute, second); - return cal.getTimeInMillis(); + private static Instant createUtcTime(int year, int monthInYear, int day, int hourOfDay, + int minute, int second) { + return LocalDateTime.of(year, monthInYear, day, hourOfDay, minute, second) + .toInstant(ZoneOffset.UTC); } } diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java index 0ad669f32060..11fb0021be62 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -65,6 +65,7 @@ import android.annotation.NonNull; import android.app.ActivityManager; import android.app.usage.AppStandbyInfo; import android.app.usage.UsageEvents; +import android.app.usage.UsageStatsManagerInternal; import android.appwidget.AppWidgetManager; import android.content.Context; import android.content.ContextWrapper; @@ -86,9 +87,11 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener; +import org.junit.After; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; @@ -425,11 +428,18 @@ public class AppStandbyControllerTests { @Before public void setUp() throws Exception { + LocalServices.addService( + UsageStatsManagerInternal.class, mock(UsageStatsManagerInternal.class)); MyContextWrapper myContext = new MyContextWrapper(InstrumentationRegistry.getContext()); mInjector = new MyInjector(myContext, Looper.getMainLooper()); mController = setupController(); } + @After + public void tearDown() { + LocalServices.removeServiceForTest(UsageStatsManagerInternal.class); + } + @Test public void testBoundWidgetPackageExempt() throws Exception { assumeTrue(mInjector.getContext().getSystemService(AppWidgetManager.class) != null); @@ -562,7 +572,7 @@ public class AppStandbyControllerTests { UsageEvents.Event ev = new UsageEvents.Event(); ev.mPackage = packageName; ev.mEventType = eventType; - controller.reportEvent(ev, USER_ID); + controller.onUsageEvent(USER_ID, ev); } private int getStandbyBucket(AppStandbyController controller, String packageName) { diff --git a/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java b/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java new file mode 100644 index 000000000000..fa8e36741bcc --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java @@ -0,0 +1,291 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.vibrator; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.same; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.ContextWrapper; +import android.hardware.input.IInputDevicesChangedListener; +import android.hardware.input.IInputManager; +import android.hardware.input.InputManager; +import android.os.Handler; +import android.os.Process; +import android.os.VibrationAttributes; +import android.os.VibrationEffect; +import android.os.test.TestLooper; +import android.platform.test.annotations.Presubmit; +import android.view.InputDevice; + +import androidx.test.InstrumentationRegistry; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +/** + * Tests for {@link InputDeviceDelegate}. + * + * Build/Install/Run: + * atest FrameworksServicesTests:InputDeviceDelegateTest + */ +@Presubmit +public class InputDeviceDelegateTest { + + private static final int UID = Process.ROOT_UID; + private static final String PACKAGE_NAME = "package"; + private static final String REASON = "some reason"; + private static final VibrationAttributes VIBRATION_ATTRIBUTES = + new VibrationAttributes.Builder().setUsage(VibrationAttributes.USAGE_ALARM).build(); + + @Rule public MockitoRule rule = MockitoJUnit.rule(); + + @Mock private IInputManager mIInputManagerMock; + + private TestLooper mTestLooper; + private ContextWrapper mContextSpy; + private InputDeviceDelegate mInputDeviceDelegate; + private IInputDevicesChangedListener mIInputDevicesChangedListener; + + @Before + public void setUp() throws Exception { + mTestLooper = new TestLooper(); + mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext())); + InputManager inputManager = InputManager.resetInstance(mIInputManagerMock); + + when(mContextSpy.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(inputManager); + doAnswer(invocation -> mIInputDevicesChangedListener = invocation.getArgument(0)) + .when(mIInputManagerMock).registerInputDevicesChangedListener(any()); + + mInputDeviceDelegate = new InputDeviceDelegate( + mContextSpy, new Handler(mTestLooper.getLooper())); + } + + @Test + public void onInputDeviceAdded_withSettingsDisabled_ignoresNewDevice() throws Exception { + when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[0]); + mInputDeviceDelegate.updateInputDeviceVibrators(/* vibrateInputDevices= */ false); + assertFalse(mInputDeviceDelegate.isAvailable()); + + when(mIInputManagerMock.getInputDevice(eq(1))).thenReturn(createInputDeviceWithVibrator(1)); + mInputDeviceDelegate.onInputDeviceAdded(1); + + assertFalse(mInputDeviceDelegate.isAvailable()); + verify(mIInputManagerMock, never()).getInputDevice(anyInt()); + } + + @Test + public void onInputDeviceAdded_withDeviceWithoutVibrator_ignoresNewDevice() throws Exception { + when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[0]); + mInputDeviceDelegate.updateInputDeviceVibrators(/* vibrateInputDevices= */ true); + assertFalse(mInputDeviceDelegate.isAvailable()); + + when(mIInputManagerMock.getInputDevice(eq(1))) + .thenReturn(createInputDeviceWithoutVibrator(1)); + updateInputDevices(new int[]{1}); + + assertFalse(mInputDeviceDelegate.isAvailable()); + verify(mIInputManagerMock).getInputDevice(eq(1)); + } + + @Test + public void onInputDeviceAdded_withDeviceWithVibrator_addsNewDevice() throws Exception { + when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[0]); + mInputDeviceDelegate.updateInputDeviceVibrators(/* vibrateInputDevices= */ true); + assertFalse(mInputDeviceDelegate.isAvailable()); + + when(mIInputManagerMock.getInputDevice(eq(1))).thenReturn(createInputDeviceWithVibrator(1)); + updateInputDevices(new int[]{1}); + + assertTrue(mInputDeviceDelegate.isAvailable()); + verify(mIInputManagerMock).getInputDevice(eq(1)); + } + + @Test + public void onInputDeviceChanged_withSettingsDisabled_ignoresDevice() throws Exception { + when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1}); + when(mIInputManagerMock.getInputDevice(eq(1))).thenReturn(createInputDeviceWithVibrator(1)); + mInputDeviceDelegate.updateInputDeviceVibrators(/* vibrateInputDevices= */ false); + + updateInputDevices(new int[]{1}); + assertFalse(mInputDeviceDelegate.isAvailable()); + verify(mIInputManagerMock, never()).getInputDevice(anyInt()); + } + + @Test + public void onInputDeviceChanged_deviceLosesVibrator_removesDevice() throws Exception { + when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1}); + when(mIInputManagerMock.getInputDevice(eq(1))) + .thenReturn(createInputDeviceWithVibrator(1), createInputDeviceWithoutVibrator(1)); + + mInputDeviceDelegate.updateInputDeviceVibrators(/* vibrateInputDevices= */ true); + assertTrue(mInputDeviceDelegate.isAvailable()); + + updateInputDevices(new int[]{1}); + assertFalse(mInputDeviceDelegate.isAvailable()); + verify(mIInputManagerMock, times(2)).getInputDevice(eq(1)); + } + + @Test + public void onInputDeviceChanged_deviceLost_removesDevice() throws Exception { + when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1}); + when(mIInputManagerMock.getInputDevice(eq(1))) + .thenReturn(createInputDeviceWithVibrator(1), (InputDevice) null); + + mInputDeviceDelegate.updateInputDeviceVibrators(/* vibrateInputDevices= */ true); + assertTrue(mInputDeviceDelegate.isAvailable()); + + updateInputDevices(new int[]{1}); + assertFalse(mInputDeviceDelegate.isAvailable()); + verify(mIInputManagerMock, times(2)).getInputDevice(eq(1)); + } + + @Test + public void onInputDeviceChanged_deviceAddsVibrator_addsDevice() throws Exception { + when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1}); + when(mIInputManagerMock.getInputDevice(eq(1))) + .thenReturn(createInputDeviceWithoutVibrator(1), createInputDeviceWithVibrator(1)); + + mInputDeviceDelegate.updateInputDeviceVibrators(/* vibrateInputDevices= */ true); + assertFalse(mInputDeviceDelegate.isAvailable()); + + updateInputDevices(new int[]{1}); + assertTrue(mInputDeviceDelegate.isAvailable()); + verify(mIInputManagerMock, times(2)).getInputDevice(eq(1)); + } + + @Test + public void onInputDeviceRemoved_removesDevice() throws Exception { + when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1, 2}); + when(mIInputManagerMock.getInputDevice(eq(1))).thenReturn( + createInputDeviceWithoutVibrator(1)); + when(mIInputManagerMock.getInputDevice(eq(2))).thenReturn(createInputDeviceWithVibrator(2)); + + mInputDeviceDelegate.updateInputDeviceVibrators(/* vibrateInputDevices= */ true); + assertTrue(mInputDeviceDelegate.isAvailable()); + + updateInputDevices(new int[]{1}); + assertFalse(mInputDeviceDelegate.isAvailable()); + } + + @Test + public void updateInputDeviceVibrators_usesFlagToLoadDeviceList() throws Exception { + when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1, 2}); + when(mIInputManagerMock.getInputDevice(eq(1))).thenReturn(createInputDeviceWithVibrator(1)); + when(mIInputManagerMock.getInputDevice(eq(2))).thenReturn(createInputDeviceWithVibrator(2)); + + mInputDeviceDelegate.updateInputDeviceVibrators(/* vibrateInputDevices= */ true); + assertTrue(mInputDeviceDelegate.isAvailable()); + + mInputDeviceDelegate.updateInputDeviceVibrators(/* vibrateInputDevices= */ false); + assertFalse(mInputDeviceDelegate.isAvailable()); + } + + @Test + public void updateInputDeviceVibrators_withDeviceWithoutVibrator_deviceIsIgnored() + throws Exception { + when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1}); + when(mIInputManagerMock.getInputDevice(eq(1))) + .thenReturn(createInputDeviceWithoutVibrator(1)); + mInputDeviceDelegate.updateInputDeviceVibrators(/* vibrateInputDevices= */ true); + assertFalse(mInputDeviceDelegate.isAvailable()); + } + + @Test + public void vibrateIfAvailable_withNoInputDevice_returnsFalse() { + VibrationEffect effect = VibrationEffect.createOneShot(100, 255); + assertFalse(mInputDeviceDelegate.isAvailable()); + assertFalse(mInputDeviceDelegate.vibrateIfAvailable( + UID, PACKAGE_NAME, effect, REASON, VIBRATION_ATTRIBUTES)); + } + + @Test + public void vibrateIfAvailable_withInputDevices_returnsTrueAndVibratesAllDevices() + throws Exception { + when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1, 2}); + when(mIInputManagerMock.getInputDevice(eq(1))).thenReturn(createInputDeviceWithVibrator(1)); + when(mIInputManagerMock.getInputDevice(eq(2))).thenReturn(createInputDeviceWithVibrator(2)); + mInputDeviceDelegate.updateInputDeviceVibrators(/* vibrateInputDevices= */ true); + + VibrationEffect effect = VibrationEffect.createOneShot(100, 255); + assertTrue(mInputDeviceDelegate.vibrateIfAvailable( + UID, PACKAGE_NAME, effect, REASON, VIBRATION_ATTRIBUTES)); + verify(mIInputManagerMock).vibrate(eq(1), same(effect), any()); + verify(mIInputManagerMock).vibrate(eq(2), same(effect), any()); + } + + @Test + public void cancelVibrateIfAvailable_withNoInputDevice_returnsFalse() throws Exception { + assertFalse(mInputDeviceDelegate.isAvailable()); + assertFalse(mInputDeviceDelegate.cancelVibrateIfAvailable()); + verify(mIInputManagerMock, never()).cancelVibrate(anyInt(), any()); + } + + @Test + public void cancelVibrateIfAvailable_withInputDevices_returnsTrueAndStopsAllDevices() + throws Exception { + when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{1, 2}); + when(mIInputManagerMock.getInputDevice(eq(1))).thenReturn(createInputDeviceWithVibrator(1)); + when(mIInputManagerMock.getInputDevice(eq(2))).thenReturn(createInputDeviceWithVibrator(2)); + mInputDeviceDelegate.updateInputDeviceVibrators(/* vibrateInputDevices= */ true); + + assertTrue(mInputDeviceDelegate.isAvailable()); + assertTrue(mInputDeviceDelegate.cancelVibrateIfAvailable()); + verify(mIInputManagerMock).cancelVibrate(eq(1), any()); + verify(mIInputManagerMock).cancelVibrate(eq(2), any()); + } + + private void updateInputDevices(int[] deviceIds) throws Exception { + int[] deviceIdsAndGenerations = new int[deviceIds.length * 2]; + for (int i = 0; i < deviceIdsAndGenerations.length; i += 2) { + deviceIdsAndGenerations[i] = deviceIds[i / 2]; + deviceIdsAndGenerations[i + 1] = 2; // update by increasing it's generation to 2. + } + // Force initialization of mIInputDevicesChangedListener, if it still haven't + InputManager.getInstance().getInputDeviceIds(); + mIInputDevicesChangedListener.onInputDevicesChanged(deviceIdsAndGenerations); + // Makes sure all callbacks from InputDeviceDelegate are executed. + mTestLooper.dispatchAll(); + } + + private InputDevice createInputDeviceWithVibrator(int id) { + return createInputDevice(id, /* hasVibrator= */ true); + } + + private InputDevice createInputDeviceWithoutVibrator(int id) { + return createInputDevice(id, /* hasVibrator= */ false); + } + + private InputDevice createInputDevice(int id, boolean hasVibrator) { + return new InputDevice(id, 0, 0, "name", 0, 0, "description", false, 0, 0, + null, hasVibrator, false, false); + } +} diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java index 2cc999283184..04c2cb3b1148 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java @@ -18,6 +18,7 @@ package com.android.server.vibrator; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.spy; @@ -33,6 +34,7 @@ import android.media.AudioManager; import android.os.Handler; import android.os.UserHandle; import android.os.VibrationAttributes; +import android.os.VibrationEffect; import android.os.Vibrator; import android.os.test.TestLooper; import android.platform.test.annotations.Presubmit; @@ -271,6 +273,15 @@ public class VibrationSettingsTest { mVibrationSettings.getCurrentIntensity(VibrationAttributes.USAGE_RINGTONE)); } + @Test + public void getFallbackEffect_returnsEffectsFromSettings() { + assertNotNull(mVibrationSettings.getFallbackEffect(VibrationEffect.EFFECT_TICK)); + assertNotNull(mVibrationSettings.getFallbackEffect(VibrationEffect.EFFECT_TEXTURE_TICK)); + assertNotNull(mVibrationSettings.getFallbackEffect(VibrationEffect.EFFECT_CLICK)); + assertNotNull(mVibrationSettings.getFallbackEffect(VibrationEffect.EFFECT_HEAVY_CLICK)); + assertNotNull(mVibrationSettings.getFallbackEffect(VibrationEffect.EFFECT_DOUBLE_CLICK)); + } + private void setUserSetting(String settingName, int value) { Settings.System.putIntForUser( mContextSpy.getContentResolver(), settingName, value, UserHandle.USER_CURRENT); diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java new file mode 100644 index 000000000000..1f163bd3282b --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java @@ -0,0 +1,342 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.vibrator; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.notNull; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import android.content.ContentResolver; +import android.content.ContextWrapper; +import android.hardware.vibrator.IVibrator; +import android.os.IBinder; +import android.os.IVibratorStateListener; +import android.os.VibrationEffect; +import android.os.Vibrator; +import android.os.test.TestLooper; +import android.platform.test.annotations.Presubmit; + +import androidx.test.InstrumentationRegistry; + +import com.android.internal.util.test.FakeSettingsProvider; +import com.android.internal.util.test.FakeSettingsProviderRule; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.InOrder; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +/** + * Tests for {@link VibratorController}. + * + * Build/Install/Run: + * atest FrameworksServicesTests:VibratorControllerTest + */ +@Presubmit +public class VibratorControllerTest { + + @Rule public MockitoRule rule = MockitoJUnit.rule(); + @Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule(); + + @Mock private VibratorController.OnVibrationCompleteListener mOnCompleteListenerMock; + @Mock private VibratorController.NativeWrapper mNativeWrapperMock; + @Mock private IVibratorStateListener mVibratorStateListenerMock; + @Mock private IBinder mVibratorStateListenerBinderMock; + + private TestLooper mTestLooper; + private ContextWrapper mContextSpy; + + @Before + public void setUp() throws Exception { + mTestLooper = new TestLooper(); + mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext())); + + ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy); + when(mContextSpy.getContentResolver()).thenReturn(contentResolver); + when(mVibratorStateListenerMock.asBinder()).thenReturn(mVibratorStateListenerBinderMock); + } + + private VibratorController createController() { + return new VibratorController(/* vibratorId= */ 0, mOnCompleteListenerMock, + mNativeWrapperMock); + } + + private VibratorController createController(int vibratorId) { + return new VibratorController(vibratorId, mOnCompleteListenerMock, mNativeWrapperMock); + } + + @Test + public void createController_initializesNativeWrapper() { + int vibratorId = 13; + VibratorController controller = createController(vibratorId); + assertEquals(vibratorId, controller.getVibratorId()); + verify(mNativeWrapperMock).init(eq(vibratorId), notNull()); + } + + @Test + public void isAvailable_withVibratorHalPresent_returnsTrue() { + when(mNativeWrapperMock.isAvailable()).thenReturn(true); + assertTrue(createController().isAvailable()); + } + + @Test + public void isAvailable_withNoVibratorHalPresent_returnsFalse() { + when(mNativeWrapperMock.isAvailable()).thenReturn(false); + assertFalse(createController().isAvailable()); + } + + @Test + public void hasCapability_withSupport_returnsTrue() { + mockVibratorCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); + assertTrue(createController().hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL)); + } + + @Test + public void hasCapability_withNoSupport_returnsFalse() { + assertFalse(createController().hasCapability(IVibrator.CAP_ALWAYS_ON_CONTROL)); + assertFalse(createController().hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL)); + assertFalse(createController().hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)); + assertFalse(createController().hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)); + assertFalse(createController().hasCapability(IVibrator.CAP_ON_CALLBACK)); + } + + @Test + public void areEffectsSupported_withNullResultFromNative_returnsSupportUnknown() { + when(mNativeWrapperMock.getSupportedEffects()).thenReturn(null); + assertArrayEquals(new int[]{Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN}, + createController().areEffectsSupported(new int[]{VibrationEffect.EFFECT_CLICK})); + } + + @Test + public void areEffectsSupported_withSomeEffectsSupported_returnsSupportYesAndNoForEffects() { + int[] effects = new int[]{VibrationEffect.EFFECT_CLICK, VibrationEffect.EFFECT_TICK}; + + when(mNativeWrapperMock.getSupportedEffects()) + .thenReturn(new int[]{VibrationEffect.EFFECT_CLICK}); + assertArrayEquals( + new int[]{Vibrator.VIBRATION_EFFECT_SUPPORT_YES, + Vibrator.VIBRATION_EFFECT_SUPPORT_NO}, + createController().areEffectsSupported(effects)); + } + + @Test + public void arePrimitivesSupported_withoutComposeCapability_returnsAlwaysFalse() { + assertArrayEquals(new boolean[]{false, false}, + createController().arePrimitivesSupported(new int[]{ + VibrationEffect.Composition.PRIMITIVE_CLICK, + VibrationEffect.Composition.PRIMITIVE_TICK + })); + } + + @Test + public void arePrimitivesSupported_withNullResultFromNative_returnsAlwaysFalse() { + mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); + when(mNativeWrapperMock.getSupportedPrimitives()).thenReturn(null); + + assertArrayEquals(new boolean[]{false, false}, + createController().arePrimitivesSupported(new int[]{ + VibrationEffect.Composition.PRIMITIVE_CLICK, + VibrationEffect.Composition.PRIMITIVE_QUICK_RISE + })); + } + + @Test + public void arePrimitivesSupported_withSomeSupportedPrimitives_returnsBasedOnNativeResult() { + mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); + when(mNativeWrapperMock.getSupportedPrimitives()) + .thenReturn(new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK}); + + assertArrayEquals(new boolean[]{true, false}, + createController().arePrimitivesSupported(new int[]{ + VibrationEffect.Composition.PRIMITIVE_CLICK, + VibrationEffect.Composition.PRIMITIVE_QUICK_RISE + })); + } + + @Test + public void setExternalControl_withCapability_enablesExternalControl() { + mockVibratorCapabilities(IVibrator.CAP_EXTERNAL_CONTROL); + VibratorController controller = createController(); + assertFalse(controller.isUnderExternalControl()); + + controller.setExternalControl(true); + assertTrue(controller.isUnderExternalControl()); + + controller.setExternalControl(false); + assertFalse(controller.isUnderExternalControl()); + + InOrder inOrderVerifier = inOrder(mNativeWrapperMock); + inOrderVerifier.verify(mNativeWrapperMock).setExternalControl(eq(true)); + inOrderVerifier.verify(mNativeWrapperMock).setExternalControl(eq(false)); + } + + @Test + public void setExternalControl_withNoCapability_ignoresExternalControl() { + VibratorController controller = createController(); + assertFalse(controller.isUnderExternalControl()); + + controller.setExternalControl(true); + assertFalse(controller.isUnderExternalControl()); + + verify(mNativeWrapperMock, never()).setExternalControl(anyBoolean()); + } + + @Test + public void updateAlwaysOn_withCapability_enablesAlwaysOnEffect() { + mockVibratorCapabilities(IVibrator.CAP_ALWAYS_ON_CONTROL); + VibrationEffect.Prebaked effect = (VibrationEffect.Prebaked) + VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK); + createController().updateAlwaysOn(1, effect); + + verify(mNativeWrapperMock).alwaysOnEnable( + eq(1L), eq((long) VibrationEffect.EFFECT_CLICK), + eq((long) VibrationEffect.EFFECT_STRENGTH_MEDIUM)); + } + + @Test + public void updateAlwaysOn_withNullEffect_disablesAlwaysOnEffect() { + mockVibratorCapabilities(IVibrator.CAP_ALWAYS_ON_CONTROL); + createController().updateAlwaysOn(1, null); + verify(mNativeWrapperMock).alwaysOnDisable(eq(1L)); + } + + @Test + public void updateAlwaysOn_withoutCapability_ignoresEffect() { + VibrationEffect.Prebaked effect = (VibrationEffect.Prebaked) + VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK); + createController().updateAlwaysOn(1, effect); + + verify(mNativeWrapperMock, never()).alwaysOnDisable(anyLong()); + verify(mNativeWrapperMock, never()).alwaysOnEnable(anyLong(), anyLong(), anyLong()); + } + + @Test + public void on_withDuration_turnsVibratorOn() { + VibratorController controller = createController(); + controller.on(100, 10); + + assertTrue(controller.isVibrating()); + verify(mNativeWrapperMock).on(eq(100L), eq(10L)); + } + + @Test + public void on_withPrebaked_performsEffect() { + when(mNativeWrapperMock.perform(anyLong(), anyLong(), anyLong())).thenReturn(10L); + VibratorController controller = createController(); + + VibrationEffect.Prebaked effect = (VibrationEffect.Prebaked) + VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK); + controller.on(effect, 11); + + assertTrue(controller.isVibrating()); + verify(mNativeWrapperMock).perform(eq((long) VibrationEffect.EFFECT_CLICK), + eq((long) VibrationEffect.EFFECT_STRENGTH_MEDIUM), eq(11L)); + } + + @Test + public void on_withComposed_performsEffect() { + mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); + VibratorController controller = createController(); + + VibrationEffect.Composed effect = (VibrationEffect.Composed) + VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f, 10) + .compose(); + controller.on(effect, 12); + + ArgumentCaptor<VibrationEffect.Composition.PrimitiveEffect[]> primitivesCaptor = + ArgumentCaptor.forClass(VibrationEffect.Composition.PrimitiveEffect[].class); + + assertTrue(controller.isVibrating()); + verify(mNativeWrapperMock).compose(primitivesCaptor.capture(), eq(12L)); + + // Check all primitive effect fields are passed down to the HAL. + assertEquals(1, primitivesCaptor.getValue().length); + VibrationEffect.Composition.PrimitiveEffect primitive = primitivesCaptor.getValue()[0]; + assertEquals(VibrationEffect.Composition.PRIMITIVE_CLICK, primitive.id); + assertEquals(0.5f, primitive.scale, /* delta= */ 1e-2); + assertEquals(10, primitive.delay); + } + + @Test + public void off_turnsOffVibrator() { + VibratorController controller = createController(); + controller.on(100, 1); + assertTrue(controller.isVibrating()); + + controller.off(); + controller.off(); + assertFalse(controller.isVibrating()); + verify(mNativeWrapperMock, times(2)).off(); + } + + @Test + public void registerVibratorStateListener_callbacksAreTriggered() throws Exception { + VibratorController controller = createController(); + + controller.registerVibratorStateListener(mVibratorStateListenerMock); + controller.on(10, 1); + controller.on(100, 2); + controller.off(); + controller.off(); + + InOrder inOrderVerifier = inOrder(mVibratorStateListenerMock); + // First notification done when listener is registered. + inOrderVerifier.verify(mVibratorStateListenerMock).onVibrating(false); + inOrderVerifier.verify(mVibratorStateListenerMock).onVibrating(eq(true)); + inOrderVerifier.verify(mVibratorStateListenerMock).onVibrating(eq(false)); + inOrderVerifier.verifyNoMoreInteractions(); + } + + @Test + public void unregisterVibratorStateListener_callbackNotTriggeredAfter() throws Exception { + VibratorController controller = createController(); + + controller.registerVibratorStateListener(mVibratorStateListenerMock); + verify(mVibratorStateListenerMock).onVibrating(false); + + controller.on(10, 1); + verify(mVibratorStateListenerMock).onVibrating(true); + + controller.unregisterVibratorStateListener(mVibratorStateListenerMock); + Mockito.clearInvocations(mVibratorStateListenerMock); + + controller.on(10, 1); + verifyNoMoreInteractions(mVibratorStateListenerMock); + } + + private void mockVibratorCapabilities(int capabilities) { + when(mNativeWrapperMock.getCapabilities()).thenReturn((long) capabilities); + } +} diff --git a/services/tests/servicestests/test-apps/PackageParserApp/Android.bp b/services/tests/servicestests/test-apps/PackageParserApp/Android.bp index c409438e94ae..f69dfe9d03c9 100644 --- a/services/tests/servicestests/test-apps/PackageParserApp/Android.bp +++ b/services/tests/servicestests/test-apps/PackageParserApp/Android.bp @@ -51,3 +51,17 @@ android_test_helper_app { resource_dirs: ["res"], manifest: "AndroidManifestApp3.xml", } + +android_test_helper_app { + name: "PackageParserTestApp4", + sdk_version: "current", + srcs: ["**/*.java"], + dex_preopt: { + enabled: false, + }, + optimize: { + enabled: false, + }, + resource_dirs: ["res"], + manifest: "AndroidManifestApp4.xml", +} diff --git a/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp4.xml b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp4.xml new file mode 100644 index 000000000000..299b9a03d815 --- /dev/null +++ b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp4.xml @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.servicestests.apps.packageparserapp" > + <application> + <uses-library android:name="android.test.runner" /> + <property android:name="android.cts.PROPERTY_RESOURCE_XML" android:resource="@xml/xml_property" /> + <property android:name="android.cts.PROPERTY_RESOURCE_INTEGER" android:resource="@integer/integer_property" /> + <property android:name="android.cts.PROPERTY_BOOLEAN" android:value="true" /> + <property android:name="android.cts.PROPERTY_BOOLEAN_VIA_RESOURCE" android:value="@bool/boolean_property" /> + <property android:name="android.cts.PROPERTY_FLOAT" android:value="3.14" /> + <property android:name="android.cts.PROPERTY_FLOAT_VIA_RESOURCE" android:value="@dimen/float_property" /> + <property android:name="android.cts.PROPERTY_INTEGER" android:value="42" /> + <property android:name="android.cts.PROPERTY_INTEGER_VIA_RESOURCE" android:value="@integer/integer_property" /> + <property android:name="android.cts.PROPERTY_STRING" android:value="koala" /> + <property android:name="android.cts.PROPERTY_STRING_VIA_RESOURCE" android:value="@string/string_property" /> + + <activity android:name="com.android.servicestests.apps.packageparserapp.MyActivity" + android:exported="true" > + <property android:name="android.cts.PROPERTY_ACTIVITY" android:value="@integer/integer_property" /> + <property android:name="android.cts.PROPERTY_COMPONENT" android:value="@integer/integer_property" /> + <property android:name="android.cts.PROPERTY_STRING" android:value="koala activity" /> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + <activity-alias android:name="com.android.servicestests.apps.packageparserapp.MyActivityAlias" + android:targetActivity="com.android.servicestests.apps.packageparserapp.MyActivity"> + <property android:name="android.cts.PROPERTY_ACTIVITY_ALIAS" android:value="@integer/integer_property" /> + <property android:name="android.cts.PROPERTY_COMPONENT" android:value="@integer/integer_property" /> + </activity-alias> + <provider android:name="com.android.servicestests.apps.packageparserapp.MyProvider" + android:authorities="propertytest"> + <property android:name="android.cts.PROPERTY_PROVIDER" android:value="@integer/integer_property" /> + </provider> + <receiver android:name="com.android.servicestests.apps.packageparserapp.MyReceiver"> + <property android:name="android.cts.PROPERTY_RECEIVER" android:value="@integer/integer_property" /> + <property android:name="android.cts.PROPERTY_STRING" android:value="koala receiver" /> + </receiver> + <service android:name="com.android.servicestests.apps.packageparserapp.MyService"> + <property android:name="android.cts.PROPERTY_SERVICE" android:value="@integer/integer_property" /> + <property android:name="android.cts.PROPERTY_COMPONENT" android:resource="@integer/integer_property" /> + </service> + </application> + + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.servicestests.apps.packageparserapp" /> +</manifest> diff --git a/services/tests/servicestests/test-apps/PackageParserApp/res/values/values.xml b/services/tests/servicestests/test-apps/PackageParserApp/res/values/values.xml index 6a4cc653494f..67ecf66f9c6b 100644 --- a/services/tests/servicestests/test-apps/PackageParserApp/res/values/values.xml +++ b/services/tests/servicestests/test-apps/PackageParserApp/res/values/values.xml @@ -16,4 +16,10 @@ <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <bool name="config_isIsolated">true</bool> -</resources>
\ No newline at end of file + <bool name="boolean_property">true</bool> + <color name="color_property">#00FF00</color> + <item name="float_property" format="float" type="dimen">2.718</item> + <dimen name="dimen_property">23dp</dimen> + <integer name="integer_property">123</integer> + <string name="string_property">giraffe</string> +</resources> diff --git a/services/tests/servicestests/test-apps/PackageParserApp/res/xml/xml_property.xml b/services/tests/servicestests/test-apps/PackageParserApp/res/xml/xml_property.xml new file mode 100644 index 000000000000..588db8dfa0c1 --- /dev/null +++ b/services/tests/servicestests/test-apps/PackageParserApp/res/xml/xml_property.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<paths> + <external-path path="Android/data/" name="files_root" /> + <external-path path="." name="external_storage_root" /> +</paths> diff --git a/services/tests/servicestests/test-apps/PackageParserApp/src/com/android/servicestests/apps/packageparserapp/MyProvider.java b/services/tests/servicestests/test-apps/PackageParserApp/src/com/android/servicestests/apps/packageparserapp/MyProvider.java new file mode 100644 index 000000000000..6627166969f3 --- /dev/null +++ b/services/tests/servicestests/test-apps/PackageParserApp/src/com/android/servicestests/apps/packageparserapp/MyProvider.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.servicestests.apps.packageparserapp; + +import android.content.ContentProvider; +import android.content.ContentValues; +import android.database.Cursor; +import android.net.Uri; + +public class MyProvider extends ContentProvider { + + @Override + public boolean onCreate() { + return true; + } + + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, + String sortOrder) { + return null; + } + + @Override + public String getType(Uri uri) { + return "text/plain"; + } + + @Override + public Uri insert(Uri uri, ContentValues values) { + return null; + } + + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + return 0; + } + + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + return 0; + } + +} diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java index 9766cb546616..00f706b13c58 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java @@ -15,6 +15,7 @@ */ package com.android.server.notification; +import static android.app.Notification.FLAG_BUBBLE; import static android.app.Notification.GROUP_ALERT_ALL; import static android.app.Notification.GROUP_ALERT_CHILDREN; import static android.app.Notification.GROUP_ALERT_SUMMARY; @@ -38,6 +39,7 @@ import static org.mockito.Matchers.anyObject; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.argThat; import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.timeout; @@ -50,9 +52,11 @@ import android.app.Notification; import android.app.Notification.Builder; import android.app.NotificationChannel; import android.app.NotificationManager; +import android.app.PendingIntent; import android.content.Context; import android.content.pm.PackageManager; import android.graphics.Color; +import android.graphics.drawable.Icon; import android.media.AudioAttributes; import android.media.AudioManager; import android.net.Uri; @@ -1596,6 +1600,77 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { assertTrue(interrupter.isInterruptive()); } + @Test + public void testBubbleSuppressedNotificationDoesntMakeSound() { + Notification.BubbleMetadata metadata = new Notification.BubbleMetadata.Builder( + mock(PendingIntent.class), mock(Icon.class)) + .build(); + + NotificationRecord record = getBuzzyNotification(); + metadata.setFlags(Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION); + record.getNotification().setBubbleMetadata(metadata); + record.setAllowBubble(true); + record.getNotification().flags |= FLAG_BUBBLE; + record.isUpdate = true; + record.setInterruptive(false); + + mService.buzzBeepBlinkLocked(record); + verifyNeverVibrate(); + } + + @Test + public void testOverflowBubbleSuppressedNotificationDoesntMakeSound() { + Notification.BubbleMetadata metadata = new Notification.BubbleMetadata.Builder( + mock(PendingIntent.class), mock(Icon.class)) + .build(); + + NotificationRecord record = getBuzzyNotification(); + metadata.setFlags(Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION); + record.getNotification().setBubbleMetadata(metadata); + record.setFlagBubbleRemoved(true); + record.setAllowBubble(true); + record.isUpdate = true; + record.setInterruptive(false); + + mService.buzzBeepBlinkLocked(record); + verifyNeverVibrate(); + } + + @Test + public void testBubbleUpdateMakesSound() { + Notification.BubbleMetadata metadata = new Notification.BubbleMetadata.Builder( + mock(PendingIntent.class), mock(Icon.class)) + .build(); + + NotificationRecord record = getBuzzyNotification(); + record.getNotification().setBubbleMetadata(metadata); + record.setAllowBubble(true); + record.getNotification().flags |= FLAG_BUBBLE; + record.isUpdate = true; + record.setInterruptive(true); + + mService.buzzBeepBlinkLocked(record); + verifyVibrate(1); + } + + @Test + public void testNewBubbleSuppressedNotifMakesSound() { + Notification.BubbleMetadata metadata = new Notification.BubbleMetadata.Builder( + mock(PendingIntent.class), mock(Icon.class)) + .build(); + + NotificationRecord record = getBuzzyNotification(); + metadata.setFlags(Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION); + record.getNotification().setBubbleMetadata(metadata); + record.setAllowBubble(true); + record.getNotification().flags |= FLAG_BUBBLE; + record.isUpdate = false; + record.setInterruptive(true); + + mService.buzzBeepBlinkLocked(record); + verifyVibrate(1); + } + static class VibrateRepeatMatcher implements ArgumentMatcher<VibrationEffect> { private final int mRepeatIndex; diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml index 7f4f3dd58812..0ed037c7e70a 100644 --- a/services/tests/wmtests/AndroidManifest.xml +++ b/services/tests/wmtests/AndroidManifest.xml @@ -65,8 +65,6 @@ android:turnScreenOn="true" /> <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ResumeWhilePausingActivity" android:resumeWhilePausing="true"/> - <activity android:name="com.android.server.wm.ScreenDecorWindowTests$TestActivity" - android:showWhenLocked="true" android:allowEmbedded="true"/> <activity android:name="com.android.server.wm.ActivityLeakTests$DetectLeakActivity" /> </application> diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java index 338ed066e1e4..f1dc098fccf6 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java @@ -31,6 +31,7 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.timeout; import android.app.ActivityOptions; @@ -53,6 +54,7 @@ import org.mockito.ArgumentMatcher; import java.util.Arrays; import java.util.concurrent.TimeUnit; +import java.util.function.ToIntFunction; /** * Tests for the {@link ActivityMetricsLaunchObserver} class. @@ -158,6 +160,41 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase { verifyNoMoreInteractions(mLaunchObserver); } + @Test + public void testLaunchState() { + final ToIntFunction<Boolean> launchTemplate = doRelaunch -> { + clearInvocations(mLaunchObserver); + onActivityLaunched(mTopActivity); + notifyTransitionStarting(mTopActivity); + if (doRelaunch) { + mActivityMetricsLogger.notifyActivityRelaunched(mTopActivity); + } + final ActivityMetricsLogger.TransitionInfoSnapshot info = + notifyWindowsDrawn(mTopActivity); + verifyOnActivityLaunchFinished(mTopActivity); + return info.getLaunchState(); + }; + + final WindowProcessController app = mTopActivity.app; + // Assume that the process is started (ActivityBuilder has mocked the returned value of + // ATMS#getProcessController) but the activity has not attached process. + mTopActivity.app = null; + assertWithMessage("Warm launch").that(launchTemplate.applyAsInt(false /* doRelaunch */)) + .isEqualTo(WaitResult.LAUNCH_STATE_WARM); + + mTopActivity.app = app; + assertWithMessage("Hot launch").that(launchTemplate.applyAsInt(false /* doRelaunch */)) + .isEqualTo(WaitResult.LAUNCH_STATE_HOT); + + assertWithMessage("Relaunch").that(launchTemplate.applyAsInt(true /* doRelaunch */)) + .isEqualTo(WaitResult.LAUNCH_STATE_RELAUNCH); + + mTopActivity.app = null; + doReturn(null).when(mAtm).getProcessController(app.mName, app.mUid); + assertWithMessage("Cold launch").that(launchTemplate.applyAsInt(false /* doRelaunch */)) + .isEqualTo(WaitResult.LAUNCH_STATE_COLD); + } + private void onActivityLaunched(ActivityRecord activity) { onIntentStarted(activity.intent); notifyActivityLaunched(START_SUCCESS, activity); @@ -168,15 +205,10 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase { @Test public void testOnActivityLaunchFinished() { - // Assume that the process is started (ActivityBuilder has mocked the returned value of - // ATMS#getProcessController) but the activity has not attached process. - mTopActivity.app = null; onActivityLaunched(mTopActivity); notifyTransitionStarting(mTopActivity); - final ActivityMetricsLogger.TransitionInfoSnapshot info = notifyWindowsDrawn(mTopActivity); - assertWithMessage("Warm launch").that(info.getLaunchState()) - .isEqualTo(WaitResult.LAUNCH_STATE_WARM); + notifyWindowsDrawn(mTopActivity); verifyOnActivityLaunchFinished(mTopActivity); verifyNoMoreInteractions(mLaunchObserver); @@ -231,8 +263,6 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase { assertWithMessage("Record start source").that(info.sourceType) .isEqualTo(SourceInfo.TYPE_LAUNCHER); assertWithMessage("Record event time").that(info.sourceEventDelayMs).isAtLeast(10); - assertWithMessage("Hot launch").that(info.getLaunchState()) - .isEqualTo(WaitResult.LAUNCH_STATE_HOT); verifyAsync(mLaunchObserver).onReportFullyDrawn(eqProto(mTopActivity), anyLong()); verifyOnActivityLaunchFinished(mTopActivity); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 35ce9234ed4c..53ade0ea64be 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -16,10 +16,8 @@ package com.android.server.wm; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; -import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION; import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS; @@ -125,61 +123,65 @@ import org.mockito.invocation.InvocationOnMock; @Presubmit @RunWith(WindowTestRunner.class) public class ActivityRecordTests extends WindowTestsBase { - private Task mStack; - private Task mTask; - private ActivityRecord mActivity; @Before public void setUp() throws Exception { - mTask = new TaskBuilder(mSupervisor) - .setCreateParentTask(true).setCreateActivity(true).build(); - mStack = mTask.getRootTask(); - mActivity = mTask.getTopNonFinishingActivity(); - setBooted(mAtm); } @Test public void testStackCleanupOnClearingTask() { - mActivity.onParentChanged(null /*newParent*/, mActivity.getTask()); - verify(mStack, times(1)).cleanUpActivityReferences(any()); + final ActivityRecord activity = createActivityWith2LevelTask(); + final Task task = activity.getTask(); + final Task rootTask = activity.getRootTask(); + activity.onParentChanged(null /*newParent*/, task); + verify(rootTask, times(1)).cleanUpActivityReferences(any()); } @Test public void testStackCleanupOnActivityRemoval() { - mTask.removeChild(mActivity); - verify(mStack, times(1)).cleanUpActivityReferences(any()); + final ActivityRecord activity = createActivityWith2LevelTask(); + final Task task = activity.getTask(); + final Task rootTask = activity.getRootTask(); + task.removeChild(activity); + verify(rootTask, times(1)).cleanUpActivityReferences(any()); } @Test public void testStackCleanupOnTaskRemoval() { - mStack.removeChild(mTask, null /*reason*/); - // Stack should be gone on task removal. - assertNull(mAtm.mRootWindowContainer.getStack(mStack.mTaskId)); + final ActivityRecord activity = createActivityWith2LevelTask(); + final Task task = activity.getTask(); + final Task rootTask = activity.getRootTask(); + rootTask.removeChild(task, null /*reason*/); + // parentTask should be gone on task removal. + assertNull(mAtm.mRootWindowContainer.getStack(rootTask.mTaskId)); } @Test public void testRemoveChildWithOverlayActivity() { - final ActivityRecord overlayActivity = - new ActivityBuilder(mAtm).setTask(mTask).build(); + final ActivityRecord activity = createActivityWithTask(); + final Task task = activity.getTask(); + final ActivityRecord overlayActivity = new ActivityBuilder(mAtm).setTask(task).build(); overlayActivity.setTaskOverlay(true); - final ActivityRecord overlayActivity2 = - new ActivityBuilder(mAtm).setTask(mTask).build(); + final ActivityRecord overlayActivity2 = new ActivityBuilder(mAtm).setTask(task).build(); overlayActivity2.setTaskOverlay(true); - mTask.removeChild(overlayActivity2, "test"); + task.removeChild(overlayActivity2, "test"); verify(mSupervisor, never()).removeTask(any(), anyBoolean(), anyBoolean(), any()); } @Test public void testNoCleanupMovingActivityInSameStack() { - final Task newTask = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(mStack).build(); - mActivity.reparent(newTask, 0, null /*reason*/); - verify(mStack, times(0)).cleanUpActivityReferences(any()); + final ActivityRecord activity = createActivityWith2LevelTask(); + final Task rootTask = activity.getRootTask(); + final Task newTask = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(rootTask).build(); + activity.reparent(newTask, 0, null /*reason*/); + verify(rootTask, times(0)).cleanUpActivityReferences(any()); } @Test public void testPausingWhenVisibleFromStopped() throws Exception { + final ActivityRecord activity = createActivityWithTask(); final MutableBoolean pauseFound = new MutableBoolean(false); doAnswer((InvocationOnMock invocationOnMock) -> { final ClientTransaction transaction = invocationOnMock.getArgument(0); @@ -187,49 +189,50 @@ public class ActivityRecordTests extends WindowTestsBase { pauseFound.value = true; } return null; - }).when(mActivity.app.getThread()).scheduleTransaction(any()); + }).when(activity.app.getThread()).scheduleTransaction(any()); - mActivity.setState(STOPPED, "testPausingWhenVisibleFromStopped"); + activity.setState(STOPPED, "testPausingWhenVisibleFromStopped"); // The activity is in the focused stack so it should be resumed. - mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */); - assertTrue(mActivity.isState(RESUMED)); + activity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */); + assertTrue(activity.isState(RESUMED)); assertFalse(pauseFound.value); // Make the activity non focusable - mActivity.setState(STOPPED, "testPausingWhenVisibleFromStopped"); - doReturn(false).when(mActivity).isFocusable(); + activity.setState(STOPPED, "testPausingWhenVisibleFromStopped"); + doReturn(false).when(activity).isFocusable(); // If the activity is not focusable, it should move to paused. - mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */); - assertTrue(mActivity.isState(PAUSING)); + activity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */); + assertTrue(activity.isState(PAUSING)); assertTrue(pauseFound.value); // Make sure that the state does not change for current non-stopping states. - mActivity.setState(INITIALIZING, "testPausingWhenVisibleFromStopped"); - doReturn(true).when(mActivity).isFocusable(); + activity.setState(INITIALIZING, "testPausingWhenVisibleFromStopped"); + doReturn(true).when(activity).isFocusable(); - mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */); + activity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */); - assertTrue(mActivity.isState(INITIALIZING)); + assertTrue(activity.isState(INITIALIZING)); // Make sure the state does not change if we are not the current top activity. - mActivity.setState(STOPPED, "testPausingWhenVisibleFromStopped behind"); + activity.setState(STOPPED, "testPausingWhenVisibleFromStopped behind"); - final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); - mStack.mTranslucentActivityWaiting = topActivity; - mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */); - assertTrue(mActivity.isState(STARTED)); + final Task task = activity.getTask(); + final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build(); + task.mTranslucentActivityWaiting = topActivity; + activity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */); + assertTrue(activity.isState(STARTED)); - mStack.mTranslucentActivityWaiting = null; + task.mTranslucentActivityWaiting = null; topActivity.setOccludesParent(false); - mActivity.setState(STOPPED, "testPausingWhenVisibleFromStopped behind non-opaque"); - mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */); - assertTrue(mActivity.isState(STARTED)); + activity.setState(STOPPED, "testPausingWhenVisibleFromStopped behind non-opaque"); + activity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */); + assertTrue(activity.isState(STARTED)); } - private void ensureActivityConfiguration() { - mActivity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */); + private void ensureActivityConfiguration(ActivityRecord activity) { + activity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */); } @Test @@ -245,136 +248,145 @@ public class ActivityRecordTests extends WindowTestsBase { @Test public void testsApplyOptionsLocked() { + final ActivityRecord activity = createActivityWithTask(); ActivityOptions activityOptions = ActivityOptions.makeBasic(); // Set and apply options for ActivityRecord. Pending options should be cleared - mActivity.updateOptionsLocked(activityOptions); - mActivity.applyOptionsLocked(); - assertNull(mActivity.pendingOptions); + activity.updateOptionsLocked(activityOptions); + activity.applyOptionsLocked(); + assertNull(activity.pendingOptions); // Set options for two ActivityRecords in same Task. Apply one ActivityRecord options. // Pending options should be cleared for both ActivityRecords - ActivityRecord activity2 = new ActivityBuilder(mAtm).setTask(mTask).build(); + ActivityRecord activity2 = new ActivityBuilder(mAtm).setTask(activity.getTask()).build(); activity2.updateOptionsLocked(activityOptions); - mActivity.updateOptionsLocked(activityOptions); - mActivity.applyOptionsLocked(); - assertNull(mActivity.pendingOptions); + activity.updateOptionsLocked(activityOptions); + activity.applyOptionsLocked(); + assertNull(activity.pendingOptions); assertNull(activity2.pendingOptions); // Set options for two ActivityRecords in separate Tasks. Apply one ActivityRecord options. // Pending options should be cleared for only ActivityRecord that was applied - Task task2 = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(mStack).build(); - activity2 = new ActivityBuilder(mAtm).setTask(task2).build(); + activity2 = new ActivityBuilder(mAtm).setCreateTask(true).build(); activity2.updateOptionsLocked(activityOptions); - mActivity.updateOptionsLocked(activityOptions); - mActivity.applyOptionsLocked(); - assertNull(mActivity.pendingOptions); + activity.updateOptionsLocked(activityOptions); + activity.applyOptionsLocked(); + assertNull(activity.pendingOptions); assertNotNull(activity2.pendingOptions); } @Test public void testNewOverrideConfigurationIncrementsSeq() { + final ActivityRecord activity = createActivityWithTask(); final Configuration newConfig = new Configuration(); - final int prevSeq = mActivity.getMergedOverrideConfiguration().seq; - mActivity.onRequestedOverrideConfigurationChanged(newConfig); - assertEquals(prevSeq + 1, mActivity.getMergedOverrideConfiguration().seq); + final int prevSeq = activity.getMergedOverrideConfiguration().seq; + activity.onRequestedOverrideConfigurationChanged(newConfig); + assertEquals(prevSeq + 1, activity.getMergedOverrideConfiguration().seq); } @Test public void testNewParentConfigurationIncrementsSeq() { + final ActivityRecord activity = createActivityWithTask(); + final Task task = activity.getTask(); final Configuration newConfig = new Configuration( - mTask.getRequestedOverrideConfiguration()); + task.getRequestedOverrideConfiguration()); newConfig.orientation = newConfig.orientation == ORIENTATION_PORTRAIT ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT; - final int prevSeq = mActivity.getMergedOverrideConfiguration().seq; - mTask.onRequestedOverrideConfigurationChanged(newConfig); - assertEquals(prevSeq + 1, mActivity.getMergedOverrideConfiguration().seq); + final int prevSeq = activity.getMergedOverrideConfiguration().seq; + task.onRequestedOverrideConfigurationChanged(newConfig); + assertEquals(prevSeq + 1, activity.getMergedOverrideConfiguration().seq); } @Test public void testSetsRelaunchReason_NotDragResizing() { - mActivity.setState(Task.ActivityState.RESUMED, "Testing"); + final ActivityRecord activity = createActivityWithTask(); + final Task task = activity.getTask(); + activity.setState(Task.ActivityState.RESUMED, "Testing"); - mTask.onRequestedOverrideConfigurationChanged(mTask.getConfiguration()); - mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(), - mActivity.getConfiguration())); + task.onRequestedOverrideConfigurationChanged(task.getConfiguration()); + activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(), + activity.getConfiguration())); - mActivity.info.configChanges &= ~CONFIG_ORIENTATION; - final Configuration newConfig = new Configuration(mTask.getConfiguration()); + activity.info.configChanges &= ~CONFIG_ORIENTATION; + final Configuration newConfig = new Configuration(task.getConfiguration()); newConfig.orientation = newConfig.orientation == ORIENTATION_PORTRAIT ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT; - mTask.onRequestedOverrideConfigurationChanged(newConfig); + task.onRequestedOverrideConfigurationChanged(newConfig); - mActivity.mRelaunchReason = ActivityTaskManagerService.RELAUNCH_REASON_NONE; + activity.mRelaunchReason = ActivityTaskManagerService.RELAUNCH_REASON_NONE; - ensureActivityConfiguration(); + ensureActivityConfiguration(activity); assertEquals(ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE, - mActivity.mRelaunchReason); + activity.mRelaunchReason); } @Test public void testSetsRelaunchReason_DragResizing() { - mActivity.setState(Task.ActivityState.RESUMED, "Testing"); + final ActivityRecord activity = createActivityWithTask(); + final Task task = activity.getTask(); + activity.setState(Task.ActivityState.RESUMED, "Testing"); - mTask.onRequestedOverrideConfigurationChanged(mTask.getConfiguration()); - mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(), - mActivity.getConfiguration())); + task.onRequestedOverrideConfigurationChanged(task.getConfiguration()); + activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(), + activity.getConfiguration())); - mActivity.info.configChanges &= ~CONFIG_ORIENTATION; - final Configuration newConfig = new Configuration(mTask.getConfiguration()); + activity.info.configChanges &= ~CONFIG_ORIENTATION; + final Configuration newConfig = new Configuration(task.getConfiguration()); newConfig.orientation = newConfig.orientation == ORIENTATION_PORTRAIT ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT; - mTask.onRequestedOverrideConfigurationChanged(newConfig); + task.onRequestedOverrideConfigurationChanged(newConfig); - doReturn(true).when(mTask).isDragResizing(); + doReturn(true).when(task).isDragResizing(); - mActivity.mRelaunchReason = ActivityTaskManagerService.RELAUNCH_REASON_NONE; + activity.mRelaunchReason = ActivityTaskManagerService.RELAUNCH_REASON_NONE; - ensureActivityConfiguration(); + ensureActivityConfiguration(activity); assertEquals(ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE, - mActivity.mRelaunchReason); + activity.mRelaunchReason); } @Test public void testSetsRelaunchReason_NonResizeConfigChanges() { - mActivity.setState(Task.ActivityState.RESUMED, "Testing"); + final ActivityRecord activity = createActivityWithTask(); + final Task task = activity.getTask(); + activity.setState(Task.ActivityState.RESUMED, "Testing"); - mTask.onRequestedOverrideConfigurationChanged(mTask.getConfiguration()); - mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(), - mActivity.getConfiguration())); + task.onRequestedOverrideConfigurationChanged(task.getConfiguration()); + activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(), + activity.getConfiguration())); - mActivity.info.configChanges &= ~ActivityInfo.CONFIG_FONT_SCALE; - final Configuration newConfig = new Configuration(mTask.getConfiguration()); + activity.info.configChanges &= ~ActivityInfo.CONFIG_FONT_SCALE; + final Configuration newConfig = new Configuration(task.getConfiguration()); newConfig.fontScale = 5; - mTask.onRequestedOverrideConfigurationChanged(newConfig); + task.onRequestedOverrideConfigurationChanged(newConfig); - mActivity.mRelaunchReason = + activity.mRelaunchReason = ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE; - ensureActivityConfiguration(); + ensureActivityConfiguration(activity); assertEquals(ActivityTaskManagerService.RELAUNCH_REASON_NONE, - mActivity.mRelaunchReason); + activity.mRelaunchReason); } @Test public void testSetRequestedOrientationUpdatesConfiguration() throws Exception { - mActivity = new ActivityBuilder(mAtm) - .setTask(mTask) + final ActivityRecord activity = new ActivityBuilder(mAtm) + .setCreateTask(true) .setConfigChanges(CONFIG_ORIENTATION | CONFIG_SCREEN_LAYOUT) .build(); - mActivity.setState(Task.ActivityState.RESUMED, "Testing"); + activity.setState(Task.ActivityState.RESUMED, "Testing"); - mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(), - mActivity.getConfiguration())); + activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(), + activity.getConfiguration())); - final Configuration newConfig = new Configuration(mActivity.getConfiguration()); + final Configuration newConfig = new Configuration(activity.getConfiguration()); final int shortSide = Math.min(newConfig.screenWidthDp, newConfig.screenHeightDp); final int longSide = Math.max(newConfig.screenWidthDp, newConfig.screenHeightDp); if (newConfig.orientation == ORIENTATION_PORTRAIT) { @@ -388,7 +400,7 @@ public class ActivityRecordTests extends WindowTestsBase { } // Mimic the behavior that display doesn't handle app's requested orientation. - final DisplayContent dc = mTask.getDisplayContent(); + final DisplayContent dc = activity.getTask().getDisplayContent(); doReturn(false).when(dc).onDescendantOrientationChanged(any(), any()); doReturn(false).when(dc).handlesOrientationChangeFromDescendant(); @@ -404,24 +416,26 @@ public class ActivityRecordTests extends WindowTestsBase { throw new IllegalStateException("Orientation in new config should be either" + "landscape or portrait."); } - mActivity.setRequestedOrientation(requestedOrientation); + activity.setRequestedOrientation(requestedOrientation); final ActivityConfigurationChangeItem expected = ActivityConfigurationChangeItem.obtain(newConfig); - verify(mAtm.getLifecycleManager()).scheduleTransaction(eq(mActivity.app.getThread()), - eq(mActivity.appToken), eq(expected)); + verify(mAtm.getLifecycleManager()).scheduleTransaction(eq(activity.app.getThread()), + eq(activity.appToken), eq(expected)); } @Test public void ignoreRequestedOrientationInFreeformWindows() { - mStack.setWindowingMode(WINDOWING_MODE_FREEFORM); + final ActivityRecord activity = createActivityWithTask(); + final Task task = activity.getTask(); + task.setWindowingMode(WINDOWING_MODE_FREEFORM); final Rect stableRect = new Rect(); - mStack.mDisplayContent.getStableRect(stableRect); + task.mDisplayContent.getStableRect(stableRect); // Carve out non-decor insets from stableRect final Rect insets = new Rect(); - final DisplayInfo displayInfo = mStack.mDisplayContent.getDisplayInfo(); - final DisplayPolicy policy = mStack.mDisplayContent.getDisplayPolicy(); + final DisplayInfo displayInfo = task.mDisplayContent.getDisplayInfo(); + final DisplayPolicy policy = task.mDisplayContent.getDisplayPolicy(); policy.getNonDecorInsetsLw(displayInfo.rotation, displayInfo.logicalWidth, displayInfo.logicalHeight, displayInfo.displayCutout, insets); policy.convertNonDecorInsetsToStableInsets(insets, displayInfo.rotation); @@ -440,27 +454,30 @@ public class ActivityRecordTests extends WindowTestsBase { bounds.left = stableRect.left + (stableRect.width() - newWidth) / 2; bounds.right = bounds.left + newWidth; } - mTask.setBounds(bounds); + task.setBounds(bounds); // Requests orientation that's different from its bounds. - mActivity.setRequestedOrientation( + activity.setRequestedOrientation( isScreenPortrait ? SCREEN_ORIENTATION_PORTRAIT : SCREEN_ORIENTATION_LANDSCAPE); // Asserts it has orientation derived from bounds. assertEquals(isScreenPortrait ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT, - mActivity.getConfiguration().orientation); + activity.getConfiguration().orientation); } @Test public void ignoreRequestedOrientationInSplitWindows() { - mStack.setWindowingMode(WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); + final ActivityRecord activity = createActivityWith2LevelTask(); + final Task task = activity.getTask(); + final Task rootTask = activity.getRootTask(); + rootTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); final Rect stableRect = new Rect(); - mStack.mDisplayContent.getStableRect(stableRect); + rootTask.mDisplayContent.getStableRect(stableRect); // Carve out non-decor insets from stableRect final Rect insets = new Rect(); - final DisplayInfo displayInfo = mStack.mDisplayContent.getDisplayInfo(); - final DisplayPolicy policy = mStack.mDisplayContent.getDisplayPolicy(); + final DisplayInfo displayInfo = rootTask.mDisplayContent.getDisplayInfo(); + final DisplayPolicy policy = rootTask.mDisplayContent.getDisplayPolicy(); policy.getNonDecorInsetsLw(displayInfo.rotation, displayInfo.logicalWidth, displayInfo.logicalHeight, displayInfo.displayCutout, insets); policy.convertNonDecorInsetsToStableInsets(insets, displayInfo.rotation); @@ -479,85 +496,91 @@ public class ActivityRecordTests extends WindowTestsBase { bounds.left = stableRect.left + (stableRect.width() - newWidth) / 2; bounds.right = bounds.left + newWidth; } - mTask.setBounds(bounds); + task.setBounds(bounds); // Requests orientation that's different from its bounds. - mActivity.setRequestedOrientation( + activity.setRequestedOrientation( isScreenPortrait ? SCREEN_ORIENTATION_PORTRAIT : SCREEN_ORIENTATION_LANDSCAPE); // Asserts it has orientation derived from bounds. assertEquals(isScreenPortrait ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT, - mActivity.getConfiguration().orientation); + activity.getConfiguration().orientation); } @Test public void testShouldMakeActive_deferredResume() { - mActivity.setState(Task.ActivityState.STOPPED, "Testing"); + final ActivityRecord activity = createActivityWithTask(); + activity.setState(Task.ActivityState.STOPPED, "Testing"); mSupervisor.beginDeferResume(); - assertEquals(false, mActivity.shouldMakeActive(null /* activeActivity */)); + assertEquals(false, activity.shouldMakeActive(null /* activeActivity */)); mSupervisor.endDeferResume(); - assertEquals(true, mActivity.shouldMakeActive(null /* activeActivity */)); + assertEquals(true, activity.shouldMakeActive(null /* activeActivity */)); } @Test public void testShouldMakeActive_nonTopVisible() { - ActivityRecord finishingActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); + final ActivityRecord activity = createActivityWithTask(); + final Task task = activity.getTask(); + ActivityRecord finishingActivity = new ActivityBuilder(mAtm).setTask(task).build(); finishingActivity.finishing = true; - ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); - mActivity.setState(Task.ActivityState.STOPPED, "Testing"); + ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build(); + activity.setState(Task.ActivityState.STOPPED, "Testing"); - assertEquals(false, mActivity.shouldMakeActive(null /* activeActivity */)); + assertEquals(false, activity.shouldMakeActive(null /* activeActivity */)); } @Test public void testShouldResume_stackVisibility() { - mActivity.setState(Task.ActivityState.STOPPED, "Testing"); - spyOn(mStack); + final ActivityRecord activity = createActivityWithTask(); + final Task task = activity.getTask(); + activity.setState(Task.ActivityState.STOPPED, "Testing"); - doReturn(TASK_VISIBILITY_VISIBLE).when(mStack).getVisibility(null); - assertEquals(true, mActivity.shouldResumeActivity(null /* activeActivity */)); + doReturn(TASK_VISIBILITY_VISIBLE).when(task).getVisibility(null); + assertEquals(true, activity.shouldResumeActivity(null /* activeActivity */)); - doReturn(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT).when(mStack).getVisibility(null); - assertEquals(false, mActivity.shouldResumeActivity(null /* activeActivity */)); + doReturn(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT).when(task).getVisibility(null); + assertEquals(false, activity.shouldResumeActivity(null /* activeActivity */)); - doReturn(TASK_VISIBILITY_INVISIBLE).when(mStack).getVisibility(null); - assertEquals(false, mActivity.shouldResumeActivity(null /* activeActivity */)); + doReturn(TASK_VISIBILITY_INVISIBLE).when(task).getVisibility(null); + assertEquals(false, activity.shouldResumeActivity(null /* activeActivity */)); } @Test public void testShouldResumeOrPauseWithResults() { - mActivity.setState(Task.ActivityState.STOPPED, "Testing"); - spyOn(mStack); + final ActivityRecord activity = createActivityWithTask(); + final Task task = activity.getTask(); + activity.setState(Task.ActivityState.STOPPED, "Testing"); - ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); - mActivity.addResultLocked(topActivity, "resultWho", 0, 0, new Intent()); + ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build(); + activity.addResultLocked(topActivity, "resultWho", 0, 0, new Intent()); topActivity.finishing = true; - doReturn(TASK_VISIBILITY_VISIBLE).when(mStack).getVisibility(null); - assertEquals(true, mActivity.shouldResumeActivity(null /* activeActivity */)); - assertEquals(false, mActivity.shouldPauseActivity(null /*activeActivity */)); + doReturn(TASK_VISIBILITY_VISIBLE).when(task).getVisibility(null); + assertEquals(true, activity.shouldResumeActivity(null /* activeActivity */)); + assertEquals(false, activity.shouldPauseActivity(null /*activeActivity */)); } @Test public void testPushConfigurationWhenLaunchTaskBehind() throws Exception { - mActivity = new ActivityBuilder(mAtm) - .setTask(mTask) + final ActivityRecord activity = new ActivityBuilder(mAtm) + .setCreateTask(true) .setLaunchTaskBehind(true) .setConfigChanges(CONFIG_ORIENTATION | CONFIG_SCREEN_LAYOUT) .build(); - mActivity.setState(Task.ActivityState.STOPPED, "Testing"); + final Task task = activity.getTask(); + activity.setState(Task.ActivityState.STOPPED, "Testing"); final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); try { doReturn(false).when(stack).isTranslucent(any()); - assertTrue(mStack.shouldBeVisible(null /* starting */)); + assertTrue(task.shouldBeVisible(null /* starting */)); - mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(), - mActivity.getConfiguration())); + activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(), + activity.getConfiguration())); - final Configuration newConfig = new Configuration(mActivity.getConfiguration()); + final Configuration newConfig = new Configuration(activity.getConfiguration()); final int shortSide = Math.min(newConfig.screenWidthDp, newConfig.screenHeightDp); final int longSide = Math.max(newConfig.screenWidthDp, newConfig.screenHeightDp); if (newConfig.orientation == ORIENTATION_PORTRAIT) { @@ -570,15 +593,15 @@ public class ActivityRecordTests extends WindowTestsBase { newConfig.screenHeightDp = longSide; } - mTask.onConfigurationChanged(newConfig); + task.onConfigurationChanged(newConfig); - mActivity.ensureActivityConfiguration(0 /* globalChanges */, + activity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */, true /* ignoreStopState */); final ActivityConfigurationChangeItem expected = ActivityConfigurationChangeItem.obtain(newConfig); verify(mAtm.getLifecycleManager()).scheduleTransaction( - eq(mActivity.app.getThread()), eq(mActivity.appToken), eq(expected)); + eq(activity.app.getThread()), eq(activity.appToken), eq(expected)); } finally { stack.getDisplayArea().removeChild(stack); } @@ -586,16 +609,18 @@ public class ActivityRecordTests extends WindowTestsBase { @Test public void testShouldStartWhenMakeClientActive() { - ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); + final ActivityRecord activity = createActivityWithTask(); + ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(activity.getTask()).build(); topActivity.setOccludesParent(false); - mActivity.setState(Task.ActivityState.STOPPED, "Testing"); - mActivity.setVisibility(true); - mActivity.makeActiveIfNeeded(null /* activeActivity */); - assertEquals(STARTED, mActivity.getState()); + activity.setState(Task.ActivityState.STOPPED, "Testing"); + activity.setVisibility(true); + activity.makeActiveIfNeeded(null /* activeActivity */); + assertEquals(STARTED, activity.getState()); } @Test public void testTakeOptions() { + final ActivityRecord activity = createActivityWithTask(); ActivityOptions opts = ActivityOptions.makeRemoteAnimation( new RemoteAnimationAdapter(new Stub() { @@ -611,13 +636,13 @@ public class ActivityRecordTests extends WindowTestsBase { } }, 0, 0)); - mActivity.updateOptionsLocked(opts); - assertNotNull(mActivity.takeOptionsLocked(true /* fromClient */)); - assertNotNull(mActivity.pendingOptions); + activity.updateOptionsLocked(opts); + assertNotNull(activity.takeOptionsLocked(true /* fromClient */)); + assertNotNull(activity.pendingOptions); - mActivity.updateOptionsLocked(ActivityOptions.makeBasic()); - assertNotNull(mActivity.takeOptionsLocked(false /* fromClient */)); - assertNull(mActivity.pendingOptions); + activity.updateOptionsLocked(ActivityOptions.makeBasic()); + assertNotNull(activity.takeOptionsLocked(false /* fromClient */)); + assertNull(activity.pendingOptions); } @Test @@ -626,7 +651,7 @@ public class ActivityRecordTests extends WindowTestsBase { Resources.getSystem().getString(R.string.config_chooserActivity)); ActivityRecord chooserActivity = new ActivityBuilder(mAtm).setComponent( chooserComponent).build(); - assertThat(mActivity.canLaunchHomeActivity(NOBODY_UID, chooserActivity)).isTrue(); + assertThat(chooserActivity.canLaunchHomeActivity(NOBODY_UID, chooserActivity)).isTrue(); } /** @@ -635,49 +660,52 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testHasSavedState() { - assertTrue(mActivity.hasSavedState()); + final ActivityRecord activity = createActivityWithTask(); + assertTrue(activity.hasSavedState()); - ActivityRecord.activityResumedLocked(mActivity.appToken); - assertFalse(mActivity.hasSavedState()); - assertNull(mActivity.getSavedState()); + ActivityRecord.activityResumedLocked(activity.appToken); + assertFalse(activity.hasSavedState()); + assertNull(activity.getSavedState()); } /** Verify the behavior of {@link ActivityRecord#setSavedState(Bundle)}. */ @Test public void testUpdateSavedState() { - mActivity.setSavedState(null /* savedState */); - assertFalse(mActivity.hasSavedState()); - assertNull(mActivity.getSavedState()); + final ActivityRecord activity = createActivityWithTask(); + activity.setSavedState(null /* savedState */); + assertFalse(activity.hasSavedState()); + assertNull(activity.getSavedState()); final Bundle savedState = new Bundle(); savedState.putString("test", "string"); - mActivity.setSavedState(savedState); - assertTrue(mActivity.hasSavedState()); - assertEquals(savedState, mActivity.getSavedState()); + activity.setSavedState(savedState); + assertTrue(activity.hasSavedState()); + assertEquals(savedState, activity.getSavedState()); } /** Verify the correct updates of saved state when activity client reports stop. */ @Test public void testUpdateSavedState_activityStopped() { + final ActivityRecord activity = createActivityWithTask(); final Bundle savedState = new Bundle(); savedState.putString("test", "string"); final PersistableBundle persistentSavedState = new PersistableBundle(); persistentSavedState.putString("persist", "string"); // Set state to STOPPING, or ActivityRecord#activityStoppedLocked() call will be ignored. - mActivity.setState(STOPPING, "test"); - mActivity.activityStopped(savedState, persistentSavedState, "desc"); - assertTrue(mActivity.hasSavedState()); - assertEquals(savedState, mActivity.getSavedState()); - assertEquals(persistentSavedState, mActivity.getPersistentSavedState()); + activity.setState(STOPPING, "test"); + activity.activityStopped(savedState, persistentSavedState, "desc"); + assertTrue(activity.hasSavedState()); + assertEquals(savedState, activity.getSavedState()); + assertEquals(persistentSavedState, activity.getPersistentSavedState()); // Sending 'null' for saved state can only happen due to timeout, so previously stored saved // states should not be overridden. - mActivity.setState(STOPPING, "test"); - mActivity.activityStopped(null /* savedState */, null /* persistentSavedState */, "desc"); - assertTrue(mActivity.hasSavedState()); - assertEquals(savedState, mActivity.getSavedState()); - assertEquals(persistentSavedState, mActivity.getPersistentSavedState()); + activity.setState(STOPPING, "test"); + activity.activityStopped(null /* savedState */, null /* persistentSavedState */, "desc"); + assertTrue(activity.hasSavedState()); + assertEquals(savedState, activity.getSavedState()); + assertEquals(persistentSavedState, activity.getPersistentSavedState()); } /** @@ -686,19 +714,20 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testFinishActivityIfPossible_cancelled() { + final ActivityRecord activity = createActivityWithTask(); // Mark activity as finishing - mActivity.finishing = true; + activity.finishing = true; assertEquals("Duplicate finish request must be ignored", FINISH_RESULT_CANCELLED, - mActivity.finishIfPossible("test", false /* oomAdj */)); - assertTrue(mActivity.finishing); - assertTrue(mActivity.isInStackLocked()); + activity.finishIfPossible("test", false /* oomAdj */)); + assertTrue(activity.finishing); + assertTrue(activity.isInStackLocked()); // Remove activity from task - mActivity.finishing = false; - mActivity.onParentChanged(null /*newParent*/, mActivity.getTask()); + activity.finishing = false; + activity.onParentChanged(null /*newParent*/, activity.getTask()); assertEquals("Activity outside of task/stack cannot be finished", FINISH_RESULT_CANCELLED, - mActivity.finishIfPossible("test", false /* oomAdj */)); - assertFalse(mActivity.finishing); + activity.finishIfPossible("test", false /* oomAdj */)); + assertFalse(activity.finishing); } /** @@ -707,20 +736,21 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testFinishActivityIfPossible_requested() { - mActivity.finishing = false; + final ActivityRecord activity = createActivityWithTask(); + activity.finishing = false; assertEquals("Currently resumed activity must be prepared removal", FINISH_RESULT_REQUESTED, - mActivity.finishIfPossible("test", false /* oomAdj */)); - assertTrue(mActivity.finishing); - assertTrue(mActivity.isInStackLocked()); + activity.finishIfPossible("test", false /* oomAdj */)); + assertTrue(activity.finishing); + assertTrue(activity.isInStackLocked()); // First request to finish activity must schedule a "destroy" request to the client. // Activity must be removed from history after the client reports back or after timeout. - mActivity.finishing = false; - mActivity.setState(STOPPED, "test"); + activity.finishing = false; + activity.setState(STOPPED, "test"); assertEquals("Activity outside of task/stack cannot be finished", FINISH_RESULT_REQUESTED, - mActivity.finishIfPossible("test", false /* oomAdj */)); - assertTrue(mActivity.finishing); - assertTrue(mActivity.isInStackLocked()); + activity.finishIfPossible("test", false /* oomAdj */)); + assertTrue(activity.finishing); + assertTrue(activity.isInStackLocked()); } /** @@ -728,26 +758,28 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testFinishActivityIfPossible_removed() { + final ActivityRecord activity = createActivityWithTask(); // Prepare the activity record to be ready for immediate removal. It should be invisible and // have no process. Otherwise, request to finish it will send a message to client first. - mActivity.setState(STOPPED, "test"); - mActivity.mVisibleRequested = false; - mActivity.nowVisible = false; + activity.setState(STOPPED, "test"); + activity.mVisibleRequested = false; + activity.nowVisible = false; // Set process to 'null' to allow immediate removal, but don't call mActivity.setProcess() - // this will cause NPE when updating task's process. - mActivity.app = null; + activity.app = null; // Put a visible activity on top, so the finishing activity doesn't have to wait until the // next activity reports idle to destroy it. - final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); + final ActivityRecord topActivity = new ActivityBuilder(mAtm) + .setTask(activity.getTask()).build(); topActivity.mVisibleRequested = true; topActivity.nowVisible = true; topActivity.setState(RESUMED, "test"); assertEquals("Activity outside of task/stack cannot be finished", FINISH_RESULT_REMOVED, - mActivity.finishIfPossible("test", false /* oomAdj */)); - assertTrue(mActivity.finishing); - assertFalse(mActivity.isInStackLocked()); + activity.finishIfPossible("test", false /* oomAdj */)); + assertTrue(activity.finishing); + assertFalse(activity.isInStackLocked()); } /** @@ -756,24 +788,26 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testFinishActivityIfPossible_adjustStackOrder() { - // Prepare the stacks with order (top to bottom): mStack, stack1, stack2. - final Task stack1 = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); - mStack.moveToFront("test"); - // The stack2 is needed here for moving back to simulate the + final ActivityRecord activity = createActivityWithTask(); + final Task task = activity.getTask(); + // Prepare the tasks with order (top to bottom): task, task1, task2. + final Task task1 = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); + task.moveToFront("test"); + // The task2 is needed here for moving back to simulate the // {@link DisplayContent#mPreferredTopFocusableStack} is cleared, so // {@link DisplayContent#getFocusedStack} will rely on the order of focusable-and-visible - // stacks. Then when mActivity is finishing, its stack will be invisible (no running - // activities in the stack) that is the key condition to verify. - final Task stack2 = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); - stack2.moveToBack("test", stack2.getBottomMostTask()); + // tasks. Then when mActivity is finishing, its task will be invisible (no running + // activities in the task) that is the key condition to verify. + final Task task2 = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); + task2.moveToBack("test", task2.getBottomMostTask()); - assertTrue(mStack.isTopStackInDisplayArea()); + assertTrue(task.isTopStackInDisplayArea()); - mActivity.setState(RESUMED, "test"); - mActivity.finishIfPossible(0 /* resultCode */, null /* resultData */, + activity.setState(RESUMED, "test"); + activity.finishIfPossible(0 /* resultCode */, null /* resultData */, null /* resultGrants */, "test", false /* oomAdj */); - assertTrue(stack1.isTopStackInDisplayArea()); + assertTrue(task1.isTopStackInDisplayArea()); } /** @@ -783,22 +817,25 @@ public class ActivityRecordTests extends WindowTestsBase { @Test public void testFinishActivityIfPossible_adjustStackOrderOrganizedRoot() { // Make mStack be a the root task that created by task organizer - mStack.mCreatedByOrganizer = true; + final Task rootableTask = new TaskBuilder(mSupervisor) + .setCreateParentTask(true).setCreateActivity(true).build(); + final Task rootTask = rootableTask.getRootTask(); + rootTask.mCreatedByOrganizer = true; - // Have two tasks (topRootableTask and mTask) as the children of mStack. - ActivityRecord topActivity = new ActivityBuilder(mActivity.mAtmService) + // Have two tasks (topRootableTask and rootableTask) as the children of rootTask. + ActivityRecord topActivity = new ActivityBuilder(mAtm) .setCreateTask(true) - .setParentTask(mStack) + .setParentTask(rootTask) .build(); Task topRootableTask = topActivity.getTask(); topRootableTask.moveToFront("test"); - assertTrue(mStack.isTopStackInDisplayArea()); + assertTrue(rootTask.isTopStackInDisplayArea()); // Finish top activity and verify the next focusable rootable task has adjusted to top. topActivity.setState(RESUMED, "test"); topActivity.finishIfPossible(0 /* resultCode */, null /* resultData */, null /* resultGrants */, "test", false /* oomAdj */); - assertEquals(mTask, mStack.getTopMostTask()); + assertEquals(rootableTask, rootTask.getTopMostTask()); } /** @@ -808,6 +845,8 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testFinishActivityIfPossible_PreferredTopStackChanged() { + final ActivityRecord activity = createActivityWithTask(); + final Task task = activity.getTask(); final ActivityRecord topActivityOnNonTopDisplay = createActivityOnDisplay(true /* defaultDisplay */, null /* process */); Task topRootableTask = topActivityOnNonTopDisplay.getRootTask(); @@ -830,8 +869,8 @@ public class ActivityRecordTests extends WindowTestsBase { topActivityOnNonTopDisplay.setState(RESUMED, "test"); topActivityOnNonTopDisplay.finishIfPossible(0 /* resultCode */, null /* resultData */, null /* resultGrants */, "test", false /* oomAdj */); - assertEquals(mTask, mStack.getTopMostTask()); - assertEquals(mStack, mActivity.getDisplayArea().mPreferredTopFocusableStack); + assertEquals(task, task.getTopMostTask()); + assertEquals(task, activity.getDisplayArea().mPreferredTopFocusableStack); } /** @@ -839,13 +878,14 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testFinishActivityIfPossible_resumedStartsPausing() { - mActivity.finishing = false; - mActivity.setState(RESUMED, "test"); + final ActivityRecord activity = createActivityWithTask(); + activity.finishing = false; + activity.setState(RESUMED, "test"); assertEquals("Currently resumed activity must be paused before removal", - FINISH_RESULT_REQUESTED, mActivity.finishIfPossible("test", false /* oomAdj */)); - assertEquals(PAUSING, mActivity.getState()); - verify(mActivity).setVisibility(eq(false)); - verify(mActivity.mDisplayContent).prepareAppTransition(eq(TRANSIT_CLOSE)); + FINISH_RESULT_REQUESTED, activity.finishIfPossible("test", false /* oomAdj */)); + assertEquals(PAUSING, activity.getState()); + verify(activity).setVisibility(eq(false)); + verify(activity.mDisplayContent).prepareAppTransition(eq(TRANSIT_CLOSE)); } /** @@ -853,14 +893,15 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testFinishActivityIfPossible_nonResumedFinishCompletesImmediately() { + final ActivityRecord activity = createActivityWithTask(); final ActivityState[] states = {INITIALIZING, STARTED, PAUSED, STOPPING, STOPPED}; for (ActivityState state : states) { - mActivity.finishing = false; - mActivity.setState(state, "test"); - reset(mActivity); + activity.finishing = false; + activity.setState(state, "test"); + reset(activity); assertEquals("Finish must be requested", FINISH_RESULT_REQUESTED, - mActivity.finishIfPossible("test", false /* oomAdj */)); - verify(mActivity).completeFinishing(anyString()); + activity.finishIfPossible("test", false /* oomAdj */)); + verify(activity).completeFinishing(anyString()); } } @@ -869,11 +910,12 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testFinishActivityIfPossible_pausing() { - mActivity.finishing = false; - mActivity.setState(PAUSING, "test"); + final ActivityRecord activity = createActivityWithTask(); + activity.finishing = false; + activity.setState(PAUSING, "test"); assertEquals("Finish must be requested", FINISH_RESULT_REQUESTED, - mActivity.finishIfPossible("test", false /* oomAdj */)); - verify(mActivity, never()).completeFinishing(anyString()); + activity.finishIfPossible("test", false /* oomAdj */)); + verify(activity, never()).completeFinishing(anyString()); } /** @@ -882,14 +924,16 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testFinishActivityIfPossible_visibleResumedPreparesAppTransition() { - mActivity.finishing = false; - mActivity.mVisibleRequested = true; - mActivity.setState(RESUMED, "test"); - mActivity.finishIfPossible("test", false /* oomAdj */); + final ActivityRecord activity = createActivityWithTask(); + clearInvocations(activity.mDisplayContent); + activity.finishing = false; + activity.mVisibleRequested = true; + activity.setState(RESUMED, "test"); + activity.finishIfPossible("test", false /* oomAdj */); - verify(mActivity).setVisibility(eq(false)); - verify(mActivity.mDisplayContent).prepareAppTransition(eq(TRANSIT_CLOSE)); - verify(mActivity.mDisplayContent, never()).executeAppTransition(); + verify(activity).setVisibility(eq(false)); + verify(activity.mDisplayContent).prepareAppTransition(eq(TRANSIT_CLOSE)); + verify(activity.mDisplayContent, never()).executeAppTransition(); } /** @@ -897,14 +941,16 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testFinishActivityIfPossible_visibleNotResumedExecutesAppTransition() { - mActivity.finishing = false; - mActivity.mVisibleRequested = true; - mActivity.setState(PAUSED, "test"); - mActivity.finishIfPossible("test", false /* oomAdj */); + final ActivityRecord activity = createActivityWithTask(); + clearInvocations(activity.mDisplayContent); + activity.finishing = false; + activity.mVisibleRequested = true; + activity.setState(PAUSED, "test"); + activity.finishIfPossible("test", false /* oomAdj */); - verify(mActivity, atLeast(1)).setVisibility(eq(false)); - verify(mActivity.mDisplayContent).prepareAppTransition(eq(TRANSIT_CLOSE)); - verify(mActivity.mDisplayContent).executeAppTransition(); + verify(activity, atLeast(1)).setVisibility(eq(false)); + verify(activity.mDisplayContent).prepareAppTransition(eq(TRANSIT_CLOSE)); + verify(activity.mDisplayContent).executeAppTransition(); } /** @@ -912,15 +958,16 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testFinishActivityIfPossible_nonVisibleNoAppTransition() { + final ActivityRecord activity = createActivityWithTask(); // Put an activity on top of test activity to make it invisible and prevent us from // accidentally resuming the topmost one again. new ActivityBuilder(mAtm).build(); - mActivity.mVisibleRequested = false; - mActivity.setState(STOPPED, "test"); + activity.mVisibleRequested = false; + activity.setState(STOPPED, "test"); - mActivity.finishIfPossible("test", false /* oomAdj */); + activity.finishIfPossible("test", false /* oomAdj */); - verify(mActivity.mDisplayContent, never()).prepareAppTransition(eq(TRANSIT_CLOSE)); + verify(activity.mDisplayContent, never()).prepareAppTransition(eq(TRANSIT_CLOSE)); } /** @@ -928,8 +975,9 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test(expected = IllegalArgumentException.class) public void testCompleteFinishing_failNotFinishing() { - mActivity.finishing = false; - mActivity.completeFinishing("test"); + final ActivityRecord activity = createActivityWithTask(); + activity.finishing = false; + activity.completeFinishing("test"); } /** @@ -937,8 +985,9 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test(expected = IllegalArgumentException.class) public void testCompleteFinishing_failResumed() { - mActivity.setState(RESUMED, "test"); - mActivity.completeFinishing("test"); + final ActivityRecord activity = createActivityWithTask(); + activity.setState(RESUMED, "test"); + activity.completeFinishing("test"); } /** @@ -947,13 +996,14 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testCompleteFinishing_pausing() { - mActivity.setState(PAUSING, "test"); - mActivity.finishing = true; + final ActivityRecord activity = createActivityWithTask(); + activity.setState(PAUSING, "test"); + activity.finishing = true; assertEquals("Activity must not be removed immediately - waiting for paused", - mActivity, mActivity.completeFinishing("test")); - assertEquals(PAUSING, mActivity.getState()); - verify(mActivity, never()).destroyIfPossible(anyString()); + activity, activity.completeFinishing("test")); + assertEquals(PAUSING, activity.getState()); + verify(activity, never()).destroyIfPossible(anyString()); } /** @@ -965,7 +1015,9 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testCompleteFinishing_keepStateOfNextInvisible() { - final ActivityRecord currentTop = mActivity; + final ActivityRecord currentTop = createActivityWithTask(); + final Task task = currentTop.getTask(); + currentTop.mVisibleRequested = currentTop.nowVisible = true; // Simulates that {@code currentTop} starts an existing activity from background (so its @@ -974,7 +1026,7 @@ public class ActivityRecordTests extends WindowTestsBase { final ActivityRecord nextTop = nextStack.getTopNonFinishingActivity(); nextTop.setState(STOPPED, "test"); - mStack.mPausingActivity = currentTop; + task.mPausingActivity = currentTop; currentTop.finishing = true; currentTop.setState(PAUSED, "test"); currentTop.completeFinishing("completePauseLocked"); @@ -991,16 +1043,18 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testCompleteFinishing_waitForNextVisible() { - final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); + final ActivityRecord activity = createActivityWithTask(); + final ActivityRecord topActivity = new ActivityBuilder(mAtm) + .setTask(activity.getTask()).build(); topActivity.mVisibleRequested = true; topActivity.nowVisible = true; topActivity.finishing = true; topActivity.setState(PAUSED, "true"); // Mark the bottom activity as not visible, so that we will wait for it before removing // the top one. - mActivity.mVisibleRequested = false; - mActivity.nowVisible = false; - mActivity.setState(STOPPED, "test"); + activity.mVisibleRequested = false; + activity.nowVisible = false; + activity.setState(STOPPED, "test"); assertEquals("Activity must not be removed immediately - waiting for next visible", topActivity, topActivity.completeFinishing("test")); @@ -1017,23 +1071,24 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testCompleteFinishing_noWaitForNextVisible_sleeping() { + final ActivityRecord activity = createActivityWithTask(); // Create a top activity on a new task - final ActivityRecord topActivity = new ActivityBuilder(mAtm).setCreateTask(true).build(); + final ActivityRecord topActivity = createActivityWithTask(); mDisplayContent.setIsSleeping(true); - doReturn(true).when(mActivity).shouldBeVisible(); + doReturn(true).when(activity).shouldBeVisible(); topActivity.mVisibleRequested = false; topActivity.nowVisible = false; topActivity.finishing = true; topActivity.setState(STOPPED, "true"); // Mark the activity behind (on a separate task) as not visible - mActivity.mVisibleRequested = false; - mActivity.nowVisible = false; - mActivity.setState(STOPPED, "test"); + activity.mVisibleRequested = false; + activity.nowVisible = false; + activity.setState(STOPPED, "test"); - clearInvocations(mActivity); + clearInvocations(activity); topActivity.completeFinishing("test"); - verify(mActivity).setState(eq(RESUMED), any()); + verify(activity).setState(eq(RESUMED), any()); verify(topActivity).destroyIfPossible(anyString()); } @@ -1042,16 +1097,18 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testCompleteFinishing_noWaitForNextVisible_alreadyInvisible() { - final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); + final ActivityRecord activity = createActivityWithTask(); + final ActivityRecord topActivity = new ActivityBuilder(mAtm) + .setTask(activity.getTask()).build(); topActivity.mVisibleRequested = false; topActivity.nowVisible = false; topActivity.finishing = true; topActivity.setState(STOPPED, "true"); // Mark the bottom activity as not visible, so that we would wait for it before removing // the top one. - mActivity.mVisibleRequested = false; - mActivity.nowVisible = false; - mActivity.setState(STOPPED, "test"); + activity.mVisibleRequested = false; + activity.nowVisible = false; + activity.setState(STOPPED, "test"); topActivity.completeFinishing("test"); @@ -1064,15 +1121,17 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testCompleteFinishing_waitForIdle() { - final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); + final ActivityRecord activity = createActivityWithTask(); + final ActivityRecord topActivity = new ActivityBuilder(mAtm) + .setTask(activity.getTask()).build(); topActivity.mVisibleRequested = true; topActivity.nowVisible = true; topActivity.finishing = true; topActivity.setState(PAUSED, "true"); // Mark the bottom activity as already visible, so that there is no need to wait for it. - mActivity.mVisibleRequested = true; - mActivity.nowVisible = true; - mActivity.setState(RESUMED, "test"); + activity.mVisibleRequested = true; + activity.nowVisible = true; + activity.setState(RESUMED, "test"); topActivity.completeFinishing("test"); @@ -1085,15 +1144,17 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testCompleteFinishing_noWaitForNextVisible_stopped() { - final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); + final ActivityRecord activity = createActivityWithTask(); + final ActivityRecord topActivity = new ActivityBuilder(mAtm) + .setTask(activity.getTask()).build(); topActivity.mVisibleRequested = false; topActivity.nowVisible = false; topActivity.finishing = true; topActivity.setState(STOPPED, "true"); // Mark the bottom activity as already visible, so that there is no need to wait for it. - mActivity.mVisibleRequested = true; - mActivity.nowVisible = true; - mActivity.setState(RESUMED, "test"); + activity.mVisibleRequested = true; + activity.nowVisible = true; + activity.setState(RESUMED, "test"); topActivity.completeFinishing("test"); @@ -1106,15 +1167,17 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testCompleteFinishing_noWaitForNextVisible_nonFocusedStack() { - final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); + final ActivityRecord activity = createActivityWithTask(); + final ActivityRecord topActivity = new ActivityBuilder(mAtm) + .setTask(activity.getTask()).build(); topActivity.mVisibleRequested = true; topActivity.nowVisible = true; topActivity.finishing = true; topActivity.setState(PAUSED, "true"); // Mark the bottom activity as already visible, so that there is no need to wait for it. - mActivity.mVisibleRequested = true; - mActivity.nowVisible = true; - mActivity.setState(RESUMED, "test"); + activity.mVisibleRequested = true; + activity.nowVisible = true; + activity.setState(RESUMED, "test"); // Add another stack to become focused and make the activity there visible. This way it // simulates finishing in non-focused stack in split-screen. @@ -1136,10 +1199,12 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testCompleteFinishing_showWhenLocked() { + final ActivityRecord activity = createActivityWithTask(); + final Task task = activity.getTask(); // Make keyguard locked and set the top activity show-when-locked. - KeyguardController keyguardController = mActivity.mTaskSupervisor.getKeyguardController(); + KeyguardController keyguardController = activity.mTaskSupervisor.getKeyguardController(); doReturn(true).when(keyguardController).isKeyguardLocked(); - final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); + final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build(); topActivity.mVisibleRequested = true; topActivity.nowVisible = true; topActivity.setState(RESUMED, "true"); @@ -1150,7 +1215,7 @@ public class ActivityRecordTests extends WindowTestsBase { topActivity.setShowWhenLocked(true); // Verify the stack-top activity is occluded keyguard. - assertEquals(topActivity, mStack.topRunningActivity()); + assertEquals(topActivity, task.topRunningActivity()); assertTrue(keyguardController.isDisplayOccluded(DEFAULT_DISPLAY)); // Finish the top activity @@ -1159,7 +1224,7 @@ public class ActivityRecordTests extends WindowTestsBase { topActivity.completeFinishing("test"); // Verify new top activity does not occlude keyguard. - assertEquals(mActivity, mStack.topRunningActivity()); + assertEquals(activity, task.topRunningActivity()); assertFalse(keyguardController.isDisplayOccluded(DEFAULT_DISPLAY)); } @@ -1169,18 +1234,19 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testCompleteFinishing_ensureActivitiesVisible() { - final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); + final ActivityRecord activity = createActivityWithTask(); + final Task task = activity.getTask(); + final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(task).build(); firstActivity.mVisibleRequested = false; firstActivity.nowVisible = false; firstActivity.setState(STOPPED, "true"); - final ActivityRecord secondActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); + final ActivityRecord secondActivity = new ActivityBuilder(mAtm).setTask(task).build(); secondActivity.mVisibleRequested = true; secondActivity.nowVisible = true; secondActivity.setState(PAUSED, "true"); - final ActivityRecord translucentActivity = - new ActivityBuilder(mAtm).setTask(mTask).build(); + final ActivityRecord translucentActivity = new ActivityBuilder(mAtm).setTask(task).build(); translucentActivity.mVisibleRequested = true; translucentActivity.nowVisible = true; translucentActivity.setState(RESUMED, "true"); @@ -1208,13 +1274,13 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testDestroyIfPossible() { + final ActivityRecord activity = createActivityWithTask(); doReturn(false).when(mRootWindowContainer).resumeFocusedStacksTopActivities(); - spyOn(mStack); - mActivity.destroyIfPossible("test"); + activity.destroyIfPossible("test"); - assertEquals(DESTROYING, mActivity.getState()); - assertTrue(mActivity.finishing); - verify(mActivity).destroyImmediately(anyString()); + assertEquals(DESTROYING, activity.getState()); + assertTrue(activity.finishing); + verify(activity).destroyImmediately(anyString()); } /** @@ -1224,23 +1290,23 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testDestroyIfPossible_lastActivityAboveEmptyHomeStack() { + final ActivityRecord activity = createActivityWithTask(); // Empty the home stack. - final Task homeStack = mActivity.getDisplayArea().getRootHomeTask(); + final Task homeStack = activity.getDisplayArea().getRootHomeTask(); homeStack.forAllLeafTasks((t) -> { homeStack.removeChild(t, "test"); }, true /* traverseTopToBottom */); - mActivity.finishing = true; + activity.finishing = true; doReturn(false).when(mRootWindowContainer).resumeFocusedStacksTopActivities(); - spyOn(mStack); // Try to destroy the last activity above the home stack. - mActivity.destroyIfPossible("test"); + activity.destroyIfPossible("test"); // Verify that the activity was not actually destroyed, but waits for next one to come up // instead. - verify(mActivity, never()).destroyImmediately(anyString()); - assertEquals(FINISHING, mActivity.getState()); - assertTrue(mActivity.mTaskSupervisor.mFinishingActivities.contains(mActivity)); + verify(activity, never()).destroyImmediately(anyString()); + assertEquals(FINISHING, activity.getState()); + assertTrue(activity.mTaskSupervisor.mFinishingActivities.contains(activity)); } /** @@ -1250,22 +1316,23 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testCompleteFinishing_lastActivityAboveEmptyHomeStack() { + final ActivityRecord activity = createActivityWithTask(); // Empty the home root task. - final Task homeRootTask = mActivity.getDisplayArea().getRootHomeTask(); + final Task homeRootTask = activity.getDisplayArea().getRootHomeTask(); homeRootTask.forAllLeafTasks((t) -> { homeRootTask.removeChild(t, "test"); }, true /* traverseTopToBottom */); - mActivity.finishing = true; - mActivity.mVisibleRequested = true; - spyOn(mStack); + activity.setState(STARTED, "test"); + activity.finishing = true; + activity.mVisibleRequested = true; // Try to finish the last activity above the home stack. - mActivity.completeFinishing("test"); + activity.completeFinishing("test"); // Verify that the activity is not destroyed immediately, but waits for next one to come up. - verify(mActivity, never()).destroyImmediately(anyString()); - assertEquals(FINISHING, mActivity.getState()); - assertTrue(mActivity.mTaskSupervisor.mFinishingActivities.contains(mActivity)); + verify(activity, never()).destroyImmediately(anyString()); + assertEquals(FINISHING, activity.getState()); + assertTrue(activity.mTaskSupervisor.mFinishingActivities.contains(activity)); } /** @@ -1274,10 +1341,11 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testDestroyImmediately_hadApp_finishing() { - mActivity.finishing = true; - mActivity.destroyImmediately("test"); + final ActivityRecord activity = createActivityWithTask(); + activity.finishing = true; + activity.destroyImmediately("test"); - assertEquals(DESTROYING, mActivity.getState()); + assertEquals(DESTROYING, activity.getState()); } /** @@ -1286,10 +1354,11 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testDestroyImmediately_hadApp_notFinishing() { - mActivity.finishing = false; - mActivity.destroyImmediately("test"); + final ActivityRecord activity = createActivityWithTask(); + activity.finishing = false; + activity.destroyImmediately("test"); - assertEquals(DESTROYED, mActivity.getState()); + assertEquals(DESTROYED, activity.getState()); } /** @@ -1298,14 +1367,15 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testDestroyImmediately_noApp_finishing() { - mActivity.app = null; - mActivity.finishing = true; - final Task task = mActivity.getTask(); + final ActivityRecord activity = createActivityWithTask(); + activity.app = null; + activity.finishing = true; + final Task task = activity.getTask(); - mActivity.destroyImmediately("test"); + activity.destroyImmediately("test"); - assertEquals(DESTROYED, mActivity.getState()); - assertNull(mActivity.getTask()); + assertEquals(DESTROYED, activity.getState()); + assertNull(activity.getTask()); assertEquals(0, task.getChildCount()); } @@ -1315,14 +1385,15 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testDestroyImmediately_noApp_notFinishing() { - mActivity.app = null; - mActivity.finishing = false; - final Task task = mActivity.getTask(); + final ActivityRecord activity = createActivityWithTask(); + activity.app = null; + activity.finishing = false; + final Task task = activity.getTask(); - mActivity.destroyImmediately("test"); + activity.destroyImmediately("test"); - assertEquals(DESTROYED, mActivity.getState()); - assertEquals(task, mActivity.getTask()); + assertEquals(DESTROYED, activity.getState()); + assertEquals(task, activity.getTask()); assertEquals(1, task.getChildCount()); } @@ -1331,11 +1402,12 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testSafelyDestroy_nonDestroyable() { - doReturn(false).when(mActivity).isDestroyable(); + final ActivityRecord activity = createActivityWithTask(); + doReturn(false).when(activity).isDestroyable(); - mActivity.safelyDestroy("test"); + activity.safelyDestroy("test"); - verify(mActivity, never()).destroyImmediately(anyString()); + verify(activity, never()).destroyImmediately(anyString()); } /** @@ -1343,29 +1415,31 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testSafelyDestroy_destroyable() { - doReturn(true).when(mActivity).isDestroyable(); + final ActivityRecord activity = createActivityWithTask(); + doReturn(true).when(activity).isDestroyable(); - mActivity.safelyDestroy("test"); + activity.safelyDestroy("test"); - verify(mActivity).destroyImmediately(anyString()); + verify(activity).destroyImmediately(anyString()); } @Test public void testRemoveFromHistory() { - final Task stack = mActivity.getRootTask(); - final Task task = mActivity.getTask(); - final WindowProcessController wpc = mActivity.app; + final ActivityRecord activity = createActivityWithTask(); + final Task rootTask = activity.getRootTask(); + final Task task = activity.getTask(); + final WindowProcessController wpc = activity.app; assertTrue(wpc.hasActivities()); - mActivity.removeFromHistory("test"); + activity.removeFromHistory("test"); - assertEquals(DESTROYED, mActivity.getState()); - assertNull(mActivity.app); - assertNull(mActivity.getTask()); + assertEquals(DESTROYED, activity.getState()); + assertNull(activity.app); + assertNull(activity.getTask()); assertFalse(wpc.hasActivities()); assertEquals(0, task.getChildCount()); assertEquals(task.getRootTask(), task); - assertEquals(0, stack.getChildCount()); + assertEquals(0, rootTask.getChildCount()); } /** @@ -1374,8 +1448,9 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test(expected = IllegalStateException.class) public void testDestroyed_notDestroying() { - mActivity.setState(STOPPED, "test"); - mActivity.destroyed("test"); + final ActivityRecord activity = createActivityWithTask(); + activity.setState(STOPPED, "test"); + activity.destroyed("test"); } /** @@ -1383,10 +1458,11 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testDestroyed_destroying() { - mActivity.setState(DESTROYING, "test"); - mActivity.destroyed("test"); + final ActivityRecord activity = createActivityWithTask(); + activity.setState(DESTROYING, "test"); + activity.destroyed("test"); - verify(mActivity).removeFromHistory(anyString()); + verify(activity).removeFromHistory(anyString()); } /** @@ -1394,15 +1470,17 @@ public class ActivityRecordTests extends WindowTestsBase { */ @Test public void testDestroyed_destroyed() { - mActivity.setState(DESTROYED, "test"); - mActivity.destroyed("test"); + final ActivityRecord activity = createActivityWithTask(); + activity.setState(DESTROYED, "test"); + activity.destroyed("test"); - verify(mActivity).removeFromHistory(anyString()); + verify(activity).removeFromHistory(anyString()); } @Test public void testActivityOverridesProcessConfig() { - final WindowProcessController wpc = mActivity.app; + final ActivityRecord activity = createActivityWithTask(); + final WindowProcessController wpc = activity.app; assertTrue(wpc.registeredForActivityConfigChanges()); assertFalse(wpc.registeredForDisplayConfigChanges()); @@ -1410,18 +1488,19 @@ public class ActivityRecordTests extends WindowTestsBase { createActivityOnDisplay(false /* defaultDisplay */, null /* process */); assertTrue(wpc.registeredForActivityConfigChanges()); - assertEquals(0, mActivity.getMergedOverrideConfiguration() + assertEquals(0, activity.getMergedOverrideConfiguration() .diff(wpc.getRequestedOverrideConfiguration())); - assertNotEquals(mActivity.getConfiguration(), + assertNotEquals(activity.getConfiguration(), secondaryDisplayActivity.getConfiguration()); } @Test public void testActivityOverridesProcessConfig_TwoActivities() { - final WindowProcessController wpc = mActivity.app; + final ActivityRecord activity = createActivityWithTask(); + final WindowProcessController wpc = activity.app; assertTrue(wpc.registeredForActivityConfigChanges()); - final Task firstTaskRecord = mActivity.getTask(); + final Task firstTaskRecord = activity.getTask(); final ActivityRecord secondActivityRecord = new ActivityBuilder(mAtm).setTask(firstTaskRecord).setUseProcess(wpc).build(); @@ -1432,11 +1511,12 @@ public class ActivityRecordTests extends WindowTestsBase { @Test public void testActivityOverridesProcessConfig_TwoActivities_SecondaryDisplay() { - final WindowProcessController wpc = mActivity.app; + final ActivityRecord activity = createActivityWithTask(); + final WindowProcessController wpc = activity.app; assertTrue(wpc.registeredForActivityConfigChanges()); final ActivityRecord secondActivityRecord = - new ActivityBuilder(mAtm).setTask(mTask).setUseProcess(wpc).build(); + new ActivityBuilder(mAtm).setTask(activity.getTask()).setUseProcess(wpc).build(); assertTrue(wpc.registeredForActivityConfigChanges()); assertEquals(0, secondActivityRecord.getMergedOverrideConfiguration() @@ -1445,7 +1525,8 @@ public class ActivityRecordTests extends WindowTestsBase { @Test public void testActivityOverridesProcessConfig_TwoActivities_DifferentTasks() { - final WindowProcessController wpc = mActivity.app; + final ActivityRecord activity = createActivityWithTask(); + final WindowProcessController wpc = activity.app; assertTrue(wpc.registeredForActivityConfigChanges()); final ActivityRecord secondActivityRecord = @@ -1458,10 +1539,11 @@ public class ActivityRecordTests extends WindowTestsBase { @Test public void testActivityOnCancelFixedRotationTransform() { - final DisplayRotation displayRotation = mActivity.mDisplayContent.getDisplayRotation(); + final ActivityRecord activity = createActivityWithTask(); + final DisplayRotation displayRotation = activity.mDisplayContent.getDisplayRotation(); spyOn(displayRotation); - final DisplayContent display = mActivity.mDisplayContent; + final DisplayContent display = activity.mDisplayContent; final int originalRotation = display.getRotation(); // Make {@link DisplayContent#sendNewConfiguration} not apply rotation immediately. @@ -1469,17 +1551,17 @@ public class ActivityRecordTests extends WindowTestsBase { doReturn((originalRotation + 1) % 4).when(displayRotation).rotationForOrientation( anyInt() /* orientation */, anyInt() /* lastRotation */); // Set to visible so the activity can freeze the screen. - mActivity.setVisibility(true); + activity.setVisibility(true); - display.rotateInDifferentOrientationIfNeeded(mActivity); - display.setFixedRotationLaunchingAppUnchecked(mActivity); + display.rotateInDifferentOrientationIfNeeded(activity); + display.setFixedRotationLaunchingAppUnchecked(activity); displayRotation.updateRotationUnchecked(true /* forceUpdate */); assertTrue(displayRotation.isRotatingSeamlessly()); // The launching rotated app should not be cleared when waiting for remote rotation. display.continueUpdateOrientationForDiffOrienLaunchingApp(); - assertTrue(display.isFixedRotationLaunchingApp(mActivity)); + assertTrue(display.isFixedRotationLaunchingApp(activity)); // Simulate the rotation has been updated to previous one, e.g. sensor updates before the // remote rotation is completed. @@ -1487,16 +1569,16 @@ public class ActivityRecordTests extends WindowTestsBase { anyInt() /* orientation */, anyInt() /* lastRotation */); display.updateOrientation(); - final DisplayInfo rotatedInfo = mActivity.getFixedRotationTransformDisplayInfo(); - mActivity.finishFixedRotationTransform(); + final DisplayInfo rotatedInfo = activity.getFixedRotationTransformDisplayInfo(); + activity.finishFixedRotationTransform(); final ScreenRotationAnimation rotationAnim = display.getRotationAnimation(); assertNotNull(rotationAnim); rotationAnim.setRotation(display.getPendingTransaction(), originalRotation); // Because the display doesn't rotate, the rotated activity needs to cancel the fixed // rotation. There should be a rotation animation to cover the change of activity. - verify(mActivity).onCancelFixedRotationTransform(rotatedInfo.rotation); - assertTrue(mActivity.isFreezingScreen()); + verify(activity).onCancelFixedRotationTransform(rotatedInfo.rotation); + assertTrue(activity.isFreezingScreen()); assertFalse(displayRotation.isRotatingSeamlessly()); assertTrue(rotationAnim.isRotating()); @@ -1504,44 +1586,46 @@ public class ActivityRecordTests extends WindowTestsBase { // the rotated activity should also be restored by clearing the transform. displayRotation.updateRotationUnchecked(true /* forceUpdate */); doReturn(false).when(displayRotation).isWaitingForRemoteRotation(); - clearInvocations(mActivity); - display.setFixedRotationLaunchingAppUnchecked(mActivity); + clearInvocations(activity); + display.setFixedRotationLaunchingAppUnchecked(activity); display.sendNewConfiguration(); assertFalse(display.hasTopFixedRotationLaunchingApp()); - assertFalse(mActivity.hasFixedRotationTransform()); + assertFalse(activity.hasFixedRotationTransform()); } @Test public void testIsSnapshotCompatible() { + final ActivityRecord activity = createActivityWithTask(); final TaskSnapshot snapshot = new TaskSnapshotPersisterTestBase.TaskSnapshotBuilder() - .setRotation(mActivity.getWindowConfiguration().getRotation()) + .setRotation(activity.getWindowConfiguration().getRotation()) .build(); - assertTrue(mActivity.isSnapshotCompatible(snapshot)); + assertTrue(activity.isSnapshotCompatible(snapshot)); - setRotatedScreenOrientationSilently(mActivity); + setRotatedScreenOrientationSilently(activity); - assertFalse(mActivity.isSnapshotCompatible(snapshot)); + assertFalse(activity.isSnapshotCompatible(snapshot)); } @Test public void testFixedRotationSnapshotStartingWindow() { + final ActivityRecord activity = createActivityWithTask(); // TaskSnapshotSurface requires a fullscreen opaque window. final WindowManager.LayoutParams params = new WindowManager.LayoutParams( WindowManager.LayoutParams.TYPE_APPLICATION_STARTING); params.width = params.height = WindowManager.LayoutParams.MATCH_PARENT; final TestWindowState w = new TestWindowState( - mAtm.mWindowManager, mock(Session.class), new TestIWindow(), params, mActivity); - mActivity.addWindow(w); + mAtm.mWindowManager, mock(Session.class), new TestIWindow(), params, activity); + activity.addWindow(w); // Assume the activity is launching in different rotation, and there was an available // snapshot accepted by {@link Activity#isSnapshotCompatible}. final TaskSnapshot snapshot = new TaskSnapshotPersisterTestBase.TaskSnapshotBuilder() - .setRotation((mActivity.getWindowConfiguration().getRotation() + 1) % 4) + .setRotation((activity.getWindowConfiguration().getRotation() + 1) % 4) .build(); - setRotatedScreenOrientationSilently(mActivity); - mActivity.setVisible(false); + setRotatedScreenOrientationSilently(activity); + activity.setVisible(false); final IWindowSession session = WindowManagerGlobal.getWindowSession(); spyOn(session); @@ -1553,7 +1637,7 @@ public class ActivityRecordTests extends WindowTestsBase { any() /* requestedVisibility */, any() /* outFrame */, any() /* outDisplayCutout */, any() /* outInputChannel */, any() /* outInsetsState */, any() /* outActiveControls */); - TaskSnapshotSurface.create(mAtm.mWindowManager, mActivity, snapshot); + TaskSnapshotSurface.create(mAtm.mWindowManager, activity, snapshot); } catch (RemoteException ignored) { } finally { reset(session); @@ -1562,8 +1646,8 @@ public class ActivityRecordTests extends WindowTestsBase { // Because the rotation of snapshot and the corresponding top activity are different, fixed // rotation should be applied when creating snapshot surface if the display rotation may be // changed according to the activity orientation. - assertTrue(mActivity.hasFixedRotationTransform()); - assertTrue(mActivity.mDisplayContent.isFixedRotationLaunchingApp(mActivity)); + assertTrue(activity.hasFixedRotationTransform()); + assertTrue(activity.mDisplayContent.isFixedRotationLaunchingApp(activity)); } /** @@ -1596,11 +1680,12 @@ public class ActivityRecordTests extends WindowTestsBase { @Test public void testActivityReparentChangesProcessOverride() { - final WindowProcessController wpc = mActivity.app; - final Task initialTask = mActivity.getTask(); + final ActivityRecord activity = createActivityWithTask(); + final WindowProcessController wpc = activity.app; + final Task initialTask = activity.getTask(); final Configuration initialConf = - new Configuration(mActivity.getMergedOverrideConfiguration()); - assertEquals(0, mActivity.getMergedOverrideConfiguration() + new Configuration(activity.getMergedOverrideConfiguration()); + assertEquals(0, activity.getMergedOverrideConfiguration() .diff(wpc.getRequestedOverrideConfiguration())); assertTrue(wpc.registeredForActivityConfigChanges()); @@ -1613,22 +1698,23 @@ public class ActivityRecordTests extends WindowTestsBase { assertEquals(newTask.getConfiguration().densityDpi, newConfig.densityDpi); // Reparent the activity and verify that config override changed. - mActivity.reparent(newTask, 0 /* top */, "test"); - assertEquals(mActivity.getConfiguration().densityDpi, newConfig.densityDpi); - assertEquals(mActivity.getMergedOverrideConfiguration().densityDpi, newConfig.densityDpi); + activity.reparent(newTask, 0 /* top */, "test"); + assertEquals(activity.getConfiguration().densityDpi, newConfig.densityDpi); + assertEquals(activity.getMergedOverrideConfiguration().densityDpi, newConfig.densityDpi); assertTrue(wpc.registeredForActivityConfigChanges()); assertNotEquals(initialConf, wpc.getRequestedOverrideConfiguration()); - assertEquals(0, mActivity.getMergedOverrideConfiguration() + assertEquals(0, activity.getMergedOverrideConfiguration() .diff(wpc.getRequestedOverrideConfiguration())); } @Test public void testActivityReparentDoesntClearProcessOverride_TwoActivities() { - final WindowProcessController wpc = mActivity.app; + final ActivityRecord activity = createActivityWithTask(); + final WindowProcessController wpc = activity.app; final Configuration initialConf = - new Configuration(mActivity.getMergedOverrideConfiguration()); - final Task initialTask = mActivity.getTask(); + new Configuration(activity.getMergedOverrideConfiguration()); + final Task initialTask = activity.getTask(); final ActivityRecord secondActivity = new ActivityBuilder(mAtm).setTask(initialTask) .setUseProcess(wpc).build(); @@ -1652,7 +1738,7 @@ public class ActivityRecordTests extends WindowTestsBase { assertNotEquals(initialConf, wpc.getRequestedOverrideConfiguration()); // Reparent the first activity and verify that config override didn't change. - mActivity.reparent(newTask, 1 /* top */, "test"); + activity.reparent(newTask, 1 /* top */, "test"); assertTrue(wpc.registeredForActivityConfigChanges()); assertEquals(0, secondActivity.getMergedOverrideConfiguration() .diff(wpc.getRequestedOverrideConfiguration())); @@ -1695,67 +1781,90 @@ public class ActivityRecordTests extends WindowTestsBase { @Test public void testFullscreenWindowCanTurnScreenOn() { - mStack.setWindowingMode(WINDOWING_MODE_FULLSCREEN); - doReturn(true).when(mActivity).getTurnScreenOnFlag(); + final ActivityRecord activity = createActivityWithTask(); + final Task task = activity.getTask(); + task.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + doReturn(true).when(activity).getTurnScreenOnFlag(); - assertTrue(mActivity.canTurnScreenOn()); + assertTrue(activity.canTurnScreenOn()); } @Test public void testFreeformWindowCanTurnScreenOn() { - mStack.setWindowingMode(WINDOWING_MODE_FREEFORM); - doReturn(true).when(mActivity).getTurnScreenOnFlag(); + final ActivityRecord activity = createActivityWithTask(); + final Task task = activity.getTask(); + task.setWindowingMode(WINDOWING_MODE_FREEFORM); + doReturn(true).when(activity).getTurnScreenOnFlag(); - assertTrue(mActivity.canTurnScreenOn()); + assertTrue(activity.canTurnScreenOn()); } @Test public void testGetLockTaskLaunchMode() { + final ActivityRecord activity = createActivityWithTask(); final ActivityOptions options = ActivityOptions.makeBasic().setLockTaskEnabled(true); - mActivity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_DEFAULT; + activity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_DEFAULT; assertEquals(LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED, - ActivityRecord.getLockTaskLaunchMode(mActivity.info, options)); + ActivityRecord.getLockTaskLaunchMode(activity.info, options)); - mActivity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_ALWAYS; + activity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_ALWAYS; assertEquals(LOCK_TASK_LAUNCH_MODE_DEFAULT, - ActivityRecord.getLockTaskLaunchMode(mActivity.info, null /*options*/)); + ActivityRecord.getLockTaskLaunchMode(activity.info, null /*options*/)); - mActivity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_NEVER; + activity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_NEVER; assertEquals(LOCK_TASK_LAUNCH_MODE_DEFAULT, - ActivityRecord.getLockTaskLaunchMode(mActivity.info, null /*options*/)); + ActivityRecord.getLockTaskLaunchMode(activity.info, null /*options*/)); - mActivity.info.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED; - mActivity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_ALWAYS; + activity.info.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED; + activity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_ALWAYS; assertEquals(LOCK_TASK_LAUNCH_MODE_ALWAYS, - ActivityRecord.getLockTaskLaunchMode(mActivity.info, null /*options*/)); + ActivityRecord.getLockTaskLaunchMode(activity.info, null /*options*/)); - mActivity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_NEVER; + activity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_NEVER; assertEquals(LOCK_TASK_LAUNCH_MODE_NEVER, - ActivityRecord.getLockTaskLaunchMode(mActivity.info, null /*options*/)); + ActivityRecord.getLockTaskLaunchMode(activity.info, null /*options*/)); } @Test public void testProcessInfoUpdateWhenSetState() { - spyOn(mActivity.app); - verifyProcessInfoUpdate(RESUMED, true /* shouldUpdate */, true /* activityChange */); - verifyProcessInfoUpdate(PAUSED, false /* shouldUpdate */, false /* activityChange */); - verifyProcessInfoUpdate(STOPPED, false /* shouldUpdate */, false /* activityChange */); - verifyProcessInfoUpdate(STARTED, true /* shouldUpdate */, true /* activityChange */); - - mActivity.app.removeActivity(mActivity, true /* keepAssociation */); - verifyProcessInfoUpdate(DESTROYING, true /* shouldUpdate */, false /* activityChange */); - verifyProcessInfoUpdate(DESTROYED, true /* shouldUpdate */, false /* activityChange */); - } - - private void verifyProcessInfoUpdate(ActivityState state, boolean shouldUpdate, - boolean activityChange) { - reset(mActivity.app); - mActivity.setState(state, "test"); - verify(mActivity.app, times(shouldUpdate ? 1 : 0)).updateProcessInfo(anyBoolean(), + final ActivityRecord activity = createActivityWithTask(); + activity.setState(INITIALIZING, "test"); + spyOn(activity.app); + verifyProcessInfoUpdate(activity, RESUMED, + true /* shouldUpdate */, true /* activityChange */); + verifyProcessInfoUpdate(activity, PAUSED, + false /* shouldUpdate */, false /* activityChange */); + verifyProcessInfoUpdate(activity, STOPPED, + false /* shouldUpdate */, false /* activityChange */); + verifyProcessInfoUpdate(activity, STARTED, + true /* shouldUpdate */, true /* activityChange */); + + activity.app.removeActivity(activity, true /* keepAssociation */); + verifyProcessInfoUpdate(activity, DESTROYING, + true /* shouldUpdate */, false /* activityChange */); + verifyProcessInfoUpdate(activity, DESTROYED, + true /* shouldUpdate */, false /* activityChange */); + } + + private void verifyProcessInfoUpdate(ActivityRecord activity, ActivityState state, + boolean shouldUpdate, boolean activityChange) { + reset(activity.app); + activity.setState(state, "test"); + verify(activity.app, times(shouldUpdate ? 1 : 0)).updateProcessInfo(anyBoolean(), eq(activityChange), anyBoolean(), anyBoolean()); } + private ActivityRecord createActivityWithTask() { + return new ActivityBuilder(mAtm).setCreateTask(true).setOnTop(true).build(); + } + + private ActivityRecord createActivityWith2LevelTask() { + final Task task = new TaskBuilder(mSupervisor) + .setCreateParentTask(true).setCreateActivity(true).build(); + return task.getTopNonFinishingActivity(); + } + /** * Creates an activity on display. For non-default display request it will also create a new * display with custom DisplayInfo. @@ -1769,9 +1878,7 @@ public class ActivityRecordTests extends WindowTestsBase { display = new TestDisplayContent.Builder(mAtm, 2000, 1000).setDensityDpi(300) .setPosition(DisplayContent.POSITION_TOP).build(); } - final Task stack = display.getDefaultTaskDisplayArea() - .createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */); - final Task task = new TaskBuilder(mSupervisor).setParentTask(stack).build(); + final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build(); return new ActivityBuilder(mAtm).setTask(task).setUseProcess(process).build(); } } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java index d872eb856a50..8ccbb8fe62ad 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java @@ -43,6 +43,7 @@ import static com.android.server.wm.Task.ActivityState.RESUMED; import static com.android.server.wm.Task.ActivityState.STOPPED; import static com.android.server.wm.Task.ActivityState.STOPPING; import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG; +import static com.android.server.wm.Task.REPARENT_KEEP_ROOT_TASK_AT_FRONT; import static com.android.server.wm.Task.REPARENT_MOVE_ROOT_TASK_TO_FRONT; import static com.android.server.wm.Task.TASK_VISIBILITY_INVISIBLE; import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE; @@ -91,59 +92,58 @@ import java.util.function.Consumer; @RunWith(WindowTestRunner.class) public class ActivityStackTests extends WindowTestsBase { private TaskDisplayArea mDefaultTaskDisplayArea; - private Task mStack; - private Task mTask; @Before public void setUp() throws Exception { mDefaultTaskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); - mStack = mDefaultTaskDisplayArea.createStack(WINDOWING_MODE_UNDEFINED, - ACTIVITY_TYPE_STANDARD, true /* onTop */); - spyOn(mStack); - mTask = new TaskBuilder(mSupervisor).setParentTask(mStack).build(); } @Test public void testResumedActivity() { - final ActivityRecord r = new ActivityBuilder(mAtm).setTask(mTask).build(); - assertNull(mStack.getResumedActivity()); + final ActivityRecord r = new ActivityBuilder(mAtm).setCreateTask(true).build(); + final Task task = r.getTask(); + assertNull(task.getResumedActivity()); r.setState(RESUMED, "testResumedActivity"); - assertEquals(r, mStack.getResumedActivity()); + assertEquals(r, task.getResumedActivity()); r.setState(PAUSING, "testResumedActivity"); - assertNull(mStack.getResumedActivity()); + assertNull(task.getResumedActivity()); } @Test public void testResumedActivityFromTaskReparenting() { - final ActivityRecord r = new ActivityBuilder(mAtm).setTask(mTask).build(); + final Task parentTask = new TaskBuilder(mSupervisor).setOnTop(true).build(); + final ActivityRecord r = new ActivityBuilder(mAtm) + .setCreateTask(true).setParentTask(parentTask).build(); + final Task task = r.getTask(); // Ensure moving task between two stacks updates resumed activity r.setState(RESUMED, "testResumedActivityFromTaskReparenting"); - assertEquals(r, mStack.getResumedActivity()); + assertEquals(r, parentTask.getResumedActivity()); - final Task destStack = mDefaultTaskDisplayArea.createStack( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - - mTask.reparent(destStack, true /* toTop */, Task.REPARENT_KEEP_ROOT_TASK_AT_FRONT, + final Task destStack = new TaskBuilder(mSupervisor).setOnTop(true).build(); + task.reparent(destStack, true /* toTop */, REPARENT_KEEP_ROOT_TASK_AT_FRONT, false /* animate */, true /* deferResume*/, "testResumedActivityFromTaskReparenting"); - assertNull(mStack.getResumedActivity()); + assertNull(parentTask.getResumedActivity()); assertEquals(r, destStack.getResumedActivity()); } @Test public void testResumedActivityFromActivityReparenting() { - final ActivityRecord r = new ActivityBuilder(mAtm).setTask(mTask).build(); + final Task parentTask = new TaskBuilder(mSupervisor).setOnTop(true).build(); + final ActivityRecord r = new ActivityBuilder(mAtm) + .setCreateTask(true).setParentTask(parentTask).build(); + final Task task = r.getTask(); // Ensure moving task between two stacks updates resumed activity r.setState(RESUMED, "testResumedActivityFromActivityReparenting"); - assertEquals(r, mStack.getResumedActivity()); + assertEquals(r, parentTask.getResumedActivity()); - final Task destStack = mDefaultTaskDisplayArea.createStack( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - mTask.reparent(destStack, true /*toTop*/, REPARENT_MOVE_ROOT_TASK_TO_FRONT, false, false, + final Task destStack = new TaskBuilder(mSupervisor).setOnTop(true).build(); + task.reparent(destStack, true /*toTop*/, REPARENT_MOVE_ROOT_TASK_TO_FRONT, + false /* animate */, false /* deferResume*/, "testResumedActivityFromActivityReparenting"); - assertNull(mStack.getResumedActivity()); + assertNull(parentTask.getResumedActivity()); assertEquals(r, destStack.getResumedActivity()); } @@ -292,7 +292,7 @@ public class ActivityStackTests extends WindowTestsBase { public void testStopActivityWhenActivityDestroyed() { final ActivityRecord r = new ActivityBuilder(mAtm).setCreateTask(true).build(); r.info.flags |= ActivityInfo.FLAG_NO_HISTORY; - mStack.moveToFront("testStopActivityWithDestroy"); + r.getTask().moveToFront("testStopActivityWithDestroy"); r.stopIfPossible(); // Mostly testing to make sure there is a crash in the call part, so if we get here we are // good-to-go! @@ -327,7 +327,8 @@ public class ActivityStackTests extends WindowTestsBase { targetActivity); final ComponentName alias = new ComponentName(DEFAULT_COMPONENT_PACKAGE_NAME, aliasActivity); - final Task task = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(mStack).build(); + final Task parentTask = new TaskBuilder(mAtm.mTaskSupervisor).build(); + final Task task = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(parentTask).build(); task.origActivity = alias; task.realActivity = target; new ActivityBuilder(mAtm).setComponent(target).setTask(task).setTargetActivity( @@ -337,14 +338,14 @@ public class ActivityStackTests extends WindowTestsBase { final ActivityRecord r1 = new ActivityBuilder(mAtm).setComponent( target).setTargetActivity(targetActivity).build(); RootWindowContainer.FindTaskResult result = new RootWindowContainer.FindTaskResult(); - result.process(r1, mStack); + result.process(r1, parentTask); assertThat(result.mRecord).isNotNull(); // Using alias activity to find task. final ActivityRecord r2 = new ActivityBuilder(mAtm).setComponent( alias).setTargetActivity(targetActivity).build(); result = new RootWindowContainer.FindTaskResult(); - result.process(r2, mStack); + result.process(r2, parentTask); assertThat(result.mRecord).isNotNull(); } @@ -735,8 +736,6 @@ public class ActivityStackTests extends WindowTestsBase { @Test public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeBehindFullscreen() { - mDefaultTaskDisplayArea.removeStack(mStack); - final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); final Task fullscreenStack = createStackForShouldBeVisibleTest( @@ -755,8 +754,6 @@ public class ActivityStackTests extends WindowTestsBase { @Test public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeBehindTranslucent() { - mDefaultTaskDisplayArea.removeStack(mStack); - final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); final Task fullscreenStack = createStackForShouldBeVisibleTest( @@ -775,8 +772,6 @@ public class ActivityStackTests extends WindowTestsBase { @Test public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeOnTop() { - mDefaultTaskDisplayArea.removeStack(mStack); - final Task fullscreenStack = createStackForShouldBeVisibleTest( mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); @@ -795,8 +790,6 @@ public class ActivityStackTests extends WindowTestsBase { @Test public void testMoveHomeStackBehindBottomMostVisibleStack_MoveHomeBehindFullscreen() { - mDefaultTaskDisplayArea.removeStack(mStack); - final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); final Task fullscreenStack1 = createStackForShouldBeVisibleTest( @@ -822,8 +815,6 @@ public class ActivityStackTests extends WindowTestsBase { @Test public void testMoveHomeStackBehindBottomMostVisibleStack_MoveHomeBehindFullscreenAndTranslucent() { - mDefaultTaskDisplayArea.removeStack(mStack); - final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); final Task fullscreenStack1 = createStackForShouldBeVisibleTest( @@ -846,8 +837,6 @@ public class ActivityStackTests extends WindowTestsBase { @Test public void testMoveHomeStackBehindStack_BehindHomeStack() { - mDefaultTaskDisplayArea.removeStack(mStack); - final Task fullscreenStack1 = createStackForShouldBeVisibleTest( mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); @@ -869,8 +858,6 @@ public class ActivityStackTests extends WindowTestsBase { @Test public void testMoveHomeStackBehindStack() { - mDefaultTaskDisplayArea.removeStack(mStack); - final Task fullscreenStack1 = createStackForShouldBeVisibleTest( mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); @@ -1019,11 +1006,12 @@ public class ActivityStackTests extends WindowTestsBase { @Test public void testFinishDisabledPackageActivities_FinishAliveActivities() { - final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); - final ActivityRecord secondActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); + final Task task = new TaskBuilder(mSupervisor).build(); + final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(task).build(); + final ActivityRecord secondActivity = new ActivityBuilder(mAtm).setTask(task).build(); firstActivity.setState(STOPPED, "testFinishDisabledPackageActivities"); secondActivity.setState(RESUMED, "testFinishDisabledPackageActivities"); - mStack.mResumedActivity = secondActivity; + task.mResumedActivity = secondActivity; // Note the activities have non-null ActivityRecord.app, so it won't remove directly. mRootWindowContainer.mFinishDisabledPackageActivitiesHelper.process( @@ -1039,10 +1027,11 @@ public class ActivityStackTests extends WindowTestsBase { @Test public void testFinishDisabledPackageActivities_RemoveNonAliveActivities() { - final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(mTask).build(); + final Task task = new TaskBuilder(mSupervisor).build(); + final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build(); // The overlay activity is not in the disabled package but it is in the same task. - final ActivityRecord overlayActivity = new ActivityBuilder(mAtm).setTask(mTask) + final ActivityRecord overlayActivity = new ActivityBuilder(mAtm).setTask(task) .setComponent(new ComponentName("package.overlay", ".OverlayActivity")).build(); // If the task only remains overlay activity, the task should also be removed. // See {@link ActivityStack#removeFromHistory}. @@ -1053,7 +1042,7 @@ public class ActivityStackTests extends WindowTestsBase { activity.app = null; overlayActivity.app = null; - assertEquals(2, mTask.getChildCount()); + assertEquals(2, task.getChildCount()); mRootWindowContainer.mFinishDisabledPackageActivitiesHelper.process( activity.packageName, null /* filterByClasses */, true /* doit */, @@ -1062,14 +1051,14 @@ public class ActivityStackTests extends WindowTestsBase { // Although the overlay activity is in another package, the non-overlay activities are // removed from the task. Since the overlay activity should be removed as well, the task // should be empty. - assertFalse(mTask.hasChild()); - assertFalse(mStack.hasChild()); + assertFalse(task.hasChild()); } @Test public void testHandleAppDied() { - final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); - final ActivityRecord secondActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); + final Task task = new TaskBuilder(mSupervisor).build(); + final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(task).build(); + final ActivityRecord secondActivity = new ActivityBuilder(mAtm).setTask(task).build(); // Making the first activity a task overlay means it will be removed from the task's // activities as well once second activity is removed as handleAppDied processes the @@ -1080,17 +1069,17 @@ public class ActivityStackTests extends WindowTestsBase { // second activity will be immediately removed as it has no state. secondActivity.setSavedState(null /* savedState */); - assertEquals(2, mTask.getChildCount()); + assertEquals(2, task.getChildCount()); secondActivity.app.handleAppDied(); - assertFalse(mTask.hasChild()); - assertFalse(mStack.hasChild()); + assertFalse(task.hasChild()); } @Test public void testHandleAppDied_RelaunchesAfterCrashDuringWindowingModeResize() { - final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(mTask).build(); + final Task task = new TaskBuilder(mSupervisor).build(); + final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build(); activity.mRelaunchReason = RELAUNCH_REASON_WINDOWING_MODE_RESIZE; activity.launchCount = 1; @@ -1098,13 +1087,13 @@ public class ActivityStackTests extends WindowTestsBase { activity.app.handleAppDied(); - assertEquals(1, mTask.getChildCount()); - assertEquals(1, mStack.getChildCount()); + assertEquals(1, task.getChildCount()); } @Test public void testHandleAppDied_NotRelaunchAfterThreeCrashesDuringWindowingModeResize() { - final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(mTask).build(); + final Task task = new TaskBuilder(mSupervisor).build(); + final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build(); activity.mRelaunchReason = RELAUNCH_REASON_WINDOWING_MODE_RESIZE; activity.launchCount = 3; @@ -1112,13 +1101,13 @@ public class ActivityStackTests extends WindowTestsBase { activity.app.handleAppDied(); - assertFalse(mTask.hasChild()); - assertFalse(mStack.hasChild()); + assertFalse(task.hasChild()); } @Test public void testHandleAppDied_RelaunchesAfterCrashDuringFreeResize() { - final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(mTask).build(); + final Task task = new TaskBuilder(mSupervisor).build(); + final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build(); activity.mRelaunchReason = RELAUNCH_REASON_FREE_RESIZE; activity.launchCount = 1; @@ -1126,13 +1115,13 @@ public class ActivityStackTests extends WindowTestsBase { activity.app.handleAppDied(); - assertEquals(1, mTask.getChildCount()); - assertEquals(1, mStack.getChildCount()); + assertEquals(1, task.getChildCount()); } @Test public void testHandleAppDied_NotRelaunchAfterThreeCrashesDuringFreeResize() { - final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(mTask).build(); + final Task task = new TaskBuilder(mSupervisor).build(); + final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build(); activity.mRelaunchReason = RELAUNCH_REASON_FREE_RESIZE; activity.launchCount = 3; @@ -1140,22 +1129,22 @@ public class ActivityStackTests extends WindowTestsBase { activity.app.handleAppDied(); - assertFalse(mTask.hasChild()); - assertFalse(mStack.hasChild()); + assertFalse(task.hasChild()); } @Test public void testCompletePauseOnResumeWhilePausingActivity() { - final ActivityRecord bottomActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); + final Task task = new TaskBuilder(mSupervisor).build(); + final ActivityRecord bottomActivity = new ActivityBuilder(mAtm).setTask(task).build(); doReturn(true).when(bottomActivity).attachedToProcess(); - mStack.mPausingActivity = null; - mStack.mResumedActivity = bottomActivity; - final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); + task.mPausingActivity = null; + task.mResumedActivity = bottomActivity; + final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build(); topActivity.info.flags |= FLAG_RESUME_WHILE_PAUSING; - mStack.startPausingLocked(false /* userLeaving */, false /* uiSleeping */, topActivity, + task.startPausingLocked(false /* userLeaving */, false /* uiSleeping */, topActivity, "test"); - verify(mStack).completePauseLocked(anyBoolean(), eq(topActivity)); + verify(task).completePauseLocked(anyBoolean(), eq(topActivity)); } @Test @@ -1234,10 +1223,11 @@ public class ActivityStackTests extends WindowTestsBase { @Test public void testStackOrderChangedOnRemoveStack() { + final Task task = new TaskBuilder(mSupervisor).build(); StackOrderChangedListener listener = new StackOrderChangedListener(); mDefaultTaskDisplayArea.registerStackOrderChangedListener(listener); try { - mDefaultTaskDisplayArea.removeStack(mStack); + mDefaultTaskDisplayArea.removeStack(task); } finally { mDefaultTaskDisplayArea.unregisterStackOrderChangedListener(listener); } @@ -1246,13 +1236,14 @@ public class ActivityStackTests extends WindowTestsBase { @Test public void testStackOrderChangedOnAddPositionStack() { - mDefaultTaskDisplayArea.removeStack(mStack); + final Task task = new TaskBuilder(mSupervisor).build(); + mDefaultTaskDisplayArea.removeStack(task); StackOrderChangedListener listener = new StackOrderChangedListener(); mDefaultTaskDisplayArea.registerStackOrderChangedListener(listener); try { - mStack.mReparenting = true; - mDefaultTaskDisplayArea.addChild(mStack, 0); + task.mReparenting = true; + mDefaultTaskDisplayArea.addChild(task, 0); } finally { mDefaultTaskDisplayArea.unregisterStackOrderChangedListener(listener); } @@ -1284,20 +1275,21 @@ public class ActivityStackTests extends WindowTestsBase { spyOn(starter); doReturn(ActivityManager.START_SUCCESS).when(starter).execute(); - final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); - final ActivityRecord secondActivity = new ActivityBuilder(mAtm).setTask(mTask) + final Task task = new TaskBuilder(mSupervisor).build(); + final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(task).build(); + final ActivityRecord secondActivity = new ActivityBuilder(mAtm).setTask(task) .setUid(firstActivity.getUid() + 1).build(); doReturn(starter).when(controller).obtainStarter(eq(firstActivity.intent), anyString()); final IApplicationThread thread = secondActivity.app.getThread(); secondActivity.app.setThread(null); // This should do nothing from a non-attached caller. - assertFalse(mStack.navigateUpTo(secondActivity /* source record */, + assertFalse(task.navigateUpTo(secondActivity /* source record */, firstActivity.intent /* destIntent */, null /* destGrants */, 0 /* resultCode */, null /* resultData */, null /* resultGrants */)); secondActivity.app.setThread(thread); - assertTrue(mStack.navigateUpTo(secondActivity /* source record */, + assertTrue(task.navigateUpTo(secondActivity /* source record */, firstActivity.intent /* destIntent */, null /* destGrants */, 0 /* resultCode */, null /* resultData */, null /* resultGrants */)); // The firstActivity uses default launch mode, so the activities between it and itself will @@ -1313,9 +1305,10 @@ public class ActivityStackTests extends WindowTestsBase { final String affinity = "affinity"; final ActivityRecord activity = new ActivityBuilder(mAtm).setAffinity(affinity) .setUid(Binder.getCallingUid()).setCreateTask(true).build(); - activity.getTask().affinity = activity.taskAffinity; + final Task task = activity.getTask(); + task.affinity = activity.taskAffinity; - assertFalse(mStack.shouldUpRecreateTaskLocked(activity, affinity)); + assertFalse(task.shouldUpRecreateTaskLocked(activity, affinity)); } @Test @@ -1323,21 +1316,23 @@ public class ActivityStackTests extends WindowTestsBase { final String affinity = "affinity"; final ActivityRecord activity = new ActivityBuilder(mAtm).setAffinity(affinity) .setUid(Binder.getCallingUid()).setCreateTask(true).build(); - activity.getTask().affinity = activity.taskAffinity; + final Task task = activity.getTask(); + task.affinity = activity.taskAffinity; final String fakeAffinity = activity.getUid() + activity.taskAffinity; - assertTrue(mStack.shouldUpRecreateTaskLocked(activity, fakeAffinity)); + assertTrue(task.shouldUpRecreateTaskLocked(activity, fakeAffinity)); } @Test public void testResetTaskWithFinishingActivities() { - final ActivityRecord taskTop = new ActivityBuilder(mAtm).setTask(mStack).build(); + final ActivityRecord taskTop = new ActivityBuilder(mAtm).setCreateTask(true).build(); + final Task task = taskTop.getTask(); // Make all activities in the task are finishing to simulate Task#getTopActivity // returns null. taskTop.finishing = true; final ActivityRecord newR = new ActivityBuilder(mAtm).build(); - final ActivityRecord result = mStack.resetTaskIfNeeded(taskTop, newR); + final ActivityRecord result = task.resetTaskIfNeeded(taskTop, newR); assertThat(result).isEqualTo(taskTop); } @@ -1345,14 +1340,15 @@ public class ActivityStackTests extends WindowTestsBase { public void testIterateOccludedActivity() { final ArrayList<ActivityRecord> occludedActivities = new ArrayList<>(); final Consumer<ActivityRecord> handleOccludedActivity = occludedActivities::add; - final ActivityRecord bottomActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); - final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); + final Task task = new TaskBuilder(mSupervisor).build(); + final ActivityRecord bottomActivity = new ActivityBuilder(mAtm).setTask(task).build(); + final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build(); // Top activity occludes bottom activity. - doReturn(true).when(mStack).shouldBeVisible(any()); + doReturn(true).when(task).shouldBeVisible(any()); assertTrue(topActivity.shouldBeVisible()); assertFalse(bottomActivity.shouldBeVisible()); - mStack.forAllOccludedActivities(handleOccludedActivity); + task.forAllOccludedActivities(handleOccludedActivity); assertThat(occludedActivities).containsExactly(bottomActivity); // Top activity doesn't occlude parent, so the bottom activity is not occluded. @@ -1360,18 +1356,18 @@ public class ActivityStackTests extends WindowTestsBase { assertTrue(bottomActivity.shouldBeVisible()); occludedActivities.clear(); - mStack.forAllOccludedActivities(handleOccludedActivity); + task.forAllOccludedActivities(handleOccludedActivity); assertThat(occludedActivities).isEmpty(); // A finishing activity should not occlude other activities behind. - final ActivityRecord finishingActivity = new ActivityBuilder(mAtm).setTask(mTask).build(); + final ActivityRecord finishingActivity = new ActivityBuilder(mAtm).setTask(task).build(); finishingActivity.finishing = true; doCallRealMethod().when(finishingActivity).occludesParent(); assertTrue(topActivity.shouldBeVisible()); assertTrue(bottomActivity.shouldBeVisible()); occludedActivities.clear(); - mStack.forAllOccludedActivities(handleOccludedActivity); + task.forAllOccludedActivities(handleOccludedActivity); assertThat(occludedActivities).isEmpty(); } @@ -1385,8 +1381,9 @@ public class ActivityStackTests extends WindowTestsBase { // Start 2 activities that their processes have not yet started. final ActivityRecord[] activities = new ActivityRecord[2]; mSupervisor.beginDeferResume(); + final Task task = new TaskBuilder(mSupervisor).build(); for (int i = 0; i < activities.length; i++) { - final ActivityRecord r = new ActivityBuilder(mAtm).setTask(mTask).build(); + final ActivityRecord r = new ActivityBuilder(mAtm).setTask(task).build(); activities[i] = r; doReturn(null).when(mAtm).getProcessController( eq(r.processName), eq(r.info.applicationInfo.uid)); @@ -1405,7 +1402,7 @@ public class ActivityStackTests extends WindowTestsBase { // Assume the top activity is going to resume and // {@link RootWindowContainer#cancelInitializingActivities} should clear the unknown // visibility records that are occluded. - mStack.resumeTopActivityUncheckedLocked(null /* prev */, null /* options */); + task.resumeTopActivityUncheckedLocked(null /* prev */, null /* options */); // Assume the top activity relayouted, just remove it directly. unknownAppVisibilityController.appRemovedOrHidden(activities[1]); // All unresolved records should be removed. @@ -1414,15 +1411,16 @@ public class ActivityStackTests extends WindowTestsBase { @Test public void testNonTopVisibleActivityNotResume() { + final Task task = new TaskBuilder(mSupervisor).build(); final ActivityRecord nonTopVisibleActivity = - new ActivityBuilder(mAtm).setTask(mTask).build(); - new ActivityBuilder(mAtm).setTask(mTask).build(); + new ActivityBuilder(mAtm).setTask(task).build(); + new ActivityBuilder(mAtm).setTask(task).build(); doReturn(false).when(nonTopVisibleActivity).attachedToProcess(); doReturn(true).when(nonTopVisibleActivity).shouldBeVisibleUnchecked(); doNothing().when(mSupervisor).startSpecificActivity(any(), anyBoolean(), anyBoolean()); - mStack.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, + task.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, false /* preserveWindows */); verify(mSupervisor).startSpecificActivity(any(), eq(false) /* andResume */, anyBoolean()); @@ -1436,16 +1434,17 @@ public class ActivityStackTests extends WindowTestsBase { private void verifyShouldSleepActivities(boolean focusedStack, boolean keyguardGoingAway, boolean displaySleeping, boolean isDefaultDisplay, boolean expected) { + final Task task = new TaskBuilder(mSupervisor).build(); final DisplayContent display = mock(DisplayContent.class); final KeyguardController keyguardController = mSupervisor.getKeyguardController(); display.isDefaultDisplay = isDefaultDisplay; - mStack.mDisplayContent = display; + task.mDisplayContent = display; doReturn(keyguardGoingAway).when(keyguardController).isKeyguardGoingAway(); doReturn(displaySleeping).when(display).isSleeping(); - doReturn(focusedStack).when(mStack).isFocusedStackOnDisplay(); + doReturn(focusedStack).when(task).isFocusedStackOnDisplay(); - assertEquals(expected, mStack.shouldSleepActivities()); + assertEquals(expected, task.shouldSleepActivities()); } private static class StackOrderChangedListener diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index f607448c09a0..565bf8b615c7 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -126,6 +126,7 @@ public class ActivityStarterTests extends WindowTestsBase { private static final String FAKE_CALLING_PACKAGE = "com.whatever.dude"; private static final int UNIMPORTANT_UID = 12345; private static final int UNIMPORTANT_UID2 = 12346; + private static final int CURRENT_IME_UID = 12347; @Before public void setUp() throws Exception { @@ -315,6 +316,12 @@ public class ActivityStarterTests extends WindowTestsBase { return prepareStarter(launchFlags, mockGetLaunchStack, LAUNCH_MULTIPLE); } + private void setupImeWindow() { + final WindowState imeWindow = createWindow(null, W_INPUT_METHOD, + "mImeWindow", CURRENT_IME_UID); + mDisplayContent.mInputMethodWindow = imeWindow; + } + /** * Creates a {@link ActivityStarter} with default parameters and necessary mocks. * @@ -415,7 +422,7 @@ public class ActivityStarterTests extends WindowTestsBase { // verify that values are passed to the modifier. Values are passed thrice -- two for // setting initial state, another when task is created. verify(modifier, times(3)).onCalculate(any(), eq(windowLayout), any(), any(), eq(options), - anyInt(), any(), any()); + anyInt(), any(), any(), any()); } /** @@ -654,6 +661,14 @@ public class ActivityStarterTests extends WindowTestsBase { UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1, UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1, false, false, false, false, true); + + setupImeWindow(); + runAndVerifyBackgroundActivityStartsSubtest( + "disallowed_callingPackageNameIsIme_notAborted", false, + CURRENT_IME_UID, false, PROCESS_STATE_TOP + 1, + UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1, + false, false, false, false, false); + } private void runAndVerifyBackgroundActivityStartsSubtest(String name, boolean shouldHaveAborted, @@ -680,14 +695,11 @@ public class ActivityStarterTests extends WindowTestsBase { boolean isCallingUidDeviceOwner, boolean isPinnedSingleInstance) { // window visibility - doReturn(callingUidHasVisibleWindow).when(mAtm.mWindowManager.mRoot) - .isAnyNonToastWindowVisibleForUid(callingUid); - doReturn(realCallingUidHasVisibleWindow).when(mAtm.mWindowManager.mRoot) - .isAnyNonToastWindowVisibleForUid(realCallingUid); - + doReturn(callingUidHasVisibleWindow).when(mAtm).hasActiveVisibleWindow(callingUid); + doReturn(realCallingUidHasVisibleWindow).when(mAtm).hasActiveVisibleWindow(realCallingUid); // process importance - doReturn(callingUidProcState).when(mAtm).getUidState(callingUid); - doReturn(realCallingUidProcState).when(mAtm).getUidState(realCallingUid); + mAtm.mActiveUids.onUidActive(callingUid, callingUidProcState); + mAtm.mActiveUids.onUidActive(realCallingUid, realCallingUidProcState); // foreground activities final IApplicationThread caller = mock(IApplicationThread.class); final WindowProcessListener listener = mock(WindowProcessListener.class); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java index f61253c77a8d..475e462bdd3d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java @@ -19,8 +19,6 @@ package com.android.server.wm; import static android.app.ActivityManager.START_DELIVERED_TO_TOP; import static android.app.ActivityManager.START_TASK_TO_FRONT; import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; @@ -36,6 +34,7 @@ import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.timeout; import android.app.WaitResult; import android.content.pm.ActivityInfo; @@ -44,10 +43,11 @@ import android.view.Display; import androidx.test.filters.MediumTest; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.concurrent.TimeUnit; + /** * Tests for the {@link ActivityTaskSupervisor} class. * @@ -58,13 +58,6 @@ import org.junit.runner.RunWith; @Presubmit @RunWith(WindowTestRunner.class) public class ActivityTaskSupervisorTests extends WindowTestsBase { - private Task mFullscreenTask; - - @Before - public void setUp() throws Exception { - mFullscreenTask = mRootWindowContainer.getDefaultTaskDisplayArea().createStack( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - } /** * Ensures that an activity is removed from the stopping activities list once it is resumed. @@ -72,7 +65,7 @@ public class ActivityTaskSupervisorTests extends WindowTestsBase { @Test public void testStoppingActivityRemovedWhenResumed() { final ActivityRecord firstActivity = new ActivityBuilder(mAtm) - .setTask(mFullscreenTask).build(); + .setCreateTask(true).build(); mSupervisor.mStoppingActivities.add(firstActivity); firstActivity.completeResumeLocked(); @@ -86,7 +79,7 @@ public class ActivityTaskSupervisorTests extends WindowTestsBase { @Test public void testReportWaitingActivityLaunchedIfNeeded() { final ActivityRecord firstActivity = new ActivityBuilder(mAtm) - .setTask(mFullscreenTask).build(); + .setCreateTask(true).build(); final WaitResult taskToFrontWait = new WaitResult(); mSupervisor.mWaitingActivityLaunched.add(taskToFrontWait); @@ -153,7 +146,7 @@ public class ActivityTaskSupervisorTests extends WindowTestsBase { @Test public void testNotifyTaskFocusChanged() { final ActivityRecord fullScreenActivityA = new ActivityBuilder(mAtm).setCreateTask(true) - .setParentTask(mFullscreenTask).build(); + .build(); final Task taskA = fullScreenActivityA.getTask(); final TaskChangeNotificationController taskChangeNotifier = @@ -166,7 +159,7 @@ public class ActivityTaskSupervisorTests extends WindowTestsBase { reset(taskChangeNotifier); final ActivityRecord fullScreenActivityB = new ActivityBuilder(mAtm).setCreateTask(true) - .setParentTask(mFullscreenTask).build(); + .build(); final Task taskB = fullScreenActivityB.getTask(); mAtm.setResumedActivityUncheckLocked(fullScreenActivityB, "resumeB"); @@ -200,4 +193,16 @@ public class ActivityTaskSupervisorTests extends WindowTestsBase { assertThat(allowedOnUntrusted).isFalse(); } + + /** + * We need to launch home again after user unlocked for those displays that do not have + * encryption aware home app. + */ + @Test + public void testStartHomeAfterUserUnlocked() { + mSupervisor.onUserUnlocked(0); + waitHandlerIdle(mAtm.mH); + verify(mRootWindowContainer, timeout(TimeUnit.SECONDS.toMillis(10))) + .startHomeOnEmptyDisplays("userUnlocked"); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java index 87a5985d507d..91b9449eddb0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java @@ -53,14 +53,12 @@ import org.junit.runner.RunWith; @RunWith(WindowTestRunner.class) public class AppChangeTransitionTests extends WindowTestsBase { - private Task mStack; private Task mTask; private ActivityRecord mActivity; public void setUpOnDisplay(DisplayContent dc) { mActivity = createActivityRecord(dc, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD); mTask = mActivity.getTask(); - mStack = mTask.getRootTask(); // Set a remote animator with snapshot disabled. Snapshots don't work in wmtests. RemoteAnimationDefinition definition = new RemoteAnimationDefinition(); @@ -143,7 +141,7 @@ public class AppChangeTransitionTests extends WindowTestsBase { // Reparenting to a display with different windowing mode may trigger // a change transition internally, but it should be cleaned-up once // the display change is complete. - mStack.reparent(mDisplayContent.getDefaultTaskDisplayArea(), true); + mTask.reparent(mDisplayContent.getDefaultTaskDisplayArea(), true); assertEquals(WINDOWING_MODE_FULLSCREEN, mTask.getWindowingMode()); diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java index f77454d440f9..d899ebe4bc1f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java @@ -65,7 +65,6 @@ import android.view.WindowManager; import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -82,94 +81,87 @@ import java.util.ArrayList; @RunWith(WindowTestRunner.class) public class AppWindowTokenTests extends WindowTestsBase { - Task mStack; - Task mTask; - ActivityRecord mActivity; - private final String mPackageName = getInstrumentation().getTargetContext().getPackageName(); - @Before - public void setUp() throws Exception { - mStack = createTaskStackOnDisplay(mDisplayContent); - mTask = createTaskInStack(mStack, 0 /* userId */); - mActivity = createNonAttachedActivityRecord(mDisplayContent); - - mTask.addChild(mActivity, 0); - } - @Test @Presubmit public void testAddWindow_Order() { - assertEquals(0, mActivity.getChildCount()); + final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build(); + assertEquals(0, activity.getChildCount()); - final WindowState win1 = createWindow(null, TYPE_APPLICATION, mActivity, "win1"); - final WindowState startingWin = createWindow(null, TYPE_APPLICATION_STARTING, mActivity, + final WindowState win1 = createWindow(null, TYPE_APPLICATION, activity, "win1"); + final WindowState startingWin = createWindow(null, TYPE_APPLICATION_STARTING, activity, "startingWin"); - final WindowState baseWin = createWindow(null, TYPE_BASE_APPLICATION, mActivity, "baseWin"); - final WindowState win4 = createWindow(null, TYPE_APPLICATION, mActivity, "win4"); + final WindowState baseWin = createWindow(null, TYPE_BASE_APPLICATION, activity, "baseWin"); + final WindowState win4 = createWindow(null, TYPE_APPLICATION, activity, "win4"); // Should not contain the windows that were added above. - assertEquals(4, mActivity.getChildCount()); - assertTrue(mActivity.mChildren.contains(win1)); - assertTrue(mActivity.mChildren.contains(startingWin)); - assertTrue(mActivity.mChildren.contains(baseWin)); - assertTrue(mActivity.mChildren.contains(win4)); + assertEquals(4, activity.getChildCount()); + assertTrue(activity.mChildren.contains(win1)); + assertTrue(activity.mChildren.contains(startingWin)); + assertTrue(activity.mChildren.contains(baseWin)); + assertTrue(activity.mChildren.contains(win4)); // The starting window should be on-top of all other windows. - assertEquals(startingWin, mActivity.mChildren.peekLast()); + assertEquals(startingWin, activity.mChildren.peekLast()); // The base application window should be below all other windows. - assertEquals(baseWin, mActivity.mChildren.peekFirst()); - mActivity.removeImmediately(); + assertEquals(baseWin, activity.mChildren.peekFirst()); + activity.removeImmediately(); } @Test @Presubmit public void testFindMainWindow() { - assertNull(mActivity.findMainWindow()); + final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build(); + assertNull(activity.findMainWindow()); - final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, mActivity, "window1"); - final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, mActivity, "window11"); - final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, mActivity, "window12"); - assertEquals(window1, mActivity.findMainWindow()); + final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, activity, "window1"); + final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, activity, "window11"); + final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, activity, "window12"); + assertEquals(window1, activity.findMainWindow()); window1.mAnimatingExit = true; - assertEquals(window1, mActivity.findMainWindow()); - final WindowState window2 = createWindow(null, TYPE_APPLICATION_STARTING, mActivity, + assertEquals(window1, activity.findMainWindow()); + final WindowState window2 = createWindow(null, TYPE_APPLICATION_STARTING, activity, "window2"); - assertEquals(window2, mActivity.findMainWindow()); - mActivity.removeImmediately(); + assertEquals(window2, activity.findMainWindow()); + activity.removeImmediately(); } @Test @Presubmit public void testGetTopFullscreenOpaqueWindow() { - assertNull(mActivity.getTopFullscreenOpaqueWindow()); + final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build(); + assertNull(activity.getTopFullscreenOpaqueWindow()); - final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, mActivity, "window1"); - final WindowState window11 = createWindow(null, TYPE_APPLICATION, mActivity, "window11"); - final WindowState window12 = createWindow(null, TYPE_APPLICATION, mActivity, "window12"); - assertEquals(window12, mActivity.getTopFullscreenOpaqueWindow()); + final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, activity, "window1"); + final WindowState window11 = createWindow(null, TYPE_APPLICATION, activity, "window11"); + final WindowState window12 = createWindow(null, TYPE_APPLICATION, activity, "window12"); + assertEquals(window12, activity.getTopFullscreenOpaqueWindow()); window12.mAttrs.width = 500; - assertEquals(window11, mActivity.getTopFullscreenOpaqueWindow()); + assertEquals(window11, activity.getTopFullscreenOpaqueWindow()); window11.mAttrs.width = 500; - assertEquals(window1, mActivity.getTopFullscreenOpaqueWindow()); + assertEquals(window1, activity.getTopFullscreenOpaqueWindow()); window1.mAttrs.alpha = 0f; - assertNull(mActivity.getTopFullscreenOpaqueWindow()); - mActivity.removeImmediately(); + assertNull(activity.getTopFullscreenOpaqueWindow()); + activity.removeImmediately(); } @UseTestDisplay(addWindows = W_ACTIVITY) @Test @FlakyTest(bugId = 131005232) public void testLandscapeSeascapeRotationByApp() { + final Task task = new TaskBuilder(mSupervisor) + .setDisplay(mDisplayContent).setCreateActivity(true).build(); + final ActivityRecord activity = task.getTopNonFinishingActivity(); final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams( TYPE_BASE_APPLICATION); attrs.setTitle("AppWindow"); - final TestWindowState appWindow = createWindowState(attrs, mActivity); - mActivity.addWindow(appWindow); + final TestWindowState appWindow = createWindowState(attrs, activity); + activity.addWindow(appWindow); // Set initial orientation and update. - mActivity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); + activity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); mDisplayContent.updateOrientation( mDisplayContent.getRequestedOverrideConfiguration(), null /* freezeThisOneIfNeeded */, false /* forceUpdate */); @@ -177,7 +169,7 @@ public class AppWindowTokenTests extends WindowTestsBase { appWindow.mResizeReported = false; // Update the orientation to perform 180 degree rotation and check that resize was reported. - mActivity.setOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE); + activity.setOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE); mDisplayContent.updateOrientation( mDisplayContent.getRequestedOverrideConfiguration(), null /* freezeThisOneIfNeeded */, false /* forceUpdate */); @@ -192,14 +184,17 @@ public class AppWindowTokenTests extends WindowTestsBase { @UseTestDisplay(addWindows = W_ACTIVITY) @Test public void testLandscapeSeascapeRotationByPolicy() { + final Task task = new TaskBuilder(mSupervisor) + .setDisplay(mDisplayContent).setCreateActivity(true).build(); + final ActivityRecord activity = task.getTopNonFinishingActivity(); // This instance has been spied in {@link TestDisplayContent}. final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation(); final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams( TYPE_BASE_APPLICATION); attrs.setTitle("RotationByPolicy"); - final TestWindowState appWindow = createWindowState(attrs, mActivity); - mActivity.addWindow(appWindow); + final TestWindowState appWindow = createWindowState(attrs, activity); + activity.addWindow(appWindow); // Set initial orientation and update. performRotation(displayRotation, Surface.ROTATION_90); @@ -220,48 +215,53 @@ public class AppWindowTokenTests extends WindowTestsBase { @Test @Presubmit public void testGetOrientation() { - mActivity.setVisible(true); + final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build(); + activity.setVisible(true); - mActivity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); + activity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); - mActivity.setOccludesParent(false); + activity.setOccludesParent(false); // Can specify orientation if app doesn't occludes parent. - assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mActivity.getOrientation()); + assertEquals(SCREEN_ORIENTATION_LANDSCAPE, activity.getOrientation()); - mActivity.setOccludesParent(true); - mActivity.setVisible(false); + activity.setOccludesParent(true); + activity.setVisible(false); // Can not specify orientation if app isn't visible even though it occludes parent. - assertEquals(SCREEN_ORIENTATION_UNSET, mActivity.getOrientation()); + assertEquals(SCREEN_ORIENTATION_UNSET, activity.getOrientation()); // Can specify orientation if the current orientation candidate is orientation behind. assertEquals(SCREEN_ORIENTATION_LANDSCAPE, - mActivity.getOrientation(SCREEN_ORIENTATION_BEHIND)); + activity.getOrientation(SCREEN_ORIENTATION_BEHIND)); } @Test @Presubmit public void testKeyguardFlagsDuringRelaunch() { + final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build(); final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams( TYPE_BASE_APPLICATION); attrs.flags |= FLAG_SHOW_WHEN_LOCKED | FLAG_DISMISS_KEYGUARD; attrs.setTitle("AppWindow"); - final TestWindowState appWindow = createWindowState(attrs, mActivity); + final TestWindowState appWindow = createWindowState(attrs, activity); // Add window with show when locked flag - mActivity.addWindow(appWindow); - assertTrue(mActivity.containsShowWhenLockedWindow() && mActivity.containsDismissKeyguardWindow()); + activity.addWindow(appWindow); + assertTrue(activity.containsShowWhenLockedWindow() + && activity.containsDismissKeyguardWindow()); // Start relaunching - mActivity.startRelaunching(); - assertTrue(mActivity.containsShowWhenLockedWindow() && mActivity.containsDismissKeyguardWindow()); + activity.startRelaunching(); + assertTrue(activity.containsShowWhenLockedWindow() + && activity.containsDismissKeyguardWindow()); // Remove window and make sure that we still report back flag - mActivity.removeChild(appWindow); - assertTrue(mActivity.containsShowWhenLockedWindow() && mActivity.containsDismissKeyguardWindow()); + activity.removeChild(appWindow); + assertTrue(activity.containsShowWhenLockedWindow() + && activity.containsDismissKeyguardWindow()); // Finish relaunching and ensure flag is now not reported - mActivity.finishRelaunching(); - assertFalse( - mActivity.containsShowWhenLockedWindow() || mActivity.containsDismissKeyguardWindow()); + activity.finishRelaunching(); + assertFalse(activity.containsShowWhenLockedWindow() + || activity.containsDismissKeyguardWindow()); } @Test @@ -281,17 +281,18 @@ public class AppWindowTokenTests extends WindowTestsBase { @Test public void testSetOrientation() { - mActivity.setVisible(true); + final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build(); + activity.setVisible(true); // Assert orientation is unspecified to start. - assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, mActivity.getOrientation()); + assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, activity.getOrientation()); - mActivity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); - assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mActivity.getOrientation()); + activity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); + assertEquals(SCREEN_ORIENTATION_LANDSCAPE, activity.getOrientation()); - mDisplayContent.removeAppToken(mActivity.token); + mDisplayContent.removeAppToken(activity.token); // Assert orientation is unset to after container is removed. - assertEquals(SCREEN_ORIENTATION_UNSET, mActivity.getOrientation()); + assertEquals(SCREEN_ORIENTATION_UNSET, activity.getOrientation()); // Reset display frozen state mWm.mDisplayFrozen = false; @@ -300,14 +301,15 @@ public class AppWindowTokenTests extends WindowTestsBase { @UseTestDisplay @Test public void testRespectTopFullscreenOrientation() { - final Configuration displayConfig = mActivity.mDisplayContent.getConfiguration(); - final Configuration activityConfig = mActivity.getConfiguration(); - mActivity.setOrientation(SCREEN_ORIENTATION_PORTRAIT); + final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build(); + final Configuration displayConfig = activity.mDisplayContent.getConfiguration(); + final Configuration activityConfig = activity.getConfiguration(); + activity.setOrientation(SCREEN_ORIENTATION_PORTRAIT); assertEquals(Configuration.ORIENTATION_PORTRAIT, displayConfig.orientation); assertEquals(Configuration.ORIENTATION_PORTRAIT, activityConfig.orientation); - final ActivityRecord topActivity = createActivityRecord(mTask); + final ActivityRecord topActivity = createActivityRecord(activity.getTask()); topActivity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); assertEquals(Configuration.ORIENTATION_LANDSCAPE, displayConfig.orientation); @@ -322,32 +324,36 @@ public class AppWindowTokenTests extends WindowTestsBase { @UseTestDisplay @Test public void testReportOrientationChange() { - mActivity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); + final Task task = new TaskBuilder(mSupervisor) + .setDisplay(mDisplayContent).setCreateActivity(true).build(); + final ActivityRecord activity = task.getTopNonFinishingActivity(); + activity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); mDisplayContent.getDisplayRotation().setFixedToUserRotation( IWindowManager.FIXED_TO_USER_ROTATION_ENABLED); - reset(mTask); - mActivity.reportDescendantOrientationChangeIfNeeded(); - verify(mTask).onConfigurationChanged(any(Configuration.class)); + reset(task); + activity.reportDescendantOrientationChangeIfNeeded(); + verify(task).onConfigurationChanged(any(Configuration.class)); } @Test @FlakyTest(bugId = 131176283) public void testCreateRemoveStartingWindow() { - mActivity.addStartingWindow(mPackageName, + final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build(); + activity.addStartingWindow(mPackageName, android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, false); waitUntilHandlersIdle(); - assertHasStartingWindow(mActivity); - mActivity.removeStartingWindow(); + assertHasStartingWindow(activity); + activity.removeStartingWindow(); waitUntilHandlersIdle(); - assertNoStartingWindow(mActivity); + assertNoStartingWindow(activity); } @Test public void testAddRemoveRace() { // There was once a race condition between adding and removing starting windows - final ActivityRecord appToken = createIsolatedTestActivityRecord(); + final ActivityRecord appToken = new ActivityBuilder(mAtm).setCreateTask(true).build(); for (int i = 0; i < 1000; i++) { appToken.addStartingWindow(mPackageName, android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, @@ -360,8 +366,8 @@ public class AppWindowTokenTests extends WindowTestsBase { @Test public void testTransferStartingWindow() { - final ActivityRecord activity1 = createIsolatedTestActivityRecord(); - final ActivityRecord activity2 = createIsolatedTestActivityRecord(); + final ActivityRecord activity1 = new ActivityBuilder(mAtm).setCreateTask(true).build(); + final ActivityRecord activity2 = new ActivityBuilder(mAtm).setCreateTask(true).build(); activity1.addStartingWindow(mPackageName, android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, false); @@ -376,8 +382,8 @@ public class AppWindowTokenTests extends WindowTestsBase { @Test public void testTransferStartingWindowWhileCreating() { - final ActivityRecord activity1 = createIsolatedTestActivityRecord(); - final ActivityRecord activity2 = createIsolatedTestActivityRecord(); + final ActivityRecord activity1 = new ActivityBuilder(mAtm).setCreateTask(true).build(); + final ActivityRecord activity2 = new ActivityBuilder(mAtm).setCreateTask(true).build(); ((TestWindowManagerPolicy) activity1.mWmService.mPolicy).setRunnableWhenAddingSplashScreen( () -> { // Surprise, ...! Transfer window in the middle of the creation flow. @@ -396,8 +402,8 @@ public class AppWindowTokenTests extends WindowTestsBase { @Test public void testTransferStartingWindowCanAnimate() { - final ActivityRecord activity1 = createIsolatedTestActivityRecord(); - final ActivityRecord activity2 = createIsolatedTestActivityRecord(); + final ActivityRecord activity1 = new ActivityBuilder(mAtm).setCreateTask(true).build(); + final ActivityRecord activity2 = new ActivityBuilder(mAtm).setCreateTask(true).build(); activity1.addStartingWindow(mPackageName, android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, false); @@ -419,34 +425,34 @@ public class AppWindowTokenTests extends WindowTestsBase { @Test public void testTransferStartingWindowFromFinishingActivity() { - mActivity.addStartingWindow(mPackageName, android.R.style.Theme, null /* compatInfo */, + final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build(); + final Task task = activity.getTask(); + activity.addStartingWindow(mPackageName, android.R.style.Theme, null /* compatInfo */, "Test", 0 /* labelRes */, 0 /* icon */, 0 /* logo */, 0 /* windowFlags */, null /* transferFrom */, true /* newTask */, true /* taskSwitch */, false /* processRunning */, false /* allowTaskSnapshot */, false /* activityCreate */); waitUntilHandlersIdle(); - assertHasStartingWindow(mActivity); - mActivity.mStartingWindowState = ActivityRecord.STARTING_WINDOW_SHOWN; + assertHasStartingWindow(activity); + activity.mStartingWindowState = ActivityRecord.STARTING_WINDOW_SHOWN; - doCallRealMethod().when(mStack).startActivityLocked( + doCallRealMethod().when(task).startActivityLocked( any(), any(), anyBoolean(), anyBoolean(), any()); // Make mVisibleSetFromTransferredStartingWindow true. - final ActivityRecord middle = new ActivityBuilder(mWm.mAtmService) - .setTask(mTask).build(); - mStack.startActivityLocked(middle, null /* focusedTopActivity */, + final ActivityRecord middle = new ActivityBuilder(mAtm).setTask(task).build(); + task.startActivityLocked(middle, null /* focusedTopActivity */, false /* newTask */, false /* keepCurTransition */, null /* options */); middle.makeFinishingLocked(); - assertNull(mActivity.mStartingWindow); + assertNull(activity.mStartingWindow); assertHasStartingWindow(middle); - final ActivityRecord top = new ActivityBuilder(mWm.mAtmService) - .setTask(mTask).build(); + final ActivityRecord top = new ActivityBuilder(mAtm).setTask(task).build(); // Expect the visibility should be updated to true when transferring starting window from // a visible activity. top.setVisible(false); // The finishing middle should be able to transfer starting window to top. - mStack.startActivityLocked(top, null /* focusedTopActivity */, + task.startActivityLocked(top, null /* focusedTopActivity */, false /* newTask */, false /* keepCurTransition */, null /* options */); assertNull(middle.mStartingWindow); @@ -459,10 +465,12 @@ public class AppWindowTokenTests extends WindowTestsBase { @Test public void testTransferStartingWindowSetFixedRotation() { - final ActivityRecord topActivity = createTestActivityRecordForGivenTask(mTask); + final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build(); + final Task task = activity.getTask(); + final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build(); topActivity.setVisible(false); - mTask.positionChildAt(topActivity, POSITION_TOP); - mActivity.addStartingWindow(mPackageName, + task.positionChildAt(topActivity, POSITION_TOP); + activity.addStartingWindow(mPackageName, android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, false); waitUntilHandlersIdle(); @@ -470,38 +478,25 @@ public class AppWindowTokenTests extends WindowTestsBase { // Make activities to have different rotation from it display and set fixed rotation // transform to activity1. int rotation = (mDisplayContent.getRotation() + 1) % 4; - mDisplayContent.setFixedRotationLaunchingApp(mActivity, rotation); + mDisplayContent.setFixedRotationLaunchingApp(activity, rotation); doReturn(rotation).when(mDisplayContent) .rotationForActivityInDifferentOrientation(topActivity); // Make sure the fixed rotation transform linked to activity2 when adding starting window // on activity2. topActivity.addStartingWindow(mPackageName, - android.R.style.Theme, null, "Test", 0, 0, 0, 0, mActivity.appToken.asBinder(), + android.R.style.Theme, null, "Test", 0, 0, 0, 0, activity.appToken.asBinder(), false, false, false, true, false); waitUntilHandlersIdle(); assertTrue(topActivity.hasFixedRotationTransform()); } - private ActivityRecord createIsolatedTestActivityRecord() { - final Task taskStack = createTaskStackOnDisplay(mDisplayContent); - final Task task = createTaskInStack(taskStack, 0 /* userId */); - return createTestActivityRecordForGivenTask(task); - } - - private ActivityRecord createTestActivityRecordForGivenTask(Task task) { - final ActivityRecord activity = createNonAttachedActivityRecord(mDisplayContent); - task.addChild(activity, 0); - waitUntilHandlersIdle(); - return activity; - } - @Test public void testTryTransferStartingWindowFromHiddenAboveToken() { // Add two tasks on top of each other. - final ActivityRecord activityTop = createIsolatedTestActivityRecord(); - final ActivityRecord activityBottom = - createTestActivityRecordForGivenTask(activityTop.getTask()); + final ActivityRecord activityTop = new ActivityBuilder(mAtm).setCreateTask(true).build(); + final ActivityRecord activityBottom = new ActivityBuilder(mAtm).build(); + activityTop.getTask().addChild(activityBottom, 0); // Add a starting window. activityTop.addStartingWindow(mPackageName, @@ -523,53 +518,58 @@ public class AppWindowTokenTests extends WindowTestsBase { @Test public void testTransitionAnimationBounds() { removeGlobalMinSizeRestriction(); + final Task task = new TaskBuilder(mSupervisor) + .setCreateParentTask(true).setCreateActivity(true).build(); + final Task rootTask = task.getRootTask(); + final ActivityRecord activity = task.getTopNonFinishingActivity(); final Rect stackBounds = new Rect(0, 0, 1000, 600); final Rect taskBounds = new Rect(100, 400, 600, 800); // Set the bounds and windowing mode to window configuration directly, otherwise the // testing setups may be discarded by configuration resolving. - mStack.getWindowConfiguration().setBounds(stackBounds); - mTask.getWindowConfiguration().setBounds(taskBounds); - mActivity.getWindowConfiguration().setBounds(taskBounds); + rootTask.getWindowConfiguration().setBounds(stackBounds); + task.getWindowConfiguration().setBounds(taskBounds); + activity.getWindowConfiguration().setBounds(taskBounds); // Check that anim bounds for freeform window match task bounds - mTask.getWindowConfiguration().setWindowingMode(WINDOWING_MODE_FREEFORM); - assertEquals(mTask.getBounds(), mActivity.getAnimationBounds(STACK_CLIP_NONE)); + task.getWindowConfiguration().setWindowingMode(WINDOWING_MODE_FREEFORM); + assertEquals(task.getBounds(), activity.getAnimationBounds(STACK_CLIP_NONE)); // STACK_CLIP_AFTER_ANIM should use task bounds since they will be clipped by // bounds animation layer. - mTask.getWindowConfiguration().setWindowingMode(WINDOWING_MODE_FULLSCREEN); - assertEquals(mTask.getBounds(), mActivity.getAnimationBounds(STACK_CLIP_AFTER_ANIM)); + task.getWindowConfiguration().setWindowingMode(WINDOWING_MODE_FULLSCREEN); + assertEquals(task.getBounds(), activity.getAnimationBounds(STACK_CLIP_AFTER_ANIM)); // Even the activity is smaller than task and it is not aligned to the top-left corner of // task, the animation bounds the same as task and position should be zero because in real // case the letterbox will fill the remaining area in task. final Rect halfBounds = new Rect(taskBounds); halfBounds.scale(0.5f); - mActivity.getWindowConfiguration().setBounds(halfBounds); + activity.getWindowConfiguration().setBounds(halfBounds); final Point animationPosition = new Point(); - mActivity.getAnimationPosition(animationPosition); + activity.getAnimationPosition(animationPosition); - assertEquals(taskBounds, mActivity.getAnimationBounds(STACK_CLIP_AFTER_ANIM)); + assertEquals(taskBounds, activity.getAnimationBounds(STACK_CLIP_AFTER_ANIM)); assertEquals(new Point(0, 0), animationPosition); // STACK_CLIP_BEFORE_ANIM should use stack bounds since it won't be clipped later. - mTask.getWindowConfiguration().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); - assertEquals(mStack.getBounds(), mActivity.getAnimationBounds(STACK_CLIP_BEFORE_ANIM)); + task.getWindowConfiguration().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); + assertEquals(rootTask.getBounds(), activity.getAnimationBounds(STACK_CLIP_BEFORE_ANIM)); } @Test public void testHasStartingWindow() { + final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build(); final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(TYPE_APPLICATION_STARTING); - final TestWindowState startingWindow = createWindowState(attrs, mActivity); - mActivity.startingDisplayed = true; - mActivity.addWindow(startingWindow); - assertTrue("Starting window should be present", mActivity.hasStartingWindow()); - mActivity.startingDisplayed = false; - assertTrue("Starting window should be present", mActivity.hasStartingWindow()); - - mActivity.removeChild(startingWindow); - assertFalse("Starting window should not be present", mActivity.hasStartingWindow()); + final TestWindowState startingWindow = createWindowState(attrs, activity); + activity.startingDisplayed = true; + activity.addWindow(startingWindow); + assertTrue("Starting window should be present", activity.hasStartingWindow()); + activity.startingDisplayed = false; + assertTrue("Starting window should be present", activity.hasStartingWindow()); + + activity.removeChild(startingWindow); + assertFalse("Starting window should not be present", activity.hasStartingWindow()); } private void assertHasStartingWindow(ActivityRecord atoken) { diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java index cfe956f77bd3..06a6882dc698 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java @@ -16,8 +16,6 @@ package com.android.server.wm; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; @@ -52,8 +50,6 @@ public class DisplayAreaGroupTest extends WindowTestsBase { private DisplayAreaGroup mDisplayAreaGroup; private TaskDisplayArea mTaskDisplayArea; - private Task mStack; - private ActivityRecord mActivity; @Before public void setUp() { @@ -65,9 +61,6 @@ public class DisplayAreaGroupTest extends WindowTestsBase { mTaskDisplayArea = new TaskDisplayArea( mDisplayContent, mWm, "TDA1", FEATURE_VENDOR_FIRST + 1); mDisplayAreaGroup.addChild(mTaskDisplayArea, POSITION_TOP); - mStack = mTaskDisplayArea.createStack( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - mActivity = new ActivityBuilder(mAtm).setTask(mStack).build(); mDisplayContent.setLastFocusedTaskDisplayArea(mTaskDisplayArea); } @@ -91,28 +84,31 @@ public class DisplayAreaGroupTest extends WindowTestsBase { @Test public void testGetRequestedOrientationForDisplay() { + final Task task = new TaskBuilder(mSupervisor) + .setTaskDisplayArea(mTaskDisplayArea).setCreateActivity(true).build(); + final ActivityRecord activity = task.getTopNonFinishingActivity(); doReturn(true).when(mDisplayContent).onDescendantOrientationChanged(any(), any()); - mActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT); + activity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT); // Display is portrait, DisplayAreaGroup inherits that mDisplayContent.setBounds(0, 0, 600, 900); assertThat(mDisplayAreaGroup.getOrientation()).isEqualTo(SCREEN_ORIENTATION_PORTRAIT); - assertThat(mActivity.getRequestedConfigurationOrientation(true /* forDisplay */)) + assertThat(activity.getRequestedConfigurationOrientation(true /* forDisplay */)) .isEqualTo(ORIENTATION_PORTRAIT); // DisplayAreaGroup is landscape, different from Display mDisplayAreaGroup.setBounds(0, 0, 600, 450); assertThat(mDisplayAreaGroup.getOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE); - assertThat(mActivity.getRequestedConfigurationOrientation(true /* forDisplay */)) + assertThat(activity.getRequestedConfigurationOrientation(true /* forDisplay */)) .isEqualTo(ORIENTATION_LANDSCAPE); // DisplayAreaGroup is portrait, same as Display mDisplayAreaGroup.setBounds(0, 0, 300, 900); assertThat(mDisplayAreaGroup.getOrientation()).isEqualTo(SCREEN_ORIENTATION_PORTRAIT); - assertThat(mActivity.getRequestedConfigurationOrientation(true /* forDisplay */)) + assertThat(activity.getRequestedConfigurationOrientation(true /* forDisplay */)) .isEqualTo(ORIENTATION_PORTRAIT); } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java index 3220d1d6a990..1198ee2ba5f4 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java @@ -16,8 +16,13 @@ package com.android.server.wm; +import static android.view.Display.DEFAULT_DISPLAY; +import static android.window.DisplayAreaOrganizer.FEATURE_ROOT; +import static android.window.DisplayAreaOrganizer.FEATURE_RUNTIME_TASK_CONTAINER_FIRST; import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -25,6 +30,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.testng.Assert.assertThrows; import android.content.res.Configuration; import android.graphics.Rect; @@ -55,9 +61,12 @@ import java.util.List; public class DisplayAreaOrganizerTest extends WindowTestsBase { private DisplayArea mTestDisplayArea; + private DisplayAreaOrganizerController mOrganizerController; @Before public void setUp() { + mOrganizerController = + mWm.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController; WindowContainer parentWindow = mDisplayContent.getDefaultTaskDisplayArea().getParent(); mTestDisplayArea = new DisplayArea(mWm, DisplayArea.Type.ANY, "TestDisplayArea", FEATURE_VENDOR_FIRST); @@ -76,8 +85,7 @@ public class DisplayAreaOrganizerTest extends WindowTestsBase { private IDisplayAreaOrganizer registerMockOrganizer(int feature, Binder binder) { final IDisplayAreaOrganizer organizer = createMockOrganizer(binder); - mWm.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController - .registerOrganizer(organizer, feature); + mOrganizerController.registerOrganizer(organizer, feature); return organizer; } @@ -87,16 +95,10 @@ public class DisplayAreaOrganizerTest extends WindowTestsBase { return organizer; } - private void unregisterMockOrganizer(IDisplayAreaOrganizer organizer) { - mWm.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController - .unregisterOrganizer(organizer); - } - @Test public void testRegisterOrganizer() throws RemoteException { - IDisplayAreaOrganizer organizer = createMockOrganizer(new Binder()); - List<DisplayAreaAppearedInfo> infos = mWm.mAtmService.mWindowOrganizerController - .mDisplayAreaOrganizerController + final IDisplayAreaOrganizer organizer = createMockOrganizer(new Binder()); + List<DisplayAreaAppearedInfo> infos = mOrganizerController .registerOrganizer(organizer, FEATURE_VENDOR_FIRST).getList(); // Return a list contains the DA, and no onDisplayAreaAppeared triggered. @@ -108,16 +110,135 @@ public class DisplayAreaOrganizerTest extends WindowTestsBase { } @Test + public void testRegisterOrganizer_alreadyRegisteredFeature() { + registerMockOrganizer(FEATURE_VENDOR_FIRST); + assertThrows(IllegalStateException.class, + () -> registerMockOrganizer(FEATURE_VENDOR_FIRST)); + } + + @Test + public void testCreateTaskDisplayArea() { + final String newTdaName = "testTda"; + final IDisplayAreaOrganizer organizer = createMockOrganizer(new Binder()); + final DisplayAreaAppearedInfo tdaInfo = mOrganizerController.createTaskDisplayArea( + organizer, DEFAULT_DISPLAY, FEATURE_ROOT, newTdaName); + + final int newTdaIndex = + mTestDisplayArea.getParent().mChildren.indexOf(mTestDisplayArea) + 1; + final WindowContainer wc = mTestDisplayArea.getParent().getChildAt(newTdaIndex); + + // A new TaskDisplayArea is created on the top. + assertThat(wc).isInstanceOf(TaskDisplayArea.class); + assertThat(tdaInfo.getDisplayAreaInfo().displayId).isEqualTo(DEFAULT_DISPLAY); + assertThat(tdaInfo.getDisplayAreaInfo().token) + .isEqualTo(wc.mRemoteToken.toWindowContainerToken()); + + final TaskDisplayArea tda = wc.asTaskDisplayArea(); + + assertThat(tda.getName()).isEqualTo(newTdaName); + assertThat(tda.mFeatureId).isEqualTo(tdaInfo.getDisplayAreaInfo().featureId); + assertThat(tda.mCreatedByOrganizer).isTrue(); + assertThat(tda.mOrganizer).isEqualTo(organizer); + } + + @Test + public void testCreateTaskDisplayArea_incrementalTdaFeatureId() { + final String newTdaName = "testTda"; + final IDisplayAreaOrganizer organizer = createMockOrganizer(new Binder()); + final DisplayAreaAppearedInfo tdaInfo1 = mOrganizerController.createTaskDisplayArea( + organizer, DEFAULT_DISPLAY, FEATURE_ROOT, newTdaName); + final DisplayAreaAppearedInfo tdaInfo2 = mOrganizerController.createTaskDisplayArea( + organizer, DEFAULT_DISPLAY, FEATURE_ROOT, newTdaName); + + // New created TDA has unique feature id starting from FEATURE_RUNTIME_TASK_CONTAINER_FIRST. + assertThat(tdaInfo1.getDisplayAreaInfo().featureId).isEqualTo( + FEATURE_RUNTIME_TASK_CONTAINER_FIRST); + assertThat(tdaInfo2.getDisplayAreaInfo().featureId).isEqualTo( + FEATURE_RUNTIME_TASK_CONTAINER_FIRST + 1); + } + + + @Test + public void testCreateTaskDisplayArea_invalidDisplayAndRoot() { + final IDisplayAreaOrganizer organizer = createMockOrganizer(new Binder()); + assertThrows(IllegalArgumentException.class, () -> + mOrganizerController.createTaskDisplayArea( + organizer, SystemServicesTestRule.sNextDisplayId + 1, FEATURE_ROOT, + "testTda")); + assertThrows(IllegalArgumentException.class, () -> + mOrganizerController.createTaskDisplayArea( + organizer, DEFAULT_DISPLAY, FEATURE_ROOT - 1, "testTda")); + } + + @Test + public void testDeleteTaskDisplayArea() { + final String newTdaName = "testTda"; + final IDisplayAreaOrganizer organizer = createMockOrganizer(new Binder()); + final DisplayAreaAppearedInfo tdaInfo = mOrganizerController.createTaskDisplayArea( + organizer, DEFAULT_DISPLAY, FEATURE_ROOT, newTdaName); + final int tdaFeatureId = tdaInfo.getDisplayAreaInfo().featureId; + + final TaskDisplayArea newTda = mDisplayContent.getItemFromDisplayAreas( + da -> da.mFeatureId == tdaFeatureId ? da.asTaskDisplayArea() : null); + spyOn(newTda); + + mOrganizerController.deleteTaskDisplayArea(newTda.mRemoteToken.toWindowContainerToken()); + + verify(newTda).remove(); + verify(newTda).removeImmediately(); + assertThat(newTda.mOrganizer).isNull(); + assertThat(newTda.isRemoved()).isTrue(); + + final TaskDisplayArea curTda = mDisplayContent.getItemFromDisplayAreas( + da -> da.mFeatureId == tdaFeatureId ? da.asTaskDisplayArea() : null); + + assertThat(curTda).isNull(); + } + + @Test + public void testUnregisterOrganizer_deleteNewCreatedTaskDisplayArea() { + final String newTdaName = "testTda"; + final IDisplayAreaOrganizer organizer = createMockOrganizer(new Binder()); + final DisplayAreaAppearedInfo tdaInfo = mOrganizerController.createTaskDisplayArea( + organizer, DEFAULT_DISPLAY, FEATURE_ROOT, newTdaName); + final int tdaFeatureId = tdaInfo.getDisplayAreaInfo().featureId; + + final TaskDisplayArea newTda = mDisplayContent.getItemFromDisplayAreas( + da -> da.mFeatureId == tdaFeatureId ? da.asTaskDisplayArea() : null); + spyOn(newTda); + + mOrganizerController.unregisterOrganizer(organizer); + + verify(newTda).remove(); + verify(newTda).removeImmediately(); + assertThat(newTda.mOrganizer).isNull(); + assertThat(newTda.isRemoved()).isTrue(); + + final TaskDisplayArea curTda = mDisplayContent.getItemFromDisplayAreas( + da -> da.mFeatureId == tdaFeatureId ? da.asTaskDisplayArea() : null); + + assertThat(curTda).isNull(); + } + + @Test + public void testDeleteTaskDisplayArea_invalidTaskDisplayArea() { + final TaskDisplayArea tda = mDisplayContent.getDefaultTaskDisplayArea(); + assertThrows(IllegalArgumentException.class, () -> + mOrganizerController.deleteTaskDisplayArea( + tda.mRemoteToken.toWindowContainerToken())); + } + + @Test public void testAppearedVanished() throws RemoteException { - IDisplayAreaOrganizer organizer = registerMockOrganizer(FEATURE_VENDOR_FIRST); - unregisterMockOrganizer(organizer); + final IDisplayAreaOrganizer organizer = registerMockOrganizer(FEATURE_VENDOR_FIRST); + mOrganizerController.unregisterOrganizer(organizer); verify(organizer).onDisplayAreaVanished(any()); } @Test public void testChanged() throws RemoteException { - IDisplayAreaOrganizer organizer = registerMockOrganizer(FEATURE_VENDOR_FIRST); + final IDisplayAreaOrganizer organizer = registerMockOrganizer(FEATURE_VENDOR_FIRST); mDisplayContent.setBounds(new Rect(0, 0, 1000, 1000)); verify(organizer).onDisplayAreaInfoChanged(any()); @@ -137,7 +258,7 @@ public class DisplayAreaOrganizerTest extends WindowTestsBase { assertThat(mTestDisplayArea.mOrganizer).isNotNull(); - unregisterMockOrganizer(createMockOrganizer(binder)); + mOrganizerController.unregisterOrganizer(createMockOrganizer(binder)); assertThat(mTestDisplayArea.mOrganizer).isNull(); } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java index 21bdc9e7785e..79b2da187680 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java @@ -236,8 +236,7 @@ public class DisplayPolicyTests extends WindowTestsBase { final WindowState activity = createBaseApplicationWindow(); activity.mAttrs.privateFlags |= PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS; - policy.adjustWindowParamsLw(activity, activity.mAttrs, 0 /* callingPid */, - 0 /* callingUid */); + policy.adjustWindowParamsLw(activity, activity.mAttrs); } private WindowState createApplicationWindow() { diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java index 94ffcdab4fa7..20775e84fd8f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java @@ -107,11 +107,9 @@ public class DisplayPolicyTestsBase extends WindowTestsBase { } void addWindow(WindowState win) { - final int callingPid = Binder.getCallingPid(); - final int callingUid = Binder.getCallingUid(); - mDisplayPolicy.adjustWindowParamsLw(win, win.mAttrs, callingPid, callingUid); - assertEquals(WindowManagerGlobal.ADD_OKAY, - mDisplayPolicy.validateAddingWindowLw(win.mAttrs, callingPid, callingUid)); + mDisplayPolicy.adjustWindowParamsLw(win, win.mAttrs); + assertEquals(WindowManagerGlobal.ADD_OKAY, mDisplayPolicy.validateAddingWindowLw( + win.mAttrs, Binder.getCallingPid(), Binder.getCallingUid())); mDisplayPolicy.addWindowLw(win, win.mAttrs); win.mHasSurface = true; } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java index b346bb810b2e..eaedd58001c1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java @@ -17,6 +17,7 @@ package com.android.server.wm; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; @@ -167,7 +168,7 @@ public class DisplayWindowSettingsProviderTests extends WindowTestsBase { mBaseSettingsStorage, mOverrideSettingsStorage); SettingsEntry overrideSettings = provider.getOverrideSettings(secondaryDisplayInfo); overrideSettings.mShouldShowSystemDecors = true; - overrideSettings.mShouldShowIme = true; + overrideSettings.mImePolicy = DISPLAY_IME_POLICY_LOCAL; provider.updateOverrideSettings(secondaryDisplayInfo, overrideSettings); assertTrue(mOverrideSettingsStorage.wasWriteSuccessful()); @@ -176,8 +177,8 @@ public class DisplayWindowSettingsProviderTests extends WindowTestsBase { getStoredDisplayAttributeValue(mOverrideSettingsStorage, "name")); assertEquals("Attribute value must be stored", "true", getStoredDisplayAttributeValue(mOverrideSettingsStorage, "shouldShowSystemDecors")); - assertEquals("Attribute value must be stored", "true", - getStoredDisplayAttributeValue(mOverrideSettingsStorage, "shouldShowIme")); + assertEquals("Attribute value must be stored", "0", + getStoredDisplayAttributeValue(mOverrideSettingsStorage, "imePolicy")); } @Test @@ -195,7 +196,7 @@ public class DisplayWindowSettingsProviderTests extends WindowTestsBase { mBaseSettingsStorage, mOverrideSettingsStorage); SettingsEntry overrideSettings = provider.getOverrideSettings(secondaryDisplayInfo); overrideSettings.mShouldShowSystemDecors = true; - overrideSettings.mShouldShowIme = true; + overrideSettings.mImePolicy = DISPLAY_IME_POLICY_LOCAL; provider.updateOverrideSettings(secondaryDisplayInfo, overrideSettings); assertTrue(mOverrideSettingsStorage.wasWriteSuccessful()); @@ -204,8 +205,8 @@ public class DisplayWindowSettingsProviderTests extends WindowTestsBase { getStoredDisplayAttributeValue(mOverrideSettingsStorage, "name")); assertEquals("Attribute value must be stored", "true", getStoredDisplayAttributeValue(mOverrideSettingsStorage, "shouldShowSystemDecors")); - assertEquals("Attribute value must be stored", "true", - getStoredDisplayAttributeValue(mOverrideSettingsStorage, "shouldShowIme")); + assertEquals("Attribute value must be stored", "0", + getStoredDisplayAttributeValue(mOverrideSettingsStorage, "imePolicy")); } /** diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java index 2ca5583c48c3..9e4cd161c478 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java @@ -22,6 +22,8 @@ import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_DISABLED; import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_ENABLED; import static android.view.WindowManager.REMOVE_CONTENT_MODE_DESTROY; import static android.view.WindowManager.REMOVE_CONTENT_MODE_MOVE_TO_PRIMARY; +import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL; +import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; @@ -49,7 +51,6 @@ import androidx.test.filters.SmallTest; import com.android.server.LocalServices; import com.android.server.policy.WindowManagerPolicy; -import com.android.server.wm.DisplayWindowSettings.SettingsProvider.SettingsEntry; import org.junit.Before; import org.junit.Test; @@ -319,17 +320,21 @@ public class DisplayWindowSettingsTests extends WindowTestsBase { } @Test - public void testPrimaryDisplayShouldShowIme() { - assertTrue(mDisplayWindowSettings.shouldShowImeLocked(mPrimaryDisplay)); + public void testPrimaryDisplayImePolicy() { + assertEquals(DISPLAY_IME_POLICY_LOCAL, + mDisplayWindowSettings.getImePolicyLocked(mPrimaryDisplay)); - mDisplayWindowSettings.setShouldShowImeLocked(mPrimaryDisplay, false); + mDisplayWindowSettings.setDisplayImePolicy(mPrimaryDisplay, + DISPLAY_IME_POLICY_FALLBACK_DISPLAY); - assertTrue(mDisplayWindowSettings.shouldShowImeLocked(mPrimaryDisplay)); + assertEquals(DISPLAY_IME_POLICY_LOCAL, + mDisplayWindowSettings.getImePolicyLocked(mPrimaryDisplay)); } @Test - public void testSecondaryDisplayDefaultToNotShowIme() { - assertFalse(mDisplayWindowSettings.shouldShowImeLocked(mSecondaryDisplay)); + public void testSecondaryDisplayDefaultToShowImeOnFallbackDisplay() { + assertEquals(DISPLAY_IME_POLICY_FALLBACK_DISPLAY, + mDisplayWindowSettings.getImePolicyLocked(mSecondaryDisplay)); } @Test @@ -400,17 +405,18 @@ public class DisplayWindowSettingsTests extends WindowTestsBase { } @Test - public void testShouldShowImeWithinForceDesktopMode() { + public void testShouldShowImeOnDisplayWithinForceDesktopMode() { try { // Presume display enabled force desktop mode from developer options. final DisplayContent dc = createMockSimulatedDisplay(); mWm.setForceDesktopModeOnExternalDisplays(true); final WindowManagerInternal wmInternal = LocalServices.getService( WindowManagerInternal.class); - // Make sure WindowManagerInter#shouldShowIme as true is due to - // mForceDesktopModeOnExternalDisplays as true. - assertFalse(mWm.mDisplayWindowSettings.shouldShowImeLocked(dc)); - assertTrue(wmInternal.shouldShowIme(dc.getDisplayId())); + // Make sure WindowManagerInter#getDisplayImePolicy is SHOW_IME_ON_DISPLAY is due to + // mForceDesktopModeOnExternalDisplays being SHOW_IME_ON_DISPLAY. + assertEquals(DISPLAY_IME_POLICY_FALLBACK_DISPLAY, + mWm.mDisplayWindowSettings.getImePolicyLocked(dc)); + assertEquals(DISPLAY_IME_POLICY_LOCAL, wmInternal.getDisplayImePolicy(dc.getDisplayId())); } finally { mWm.setForceDesktopModeOnExternalDisplays(false); } diff --git a/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java index a9f6b50d4be5..f75c98f39323 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java @@ -18,14 +18,28 @@ package com.android.server.wm; import static android.view.Display.DEFAULT_DISPLAY; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; + 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; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import android.content.Context; +import android.content.res.Resources; import android.graphics.Rect; +import android.hardware.display.DisplayManagerGlobal; +import android.os.IBinder; import android.platform.test.annotations.Presubmit; +import android.view.Display; +import android.view.IWindowManager; import android.view.WindowManager; +import android.view.WindowManagerGlobal; import com.android.server.inputmethod.InputMethodManagerService; import com.android.server.inputmethod.InputMethodMenuController; @@ -45,10 +59,34 @@ import org.junit.runner.RunWith; public class InputMethodMenuControllerTest extends WindowTestsBase { private InputMethodMenuController mController; + private TestDisplayContent mSecondaryDisplay; @Before - public void setUp() { + public void setUp() throws Exception { mController = new InputMethodMenuController(mock(InputMethodManagerService.class)); + + // Mock addWindowTokenWithOptions to create a test window token. + IWindowManager wms = WindowManagerGlobal.getWindowManagerService(); + spyOn(wms); + doAnswer(invocation -> { + Object[] args = invocation.getArguments(); + final IBinder token = (IBinder) args[0]; + final int windowType = (int) args[1]; + new WindowToken(mWm, token, windowType, true /* persistOnEmpty */, + mDefaultDisplay, true /* ownerCanManageAppTokens */, 1000 /* ownerUid */, + false /* roundedCornerOverlay */, true /* fromClientToken */); + return WindowManagerGlobal.ADD_OKAY; + }).when(wms).addWindowTokenWithOptions(any(), anyInt(), anyInt(), any(), anyString()); + + mSecondaryDisplay = new TestDisplayContent.Builder(mAtm, 1000, 1000).build(); + + // Mock DisplayManagerGlobal to return test display when obtaining Display instance. + final int displayId = mSecondaryDisplay.getDisplayId(); + final Display display = mSecondaryDisplay.getDisplay(); + DisplayManagerGlobal displayManagerGlobal = DisplayManagerGlobal.getInstance(); + spyOn(displayManagerGlobal); + doReturn(display).when(displayManagerGlobal).getCompatibleDisplay(eq(displayId), + (Resources) any()); } @Test @@ -60,9 +98,9 @@ public class InputMethodMenuControllerTest extends WindowTestsBase { // Obtain the context again and check they are the same instance and match the display // metrics of the secondary display. final Context contextOnSecondaryDisplay = mController.getSettingsContext( - mDisplayContent.getDisplayId()); + mSecondaryDisplay.getDisplayId()); - assertImeSwitchContextMetricsValidity(contextOnSecondaryDisplay, mDisplayContent); + assertImeSwitchContextMetricsValidity(contextOnSecondaryDisplay, mSecondaryDisplay); assertThat(contextOnDefaultDisplay.getWindowContextToken()) .isEqualTo(contextOnSecondaryDisplay.getWindowContextToken()); } diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java index e514ac04efbe..cd428e10a437 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java @@ -33,6 +33,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.server.wm.ActivityStarter.Request; import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS; import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE; import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_DONE; @@ -92,11 +93,12 @@ public class LaunchParamsControllerTests extends WindowTestsBase { final ActivityRecord source = new ActivityBuilder(mAtm).build(); final WindowLayout layout = new WindowLayout(0, 0, 0, 0, 0, 0, 0); final ActivityOptions options = mock(ActivityOptions.class); + final Request request = new Request(); mController.calculate(record.getTask(), layout, record, source, options, PHASE_BOUNDS, - new LaunchParams()); + new LaunchParams(), request); verify(positioner, times(1)).onCalculate(eq(record.getTask()), eq(layout), eq(record), - eq(source), eq(options), anyInt(), any(), any()); + eq(source), eq(options), anyInt(), any(), any(), eq(request)); } /** @@ -119,9 +121,9 @@ public class LaunchParamsControllerTests extends WindowTestsBase { mPersister.putLaunchParams(userId, name, expected); mController.calculate(activity.getTask(), null /*layout*/, activity, null /*source*/, - null /*options*/, PHASE_BOUNDS, new LaunchParams()); + null /*options*/, PHASE_BOUNDS, new LaunchParams(), null /* request */); verify(positioner, times(1)).onCalculate(any(), any(), any(), any(), any(), anyInt(), - eq(expected), any()); + eq(expected), any(), any()); } /** @@ -132,16 +134,17 @@ public class LaunchParamsControllerTests extends WindowTestsBase { final LaunchParamsModifier ignoredPositioner = mock(LaunchParamsModifier.class); final LaunchParamsModifier earlyExitPositioner = - (task, layout, activity, source, options, phase, currentParams, outParams) + (task, layout, activity, source, options, phase, currentParams, outParams, request) -> RESULT_DONE; mController.registerModifier(ignoredPositioner); mController.registerModifier(earlyExitPositioner); mController.calculate(null /*task*/, null /*layout*/, null /*activity*/, - null /*source*/, null /*options*/, PHASE_BOUNDS, new LaunchParams()); + null /*source*/, null /*options*/, PHASE_BOUNDS, new LaunchParams(), + null /* request */); verify(ignoredPositioner, never()).onCalculate(any(), any(), any(), any(), any(), anyInt(), - any(), any()); + any(), any(), any()); } /** @@ -157,20 +160,22 @@ public class LaunchParamsControllerTests extends WindowTestsBase { mController.registerModifier(firstPositioner); mController.calculate(null /*task*/, null /*layout*/, null /*activity*/, - null /*source*/, null /*options*/, PHASE_BOUNDS, new LaunchParams()); + null /*source*/, null /*options*/, PHASE_BOUNDS, new LaunchParams(), + null /* request */); verify(firstPositioner, times(1)).onCalculate(any(), any(), any(), any(), any(), anyInt(), - any(), any()); + any(), any(), any()); final LaunchParamsModifier secondPositioner = spy(earlyExitPositioner); mController.registerModifier(secondPositioner); mController.calculate(null /*task*/, null /*layout*/, null /*activity*/, - null /*source*/, null /*options*/, PHASE_BOUNDS, new LaunchParams()); + null /*source*/, null /*options*/, PHASE_BOUNDS, new LaunchParams(), + null /* request */); verify(firstPositioner, times(1)).onCalculate(any(), any(), any(), any(), any(), anyInt(), - any(), any()); + any(), any(), any()); verify(secondPositioner, times(1)).onCalculate(any(), any(), any(), any(), any(), anyInt(), - any(), any()); + any(), any(), any()); } /** @@ -192,10 +197,10 @@ public class LaunchParamsControllerTests extends WindowTestsBase { mController.registerModifier(positioner2); mController.calculate(null /*task*/, null /*layout*/, null /*activity*/, null /*source*/, - null /*options*/, PHASE_BOUNDS, new LaunchParams()); + null /*options*/, PHASE_BOUNDS, new LaunchParams(), null /* request */); verify(positioner1, times(1)).onCalculate(any(), any(), any(), any(), any(), anyInt(), - eq(positioner2.getLaunchParams()), any()); + eq(positioner2.getLaunchParams()), any(), any()); } /** @@ -218,7 +223,7 @@ public class LaunchParamsControllerTests extends WindowTestsBase { final LaunchParams result = new LaunchParams(); mController.calculate(null /*task*/, null /*layout*/, null /*activity*/, null /*source*/, - null /*options*/, PHASE_BOUNDS, result); + null /*options*/, PHASE_BOUNDS, result, null /* request */); assertEquals(result, positioner2.getLaunchParams()); } @@ -237,17 +242,17 @@ public class LaunchParamsControllerTests extends WindowTestsBase { // VR activities should always land on default display. mController.calculate(null /*task*/, null /*layout*/, vrActivity /*activity*/, - null /*source*/, null /*options*/, PHASE_BOUNDS, result); + null /*source*/, null /*options*/, PHASE_BOUNDS, result, null /* request */); assertEquals(mRootWindowContainer.getDefaultTaskDisplayArea(), result.mPreferredTaskDisplayArea); // Otherwise, always lands on VR 2D display. final ActivityRecord vr2dActivity = new ActivityBuilder(mAtm).build(); mController.calculate(null /*task*/, null /*layout*/, vr2dActivity /*activity*/, - null /*source*/, null /*options*/, PHASE_BOUNDS, result); + null /*source*/, null /*options*/, PHASE_BOUNDS, result, null /* request */); assertEquals(vrDisplay.getDefaultTaskDisplayArea(), result.mPreferredTaskDisplayArea); mController.calculate(null /*task*/, null /*layout*/, null /*activity*/, null /*source*/, - null /*options*/, PHASE_BOUNDS, result); + null /*options*/, PHASE_BOUNDS, result, null /* request */); assertEquals(vrDisplay.getDefaultTaskDisplayArea(), result.mPreferredTaskDisplayArea); mAtm.mVr2dDisplayId = INVALID_DISPLAY; @@ -269,9 +274,9 @@ public class LaunchParamsControllerTests extends WindowTestsBase { final ActivityOptions options = mock(ActivityOptions.class); mController.calculate(record.getTask(), layout, record, source, options, PHASE_BOUNDS, - new LaunchParams()); + new LaunchParams(), null /* request */); verify(positioner, times(1)).onCalculate(eq(record.getTask()), eq(layout), eq(record), - eq(source), eq(options), eq(PHASE_BOUNDS), any(), any()); + eq(source), eq(options), eq(PHASE_BOUNDS), any(), any(), any()); } /** @@ -403,8 +408,9 @@ public class LaunchParamsControllerTests extends WindowTestsBase { @Override public int onCalculate(Task task, WindowLayout layout, ActivityRecord activity, - ActivityRecord source, ActivityOptions options, int phase, - LaunchParams currentParams, LaunchParams outParams) { + ActivityRecord source, ActivityOptions options, int phase, + LaunchParams currentParams, LaunchParams outParams, + Request request) { outParams.set(mParams); return mReturnVal; } diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java index d919d58a4f95..7812934bb52f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java @@ -17,7 +17,6 @@ package com.android.server.wm; import static android.app.ActivityManager.RECENT_WITH_EXCLUDED; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; @@ -48,7 +47,7 @@ import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -58,12 +57,10 @@ import static java.lang.Integer.MAX_VALUE; import android.app.ActivityManager.RecentTaskInfo; import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityTaskManager; -import android.app.WindowConfiguration; import android.content.ComponentName; import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; import android.content.pm.UserInfo; -import android.content.res.Configuration; import android.graphics.Rect; import android.os.Bundle; import android.os.RemoteException; @@ -100,14 +97,14 @@ public class RecentTasksTest extends WindowTestsBase { private static final int TEST_USER_0_ID = 0; private static final int TEST_USER_1_ID = 10; private static final int TEST_QUIET_USER_ID = 20; - private static final UserInfo DEFAULT_USER_INFO = new UserInfo(); + private static final UserInfo DEFAULT_USER_INFO = new UserInfo(TEST_USER_0_ID, + "default", 0 /* flags */); private static final UserInfo QUIET_PROFILE_USER_INFO = new UserInfo(TEST_QUIET_USER_ID, "quiet_profile", null /* iconPath */, UserInfo.FLAG_QUIET_MODE, UserManager.USER_TYPE_PROFILE_MANAGED); private static final int INVALID_STACK_ID = 999; private TaskDisplayArea mTaskContainer; - private Task mStack; private TestTaskPersister mTaskPersister; private TestRecentTasks mRecentTasks; private TestRunningTasks mRunningTasks; @@ -133,8 +130,6 @@ public class RecentTasksTest extends WindowTestsBase { mRunningTasks = new TestRunningTasks(); mAtm.mTaskSupervisor.setRunningTasks(mRunningTasks); - mStack = mTaskContainer.createStack( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); mCallbacksRecorder = new CallbacksRecorder(); mRecentTasks.registerCallback(mCallbacksRecorder); @@ -214,9 +209,9 @@ public class RecentTasksTest extends WindowTestsBase { // Add N+1 tasks to ensure the previous task is trimmed mRecentTasks.add(mTasks.get(1)); + triggerTrimAndAssertTrimmed(mTasks.get(0)); verify(mTaskPersister, times(1)).wakeup(eq(mTasks.get(0)), anyBoolean()); verify(mTaskPersister, times(1)).wakeup(eq(mTasks.get(1)), anyBoolean()); - assertTrimmed(mTasks.get(0)); } @Test @@ -279,31 +274,6 @@ public class RecentTasksTest extends WindowTestsBase { } @Test - public void testAddTasksInVisibilityUpdate_expectNoTrim() { - mRecentTasks.setOnlyTestVisibleRange(); - mRecentTasks.setParameters(-1 /* min */, 1 /* max */, -1 /* ms */); - mRecentTasks.add(mTasks.get(0)); - - doAnswer(invocation -> { - assertTrue(mSupervisor.inActivityVisibilityUpdate()); - // Simulate an activity is resumed by EnsureActivitiesVisibleHelper. If its state is - // change to RESUMED, it will also be added to recents. - mRecentTasks.add(mTasks.get(1)); - invocation.callRealMethod(); - return null; - }).when(mSupervisor).endActivityVisibilityUpdate(); - - mTaskContainer.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, - false /* preserveWindows */, false /* notifyClients */, false /* userLeaving */); - - assertFalse(mSupervisor.inActivityVisibilityUpdate()); - assertThat(mCallbacksRecorder.mAdded).hasSize(2); - // Expect nothing is trimmed because we don't want the loop of ensure-visibility to be - // impacted by the arbitrary number of task removals. - assertNoTasksTrimmed(); - } - - @Test public void testAddTasksMultipleTasks_expectRemovedNoTrim() { // Add multiple same-affinity non-document tasks, ensure that it removes the other task, // but that it does not trim it @@ -336,7 +306,7 @@ public class RecentTasksTest extends WindowTestsBase { .setParentTask(mTaskContainer.getRootHomeTask()).build(); Task task2 = createTaskBuilder(".Task1") .setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK) - .setParentTask(mStack).build(); + .build(); mRecentTasks.add(task1); mRecentTasks.add(task2); assertThat(mCallbacksRecorder.mAdded).hasSize(2); @@ -351,17 +321,15 @@ public class RecentTasksTest extends WindowTestsBase { // Test with undefined activity type since the type is not persisted by the task persister // and we want to ensure that a new task will match a restored task Task task1 = createTaskBuilder(".Task1") + .setActivityType(ACTIVITY_TYPE_UNDEFINED) .setFlags(FLAG_ACTIVITY_NEW_TASK) - .setParentTask(mStack) .build(); - setTaskActivityType(task1, ACTIVITY_TYPE_UNDEFINED); assertThat(task1.getActivityType()).isEqualTo(ACTIVITY_TYPE_UNDEFINED); mRecentTasks.add(task1); mCallbacksRecorder.clear(); Task task2 = createTaskBuilder(".Task1") .setFlags(FLAG_ACTIVITY_NEW_TASK) - .setParentTask(mStack) .build(); assertEquals(ACTIVITY_TYPE_STANDARD, task2.getActivityType()); mRecentTasks.add(task2); @@ -375,18 +343,16 @@ public class RecentTasksTest extends WindowTestsBase { @Test public void testAddTaskCompatibleActivityTypeDifferentUser_expectNoRemove() { Task task1 = createTaskBuilder(".Task1") + .setActivityType(ACTIVITY_TYPE_UNDEFINED) .setFlags(FLAG_ACTIVITY_NEW_TASK) - .setParentTask(mStack) .setUserId(TEST_USER_0_ID) .build(); - setTaskActivityType(task1, ACTIVITY_TYPE_UNDEFINED); assertEquals(ACTIVITY_TYPE_UNDEFINED, task1.getActivityType()); mRecentTasks.add(task1); mCallbacksRecorder.clear(); Task task2 = createTaskBuilder(".Task1") .setFlags(FLAG_ACTIVITY_NEW_TASK) - .setParentTask(mStack) .setUserId(TEST_USER_1_ID) .build(); assertEquals(ACTIVITY_TYPE_STANDARD, task2.getActivityType()); @@ -401,18 +367,15 @@ public class RecentTasksTest extends WindowTestsBase { public void testAddTaskCompatibleWindowingMode_expectRemove() { Task task1 = createTaskBuilder(".Task1") .setFlags(FLAG_ACTIVITY_NEW_TASK) - .setParentTask(mStack) .build(); - setTaskWindowingMode(task1, WINDOWING_MODE_UNDEFINED); - assertEquals(WINDOWING_MODE_UNDEFINED, task1.getWindowingMode()); + doReturn(WINDOWING_MODE_UNDEFINED).when(task1).getWindowingMode(); mRecentTasks.add(task1); mCallbacksRecorder.clear(); Task task2 = createTaskBuilder(".Task1") + .setWindowingMode(WINDOWING_MODE_FULLSCREEN) .setFlags(FLAG_ACTIVITY_NEW_TASK) - .setParentTask(mStack) .build(); - setTaskWindowingMode(task2, WINDOWING_MODE_FULLSCREEN); assertEquals(WINDOWING_MODE_FULLSCREEN, task2.getWindowingMode()); mRecentTasks.add(task2); @@ -426,18 +389,16 @@ public class RecentTasksTest extends WindowTestsBase { @Test public void testAddTaskIncompatibleWindowingMode_expectNoRemove() { Task task1 = createTaskBuilder(".Task1") + .setWindowingMode(WINDOWING_MODE_FULLSCREEN) .setFlags(FLAG_ACTIVITY_NEW_TASK) - .setParentTask(mStack) .build(); - setTaskWindowingMode(task1, WINDOWING_MODE_FULLSCREEN); assertEquals(WINDOWING_MODE_FULLSCREEN, task1.getWindowingMode()); mRecentTasks.add(task1); Task task2 = createTaskBuilder(".Task1") + .setWindowingMode(WINDOWING_MODE_PINNED) .setFlags(FLAG_ACTIVITY_NEW_TASK) - .setParentTask(mStack) .build(); - setTaskWindowingMode(task2, WINDOWING_MODE_PINNED); assertEquals(WINDOWING_MODE_PINNED, task2.getWindowingMode()); mRecentTasks.add(task2); @@ -453,19 +414,19 @@ public class RecentTasksTest extends WindowTestsBase { // Add task to recents final String taskAffinity = "affinity"; final int uid = 10123; - final Task task1 = createTaskBuilder(".Task1").setParentTask(mStack).build(); + final Task task1 = createTaskBuilder(".Task1").build(); task1.affinity = ActivityRecord.computeTaskAffinity(taskAffinity, uid, LAUNCH_MULTIPLE); mRecentTasks.add(task1); // Add another task to recents, and make sure the previous task was removed. - final Task task2 = createTaskBuilder(".Task2").setParentTask(mStack).build(); + final Task task2 = createTaskBuilder(".Task2").build(); task2.affinity = ActivityRecord.computeTaskAffinity(taskAffinity, uid, LAUNCH_MULTIPLE); mRecentTasks.add(task2); assertEquals(1, mRecentTasks.getRecentTasks(MAX_VALUE, 0 /* flags */, true /* getTasksAllowed */, TEST_USER_0_ID, 0).getList().size()); // Add another single-instance task to recents, and make sure no task is removed. - final Task task3 = createTaskBuilder(".Task3").setParentTask(mStack).build(); + final Task task3 = createTaskBuilder(".Task3").build(); task3.affinity = ActivityRecord.computeTaskAffinity(taskAffinity, uid, LAUNCH_SINGLE_INSTANCE); mRecentTasks.add(task3); @@ -521,6 +482,7 @@ public class RecentTasksTest extends WindowTestsBase { // Go home to trigger the removal of untracked tasks. mRecentTasks.add(createTaskBuilder(".Home").setParentTask(mTaskContainer.getRootHomeTask()) .build()); + triggerIdleToTrim(); // The task was added into recents again so it is not hidden and shouldn't be removed. assertNotNull(task1.getTopNonFinishingActivity()); @@ -615,7 +577,7 @@ public class RecentTasksTest extends WindowTestsBase { mRecentTasks.add(mTasks.get(2)); // Ensure that the last task was trimmed as an inactive task - assertTrimmed(mTasks.get(0)); + triggerTrimAndAssertTrimmed(mTasks.get(0)); } @Test @@ -630,7 +592,7 @@ public class RecentTasksTest extends WindowTestsBase { mRecentTasks.add(mTasks.get(1)); // Ensure that the quiet user's tasks was trimmed once the new tasks were added - assertTrimmed(qt1, qt2); + triggerTrimAndAssertTrimmed(qt1, qt2); } @Test @@ -645,12 +607,8 @@ public class RecentTasksTest extends WindowTestsBase { // Force a small sleep just beyond the session duration SystemClock.sleep(75); - Task t2 = createTaskBuilder(".Task2").build(); - t2.touchActiveTime(); - mRecentTasks.add(t2); - // Assert that the old task has been removed due to being out of the active session - assertTrimmed(t1); + triggerTrimAndAssertTrimmed(t1); } @Test @@ -672,15 +630,15 @@ public class RecentTasksTest extends WindowTestsBase { mRecentTasks.add(excludedTask2); // The last excluded task should be trimmed, while the first-most excluded task should not - assertTrimmed(excludedTask1); + triggerTrimAndAssertTrimmed(excludedTask1); } @Test public void testVisibleTasks_excludedFromRecents_firstTaskNotVisible() { // Create some set of tasks, some of which are visible and some are not - Task homeTask = setTaskActivityType( - createTaskBuilder("com.android.pkg1", ".HomeTask").build(), - ACTIVITY_TYPE_HOME); + Task homeTask = createTaskBuilder("com.android.pkg1", ".HomeTask") + .setParentTask(mTaskContainer.getRootHomeTask()) + .build(); homeTask.mUserSetupComplete = true; mRecentTasks.add(homeTask); Task excludedTask1 = createTaskBuilder(".ExcludedTask1") @@ -699,9 +657,9 @@ public class RecentTasksTest extends WindowTestsBase { Task t1 = createTaskBuilder("com.android.pkg1", ".Task1").build(); t1.mUserSetupComplete = true; mRecentTasks.add(t1); - Task homeTask = setTaskActivityType( - createTaskBuilder("com.android.pkg1", ".HomeTask").build(), - ACTIVITY_TYPE_HOME); + Task homeTask = createTaskBuilder("com.android.pkg1", ".HomeTask") + .setParentTask(mTaskContainer.getRootHomeTask()) + .build(); homeTask.mUserSetupComplete = true; mRecentTasks.add(homeTask); Task excludedTask1 = createTaskBuilder(".ExcludedTask1") @@ -739,7 +697,7 @@ public class RecentTasksTest extends WindowTestsBase { mRecentTasks.add(mTasks.get(4)); // Ensure that there are a minimum number of tasks regardless of session length - assertNoTasksTrimmed(); + triggerTrimAndAssertNoTasksTrimmed(); } @Test @@ -754,7 +712,7 @@ public class RecentTasksTest extends WindowTestsBase { } // Ensure that only the last number of max tasks are kept - assertTrimmed(mTasks.get(0), mTasks.get(1)); + triggerTrimAndAssertTrimmed(mTasks.get(0), mTasks.get(1)); } /** @@ -782,12 +740,12 @@ public class RecentTasksTest extends WindowTestsBase { mRecentTasks.add(mTasks.get(3)); // excludedTask is not trimmed. - assertTrimmed(mTasks.get(0)); + triggerTrimAndAssertTrimmed(mTasks.get(0)); mRecentTasks.removeAllVisibleTasks(TEST_USER_0_ID); // Only visible tasks removed. - assertTrimmed(mTasks.get(0), mTasks.get(1), mTasks.get(2), mTasks.get(3)); + triggerTrimAndAssertTrimmed(mTasks.get(0), mTasks.get(1), mTasks.get(2), mTasks.get(3)); } @Test @@ -910,7 +868,7 @@ public class RecentTasksTest extends WindowTestsBase { mRecentTasks.add(createTaskBuilder(".Task2").setParentTask(aboveHomeStack).build()); mRecentTasks.add(createTaskBuilder(".Task3").setParentTask(aboveHomeStack).build()); - assertNoTasksTrimmed(); + triggerTrimAndAssertNoTasksTrimmed(); } @Test @@ -932,7 +890,7 @@ public class RecentTasksTest extends WindowTestsBase { mRecentTasks.add(createTaskBuilder(".HomeTask1").setParentTask(homeStack).build()); mRecentTasks.add(createTaskBuilder(".Task2").setParentTask(aboveHomeStack).build()); - assertTrimmed(behindHomeTask); + triggerTrimAndAssertTrimmed(behindHomeTask); } @Test @@ -951,7 +909,7 @@ public class RecentTasksTest extends WindowTestsBase { mRecentTasks.add(createTaskBuilder(".Task2").setParentTask(otherDisplayStack).build()); mRecentTasks.add(createTaskBuilder(".HomeTask2").setParentTask(homeStack).build()); - assertNoTasksTrimmed(); + triggerTrimAndAssertNoTasksTrimmed(); } @Test @@ -978,14 +936,12 @@ public class RecentTasksTest extends WindowTestsBase { // Create some set of tasks, some of which are visible and some are not Task t1 = createTaskBuilder("com.android.pkg1", ".Task1").build(); mRecentTasks.add(t1); - mRecentTasks.add(setTaskActivityType( - createTaskBuilder("com.android.pkg1", ".HomeTask").build(), - ACTIVITY_TYPE_HOME)); + mRecentTasks.add(createTaskBuilder("com.android.pkg1", ".HomeTask") + .setParentTask(mTaskContainer.getRootHomeTask()).build()); Task t2 = createTaskBuilder("com.android.pkg2", ".Task2").build(); mRecentTasks.add(t2); - mRecentTasks.add(setTaskWindowingMode( - createTaskBuilder("com.android.pkg1", ".PipTask").build(), - WINDOWING_MODE_PINNED)); + mRecentTasks.add(createTaskBuilder("com.android.pkg1", ".PipTask") + .setWindowingMode(WINDOWING_MODE_PINNED).build()); Task t3 = createTaskBuilder("com.android.pkg3", ".Task3").build(); mRecentTasks.add(t3); @@ -1005,7 +961,7 @@ public class RecentTasksTest extends WindowTestsBase { // Remove all the visible tasks and ensure that they are removed mRecentTasks.removeAllVisibleTasks(TEST_USER_0_ID); - assertTrimmed(t1, t2, t3, t4, t5, t6, t7); + triggerTrimAndAssertTrimmed(t1, t2, t3, t4, t5, t6, t7); } @Test @@ -1030,7 +986,7 @@ public class RecentTasksTest extends WindowTestsBase { // Remove all the visible tasks and ensure that they are removed mRecentTasks.removeAllVisibleTasks(TEST_USER_0_ID); - assertTrimmed(t1, t2); + triggerTrimAndAssertTrimmed(t1, t2); } @Test @@ -1040,7 +996,6 @@ public class RecentTasksTest extends WindowTestsBase { mRecentTasks.add(task); // Only keep the task in RecentTasks. task.removeIfPossible(); - mStack.removeIfPossible(); // The following APIs should not restore task from recents to the active list. assertNotRestoreTask(() -> mAtm.setFocusedTask(taskId)); @@ -1236,7 +1191,6 @@ public class RecentTasksTest extends WindowTestsBase { private TaskBuilder createTaskBuilder(String packageName, String className) { return new TaskBuilder(mAtm.mTaskSupervisor) .setComponent(new ComponentName(packageName, className)) - .setParentTask(mStack) .setUserId(TEST_USER_0_ID); } @@ -1253,27 +1207,19 @@ public class RecentTasksTest extends WindowTestsBase { return task; } - private Task setTaskActivityType(Task task, - @WindowConfiguration.ActivityType int activityType) { - Configuration config1 = new Configuration(); - config1.windowConfiguration.setActivityType(activityType); - task.onConfigurationChanged(config1); - return task; - } - - private Task setTaskWindowingMode(Task task, - @WindowConfiguration.WindowingMode int windowingMode) { - Configuration config1 = new Configuration(); - config1.windowConfiguration.setWindowingMode(windowingMode); - task.onConfigurationChanged(config1); - return task; + private void triggerIdleToTrim() { + doNothing().when(mAtm).scheduleAppGcsLocked(); + final ActivityRecord r = mRootWindowContainer.topRunningActivity(); + mSupervisor.activityIdleInternal(r != null ? r : mock(ActivityRecord.class), + false /* fromTimeout */, false /* processPausingActivities */, null /* config */); } - private void assertNoTasksTrimmed() { - assertTrimmed(); + private void triggerTrimAndAssertNoTasksTrimmed() { + triggerTrimAndAssertTrimmed(); } - private void assertTrimmed(Task... tasks) { + private void triggerTrimAndAssertTrimmed(Task... tasks) { + triggerIdleToTrim(); final ArrayList<Task> trimmed = mCallbacksRecorder.mTrimmed; final ArrayList<Task> removed = mCallbacksRecorder.mRemoved; assertWithMessage("Expected " + tasks.length + " trimmed tasks, got " + trimmed.size()) diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java index 62aa02f609ad..42193c8fce78 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java @@ -17,11 +17,6 @@ package com.android.server.wm; import static android.view.Display.DEFAULT_DISPLAY; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; -import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE; -import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; -import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; @@ -35,7 +30,6 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; import android.app.WindowConfiguration; import android.content.ComponentName; @@ -58,57 +52,6 @@ import org.junit.runner.RunWith; @RunWith(WindowTestRunner.class) public class RootWindowContainerTests extends WindowTestsBase { - private static final int FAKE_CALLING_UID = 667; - - @Test - public void testIsAnyNonToastWindowVisibleForUid_oneToastOneAppStartOneNonToastBothVisible() { - final WindowState toastyToast = createWindow(null, TYPE_TOAST, "toast", FAKE_CALLING_UID); - final WindowState app = createWindow(null, TYPE_APPLICATION, "app", FAKE_CALLING_UID); - final WindowState appStart = createWindow(null, TYPE_APPLICATION_STARTING, "appStarting", - FAKE_CALLING_UID); - toastyToast.mHasSurface = true; - app.mHasSurface = true; - appStart.mHasSurface = true; - - assertTrue(toastyToast.isVisibleNow()); - assertTrue(app.isVisibleNow()); - assertTrue(appStart.isVisibleNow()); - assertTrue(mWm.mRoot.isAnyNonToastWindowVisibleForUid(FAKE_CALLING_UID)); - } - - @Test - public void testIsAnyNonToastWindowVisibleForUid_onlyToastVisible() { - final WindowState toastyToast = createWindow(null, TYPE_TOAST, "toast", FAKE_CALLING_UID); - toastyToast.mHasSurface = true; - - assertTrue(toastyToast.isVisibleNow()); - assertFalse(mWm.mRoot.isAnyNonToastWindowVisibleForUid(FAKE_CALLING_UID)); - } - - @Test - public void testIsAnyNonToastWindowVisibleForUid_onlyAppStartingVisible() { - final WindowState appStart = createWindow(null, TYPE_APPLICATION_STARTING, "appStarting", - FAKE_CALLING_UID); - appStart.mHasSurface = true; - - assertTrue(appStart.isVisibleNow()); - assertFalse(mWm.mRoot.isAnyNonToastWindowVisibleForUid(FAKE_CALLING_UID)); - } - - @Test - public void testIsAnyNonToastWindowVisibleForUid_aFewNonToastButNoneVisible() { - final WindowState statusBar = - createWindow(null, TYPE_STATUS_BAR, "statusBar", FAKE_CALLING_UID); - final WindowState notificationShade = createWindow(null, TYPE_NOTIFICATION_SHADE, - "notificationShade", FAKE_CALLING_UID); - final WindowState app = createWindow(null, TYPE_APPLICATION, "app", FAKE_CALLING_UID); - - assertFalse(statusBar.isVisibleNow()); - assertFalse(notificationShade.isVisibleNow()); - assertFalse(app.isVisibleNow()); - assertFalse(mWm.mRoot.isAnyNonToastWindowVisibleForUid(FAKE_CALLING_UID)); - } - @Test public void testUpdateDefaultDisplayWindowingModeOnSettingsRetrieved() { assertEquals(WindowConfiguration.WINDOWING_MODE_FULLSCREEN, diff --git a/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java b/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java deleted file mode 100644 index 25ba6db38e05..000000000000 --- a/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java +++ /dev/null @@ -1,382 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm; - -import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; -import static android.graphics.Color.RED; -import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY; -import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION; -import static android.view.Display.DEFAULT_DISPLAY; -import static android.view.Gravity.BOTTOM; -import static android.view.Gravity.LEFT; -import static android.view.Gravity.RIGHT; -import static android.view.Gravity.TOP; -import static android.view.InsetsState.ITYPE_CLIMATE_BAR; -import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR; -import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; -import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; -import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; -import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; -import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; - -import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; - -import static com.google.common.truth.Truth.assertThat; - -import static org.junit.Assert.assertEquals; - -import android.app.Activity; -import android.app.ActivityOptions; -import android.app.Instrumentation; -import android.content.Context; -import android.content.Intent; -import android.graphics.PixelFormat; -import android.graphics.Point; -import android.hardware.display.DisplayManager; -import android.hardware.display.VirtualDisplay; -import android.media.ImageReader; -import android.os.Handler; -import android.os.SystemClock; -import android.platform.test.annotations.Presubmit; -import android.util.Pair; -import android.view.Display; -import android.view.DisplayInfo; -import android.view.View; -import android.view.WindowInsets; -import android.view.WindowManager; -import android.widget.TextView; - -import androidx.test.filters.SmallTest; - -import com.android.compatibility.common.util.SystemUtil; - -import org.junit.After; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; - -import java.util.ArrayList; -import java.util.function.BooleanSupplier; - -/** - * Tests for the {@link android.view.WindowManager.LayoutParams#PRIVATE_FLAG_IS_SCREEN_DECOR} flag. - * - * Build/Install/Run: - * atest WmTests:ScreenDecorWindowTests - */ -// TODO: Add test for FLAG_FULLSCREEN which hides the status bar and also other flags. -// TODO: Test non-Activity windows. -@SmallTest -@Presubmit -public class ScreenDecorWindowTests { - - private final Context mContext = getInstrumentation().getTargetContext(); - private final Instrumentation mInstrumentation = getInstrumentation(); - - private WindowManager mWm; - private ArrayList<View> mWindows = new ArrayList<>(); - - private Activity mTestActivity; - private VirtualDisplay mDisplay; - private ImageReader mImageReader; - - private int mDecorThickness; - private int mHalfDecorThickness; - - @Before - public void setUp() { - final Pair<VirtualDisplay, ImageReader> result = createDisplay(); - mDisplay = result.first; - mImageReader = result.second; - final Display display = mDisplay.getDisplay(); - final Context dContext = mContext.createDisplayContext(display); - mWm = dContext.getSystemService(WindowManager.class); - mTestActivity = startActivityOnDisplay(TestActivity.class, display.getDisplayId()); - final Point size = new Point(); - mDisplay.getDisplay().getRealSize(size); - mDecorThickness = Math.min(size.x, size.y) / 3; - mHalfDecorThickness = mDecorThickness / 2; - } - - @After - public void tearDown() { - while (!mWindows.isEmpty()) { - removeWindow(mWindows.get(0)); - } - finishActivity(mTestActivity); - mDisplay.release(); - mImageReader.close(); - } - - @Test - public void testScreenSides() { - // Decor on top - final View decorWindow = createDecorWindow(TOP, MATCH_PARENT, mDecorThickness); - assertInsetGreaterOrEqual(mTestActivity, TOP, mDecorThickness); - - // Decor at the bottom - updateWindow(decorWindow, BOTTOM, MATCH_PARENT, mDecorThickness, 0, 0); - assertInsetGreaterOrEqual(mTestActivity, BOTTOM, mDecorThickness); - - // Decor to the left - updateWindow(decorWindow, LEFT, mDecorThickness, MATCH_PARENT, 0, 0); - assertInsetGreaterOrEqual(mTestActivity, LEFT, mDecorThickness); - - // Decor to the right - updateWindow(decorWindow, RIGHT, mDecorThickness, MATCH_PARENT, 0, 0); - assertInsetGreaterOrEqual(mTestActivity, RIGHT, mDecorThickness); - } - - // Decor windows (i.e windows using PRIVATE_FLAG_IS_SCREEN_DECOR) are no longer supported. - // PRIVATE_FLAG_IS_SCREEN_DECOR and related code will be deprecated/removed soon. - @Ignore - @Test - public void testMultipleDecors() { - // Test 2 decor windows on-top. - createDecorWindow(TOP, MATCH_PARENT, mHalfDecorThickness); - assertInsetGreaterOrEqual(mTestActivity, TOP, mHalfDecorThickness); - createDecorWindow(TOP, MATCH_PARENT, mDecorThickness); - assertInsetGreaterOrEqual(mTestActivity, TOP, mDecorThickness); - - // And one at the bottom. - createDecorWindow(BOTTOM, MATCH_PARENT, mHalfDecorThickness); - assertInsetGreaterOrEqual(mTestActivity, TOP, mDecorThickness); - assertInsetGreaterOrEqual(mTestActivity, BOTTOM, mHalfDecorThickness); - } - - @Test - public void testFlagChange() { - WindowInsets initialInsets = getInsets(mTestActivity); - - final View decorWindow = createDecorWindow(TOP, MATCH_PARENT, mDecorThickness); - assertTopInsetEquals(mTestActivity, mDecorThickness); - - updateWindow(decorWindow, TOP, MATCH_PARENT, mDecorThickness, - 0, PRIVATE_FLAG_IS_SCREEN_DECOR); - - // TODO: fix test and re-enable assertion. - // initialInsets was not actually immutable and just updated to the current insets, - // meaning this assertion never actually tested anything. Now that WindowInsets actually is - // immutable, it turns out the test was broken. - // assertTopInsetEquals(mTestActivity, initialInsets.getSystemWindowInsetTop()); - - updateWindow(decorWindow, TOP, MATCH_PARENT, mDecorThickness, - PRIVATE_FLAG_IS_SCREEN_DECOR, PRIVATE_FLAG_IS_SCREEN_DECOR); - assertTopInsetEquals(mTestActivity, mDecorThickness); - } - - @Test - public void testRemoval() { - WindowInsets initialInsets = getInsets(mTestActivity); - - final View decorWindow = createDecorWindow(TOP, MATCH_PARENT, mDecorThickness); - assertInsetGreaterOrEqual(mTestActivity, TOP, mDecorThickness); - - removeWindow(decorWindow); - assertTopInsetEquals(mTestActivity, initialInsets.getSystemWindowInsetTop()); - } - - @Test - public void testProvidesInsetsTypes() { - int[] providesInsetsTypes = new int[]{ITYPE_CLIMATE_BAR}; - final View win = createWindow("StatusBarSubPanel", TOP, MATCH_PARENT, mDecorThickness, RED, - FLAG_LAYOUT_IN_SCREEN, 0, providesInsetsTypes); - - assertInsetGreaterOrEqual(mTestActivity, TOP, mDecorThickness); - } - - private View createDecorWindow(int gravity, int width, int height) { - int[] providesInsetsTypes = - new int[]{gravity == TOP ? ITYPE_CLIMATE_BAR : ITYPE_EXTRA_NAVIGATION_BAR}; - return createWindow("decorWindow", gravity, width, height, RED, - FLAG_LAYOUT_IN_SCREEN, PRIVATE_FLAG_IS_SCREEN_DECOR, providesInsetsTypes); - } - - private View createWindow(String name, int gravity, int width, int height, int color, int flags, - int privateFlags, int[] providesInsetsTypes) { - - final View[] viewHolder = new View[1]; - final int finalFlag = flags - | FLAG_NOT_FOCUSABLE | FLAG_WATCH_OUTSIDE_TOUCH | FLAG_NOT_TOUCHABLE; - - // Needs to run on the UI thread. - Handler.getMain().runWithScissors(() -> { - final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( - width, height, TYPE_APPLICATION_OVERLAY, finalFlag, PixelFormat.OPAQUE); - lp.gravity = gravity; - lp.privateFlags |= privateFlags; - lp.providesInsetsTypes = providesInsetsTypes; - - final TextView view = new TextView(mContext); - view.setText("ScreenDecorWindowTests - " + name); - view.setBackgroundColor(color); - mWm.addView(view, lp); - mWindows.add(view); - viewHolder[0] = view; - }, 0); - - waitForIdle(); - return viewHolder[0]; - } - - private void updateWindow(View v, int gravity, int width, int height, - int privateFlags, int privateFlagsMask) { - // Needs to run on the UI thread. - Handler.getMain().runWithScissors(() -> { - final WindowManager.LayoutParams lp = (WindowManager.LayoutParams) v.getLayoutParams(); - lp.gravity = gravity; - lp.width = width; - lp.height = height; - setPrivateFlags(lp, privateFlags, privateFlagsMask); - - mWm.updateViewLayout(v, lp); - }, 0); - - waitForIdle(); - } - - private void removeWindow(View v) { - Handler.getMain().runWithScissors(() -> mWm.removeView(v), 0); - mWindows.remove(v); - waitForIdle(); - } - - private WindowInsets getInsets(Activity a) { - return new WindowInsets(a.getWindow().getDecorView().getRootWindowInsets()); - } - - /** - * Set the flags of the window, as per the - * {@link WindowManager.LayoutParams WindowManager.LayoutParams} - * flags. - * - * @param flags The new window flags (see WindowManager.LayoutParams). - * @param mask Which of the window flag bits to modify. - */ - public void setPrivateFlags(WindowManager.LayoutParams lp, int flags, int mask) { - lp.flags = (lp.flags & ~mask) | (flags & mask); - } - - /** - * Asserts the top inset of {@param activity} is equal to {@param expected} waiting as needed. - */ - private void assertTopInsetEquals(Activity activity, int expected) { - waitForTopInsetEqual(activity, expected); - assertEquals(expected, getInsets(activity).getSystemWindowInsetTop()); - } - - private void waitForTopInsetEqual(Activity activity, int expected) { - waitFor(() -> getInsets(activity).getSystemWindowInsetTop() == expected); - } - - /** - * Asserts the inset at {@param side} of {@param activity} is equal to {@param expected} - * waiting as needed. - */ - private void assertInsetGreaterOrEqual(Activity activity, int side, int expected) { - waitForInsetGreaterOrEqual(activity, side, expected); - - final WindowInsets insets = getInsets(activity); - switch (side) { - case TOP: - assertThat(insets.getSystemWindowInsetTop()).isAtLeast(expected); - break; - case BOTTOM: - assertThat(insets.getSystemWindowInsetBottom()).isAtLeast(expected); - break; - case LEFT: - assertThat(insets.getSystemWindowInsetLeft()).isAtLeast(expected); - break; - case RIGHT: - assertThat(insets.getSystemWindowInsetRight()).isAtLeast(expected); - break; - } - } - - private void waitForInsetGreaterOrEqual(Activity activity, int side, int expected) { - waitFor(() -> { - final WindowInsets insets = getInsets(activity); - switch (side) { - case TOP: return insets.getSystemWindowInsetTop() >= expected; - case BOTTOM: return insets.getSystemWindowInsetBottom() >= expected; - case LEFT: return insets.getSystemWindowInsetLeft() >= expected; - case RIGHT: return insets.getSystemWindowInsetRight() >= expected; - default: return true; - } - }); - } - - private void waitFor(BooleanSupplier waitCondition) { - int retriesLeft = 5; - do { - if (waitCondition.getAsBoolean()) { - break; - } - SystemClock.sleep(500); - } while (retriesLeft-- > 0); - } - - private void finishActivity(Activity a) { - if (a == null) { - return; - } - a.finish(); - waitForIdle(); - } - - private void waitForIdle() { - mInstrumentation.waitForIdleSync(); - } - - private Activity startActivityOnDisplay(Class<?> cls, int displayId) { - final Intent intent = new Intent(mContext, cls); - intent.addFlags(FLAG_ACTIVITY_NEW_TASK); - final ActivityOptions options = ActivityOptions.makeBasic(); - options.setLaunchDisplayId(displayId); - - final Activity activity = SystemUtil.runWithShellPermissionIdentity( - () -> mInstrumentation.startActivitySync(intent, options.toBundle()), - "android.permission.ACTIVITY_EMBEDDING"); - waitForIdle(); - - assertEquals(displayId, activity.getDisplayId()); - return activity; - } - - private Pair<VirtualDisplay, ImageReader> createDisplay() { - final DisplayManager dm = mContext.getSystemService(DisplayManager.class); - final DisplayInfo displayInfo = new DisplayInfo(); - final Display defaultDisplay = dm.getDisplay(DEFAULT_DISPLAY); - defaultDisplay.getDisplayInfo(displayInfo); - final String name = "ScreenDecorWindowTests"; - int flags = VIRTUAL_DISPLAY_FLAG_PRESENTATION | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY; - - final ImageReader imageReader = ImageReader.newInstance( - displayInfo.logicalWidth, displayInfo.logicalHeight, PixelFormat.RGBA_8888, 2); - - final VirtualDisplay display = dm.createVirtualDisplay(name, displayInfo.logicalWidth, - displayInfo.logicalHeight, displayInfo.logicalDensityDpi, imageReader.getSurface(), - flags); - - return Pair.create(display, imageReader); - } - - public static class TestActivity extends Activity { - } -} diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index 2326237902fb..a4bf5948c6a3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -72,13 +72,11 @@ import java.util.ArrayList; @Presubmit @RunWith(WindowTestRunner.class) public class SizeCompatTests extends WindowTestsBase { - private Task mStack; private Task mTask; private ActivityRecord mActivity; private void setUpApp(DisplayContent display) { - mStack = new TaskBuilder(mSupervisor).setDisplay(display).setCreateActivity(true).build(); - mTask = mStack.getBottomMostTask(); + mTask = new TaskBuilder(mSupervisor).setDisplay(display).setCreateActivity(true).build(); mActivity = mTask.getTopNonFinishingActivity(); } @@ -97,7 +95,7 @@ public class SizeCompatTests extends WindowTestsBase { prepareUnresizable(1.5f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED); final Rect originalOverrideBounds = new Rect(mActivity.getBounds()); - resizeDisplay(mStack.mDisplayContent, 600, 1200); + resizeDisplay(mTask.mDisplayContent, 600, 1200); // The visible activity should recompute configuration according to the last parent bounds. mAtm.restartActivityProcessIfVisible(mActivity.appToken); @@ -196,7 +194,7 @@ public class SizeCompatTests extends WindowTestsBase { final int originalDpi = mActivity.getConfiguration().densityDpi; // Move the non-resizable activity to the new display. - mStack.reparent(newDisplay.getDefaultTaskDisplayArea(), true /* onTop */); + mTask.reparent(newDisplay.getDefaultTaskDisplayArea(), true /* onTop */); assertEquals(originalBounds.width(), mActivity.getBounds().width()); assertEquals(originalBounds.height(), mActivity.getBounds().height()); @@ -321,7 +319,7 @@ public class SizeCompatTests extends WindowTestsBase { .setCanRotate(false).setNotch(notchHeight).build(); // Move the non-resizable activity to the new display. - mStack.reparent(newDisplay.getDefaultTaskDisplayArea(), true /* onTop */); + mTask.reparent(newDisplay.getDefaultTaskDisplayArea(), true /* onTop */); // The configuration bounds [820, 0 - 1820, 2500] should keep the same. assertEquals(origWidth, configBounds.width()); assertEquals(origHeight, configBounds.height()); @@ -363,7 +361,7 @@ public class SizeCompatTests extends WindowTestsBase { // Although the activity is fixed orientation, force rotate the display. rotateDisplay(mActivity.mDisplayContent, ROTATION_270); - assertEquals(ROTATION_270, mStack.getWindowConfiguration().getRotation()); + assertEquals(ROTATION_270, mTask.getWindowConfiguration().getRotation()); assertEquals(origBounds.width(), currentBounds.width()); // The notch is on horizontal side, so current height changes from 1460 to 1400. @@ -436,7 +434,7 @@ public class SizeCompatTests extends WindowTestsBase { public void testResetNonVisibleActivity() { setUpDisplaySizeWithApp(1000, 2500); prepareUnresizable(1.5f, SCREEN_ORIENTATION_UNSPECIFIED); - final DisplayContent display = mStack.mDisplayContent; + final DisplayContent display = mTask.mDisplayContent; // Resize the display so the activity is in size compatibility mode. resizeDisplay(display, 900, 1800); @@ -488,7 +486,7 @@ public class SizeCompatTests extends WindowTestsBase { }); // Resize the display so that the activity exercises size-compat mode. - resizeDisplay(mStack.mDisplayContent, 1000, 2500); + resizeDisplay(mTask.mDisplayContent, 1000, 2500); // Expect the exact token when the activity is in size compatibility mode. assertEquals(1, compatTokens.size()); @@ -501,7 +499,7 @@ public class SizeCompatTests extends WindowTestsBase { activity.restartProcessIfVisible(); // The full lifecycle isn't hooked up so manually set state to resumed activity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatMode"); - mStack.mDisplayContent.handleActivitySizeCompatModeIfNeeded(activity); + mTask.mDisplayContent.handleActivitySizeCompatModeIfNeeded(activity); // Expect null token when switching to non-size-compat mode activity. assertEquals(1, compatTokens.size()); @@ -525,13 +523,13 @@ public class SizeCompatTests extends WindowTestsBase { // The non-resizable activity should not be size compat because it is on a resizable task // in multi-window mode. - mStack.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM); + mTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM); assertFalse(activity.shouldUseSizeCompatMode()); // The non-resizable activity should not be size compat because the display support // changing windowing mode from fullscreen to freeform. - mStack.mDisplayContent.setDisplayWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM); - mStack.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN); + mTask.mDisplayContent.setDisplayWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM); + mTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN); assertFalse(activity.shouldUseSizeCompatMode()); } @@ -783,7 +781,7 @@ public class SizeCompatTests extends WindowTestsBase { assertEquals(mTask.getLastTaskBoundsComputeActivity(), mActivity); final Rect activityBounds = new Rect(mActivity.getBounds()); - mStack.resumeTopActivityUncheckedLocked(null /* prev */, null /* options */); + mTask.resumeTopActivityUncheckedLocked(null /* prev */, null /* options */); // App still in size compat, and the bounds don't change. verify(mActivity, never()).clearSizeCompatMode(); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java index 4bd8edd44f5c..28ba710797c9 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java @@ -70,23 +70,22 @@ import org.junit.runner.RunWith; @RunWith(WindowTestRunner.class) public class TaskDisplayAreaTests extends WindowTestsBase { - private Task mPinnedStack; + private Task mPinnedTask; @Before public void setUp() throws Exception { - mPinnedStack = createTaskStackOnDisplay( + mPinnedTask = createTaskStackOnDisplay( WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, mDisplayContent); // Stack should contain visible app window to be considered visible. - final Task pinnedTask = createTaskInStack(mPinnedStack, 0 /* userId */); - assertFalse(mPinnedStack.isVisible()); + assertFalse(mPinnedTask.isVisible()); final ActivityRecord pinnedApp = createNonAttachedActivityRecord(mDisplayContent); - pinnedTask.addChild(pinnedApp, 0 /* addPos */); - assertTrue(mPinnedStack.isVisible()); + mPinnedTask.addChild(pinnedApp, 0 /* addPos */); + assertTrue(mPinnedTask.isVisible()); } @After public void tearDown() throws Exception { - mPinnedStack.removeImmediately(); + mPinnedTask.removeImmediately(); } @Test @@ -118,19 +117,19 @@ public class TaskDisplayAreaTests extends WindowTestsBase { final int stack1Pos = taskStackContainer.mChildren.indexOf(stack1); final int stack2Pos = taskStackContainer.mChildren.indexOf(stack2); - final int pinnedStackPos = taskStackContainer.mChildren.indexOf(mPinnedStack); + final int pinnedStackPos = taskStackContainer.mChildren.indexOf(mPinnedTask); assertThat(pinnedStackPos).isGreaterThan(stack2Pos); assertThat(stack2Pos).isGreaterThan(stack1Pos); - taskStackContainer.positionChildAt(WindowContainer.POSITION_BOTTOM, mPinnedStack, false); + taskStackContainer.positionChildAt(WindowContainer.POSITION_BOTTOM, mPinnedTask, false); assertEquals(taskStackContainer.mChildren.get(stack1Pos), stack1); assertEquals(taskStackContainer.mChildren.get(stack2Pos), stack2); - assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedStack); + assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedTask); - taskStackContainer.positionChildAt(1, mPinnedStack, false); + taskStackContainer.positionChildAt(1, mPinnedTask, false); assertEquals(taskStackContainer.mChildren.get(stack1Pos), stack1); assertEquals(taskStackContainer.mChildren.get(stack2Pos), stack2); - assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedStack); + assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedTask); } @Test @@ -141,16 +140,16 @@ public class TaskDisplayAreaTests extends WindowTestsBase { final WindowContainer taskStackContainer = stack1.getParent(); final int stackPos = taskStackContainer.mChildren.indexOf(stack1); - final int pinnedStackPos = taskStackContainer.mChildren.indexOf(mPinnedStack); + final int pinnedStackPos = taskStackContainer.mChildren.indexOf(mPinnedTask); assertThat(pinnedStackPos).isGreaterThan(stackPos); taskStackContainer.positionChildAt(WindowContainer.POSITION_TOP, stack1, false); assertEquals(taskStackContainer.mChildren.get(stackPos), stack1); - assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedStack); + assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedTask); taskStackContainer.positionChildAt(taskStackContainer.mChildren.size() - 1, stack1, false); assertEquals(taskStackContainer.mChildren.get(stackPos), stack1); - assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedStack); + assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedTask); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java index 98520bb7a20b..4f55322e2085 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java @@ -35,6 +35,7 @@ import static android.view.InsetsState.ITYPE_STATUS_BAR; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; +import static com.android.server.wm.ActivityStarter.Request; import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE; import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP; @@ -42,6 +43,9 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import android.app.ActivityOptions; import android.content.pm.ActivityInfo; @@ -265,6 +269,180 @@ public class TaskLaunchParamsModifierTests extends WindowTestsBase { mResult.mPreferredTaskDisplayArea); } + @Test + public void testUsesDisplayAreaFromTopMostActivityInApplicationIfAvailable() { + final String processName = "processName"; + final int uid = 124214; + final TestDisplayContent firstScreen = createNewDisplayContent(WINDOWING_MODE_FULLSCREEN); + final TestDisplayContent secondScreen = createNewDisplayContent(WINDOWING_MODE_FULLSCREEN); + final TaskDisplayArea expectedDisplayArea = secondScreen.getDefaultTaskDisplayArea(); + final WindowProcessController controller = mock(WindowProcessController.class); + + when(controller.getTopActivityDisplayArea()).thenReturn(expectedDisplayArea); + + when(mActivity.getProcessName()).thenReturn(processName); + when(mActivity.getUid()).thenReturn(uid); + doReturn(controller) + .when(mSupervisor.mService) + .getProcessController(processName, uid); + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate( + null /* task */, + null /* layout */, + mActivity /* activity */, + null /* source */, + null /* options */, + -1 /* phase */, + mCurrent, + mResult, + null /* request */ + )); + + assertEquals(expectedDisplayArea, mResult.mPreferredTaskDisplayArea); + } + + @Test + public void testUsesDisplayAreaFromLaunchingActivityIfApplicationLaunching() { + final String processName = "processName"; + final int uid = 124214; + final TestDisplayContent firstScreen = createNewDisplayContent(WINDOWING_MODE_FULLSCREEN); + final TestDisplayContent secondScreen = createNewDisplayContent(WINDOWING_MODE_FULLSCREEN); + final TaskDisplayArea expectedTaskDisplayArea = secondScreen.getDefaultTaskDisplayArea(); + final WindowProcessController controller = mock(WindowProcessController.class); + + when(controller.getTopActivityDisplayArea()).thenReturn(expectedTaskDisplayArea); + + when(mActivity.getProcessName()).thenReturn(processName); + when(mActivity.getUid()).thenReturn(uid); + doReturn(null) + .when(mSupervisor.mService) + .getProcessController(processName, uid); + + doReturn(controller) + .when(mSupervisor.mService) + .getProcessController(mActivity.launchedFromPid, mActivity.launchedFromUid); + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate( + null /* task */, + null /* layout */, + mActivity /* activity */, + null /* source */, + null /* options */, + -1 /* phase */, + mCurrent, + mResult, + null /* request */ + )); + + assertEquals(expectedTaskDisplayArea, mResult.mPreferredTaskDisplayArea); + } + + @Test + public void testDisplayAreaFromLaunchingActivityTakesPrecedence() { + final String processName = "processName"; + final int uid = 124214; + final TestDisplayContent firstScreen = createNewDisplayContent(WINDOWING_MODE_FULLSCREEN); + final TestDisplayContent secondScreen = createNewDisplayContent(WINDOWING_MODE_FULLSCREEN); + final TaskDisplayArea firstTaskDisplayArea = firstScreen.getDefaultTaskDisplayArea(); + final TaskDisplayArea expectedTaskDisplayArea = secondScreen.getDefaultTaskDisplayArea(); + final WindowProcessController controllerForLaunching = mock(WindowProcessController.class); + final WindowProcessController controllerForApplication = + mock(WindowProcessController.class); + + when(mActivity.getProcessName()).thenReturn(processName); + when(mActivity.getUid()).thenReturn(uid); + + when(controllerForApplication.getTopActivityDisplayArea()).thenReturn(firstTaskDisplayArea); + when(controllerForLaunching.getTopActivityDisplayArea()) + .thenReturn(expectedTaskDisplayArea); + + doReturn(controllerForApplication) + .when(mSupervisor.mService) + .getProcessController(processName, uid); + doReturn(controllerForLaunching) + .when(mSupervisor.mService) + .getProcessController(mActivity.launchedFromPid, mActivity.launchedFromUid); + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate( + null /* task */, + null /* layout */, + mActivity /* activity */, + null /* source */, + null /* options */, + -1 /* phase */, + mCurrent, + mResult, + null /* request */ + )); + + assertEquals(expectedTaskDisplayArea, mResult.mPreferredTaskDisplayArea); + } + + @Test + public void testUsesDisplayAreaOriginalProcessAsLastResort() { + final TestDisplayContent firstScreen = createNewDisplayContent(WINDOWING_MODE_FULLSCREEN); + final TestDisplayContent secondScreen = createNewDisplayContent(WINDOWING_MODE_FULLSCREEN); + final TaskDisplayArea expectedTaskDisplayArea = secondScreen.getDefaultTaskDisplayArea(); + final Request request = new Request(); + request.realCallingPid = 12412413; + request.realCallingUid = 235424; + + final WindowProcessController controller = mock(WindowProcessController.class); + + when(controller.getTopActivityDisplayArea()).thenReturn(expectedTaskDisplayArea); + + doReturn(null) + .when(mSupervisor.mService) + .getProcessController(mActivity.processName, mActivity.info.applicationInfo.uid); + + doReturn(null) + .when(mSupervisor.mService) + .getProcessController(mActivity.launchedFromPid, mActivity.launchedFromUid); + + doReturn(controller) + .when(mSupervisor.mService) + .getProcessController(request.realCallingPid, request.realCallingUid); + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate( + null /* task */, + null /* layout */, + mActivity /* activity */, + null /* source */, + null /* options */, + -1 /* phase */, + mCurrent, + mResult, + request + )); + + assertEquals(expectedTaskDisplayArea, mResult.mPreferredTaskDisplayArea); + } + + @Test + public void testUsesDefaultDisplayAreaIfWindowProcessControllerIsNotPresent() { + doReturn(null) + .when(mSupervisor.mService) + .getProcessController(mActivity.processName, mActivity.info.applicationInfo.uid); + + doReturn(null) + .when(mSupervisor.mService) + .getProcessController(mActivity.launchedFromPid, mActivity.launchedFromUid); + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate( + null /* task */, + null /* layout */, + mActivity /* activity */, + null /* source */, + null /* options */, + -1 /* phase */, + mCurrent, + mResult, + null /* request */ + )); + + assertEquals(DEFAULT_DISPLAY, mResult.mPreferredTaskDisplayArea.getDisplayId()); + } + // ===================================== // Launch Windowing Mode Related Tests // ===================================== diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java index 165d4681c9e0..4909b1d7629e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java @@ -17,6 +17,7 @@ package com.android.server.wm; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN; import static android.window.TransitionInfo.TRANSIT_HIDE; @@ -46,16 +47,24 @@ import org.junit.runner.RunWith; @RunWith(WindowTestRunner.class) public class TransitionTests extends WindowTestsBase { + private Transition createTestTransition(int transitType) { + TransitionController controller = mock(TransitionController.class); + BLASTSyncEngine sync = new BLASTSyncEngine(mWm); + return new Transition(transitType, 0 /* flags */, controller, sync); + } + @Test public void testCreateInfo_NewTask() { + final Transition transition = createTestTransition(TRANSIT_OLD_TASK_OPEN); + ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; + ArraySet<WindowContainer> participants = transition.mParticipants; + final Task newTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent); final Task oldTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent); final ActivityRecord closing = createActivityRecord(oldTask); final ActivityRecord opening = createActivityRecord(newTask); - ArrayMap<WindowContainer, Transition.ChangeInfo> changes = new ArrayMap<>(); - ArraySet<WindowContainer> participants = new ArraySet(); // Start states. changes.put(newTask, new Transition.ChangeInfo(false /* vis */, true /* exChg */)); changes.put(oldTask, new Transition.ChangeInfo(true /* vis */, true /* exChg */)); @@ -70,29 +79,32 @@ public class TransitionTests extends WindowTestsBase { // Check basic both tasks participating participants.add(oldTask); participants.add(newTask); - TransitionInfo info = - Transition.calculateTransitionInfo(transitType, participants, changes); + ArraySet<WindowContainer> targets = Transition.calculateTargets(participants, changes); + TransitionInfo info = Transition.calculateTransitionInfo(transitType, targets, changes); assertEquals(2, info.getChanges().size()); assertEquals(transitType, info.getType()); // Check that children are pruned participants.add(opening); participants.add(closing); - info = Transition.calculateTransitionInfo(transitType, participants, changes); + targets = Transition.calculateTargets(participants, changes); + info = Transition.calculateTransitionInfo(transitType, targets, changes); assertEquals(2, info.getChanges().size()); assertNotNull(info.getChange(newTask.mRemoteToken.toWindowContainerToken())); assertNotNull(info.getChange(oldTask.mRemoteToken.toWindowContainerToken())); // Check combined prune and promote participants.remove(newTask); - info = Transition.calculateTransitionInfo(transitType, participants, changes); + targets = Transition.calculateTargets(participants, changes); + info = Transition.calculateTransitionInfo(transitType, targets, changes); assertEquals(2, info.getChanges().size()); assertNotNull(info.getChange(newTask.mRemoteToken.toWindowContainerToken())); assertNotNull(info.getChange(oldTask.mRemoteToken.toWindowContainerToken())); // Check multi promote participants.remove(oldTask); - info = Transition.calculateTransitionInfo(transitType, participants, changes); + targets = Transition.calculateTargets(participants, changes); + info = Transition.calculateTransitionInfo(transitType, targets, changes); assertEquals(2, info.getChanges().size()); assertNotNull(info.getChange(newTask.mRemoteToken.toWindowContainerToken())); assertNotNull(info.getChange(oldTask.mRemoteToken.toWindowContainerToken())); @@ -100,6 +112,10 @@ public class TransitionTests extends WindowTestsBase { @Test public void testCreateInfo_NestedTasks() { + final Transition transition = createTestTransition(TRANSIT_OLD_TASK_OPEN); + ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; + ArraySet<WindowContainer> participants = transition.mParticipants; + final Task newTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent); final Task newNestedTask = createTaskInStack(newTask, 0); @@ -109,8 +125,6 @@ public class TransitionTests extends WindowTestsBase { final ActivityRecord closing = createActivityRecord(oldTask); final ActivityRecord opening = createActivityRecord(newNestedTask); final ActivityRecord opening2 = createActivityRecord(newNestedTask2); - ArrayMap<WindowContainer, Transition.ChangeInfo> changes = new ArrayMap<>(); - ArraySet<WindowContainer> participants = new ArraySet(); // Start states. changes.put(newTask, new Transition.ChangeInfo(false /* vis */, true /* exChg */)); changes.put(newNestedTask, new Transition.ChangeInfo(false /* vis */, true /* exChg */)); @@ -130,8 +144,8 @@ public class TransitionTests extends WindowTestsBase { participants.add(oldTask); participants.add(opening); participants.add(opening2); - TransitionInfo info = - Transition.calculateTransitionInfo(transitType, participants, changes); + ArraySet<WindowContainer> targets = Transition.calculateTargets(participants, changes); + TransitionInfo info = Transition.calculateTransitionInfo(transitType, targets, changes); assertEquals(2, info.getChanges().size()); assertEquals(transitType, info.getType()); assertNotNull(info.getChange(newTask.mRemoteToken.toWindowContainerToken())); @@ -139,7 +153,8 @@ public class TransitionTests extends WindowTestsBase { // Check that unchanging but visible descendant of sibling prevents promotion participants.remove(opening2); - info = Transition.calculateTransitionInfo(transitType, participants, changes); + targets = Transition.calculateTargets(participants, changes); + info = Transition.calculateTransitionInfo(transitType, targets, changes); assertEquals(2, info.getChanges().size()); assertNotNull(info.getChange(newNestedTask.mRemoteToken.toWindowContainerToken())); assertNotNull(info.getChange(oldTask.mRemoteToken.toWindowContainerToken())); @@ -147,6 +162,9 @@ public class TransitionTests extends WindowTestsBase { @Test public void testCreateInfo_DisplayArea() { + final Transition transition = createTestTransition(TRANSIT_OLD_TASK_OPEN); + ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; + ArraySet<WindowContainer> participants = transition.mParticipants; final Task showTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent); final Task showNestedTask = createTaskInStack(showTask, 0); @@ -155,8 +173,6 @@ public class TransitionTests extends WindowTestsBase { final DisplayArea tda = showTask.getDisplayArea(); final ActivityRecord showing = createActivityRecord(showNestedTask); final ActivityRecord showing2 = createActivityRecord(showTask2); - ArrayMap<WindowContainer, Transition.ChangeInfo> changes = new ArrayMap<>(); - ArraySet<WindowContainer> participants = new ArraySet(); // Start states. changes.put(showTask, new Transition.ChangeInfo(false /* vis */, true /* exChg */)); changes.put(showNestedTask, new Transition.ChangeInfo(false /* vis */, true /* exChg */)); @@ -173,8 +189,8 @@ public class TransitionTests extends WindowTestsBase { // Check promotion to DisplayArea participants.add(showing); participants.add(showing2); - TransitionInfo info = - Transition.calculateTransitionInfo(transitType, participants, changes); + ArraySet<WindowContainer> targets = Transition.calculateTargets(participants, changes); + TransitionInfo info = Transition.calculateTransitionInfo(transitType, targets, changes); assertEquals(1, info.getChanges().size()); assertEquals(transitType, info.getType()); assertNotNull(info.getChange(tda.mRemoteToken.toWindowContainerToken())); @@ -182,22 +198,21 @@ public class TransitionTests extends WindowTestsBase { ITaskOrganizer mockOrg = mock(ITaskOrganizer.class); // Check that organized tasks get reported even if not top showTask.mTaskOrganizer = mockOrg; - info = Transition.calculateTransitionInfo(transitType, participants, changes); + targets = Transition.calculateTargets(participants, changes); + info = Transition.calculateTransitionInfo(transitType, targets, changes); assertEquals(2, info.getChanges().size()); assertNotNull(info.getChange(tda.mRemoteToken.toWindowContainerToken())); assertNotNull(info.getChange(showTask.mRemoteToken.toWindowContainerToken())); // Even if DisplayArea explicitly participating participants.add(tda); - info = Transition.calculateTransitionInfo(transitType, participants, changes); + targets = Transition.calculateTargets(participants, changes); + info = Transition.calculateTransitionInfo(transitType, targets, changes); assertEquals(2, info.getChanges().size()); } @Test public void testCreateInfo_existenceChange() { - TransitionController controller = mock(TransitionController.class); - BLASTSyncEngine sync = new BLASTSyncEngine(mWm); - Transition transition = new Transition( - TRANSIT_OLD_TASK_OPEN, 0 /* flags */, controller, sync); + final Transition transition = createTestTransition(TRANSIT_OLD_TASK_OPEN); final Task openTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent); @@ -214,8 +229,9 @@ public class TransitionTests extends WindowTestsBase { opening.mVisibleRequested = true; closing.mVisibleRequested = false; - TransitionInfo info = Transition.calculateTransitionInfo( - 0, transition.mParticipants, transition.mChanges); + ArraySet<WindowContainer> targets = Transition.calculateTargets( + transition.mParticipants, transition.mChanges); + TransitionInfo info = Transition.calculateTransitionInfo(0, targets, transition.mChanges); assertEquals(2, info.getChanges().size()); // There was an existence change on open, so it should be OPEN rather than SHOW assertEquals(TRANSIT_OPEN, @@ -224,4 +240,39 @@ public class TransitionTests extends WindowTestsBase { assertEquals(TRANSIT_HIDE, info.getChange(closeTask.mRemoteToken.toWindowContainerToken()).getMode()); } + + @Test + public void testCreateInfo_ordering() { + final Transition transition = createTestTransition(TRANSIT_OLD_TASK_OPEN); + // pick some number with a high enough chance of being out-of-order when added to set. + final int taskCount = 6; + + final Task[] tasks = new Task[taskCount]; + for (int i = 0; i < taskCount; ++i) { + // Each add goes on top, so at the end of this, task[9] should be on top + tasks[i] = createTaskStackOnDisplay(WINDOWING_MODE_FREEFORM, + ACTIVITY_TYPE_STANDARD, mDisplayContent); + final ActivityRecord act = createActivityRecord(tasks[i]); + // alternate so that the transition doesn't get promoted to the display area + act.mVisibleRequested = (i % 2) == 0; // starts invisible + } + + // doesn't matter which order collected since participants is a set + for (int i = 0; i < taskCount; ++i) { + transition.collectExistenceChange(tasks[i]); + final ActivityRecord act = tasks[i].getTopMostActivity(); + transition.collect(act); + tasks[i].getTopMostActivity().mVisibleRequested = (i % 2) != 0; + } + + ArraySet<WindowContainer> targets = Transition.calculateTargets( + transition.mParticipants, transition.mChanges); + TransitionInfo info = Transition.calculateTransitionInfo(0, targets, transition.mChanges); + assertEquals(taskCount, info.getChanges().size()); + // verify order is top-to-bottem + for (int i = 0; i < taskCount; ++i) { + assertEquals(tasks[taskCount - i - 1].mRemoteToken.toWindowContainerToken(), + info.getChanges().get(i).getContainer()); + } + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java index 5afcedb10dd6..3057558a6e98 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java @@ -27,10 +27,13 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; +import static org.mockito.Mockito.when; import android.Manifest; import android.app.IApplicationThread; @@ -327,6 +330,45 @@ public class WindowProcessControllerTests extends WindowTestsBase { return new ActivityBuilder(mAtm).setCreateTask(true).setUseProcess(wpc).build(); } + @Test + public void testTopActivityDisplayAreaMatchesTopMostActivity_noActivities() { + assertNull(mWpc.getTopActivityDisplayArea()); + } + + @Test + public void testTopActivityDisplayAreaMatchesTopMostActivity_singleActivity() { + final ActivityRecord activityRecord = new ActivityBuilder(mSupervisor.mService).build(); + final TaskDisplayArea expectedDisplayArea = mock(TaskDisplayArea.class); + + when(activityRecord.getDisplayArea()) + .thenReturn(expectedDisplayArea); + + mWpc.addActivityIfNeeded(activityRecord); + + assertEquals(expectedDisplayArea, mWpc.getTopActivityDisplayArea()); + } + + /** + * Test that top most activity respects z-order. + */ + @Test + public void testTopActivityDisplayAreaMatchesTopMostActivity_multipleActivities() { + final ActivityRecord bottomRecord = new ActivityBuilder(mSupervisor.mService).build(); + final TaskDisplayArea bottomDisplayArea = mock(TaskDisplayArea.class); + final ActivityRecord topRecord = new ActivityBuilder(mSupervisor.mService).build(); + final TaskDisplayArea topDisplayArea = mock(TaskDisplayArea.class); + + when(bottomRecord.getDisplayArea()).thenReturn(bottomDisplayArea); + when(topRecord.getDisplayArea()).thenReturn(topDisplayArea); + doReturn(-1).when(bottomRecord).compareTo(topRecord); + doReturn(1).when(topRecord).compareTo(bottomRecord); + + mWpc.addActivityIfNeeded(topRecord); + mWpc.addActivityIfNeeded(bottomRecord); + + assertEquals(topDisplayArea, mWpc.getTopActivityDisplayArea()); + } + private TestDisplayContent createTestDisplayContentInContainer() { return new TestDisplayContent.Builder(mAtm, 1000, 1500).build(); } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index 75d2c5159187..227eba2a041b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -35,6 +35,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; +import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; @@ -647,6 +648,8 @@ public class WindowStateTests extends WindowTestsBase { win1.mSurfaceControl = mock(SurfaceControl.class); win1.mAttrs.surfaceInsets.set(1, 2, 3, 4); win1.getFrame().offsetTo(WINDOW_OFFSET, 0); + // Simulate layout + win1.mRelayoutCalled = true; win1.updateSurfacePosition(t); win1.getTransformationMatrix(values, matrix); @@ -731,6 +734,39 @@ public class WindowStateTests extends WindowTestsBase { handle.inputFeatures); } + @Test + public void testHasActiveVisibleWindow() { + final int uid = ActivityBuilder.DEFAULT_FAKE_UID; + mAtm.mActiveUids.onUidActive(uid, 0 /* any proc state */); + + final WindowState app = createWindow(null, TYPE_APPLICATION, "app", uid); + app.mActivityRecord.setVisible(false); + app.mActivityRecord.setVisibility(false /* visible */, false /* deferHidingClient */); + assertFalse(mAtm.hasActiveVisibleWindow(uid)); + + app.mActivityRecord.setVisibility(true /* visible */, false /* deferHidingClient */); + assertTrue(mAtm.hasActiveVisibleWindow(uid)); + + // Make the activity invisible and add a visible toast. The uid should have no active + // visible window because toast can be misused by legacy app to bypass background check. + app.mActivityRecord.setVisibility(false /* visible */, false /* deferHidingClient */); + final WindowState overlay = createWindow(null, TYPE_APPLICATION_OVERLAY, "overlay", uid); + final WindowState toast = createWindow(null, TYPE_TOAST, app.mToken, "toast", uid); + toast.onSurfaceShownChanged(true); + assertFalse(mAtm.hasActiveVisibleWindow(uid)); + + // Though starting window should belong to system. Make sure it is ignored to avoid being + // allow-list unexpectedly, see b/129563343. + final WindowState starting = + createWindow(null, TYPE_APPLICATION_STARTING, app.mToken, "starting", uid); + starting.onSurfaceShownChanged(true); + assertFalse(mAtm.hasActiveVisibleWindow(uid)); + + // Make the application overlay window visible. It should be a valid active visible window. + overlay.onSurfaceShownChanged(true); + assertTrue(mAtm.hasActiveVisibleWindow(uid)); + } + @UseTestDisplay(addWindows = W_ACTIVITY) @Test public void testNeedsRelativeLayeringToIme_notAttached() { diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index ee1034c561ab..6c046bda1444 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -40,6 +40,8 @@ import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; +import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL; +import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; @@ -80,6 +82,7 @@ import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; import android.view.View; import android.view.WindowManager; +import android.view.WindowManager.DisplayImePolicy; import android.window.ITaskOrganizer; import com.android.internal.util.ArrayUtils; @@ -174,7 +177,7 @@ class WindowTestsBase extends SystemServiceTestsBase { private void createTestDisplay(UseTestDisplay annotation) { beforeCreateTestDisplay(); - mDisplayContent = createNewDisplay(true /* supportIme */); + mDisplayContent = createNewDisplayWithImeSupport(DISPLAY_IME_POLICY_LOCAL); final boolean addAll = annotation.addAllCommonWindows(); final @CommonTypes int[] requestedWindows = annotation.addWindows(); @@ -482,26 +485,26 @@ class WindowTestsBase extends SystemServiceTestsBase { /** Creates a {@link DisplayContent} that supports IME and adds it to the system. */ DisplayContent createNewDisplay() { - return createNewDisplay(true /* supportIme */); + return createNewDisplayWithImeSupport(DISPLAY_IME_POLICY_LOCAL); } /** Creates a {@link DisplayContent} and adds it to the system. */ - private DisplayContent createNewDisplay(boolean supportIme) { - return createNewDisplay(mDisplayInfo, supportIme); + private DisplayContent createNewDisplayWithImeSupport(@DisplayImePolicy int imePolicy) { + return createNewDisplay(mDisplayInfo, imePolicy); } /** Creates a {@link DisplayContent} that supports IME and adds it to the system. */ DisplayContent createNewDisplay(DisplayInfo info) { - return createNewDisplay(info, true /* supportIme */); + return createNewDisplay(info, DISPLAY_IME_POLICY_LOCAL); } /** Creates a {@link DisplayContent} and adds it to the system. */ - private DisplayContent createNewDisplay(DisplayInfo info, boolean supportIme) { + private DisplayContent createNewDisplay(DisplayInfo info, @DisplayImePolicy int imePolicy) { final DisplayContent display = new TestDisplayContent.Builder(mAtm, info).build(); final DisplayContent dc = display.mDisplayContent; // this display can show IME. - dc.mWmService.mDisplayWindowSettings.setShouldShowImeLocked(dc, supportIme); + dc.mWmService.mDisplayWindowSettings.setDisplayImePolicy(dc, imePolicy); return dc; } @@ -516,7 +519,7 @@ class WindowTestsBase extends SystemServiceTestsBase { DisplayInfo displayInfo = new DisplayInfo(); displayInfo.copyFrom(mDisplayInfo); displayInfo.state = displayState; - return createNewDisplay(displayInfo, true /* supportIme */); + return createNewDisplay(displayInfo, DISPLAY_IME_POLICY_LOCAL); } /** Creates a {@link TestWindowState} */ @@ -532,7 +535,7 @@ class WindowTestsBase extends SystemServiceTestsBase { displayInfo.copyFrom(mDisplayInfo); displayInfo.type = Display.TYPE_VIRTUAL; displayInfo.ownerUid = SYSTEM_UID; - return createNewDisplay(displayInfo, false /* supportIme */); + return createNewDisplay(displayInfo, DISPLAY_IME_POLICY_FALLBACK_DISPLAY); } IDisplayWindowInsetsController createDisplayWindowInsetsController() { @@ -637,6 +640,7 @@ class WindowTestsBase extends SystemServiceTestsBase { * Builder for creating new activities. */ protected static class ActivityBuilder { + static final int DEFAULT_FAKE_UID = 12345; // An id appended to the end of the component name to make it unique private static int sCurrentActivityId = 0; @@ -647,7 +651,7 @@ class WindowTestsBase extends SystemServiceTestsBase { private Task mTask; private String mProcessName = "name"; private String mAffinity; - private int mUid = 12345; + private int mUid = DEFAULT_FAKE_UID; private boolean mCreateTask = false; private Task mParentTask; private int mActivityFlags; @@ -840,10 +844,13 @@ class WindowTestsBase extends SystemServiceTestsBase { // to set it somewhere else since we can't mock resources. doReturn(true).when(activity).occludesParent(); doReturn(true).when(activity).fillsParent(); + mTask.addChild(activity); if (mOnTop) { + // Move the task to front after activity added. + // Or {@link TaskDisplayArea#mPreferredTopFocusableStack} could be other stacks + // (e.g. home stack). mTask.moveToFront("createActivity"); } - mTask.addChild(activity); // Make visible by default... activity.setVisible(true); } @@ -854,7 +861,7 @@ class WindowTestsBase extends SystemServiceTestsBase { } else { wpc = new WindowProcessController(mService, aInfo.applicationInfo, mProcessName, mUid, - UserHandle.getUserId(12345), mock(Object.class), + UserHandle.getUserId(mUid), mock(Object.class), mock(WindowProcessListener.class)); wpc.setThread(mock(IApplicationThread.class)); } diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 8e56e5bb2d85..aa36e47a359b 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -82,6 +82,7 @@ import android.util.SparseArray; import android.util.SparseBooleanArray; import android.util.SparseIntArray; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.content.PackageMonitor; import com.android.internal.os.BackgroundThread; import com.android.internal.util.CollectionUtils; @@ -90,7 +91,6 @@ import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.IndentingPrintWriter; import com.android.server.LocalServices; import com.android.server.SystemService; -import com.android.server.SystemService.TargetUser; import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener; import java.io.BufferedReader; @@ -178,6 +178,8 @@ public class UsageStatsService extends SystemService implements private final SparseArray<LinkedList<Event>> mReportedEvents = new SparseArray<>(); final SparseArray<ArraySet<String>> mUsageReporters = new SparseArray(); final SparseArray<ActivityData> mVisibleActivities = new SparseArray(); + private final ArraySet<UsageStatsManagerInternal.UsageEventListener> mUsageEventListeners = + new ArraySet<>(); private static class ActivityData { private final String mTaskRootPackage; @@ -202,8 +204,24 @@ public class UsageStatsService extends SystemService implements } }; + @VisibleForTesting + static class Injector { + AppStandbyInternal getAppStandbyController(Context context) { + return AppStandbyInternal.newAppStandbyController( + UsageStatsService.class.getClassLoader(), context); + } + } + + private final Injector mInjector; + public UsageStatsService(Context context) { + this(context, new Injector()); + } + + @VisibleForTesting + UsageStatsService(Context context, Injector injector) { super(context); + mInjector = injector; } @Override @@ -214,8 +232,7 @@ public class UsageStatsService extends SystemService implements mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); mHandler = new H(BackgroundThread.get().getLooper()); - mAppStandby = AppStandbyInternal.newAppStandbyController( - UsageStatsService.class.getClassLoader(), getContext()); + mAppStandby = mInjector.getAppStandbyController(getContext()); mAppTimeLimit = new AppTimeLimitController( new AppTimeLimitController.TimeLimitCallbackListener() { @@ -262,6 +279,11 @@ public class UsageStatsService extends SystemService implements publishLocalService(UsageStatsManagerInternal.class, new LocalService()); publishLocalService(AppStandbyInternal.class, mAppStandby); + publishBinderServices(); + } + + @VisibleForTesting + void publishBinderServices() { publishBinderService(Context.USAGE_STATS_SERVICE, new BinderService()); } @@ -928,7 +950,10 @@ public class UsageStatsService extends SystemService implements service.reportEvent(event); } - mAppStandby.reportEvent(event, userId); + final int size = mUsageEventListeners.size(); + for (int i = 0; i < size; ++i) { + mUsageEventListeners.valueAt(i).onUsageEvent(userId, event); + } } /** @@ -1151,6 +1176,25 @@ public class UsageStatsService extends SystemService implements } } + /** + * Called via the local interface. + */ + private void registerListener(@NonNull UsageStatsManagerInternal.UsageEventListener listener) { + synchronized (mLock) { + mUsageEventListeners.add(listener); + } + } + + /** + * Called via the local interface. + */ + private void unregisterListener( + @NonNull UsageStatsManagerInternal.UsageEventListener listener) { + synchronized (mLock) { + mUsageEventListeners.remove(listener); + } + } + private String buildFullToken(String packageName, String token) { final StringBuilder sb = new StringBuilder(packageName.length() + token.length() + 1); sb.append(packageName); @@ -2317,6 +2361,22 @@ public class UsageStatsService extends SystemService implements public boolean updatePackageMappingsData() { return UsageStatsService.this.updatePackageMappingsData(); } + + /** + * Register a listener that will be notified of every new usage event. + */ + @Override + public void registerListener(@NonNull UsageEventListener listener) { + UsageStatsService.this.registerListener(listener); + } + + /** + * Unregister a listener from being notified of every new usage event. + */ + @Override + public void unregisterListener(@NonNull UsageEventListener listener) { + UsageStatsService.this.unregisterListener(listener); + } } private class MyPackageMonitor extends PackageMonitor { diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java index 632ad4c33e36..4bf93a26ec03 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java @@ -60,7 +60,6 @@ import java.util.Iterator; import java.util.Map; import java.util.Objects; import java.util.UUID; -import java.util.function.BiFunction; /** * Helper for {@link SoundTrigger} APIs. Supports two types of models: @@ -118,8 +117,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { private PowerSaveModeListener mPowerSaveModeListener; - private final BiFunction<Integer, SoundTrigger.StatusListener, SoundTriggerModule> - mModuleProvider; + private final SoundTriggerModuleProvider mModuleProvider; // Handler to process call state changes will delay to allow time for the audio // and sound trigger HALs to process the end of call notifications @@ -128,12 +126,32 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { private static final int MSG_CALL_STATE_CHANGED = 0; private static final int CALL_INACTIVE_MSG_DELAY_MS = 1000; - SoundTriggerHelper(Context context, - @NonNull BiFunction<Integer, SoundTrigger.StatusListener, - SoundTriggerModule> moduleProvider) { + /** + * Provider interface for retrieving SoundTriggerModule instances + */ + public interface SoundTriggerModuleProvider { + /** + * Populate module properties for all available modules + * + * @param modules List of ModuleProperties to be populated + * @return Status int 0 on success. + */ + int listModuleProperties(@NonNull ArrayList<SoundTrigger.ModuleProperties> modules); + + /** + * Get SoundTriggerModule based on {@link SoundTrigger.ModuleProperties#getId()} + * + * @param moduleId Module ID + * @param statusListener Client listener to be associated with the returned module + * @return Module associated with moduleId + */ + SoundTriggerModule getModule(int moduleId, SoundTrigger.StatusListener statusListener); + } + + SoundTriggerHelper(Context context, SoundTriggerModuleProvider moduleProvider) { ArrayList <ModuleProperties> modules = new ArrayList<>(); mModuleProvider = moduleProvider; - int status = SoundTrigger.listModules(modules); + int status = mModuleProvider.listModuleProperties(modules); mContext = context; mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); @@ -272,7 +290,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { private int prepareForRecognition(ModelData modelData) { if (mModule == null) { - mModule = mModuleProvider.apply(mModuleProperties.getId(), this); + mModule = mModuleProvider.getModule(mModuleProperties.getId(), this); if (mModule == null) { Slog.w(TAG, "prepareForRecognition: cannot attach to sound trigger module"); return STATUS_ERROR; diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java index 2a5bfce9bb33..bd678fd54063 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java @@ -51,6 +51,7 @@ import android.hardware.soundtrigger.SoundTrigger.ModelParamRange; import android.hardware.soundtrigger.SoundTrigger.ModuleProperties; import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig; import android.hardware.soundtrigger.SoundTrigger.SoundModel; +import android.hardware.soundtrigger.SoundTriggerModule; import android.media.AudioAttributes; import android.media.AudioFormat; import android.media.AudioRecord; @@ -219,9 +220,20 @@ public class SoundTriggerService extends SystemService { Identity originatorIdentity = IdentityContext.getNonNull(); return new SoundTriggerHelper(mContext, - (moduleId, listener) -> SoundTrigger.attachModuleAsMiddleman(moduleId, listener, - null, - middlemanIdentity, originatorIdentity)); + new SoundTriggerHelper.SoundTriggerModuleProvider() { + @Override + public int listModuleProperties(ArrayList<ModuleProperties> modules) { + return SoundTrigger.listModulesAsMiddleman(modules, middlemanIdentity, + originatorIdentity); + } + + @Override + public SoundTriggerModule getModule(int moduleId, + SoundTrigger.StatusListener statusListener) { + return SoundTrigger.attachModuleAsMiddleman(moduleId, statusListener, null, + middlemanIdentity, originatorIdentity); + } + }); } class SoundTriggerServiceStub extends ISoundTriggerService.Stub { diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java index 5024ae27ee49..835ecaa8c90d 100644 --- a/telecomm/java/android/telecom/PhoneAccount.java +++ b/telecomm/java/android/telecom/PhoneAccount.java @@ -361,7 +361,13 @@ public final class PhoneAccount implements Parcelable { */ public static final int CAPABILITY_ADHOC_CONFERENCE_CALLING = 0x4000; - /* NEXT CAPABILITY: 0x8000 */ + /** + * Flag indicating whether this {@link PhoneAccount} is capable of supporting the call composer + * functionality for enriched calls. + */ + public static final int CAPABILITY_CALL_COMPOSER = 0x8000; + + /* NEXT CAPABILITY: 0x10000 */ /** * URI scheme for telephone number URIs. @@ -1088,6 +1094,9 @@ public final class PhoneAccount implements Parcelable { if (hasCapabilities(CAPABILITY_ADHOC_CONFERENCE_CALLING)) { sb.append("AdhocConf"); } + if (hasCapabilities(CAPABILITY_CALL_COMPOSER)) { + sb.append("CallComposer "); + } return sb.toString(); } diff --git a/telephony/java/android/telephony/Annotation.java b/telephony/java/android/telephony/Annotation.java index 031c3376f3ba..f900c387f060 100644 --- a/telephony/java/android/telephony/Annotation.java +++ b/telephony/java/android/telephony/Annotation.java @@ -634,4 +634,15 @@ public class Annotation { TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA, TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE}) public @interface OverrideNetworkType {} + + /** + * Result of a thermal mitigation request. + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "THERMAL_MITIGATION_RESULT_" }, value = { + TelephonyManager.THERMAL_MITIGATION_RESULT_SUCCESS, + TelephonyManager.THERMAL_MITIGATION_RESULT_MODEM_ERROR, + TelephonyManager.THERMAL_MITIGATION_RESULT_INVALID_STATE, + TelephonyManager.THERMAL_MITIGATION_RESULT_UNKNOWN_ERROR}) + public @interface ThermalMitigationResult {} } diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 902dc0619014..dad18ffe4fa7 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -1881,9 +1881,8 @@ public class CarrierConfigManager { * "APN_1, ERROR_CODE_1 : CARRIER_ACTION_IDX_1, CARRIER_ACTION_IDX_2...", * "APN_1, ERROR_CODE_2 : CARRIER_ACTION_IDX_1 " * } - * Where {@code APN_1} is a string defined in - * com.android.internal.telephony.PhoneConstants - * Example: "default" + * Where {@code APN_1} is an integer defined in {@link android.telephony.data.ApnSetting} + * (e.g. {@link android.telephony.data.ApnSetting#TYPE_DEFAULT} * * {@code ERROR_CODE_1} is an integer defined in android.telephony.DataFailCause * Example: diff --git a/telephony/java/android/telephony/DataThrottlingRequest.aidl b/telephony/java/android/telephony/DataThrottlingRequest.aidl new file mode 100644 index 000000000000..e1a3b66d2433 --- /dev/null +++ b/telephony/java/android/telephony/DataThrottlingRequest.aidl @@ -0,0 +1,19 @@ +/* +* Copyright (C) 2020 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +package android.telephony; + +parcelable DataThrottlingRequest;
\ No newline at end of file diff --git a/telephony/java/android/telephony/DataThrottlingRequest.java b/telephony/java/android/telephony/DataThrottlingRequest.java new file mode 100644 index 000000000000..f50bb58c4b2e --- /dev/null +++ b/telephony/java/android/telephony/DataThrottlingRequest.java @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.telephony; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + + +/** + * Class stores information related to the type of data throttling request. Must be populated as + * field in {@link ThermalMitigationRequest} for sending of thermal mitigation request at {@link + * TelephonyManager#sendThermalMitigationRequest(ThermalMitigationResult)}. + * @hide + */ +@SystemApi +public final class DataThrottlingRequest implements Parcelable { + /** + * Clear all existing data throttling, enable data, and attempt to enable radio for thermal + * mitigation all within the requested completion window. Note that attempting to enable radio + * will not guarantee that radio will actually be enabled. + * + * @hide + */ + @SystemApi + public static final int DATA_THROTTLING_ACTION_NO_DATA_THROTTLING = 0; + + /** + * Enact secondary carrier data throttling within specified completion window. This also + * attempts to enables radio if currently disabled for thermal mitigation, enables data, and + * removes any existing data throttling on primary carrier. Note that attempting to enable radio + * will not guarantee that radio will actually be enabled. + * + * @hide + */ + @SystemApi + public static final int DATA_THROTTLING_ACTION_THROTTLE_SECONDARY_CARRIER = 1; + + /** + * Enact primary carrier data throttling within specified completion window. This also attempts + * to enable radio if currently disabled for thermal mitigation and disables data on secondary + * carrier if currently enabled. Note that attempting to enable radio will not guarantee that + * radio will actually be enabled. + * + * @hide + */ + @SystemApi + public static final int DATA_THROTTLING_ACTION_THROTTLE_PRIMARY_CARRIER = 2; + + /** + * Immediately hold on to the current level of data throttling indicating that the current level + * of data throttling has alleviated the thermal concerns which caused the original data + * throttling request. A thermal module should remain actively monitoring the temperature levels + * and request an appropriate thermal mitigation action. {@link + * #THERMAL_MITIGATION_RESULT_INVALID_PARAMETERS} will be returned if completion window is not + * 0. + * + * @hide + */ + @SystemApi + public static final int DATA_THROTTLING_ACTION_HOLD = 3; + + /** + * Type of data throttling action to carry out. + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "DATA_THROTTLING_ACTION_" }, value = { + DATA_THROTTLING_ACTION_NO_DATA_THROTTLING, + DATA_THROTTLING_ACTION_THROTTLE_SECONDARY_CARRIER, + DATA_THROTTLING_ACTION_THROTTLE_PRIMARY_CARRIER, + DATA_THROTTLING_ACTION_HOLD}) + public @interface DataThrottlingAction {} + + /** + * Represents the data throttling action that will be requested. See {@link + * DATA_THROTTLING_ACTION_NO_DATA_THROTTLING}, {@link + * #DATA_THROTTLING_ACTION_THROTTLE_SECONDARY_CARRIER}, {@link + * #DATA_THROTTLING_ACTION_THROTTLE_PRIMARY_CARRIER}, and {@link + * #DATA_THROTTLING_ACTION_HOLD} for more details. + **/ + private @DataThrottlingAction int mDataThrottlingAction; + /** + * Represents the time over which modem should gradually execute the data thorttling request. + */ + private long mCompletionDurationMillis; + + private DataThrottlingRequest(@NonNull int dataThrottlingAction, + long completionDurationMillis) { + mDataThrottlingAction = dataThrottlingAction; + mCompletionDurationMillis = completionDurationMillis; + } + + private DataThrottlingRequest(Parcel in) { + mDataThrottlingAction = in.readInt(); + mCompletionDurationMillis = in.readLong(); + } + + /** + * Implement the Parcelable interface + */ + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mDataThrottlingAction); + dest.writeLong(mCompletionDurationMillis); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public String toString() { + return "[DataThrottlingRequest " + + ", DataThrottlingAction=" + mDataThrottlingAction + + ", completionDurationMillis=" + mCompletionDurationMillis + + "]"; + } + + /** + * @return the dataThrottlingAction. + */ + public @DataThrottlingAction int getDataThrottlingAction() { + return mDataThrottlingAction; + } + + /** + * @return the completionDurationMillis which represents the time over which modem should + * gradually execute the data thorttling request. + */ + public long getCompletionDurationMillis() { + return mCompletionDurationMillis; + } + + public static final @NonNull Parcelable.Creator<DataThrottlingRequest> CREATOR = + new Parcelable.Creator<DataThrottlingRequest>() { + + @Override + public DataThrottlingRequest createFromParcel(Parcel in) { + return new DataThrottlingRequest(in); + } + + @Override + public DataThrottlingRequest[] newArray(int size) { + return new DataThrottlingRequest[size]; + } + }; + + /** + * Provides a convenient way to set the fields of a {@link DataThrottlingRequest} when creating + * a new instance. + * + * <p>The example below shows how you might create a new {@code DataThrottlingRequest}: + * + * <pre><code> + * + * DataThrottlingRequest dp = new DataThrottlingRequest.Builder() + * .setDataThrottlingAction( + * DataThrottlingRequest.DATA_THROTTLING_ACTION_THROTTLE_SECONDARY_CARRIER) + * .setCompletionDurationMillis(10000L) + * .build(); + * </code></pre> + * + * @hide + */ + @SystemApi + public static final class Builder { + private @DataThrottlingAction int mDataThrottlingAction; + private long mCompletionDurationMillis; + + /** + * Default constructor for Builder. + */ + public Builder() {} + + /** + * Set the data throttling action. + * + * @param dataThrottlingAction data throttling action. + * + * @return The same instance of the builder. + */ + public @NonNull Builder setDataThrottlingAction( + @DataThrottlingAction int dataThrottlingAction) { + mDataThrottlingAction = dataThrottlingAction; + return this; + } + + /** + * Set the completion duration. + * + * @param completionDurationMillis completion duration in millis which represents the time + * over which modem should gradually execute the data thorttling request. This can + * never be a negative number and must be 0 for {@link #DATA_THROTTLING_ACTION_HOLD}. + * Otherwise, an IllegalArgumentException will be thrown. + * + * @return The same instance of the builder. + */ + public @NonNull Builder setCompletionDurationMillis(long completionDurationMillis) { + mCompletionDurationMillis = completionDurationMillis; + return this; + } + + /** + * Build the DataThrottlingRequest. + * + * @return the DataThrottlingRequest object. + */ + public @NonNull DataThrottlingRequest build() { + if (mCompletionDurationMillis < 0) { + throw new IllegalArgumentException("completionDurationMillis cannot be a negative " + + "number"); + } + + if (mDataThrottlingAction == DataThrottlingRequest.DATA_THROTTLING_ACTION_HOLD + && mCompletionDurationMillis != 0) { + throw new IllegalArgumentException("completionDurationMillis must be 0 for " + + "DataThrottlingRequest.DATA_THROTTLING_ACTION_HOLD"); + } + + return new DataThrottlingRequest(mDataThrottlingAction, mCompletionDurationMillis); + } + } + +} diff --git a/telephony/java/android/telephony/PhoneCapability.java b/telephony/java/android/telephony/PhoneCapability.java index 08b6edab3f5c..b785037e51c1 100644 --- a/telephony/java/android/telephony/PhoneCapability.java +++ b/telephony/java/android/telephony/PhoneCapability.java @@ -29,6 +29,7 @@ import java.util.Objects; * Define capability of a modem group. That is, the capabilities * are shared between those modems defined by list of modem IDs. * + * @hide */ public final class PhoneCapability implements Parcelable { // Hardcoded default DSDS capability. diff --git a/telephony/java/android/telephony/PhysicalChannelConfig.java b/telephony/java/android/telephony/PhysicalChannelConfig.java index 95c69ba470c4..8d49e15da934 100644 --- a/telephony/java/android/telephony/PhysicalChannelConfig.java +++ b/telephony/java/android/telephony/PhysicalChannelConfig.java @@ -29,6 +29,10 @@ import java.lang.annotation.RetentionPolicy; import java.util.Arrays; import java.util.Objects; +/** + * @hide + */ +@SystemApi public final class PhysicalChannelConfig implements Parcelable { // TODO(b/72993578) consolidate these enums in a central location. @@ -82,7 +86,7 @@ public final class PhysicalChannelConfig implements Parcelable { private int mFrequencyRange; /** - * The absolute radio frequency channel number, {@link #CHANNEL_NUMBER_UNKNOWN} if unknown. + * The absolute radio frequency channel number, {@link CHANNEL_NUMBER_UNKNOWN} if unknown. */ private int mChannelNumber; @@ -93,7 +97,7 @@ public final class PhysicalChannelConfig implements Parcelable { private int[] mContextIds; /** - * The physical cell identifier for this cell - PCI, PSC, {@link #PHYSICAL_CELL_ID_UNKNOWN} + * The physical cell identifier for this cell - PCI, PSC, {@link PHYSICAL_CELL_ID_UNKNOWN} * if unknown. */ private int mPhysicalCellId; @@ -149,7 +153,7 @@ public final class PhysicalChannelConfig implements Parcelable { /** * @return the absolute radio frequency channel number for this physical channel, - * {@link #CHANNEL_NUMBER_UNKNOWN} if unknown. + * {@link CHANNEL_NUMBER_UNKNOWN} if unknown. */ public int getChannelNumber() { return mChannelNumber; @@ -165,7 +169,7 @@ public final class PhysicalChannelConfig implements Parcelable { * In 5G RAN, this value is physical layer cell identity. The range is [0, 1007]. * Reference: 3GPP TS 38.211 section 7.4.2.1. * - * @return the physical cell identifier for this cell, {@link #PHYSICAL_CELL_ID_UNKNOWN} + * @return the physical cell identifier for this cell, {@link PHYSICAL_CELL_ID_UNKNOWN} * if {@link android.telephony.CellInfo#UNAVAILABLE}. */ @IntRange(from = 0, to = 1007) diff --git a/telephony/java/android/telephony/RadioInterfaceCapabilities.java b/telephony/java/android/telephony/RadioInterfaceCapabilities.java new file mode 100644 index 000000000000..7c7eb9fbbeb2 --- /dev/null +++ b/telephony/java/android/telephony/RadioInterfaceCapabilities.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony; + +import android.util.ArraySet; + +/** + * Contains the set of supported capabilities that the Radio Interface supports on this device. + * + * @hide + */ +public class RadioInterfaceCapabilities { + + private final ArraySet<String> mSupportedCapabilities; + + + public RadioInterfaceCapabilities() { + mSupportedCapabilities = new ArraySet<>(); + } + + /** + * Marks a capability as supported + * + * @param capabilityName the name of the capability + */ + public void addSupportedCapability( + @TelephonyManager.RadioInterfaceCapability String capabilityName) { + mSupportedCapabilities.add(capabilityName); + } + + /** + * Whether the capability is supported + * + * @param capabilityName the name of the capability + */ + public boolean isSupported(String capabilityName) { + return mSupportedCapabilities.contains(capabilityName); + } +} diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java index 964cf76d17f6..572aed3e6a11 100644 --- a/telephony/java/android/telephony/SmsManager.java +++ b/telephony/java/android/telephony/SmsManager.java @@ -447,6 +447,9 @@ public final class SmsManager { * <code>RESULT_RIL_NO_RESOURCES</code><br> * <code>RESULT_RIL_CANCELLED</code><br> * <code>RESULT_RIL_SIM_ABSENT</code><br> + * <code>RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED</code><br> + * <code>RESULT_RIL_ACCESS_BARRED</code><br> + * <code>RESULT_RIL_BLOCKED_DUE_TO_CALL</code><br> * For <code>RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors, * the sentIntent may include the extra "errorCode" containing a radio technology specific * value, generally only useful for troubleshooting.<br> @@ -561,6 +564,9 @@ public final class SmsManager { * <code>RESULT_RIL_NO_RESOURCES</code><br> * <code>RESULT_RIL_CANCELLED</code><br> * <code>RESULT_RIL_SIM_ABSENT</code><br> + * <code>RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED</code><br> + * <code>RESULT_RIL_ACCESS_BARRED</code><br> + * <code>RESULT_RIL_BLOCKED_DUE_TO_CALL</code><br> * For <code>RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors, * the sentIntent may include the extra "errorCode" containing a radio technology specific * value, generally only useful for troubleshooting.<br> @@ -962,6 +968,9 @@ public final class SmsManager { * <code>RESULT_RIL_NO_RESOURCES</code><br> * <code>RESULT_RIL_CANCELLED</code><br> * <code>RESULT_RIL_SIM_ABSENT</code><br> + * <code>RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED</code><br> + * <code>RESULT_RIL_ACCESS_BARRED</code><br> + * <code>RESULT_RIL_BLOCKED_DUE_TO_CALL</code><br> * For <code>RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors, * the sentIntent may include the extra "errorCode" containing a radio technology specific * value, generally only useful for troubleshooting.<br> @@ -1220,6 +1229,9 @@ public final class SmsManager { * <code>RESULT_RIL_NO_RESOURCES</code><br> * <code>RESULT_RIL_CANCELLED</code><br> * <code>RESULT_RIL_SIM_ABSENT</code><br> + * <code>RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED</code><br> + * <code>RESULT_RIL_ACCESS_BARRED</code><br> + * <code>RESULT_RIL_BLOCKED_DUE_TO_CALL</code><br> * For <code>RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors, * the sentIntent may include the extra "errorCode" containing a radio technology specific * value, generally only useful for troubleshooting.<br> @@ -1419,6 +1431,9 @@ public final class SmsManager { * <code>RESULT_RIL_NO_RESOURCES</code><br> * <code>RESULT_RIL_CANCELLED</code><br> * <code>RESULT_RIL_SIM_ABSENT</code><br> + * <code>RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED</code><br> + * <code>RESULT_RIL_ACCESS_BARRED</code><br> + * <code>RESULT_RIL_BLOCKED_DUE_TO_CALL</code><br> * For <code>RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors, * the sentIntent may include the extra "errorCode" containing a radio technology specific * value, generally only useful for troubleshooting.<br> @@ -2298,7 +2313,10 @@ public final class SmsManager { RESULT_RIL_OPERATION_NOT_ALLOWED, RESULT_RIL_NO_RESOURCES, RESULT_RIL_CANCELLED, - RESULT_RIL_SIM_ABSENT + RESULT_RIL_SIM_ABSENT, + RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED, + RESULT_RIL_ACCESS_BARRED, + RESULT_RIL_BLOCKED_DUE_TO_CALL }) @Retention(RetentionPolicy.SOURCE) public @interface Result {} @@ -2563,6 +2581,21 @@ public final class SmsManager { */ public static final int RESULT_RIL_SIM_ABSENT = 120; + /** + * 1X voice and SMS are not allowed simulteneously. + */ + public static final int RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED = 121; + + /** + * Access is barred. + */ + public static final int RESULT_RIL_ACCESS_BARRED = 122; + + /** + * SMS is blocked due to call control, e.g., resource unavailable in the SMR entity. + */ + public static final int RESULT_RIL_BLOCKED_DUE_TO_CALL = 123; + // SMS receiving results sent as a "result" extra in {@link Intents.SMS_REJECTED_ACTION} /** diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 683f200c1a35..643d8c8ba2a5 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -78,6 +78,7 @@ import android.telephony.Annotation.CarrierPrivilegeStatus; import android.telephony.Annotation.NetworkType; import android.telephony.Annotation.RadioPowerState; import android.telephony.Annotation.SimActivationState; +import android.telephony.Annotation.ThermalMitigationResult; import android.telephony.Annotation.UiccAppType; import android.telephony.CallForwardingInfo.CallForwardingReason; import android.telephony.VisualVoicemailService.VisualVoicemailTask; @@ -123,7 +124,6 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Objects; -import java.util.Set; import java.util.UUID; import java.util.concurrent.Executor; import java.util.function.Consumer; @@ -155,7 +155,6 @@ import java.util.function.Consumer; public class TelephonyManager { private static final String TAG = "TelephonyManager"; - private TelephonyRegistryManager mTelephonyRegistryMgr; /** * To expand the error codes for {@link TelephonyManager#updateAvailableNetworks} and * {@link TelephonyManager#setPreferredOpportunisticDataSubscription}. @@ -1576,177 +1575,157 @@ public class TelephonyManager { "android.telephony.extra.PHONE_IN_ECM_STATE"; /** - * <p>Broadcast Action: when data connections get redirected with validation failure. - * intended for sim/account status checks and only sent to the specified carrier app - * The intent will have the following extra values:</p> + * Broadcast action sent when a data connection is redirected with validation failure. + * + * This action is intended for sim/account status checks and only sent to the carrier apps + * specified in the carrier config for the subscription ID that's attached to this intent. + * + * The intent will have the following extra values: * <ul> - * <li>{@link #EXTRA_APN_TYPE}</li><dd>A string with the apn type.</dd> - * <li>{@link #EXTRA_APN_TYPE_INT}</li><dd>A integer with the apn type.</dd> - * <li>{@link #EXTRA_REDIRECTION_URL}</li><dd>redirection url string</dd> - * <li>subId</li><dd>Sub Id which associated the data connection failure.</dd> + * <li>{@link #EXTRA_APN_TYPE}</li><dd>An integer indicating the apn type.</dd> + * <li>{@link #EXTRA_REDIRECTION_URL}</li><dd>A string indicating the redirection url</dd> + * <li>{@link SubscriptionManager#EXTRA_SUBSCRIPTION_INDEX}</li> + * <dd>The subscription ID on which the validation failure happened.</dd> * </ul> * <p class="note">This is a protected intent that can only be sent by the system.</p> - * @hide */ - @SuppressLint("ActionValue") + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CARRIER_SIGNAL_REDIRECTED = - "com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED"; + "android.telephony.action.CARRIER_SIGNAL_REDIRECTED"; /** - * <p>Broadcast Action: when data connections setup fails. - * intended for sim/account status checks and only sent to the specified carrier app - * The intent will have the following extra values:</p> + * Broadcast action sent when a data connection setup fails. + * + * This action is intended for sim/account status checks and only sent to the carrier apps + * specified in the carrier config for the subscription ID that's attached to this intent. + * + * The intent will have the following extra values: * <ul> - * <li>{@link #EXTRA_APN_TYPE}</li><dd>A string with the apn type.</dd> - * <li>{@link #EXTRA_APN_TYPE_INT}</li><dd>A integer with the apn type.</dd> - * <li>{@link #EXTRA_ERROR_CODE}</li><dd>A integer with dataFailCause.</dd> - * <li>subId</li><dd>Sub Id which associated the data connection failure.</dd> + * <li>{@link #EXTRA_APN_TYPE}</li><dd>An integer indicating the apn type.</dd> + * <li>{@link #EXTRA_DATA_FAIL_CAUSE}</li><dd>A integer indicating the data fail cause.</dd> + * <li>{@link SubscriptionManager#EXTRA_SUBSCRIPTION_INDEX}</li> + * <dd>The subscription ID on which the data setup failure happened.</dd> * </ul> * <p class="note">This is a protected intent that can only be sent by the system. </p> - * @hide */ - @SuppressLint("ActionValue") + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED = - "com.android.internal.telephony.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED"; + "android.telephony.action.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED"; /** - * <p>Broadcast Action: when pco value is available. - * intended for sim/account status checks and only sent to the specified carrier app + * Broadcast action sent when a PCO value becomes available from the modem. + * + * This action is intended for sim/account status checks and only sent to the carrier apps + * specified in the carrier config for the subscription ID that's attached to this intent. + * * The intent will have the following extra values:</p> * <ul> - * <li>{@link #EXTRA_APN_TYPE}</li><dd>A string with the apn type.</dd> - * <li>{@link #EXTRA_APN_TYPE_INT}</li><dd>A integer with the apn type.</dd> - * <li>{@link #EXTRA_APN_PROTOCOL}</li><dd>A string with the protocol of the apn connection - * (IP,IPV6, IPV4V6)</dd> - * <li>{@link #EXTRA_APN_PROTOCOL_INT}</li><dd>A integer with the protocol of the apn - * connection (IP,IPV6, IPV4V6)</dd> - * <li>{@link #EXTRA_PCO_ID}</li><dd>An integer indicating the pco id for the data.</dd> - * <li>{@link #EXTRA_PCO_VALUE}</li><dd>A byte array of pco data read from modem.</dd> - * <li>subId</li><dd>Sub Id which associated the data connection.</dd> + * <li>{@link #EXTRA_APN_TYPE}</li><dd>An integer indicating the apn type.</dd> + * <li>{@link #EXTRA_APN_PROTOCOL}</li><dd>An integer indicating the protocol of the apn + * connection</dd> + * <li>{@link #EXTRA_PCO_ID}</li><dd>An integer indicating the PCO id for the data.</dd> + * <li>{@link #EXTRA_PCO_VALUE}</li><dd>A byte array of PCO data read from modem.</dd> + * <li>{@link SubscriptionManager#EXTRA_SUBSCRIPTION_INDEX}</li> + * <dd>The subscription ID for which the PCO info was received.</dd> * </ul> * <p class="note">This is a protected intent that can only be sent by the system. </p> - * @hide */ - @SuppressLint("ActionValue") + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CARRIER_SIGNAL_PCO_VALUE = - "com.android.internal.telephony.CARRIER_SIGNAL_PCO_VALUE"; + "android.telephony.action.CARRIER_SIGNAL_PCO_VALUE"; /** - * <p>Broadcast Action: when system default network available/unavailable with - * carrier-disabled mobile data. Intended for carrier apps to set/reset carrier actions when - * other network becomes system default network, Wi-Fi for example. + * Broadcast action sent when the availability of the system default network changes. + * + * @see ConnectivityManager#registerDefaultNetworkCallback(ConnectivityManager.NetworkCallback) + * + * This action is intended for carrier apps to set/reset carrier actions. It is only sent to the + * carrier apps specified in the carrier config for the subscription ID attached to this intent. + * * The intent will have the following extra values:</p> * <ul> * <li>{@link #EXTRA_DEFAULT_NETWORK_AVAILABLE}</li> - * <dd>A boolean indicates default network available.</dd> - * <li>subId</li><dd>Sub Id which associated the default data.</dd> + * <dd>{@code true} if the default network is now available, {@code false} otherwise.</dd> + * <li>{@link SubscriptionManager#EXTRA_SUBSCRIPTION_INDEX}</li> + * <dd>The subscription ID on which the default network availability changed.</dd> * </ul> * <p class="note">This is a protected intent that can only be sent by the system. </p> - * @hide */ - @SuppressLint("ActionValue") + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE = - "com.android.internal.telephony.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE"; + "android.telephony.action.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE"; /** - * <p>Broadcast Action: when framework reset all carrier actions on sim load or absent. - * intended for carrier apps clean up (clear UI e.g.) and only sent to the specified carrier app + * Broadcast action sent when carrier apps should reset their internal state. + * + * Sent when certain events such as turning on/off mobile data, removing the SIM, etc. require + * carrier apps to reset their state. + * + * This action is intended to signal carrier apps to perform cleanup operations. It is only sent + * to the carrier apps specified in the carrier config for the subscription ID attached to + * this intent. + * * The intent will have the following extra values:</p> * <ul> - * <li>subId</li><dd>Sub Id which associated the data connection failure.</dd> + * <li>{@link SubscriptionManager#EXTRA_SUBSCRIPTION_INDEX}</li> + * <dd>The subscription ID for which state should be reset.</dd> * </ul> * <p class="note">This is a protected intent that can only be sent by the system.</p> - * @hide */ - @SuppressLint("ActionValue") + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CARRIER_SIGNAL_RESET = - "com.android.internal.telephony.CARRIER_SIGNAL_RESET"; + "android.telephony.action.CARRIER_SIGNAL_RESET"; - // CARRIER_SIGNAL_ACTION extra keys /** - * An string extra of redirected url upon {@link #ACTION_CARRIER_SIGNAL_REDIRECTED}. - * @hide + * String extra containing the redirection URL sent with + * {@link #ACTION_CARRIER_SIGNAL_REDIRECTED}. */ - @SuppressLint("ActionValue") - public static final String EXTRA_REDIRECTION_URL = "redirectionUrl"; + public static final String EXTRA_REDIRECTION_URL = "android.telephony.extra.REDIRECTION_URL"; /** - * An integer extra of error code upon {@link #ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED}. - * Check {@link DataFailCause} for all possible values. - * @hide - */ - @SuppressLint("ActionValue") - public static final String EXTRA_ERROR_CODE = "errorCode"; - - /** - * An string extra of corresponding apn type upon - * {@link #ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED}, - * {@link #ACTION_CARRIER_SIGNAL_REDIRECTED} and - * {@link #ACTION_CARRIER_SIGNAL_PCO_VALUE} broadcasts. - * @deprecated This is kept for backward compatibility reason. Use {@link #EXTRA_APN_TYPE_INT} - * instead. + * An integer extra containing the data fail cause. * - * @hide + * Sent with {@link #ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED}. See {@link DataFailCause} + * for a list of possible values. */ - @Deprecated - @SuppressLint("ActionValue") - public static final String EXTRA_APN_TYPE = "apnType"; - - /** - * An string integer of corresponding apn type upon - * {@link #ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED}, - * {@link #ACTION_CARRIER_SIGNAL_REDIRECTED} and - * {@link #ACTION_CARRIER_SIGNAL_PCO_VALUE} broadcasts. - * Check {@link ApnSetting} TYPE_* for its values. - * @hide - */ - @SuppressLint("ActionValue") - public static final String EXTRA_APN_TYPE_INT = "apnTypeInt"; + public static final String EXTRA_DATA_FAIL_CAUSE = "android.telephony.extra.DATA_FAIL_CAUSE"; /** - * An string extra with the protocol of the apn connection (IP,IPV6, IPV4V6) upon - * {@link #ACTION_CARRIER_SIGNAL_PCO_VALUE} broadcasts. - * @deprecated This is kept for backward compatibility reason. - * Use {@link #EXTRA_APN_PROTOCOL_INT} instead. + * An integer extra containing the APN type. * - * @hide + * Sent with the {@link #ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED}, + * {@link #ACTION_CARRIER_SIGNAL_REDIRECTED}, and {@link #ACTION_CARRIER_SIGNAL_PCO_VALUE} + * broadcasts. + * See the {@code TYPE_} constants in {@link ApnSetting} for a list of possible values. */ - @Deprecated - @SuppressLint("ActionValue") - public static final String EXTRA_APN_PROTOCOL = "apnProto"; + public static final String EXTRA_APN_TYPE = "android.telephony.extra.APN_TYPE"; /** - * An integer extra with the protocol of the apn connection (IP,IPV6, IPV4V6) upon - * {@link #ACTION_CARRIER_SIGNAL_PCO_VALUE} broadcasts. - * Check {@link ApnSetting} PROTOCOL_* for its values. - * @hide + * An integer extra containing the protocol of the apn connection. + * + * Sent with the {@link #ACTION_CARRIER_SIGNAL_PCO_VALUE} broadcast. + * See the {@code PROTOCOL_*} constants in {@link ApnSetting} for a list of possible values. */ - @SuppressLint("ActionValue") - public static final String EXTRA_APN_PROTOCOL_INT = "apnProtoInt"; + public static final String EXTRA_APN_PROTOCOL = "android.telephony.extra.APN_PROTOCOL"; /** - * An integer extra indicating the pco id for the data upon - * {@link #ACTION_CARRIER_SIGNAL_PCO_VALUE} broadcasts. - * @hide + * An integer extra indicating the ID for the PCO data. + * Sent with the {@link #ACTION_CARRIER_SIGNAL_PCO_VALUE} broadcast. */ - @SuppressLint("ActionValue") - public static final String EXTRA_PCO_ID = "pcoId"; + public static final String EXTRA_PCO_ID = "android.telephony.extra.PCO_ID"; /** - * An extra of byte array of pco data read from modem upon - * {@link #ACTION_CARRIER_SIGNAL_PCO_VALUE} broadcasts. - * @hide + * A byte array extra containing PCO data read from the modem. + * Sent with the {@link #ACTION_CARRIER_SIGNAL_PCO_VALUE} broadcast. */ - @SuppressLint("ActionValue") - public static final String EXTRA_PCO_VALUE = "pcoValue"; + public static final String EXTRA_PCO_VALUE = "android.telephony.extra.PCO_VALUE"; /** - * An boolean extra indicating default network available upon - * {@link #ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE} broadcasts. - * @hide + * A boolean extra indicating the availability of the default network. + * Sent with the {@link #ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE} broadcast. */ - @SuppressLint("ActionValue") - public static final String EXTRA_DEFAULT_NETWORK_AVAILABLE = "defaultNetworkAvailable"; + public static final String EXTRA_DEFAULT_NETWORK_AVAILABLE = + "android.telephony.extra.DEFAULT_NETWORK_AVAILABLE"; /** * <p>Broadcast Action: The emergency call state is changed. @@ -5595,22 +5574,12 @@ public class TelephonyManager { * @param events The telephony state(s) of interest to the listener, * as a bitwise-OR combination of {@link PhoneStateListener} * LISTEN_ flags. - * @deprecated Use {@link #registerPhoneStateListener(Executor, PhoneStateListener)}. + * @deprecated use {@link #listen(long, PhoneStateListener) instead due to the event number + * limit increased to 64. */ @Deprecated public void listen(PhoneStateListener listener, int events) { - boolean notifyNow = getITelephony() != null; - mTelephonyRegistryMgr = mContext.getSystemService(TelephonyRegistryManager.class); - if (mTelephonyRegistryMgr != null) { - if (events != PhoneStateListener.LISTEN_NONE) { - mTelephonyRegistryMgr.registerPhoneStateListenerWithEvents(getSubId(), - getOpPackageName(), getAttributionTag(), listener, events, notifyNow); - } else { - unregisterPhoneStateListener(listener); - } - } else { - throw new IllegalStateException("telephony service is null."); - } + listen(events, listener); } /** @@ -5646,21 +5615,18 @@ public class TelephonyManager { * LISTEN_ flags. * @param listener The {@link PhoneStateListener} object to register * (or unregister) - * @deprecated Use {@link #registerPhoneStateListener(Executor, PhoneStateListener)}. */ - @Deprecated public void listen(long events, @NonNull PhoneStateListener listener) { - mTelephonyRegistryMgr = mContext.getSystemService(TelephonyRegistryManager.class); - if (mTelephonyRegistryMgr != null) { - if (events != PhoneStateListener.LISTEN_NONE) { - mTelephonyRegistryMgr.registerPhoneStateListenerWithEvents(getSubId(), - getOpPackageName(), getAttributionTag(), listener, - Long.valueOf(events).intValue(), getITelephony() != null); - } else { - unregisterPhoneStateListener(listener); - } + if (mContext == null) return; + boolean notifyNow = (getITelephony() != null); + TelephonyRegistryManager telephonyRegistry = + (TelephonyRegistryManager) + mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE); + if (telephonyRegistry != null) { + telephonyRegistry.listenForSubscriber(mSubId, getOpPackageName(), getAttributionTag(), + listener, events, notifyNow); } else { - throw new IllegalStateException("telephony service is null."); + Rlog.w(TAG, "telephony registry not ready."); } } @@ -13215,6 +13181,37 @@ public class TelephonyManager { } /** + * Get which bands the modem's background scan is acting on, specified by + * {@link #setSystemSelectionChannels}. + * + * <p>Requires Permission: + * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE} + * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @return a list of {@link RadioAccessSpecifier}, or an empty list if no bands are specified. + * @throws IllegalStateException if the Telephony process is not currently available. + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public @NonNull List<RadioAccessSpecifier> getSystemSelectionChannels() { + try { + ITelephony service = getITelephony(); + if (service != null) { + return service.getSystemSelectionChannels(getSubId()); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + if (!isSystemProcess()) { + ex.rethrowAsRuntimeException(); + } + } + return new ArrayList<>(); + } + + /** * Verifies whether the input MCC/MNC and MVNO correspond to the current carrier. * * @param mccmnc the carrier's mccmnc that you want to match @@ -14194,69 +14191,140 @@ public class TelephonyManager { return Collections.emptyList(); } + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"RADIO_INTERFACE_CAPABILITY_"}, + value = {}) + public @interface RadioInterfaceCapability {} + /** - * Registers a listener object to receive notification of changes - * in specified telephony states. - * <p> - * To register a listener, pass a {@link PhoneStateListener} which implements - * interfaces of events. For example, - * FakeServiceStateChangedListener extends {@link PhoneStateListener} implements - * {@link PhoneStateListener.ServiceStateChangedListener}. + * Whether the device supports a given capability on the radio interface. * - * At registration, and when a specified telephony state changes, the telephony manager invokes - * the appropriate callback method on the listener object and passes the current (updated) - * values. - * <p> + * If the capability is not in the set of radio interface capabilities, false is returned. * - * If this TelephonyManager object has been created with {@link #createForSubscriptionId}, - * applies to the given subId. Otherwise, applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. To listen events for multiple subIds, - * pass a separate listener object to each TelephonyManager object created with - * {@link #createForSubscriptionId}. + * @param capability the name of the capability to check for + * @return the availability of the capability * - * Note: if you call this method while in the middle of a binder transaction, you <b>must</b> - * call {@link android.os.Binder#clearCallingIdentity()} before calling this method. A - * {@link SecurityException} will be thrown otherwise. - * - * This API should be used sparingly -- large numbers of listeners will cause system - * instability. If a process has registered too many listeners without unregistering them, it - * may encounter an {@link IllegalStateException} when trying to register more listeners. - * - * @param executor The executor of where the callback will execute. - * @param listener The {@link PhoneStateListener} object to register. + * @hide */ - public void registerPhoneStateListener(@NonNull @CallbackExecutor Executor executor, - @NonNull PhoneStateListener listener) { - if (executor == null || listener == null) { - throw new IllegalStateException("telephony service is null."); - } - mTelephonyRegistryMgr = (TelephonyRegistryManager) - mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE); - if (mTelephonyRegistryMgr != null) { - mTelephonyRegistryMgr.registerPhoneStateListener(executor, getSubId(), - getOpPackageName(), getAttributionTag(), listener, getITelephony() != null); - } else { - throw new IllegalStateException("telephony service is null."); + public boolean isRadioInterfaceCapabilitySupported( + @NonNull @RadioInterfaceCapability String capability) { + try { + if (capability == null) return false; + + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.isRadioInterfaceCapabilitySupported(capability); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + if (!isSystemProcess()) { + ex.rethrowAsRuntimeException(); + } } + return false; } /** - * Unregister an existing {@link PhoneStateListener}. + * Indicates that the thermal mitigation request was completed successfully. * - * @param listener The {@link PhoneStateListener} object to unregister. + * @hide */ - public void unregisterPhoneStateListener(@NonNull PhoneStateListener listener) { + @SystemApi + public static final int THERMAL_MITIGATION_RESULT_SUCCESS = 0; - if (mContext == null) { - throw new IllegalStateException("telephony service is null."); - } + /** + * Indicates that the thermal mitigation request was not completed because of a modem error. + * + * @hide + */ + @SystemApi + public static final int THERMAL_MITIGATION_RESULT_MODEM_ERROR = 1; - mTelephonyRegistryMgr = mContext.getSystemService(TelephonyRegistryManager.class); - if (mTelephonyRegistryMgr != null) { - mTelephonyRegistryMgr.unregisterPhoneStateListener(getSubId(), getOpPackageName(), - getAttributionTag(), listener, getITelephony() != null); - } else { + /** + * Indicates that the thermal mitigation request was not completed because the modem is not + * available. + * + * @hide + */ + @SystemApi + public static final int THERMAL_MITIGATION_RESULT_MODEM_NOT_AVAILABLE = 2; + + /** + * Indicates that the thermal mitigation request could not power off the radio due to the device + * either being in an active voice call, device pending an emergency call, or any other state + * that would dissallow powering off of radio. + * + * @hide + */ + @SystemApi + public static final int THERMAL_MITIGATION_RESULT_INVALID_STATE = 3; + + /** + * Indicates that the thermal mitigation request resulted an unknown error. + * + * @hide + */ + @SystemApi + public static final int THERMAL_MITIGATION_RESULT_UNKNOWN_ERROR = 4; + + /** + * Thermal mitigation request to control functionalities at modem. Thermal mitigation is done + * per-subscription. Caller must be sure to bind the TelephonyManager instance to subId by + * calling {@link #createForSubscriptionId(int)} if they want thermal mitigation on a specific + * subscription Id. Otherwise, TelephonyManager will use the default subscription. + * + * Calling this does not guarantee that the thermal mitigation action requested was done to + * completion. A thermal module should actively monitor the temperature levels and request an + * appropriate thermal mitigation action. Every action is assumed to be done 'on top of' the + * previous action, where the order of actions from least thermal mitigation to most is as + * follows: + * <ol> + * <li>{@link ThermalMitigationRequest#THERMAL_MITIGATION_ACTION_DATA_THROTTLING}</li> + * <ol> + * <li>{@link DataThrottlingRequest#DATA_THROTTLING_ACTION_NO_DATA_THROTTLING}</li> + * <li>{@link DataThrottlingRequest#DATA_THROTTLING_ACTION_THROTTLE_SECONDARY_CARRIER}</li> + * <li>{@link DataThrottlingRequest#DATA_THROTTLING_ACTION_THROTTLE_PRIMARY_CARRIER}</li> + * </ol> + * <li>{@link ThermalMitigationRequest#THERMAL_MITIGATION_ACTION_VOICE_ONLY}</li> + * <li>{@link ThermalMitigationRequest#THERMAL_MITIGATION_ACTION_RADIO_OFF}</li> + * </ol> + * + * So, for example, requesting {@link + * DataThrottlingRequest#DATA_THROTTLING_ACTION_THROTTLE_PRIMARY_CARRIER} will ensure that the + * data on secondary carrier has been disabled before throttling on primary carrier. {@link + * ThermalMitigationRequest#THERMAL_MITIGATION_ACTION_VOICE_ONLY} will ensure that data on both + * primary and secondary have been disabled. {@link + * ThermalMitigationRequest#THERMAL_MITIGATION_ACTION_RADIO_OFF} will ensure that voice is + * disabled and that data on both primary and secondary carriers are disabled before turning + * radio off. {@link DataThrottlingRequest#DATA_THROTTLING_ACTION_HOLD} is not part of the order + * and can be used at any time during data throttling to hold onto the current level of data + * throttling. + * + * @param thermalMitigationRequest Thermal mitigation request. See {@link + * ThermalMitigationRequest} for details. + * + * @throws IllegalStateException if the Telephony process is not currently available. + * @throws IllegalArgumentException if the thermalMitigationRequest had invalid parameters. + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @ThermalMitigationResult + public int sendThermalMitigationRequest( + @NonNull ThermalMitigationRequest thermalMitigationRequest) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.sendThermalMitigationRequest(getSubId(), thermalMitigationRequest); + } throw new IllegalStateException("telephony service is null."); + } catch (RemoteException ex) { + Log.e(TAG, "Telephony#thermalMitigationRequest RemoteException", ex); + ex.rethrowFromSystemServer(); } + return THERMAL_MITIGATION_RESULT_UNKNOWN_ERROR; } } diff --git a/telephony/java/android/telephony/ThermalMitigationRequest.aidl b/telephony/java/android/telephony/ThermalMitigationRequest.aidl new file mode 100644 index 000000000000..a912f77a1de8 --- /dev/null +++ b/telephony/java/android/telephony/ThermalMitigationRequest.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony; + +parcelable ThermalMitigationRequest;
\ No newline at end of file diff --git a/telephony/java/android/telephony/ThermalMitigationRequest.java b/telephony/java/android/telephony/ThermalMitigationRequest.java new file mode 100644 index 000000000000..91ad9c3e1f51 --- /dev/null +++ b/telephony/java/android/telephony/ThermalMitigationRequest.java @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.telephony; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + + +/** + * Class stores information related to the type of data throttling request to be sent to {@link + * TelephonyManager#sendThermalMitigationRequest(ThermalMitigationResult)}. + * @hide + */ +@SystemApi +public final class ThermalMitigationRequest implements Parcelable { + /** + * Sent as a thermal mititgation action to {@link + * TelephonyManager#sendThermalMitigationRequest(ThermalMitigationResult)} to start data + * throttling. {@link TelephonyManager#InvalidThermalMitigationRequestException} will be thrown + * if dataThrottlingRequest is {@code null} or if completion duration is < 0. + * + * @hide + */ + @SystemApi + public static final int THERMAL_MITIGATION_ACTION_DATA_THROTTLING = 0; + + /** + * Sent as a thermal mititgation action to {@link + * TelephonyManager#sendThermalMitigationRequest(ThermalMitigationResult)} to allow only voice + * calls and internet data will not be available. This attempts to enable radio if currently + * disabled for thermal mitigation with no guarantee of it actually turning on. + * dataThrottlingRequest must be {@code null} or {@link + * TelephonyManager#InvalidThermalMitigationRequestException} will be thrown. + * + * @hide + */ + @SystemApi + public static final int THERMAL_MITIGATION_ACTION_VOICE_ONLY = 1; + + /** + * Sent as a thermal mititgation action to {@link' + * TelephonyManager#sendThermalMitigationRequest(ThermalMitigationResult)} to turn radio off. If + * radio is not able to be powered off because of an ongoing voice call, pending emergency call, + * or any other state that wouldn't allow radio off, {@link + * TelephonyManager#THERMAL_MITIGATION_RESULT_INVALID_STATE}. + * dataThrottlingRequest must be {@code null} or + * {@link TelephonyManager#InvalidThermalMitigationRequestException} will be returned. + * + * @hide + */ + @SystemApi + public static final int THERMAL_MITIGATION_ACTION_RADIO_OFF = 2; + + /** + * Type of thermal mitigation action. + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "THERMAL_MITIGATION_ACTION_" }, value = { + THERMAL_MITIGATION_ACTION_DATA_THROTTLING, + THERMAL_MITIGATION_ACTION_VOICE_ONLY, + THERMAL_MITIGATION_ACTION_RADIO_OFF}) + public @interface ThermalMitigationAction {} + + private @ThermalMitigationAction int mThermalMitigationAction; + private DataThrottlingRequest mDataThrottlingRequest; + + /** + * @param thermalMitigationAction thermal mitigation action. + * @param dataThrottlingRequest is the parameters for more fine-controlled data throttling. This + * is only applicable if thermalMitigationAction is + * {@link #THERMAL_MITIGATION_ACTION_DATA_THROTTLING}. Otherwise, it must be set to + * {@code null}. See {@link DataThrottlingRequest} for more details. + */ + private ThermalMitigationRequest(@ThermalMitigationAction int thermalMitigationAction, + @Nullable DataThrottlingRequest dataThrottlingRequest) { + mThermalMitigationAction = thermalMitigationAction; + mDataThrottlingRequest = dataThrottlingRequest; + } + + private ThermalMitigationRequest(Parcel in) { + mThermalMitigationAction = in.readInt(); + mDataThrottlingRequest = in.readParcelable(DataThrottlingRequest.class.getClassLoader()); + } + + /** + * Implement the Parcelable interface + */ + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mThermalMitigationAction); + dest.writeParcelable(mDataThrottlingRequest, 0); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public String toString() { + return "[ThermalMitigationRequest " + + ", thermalMitigationAction=" + mThermalMitigationAction + + ", dataThrottlingRequest=" + mDataThrottlingRequest + + "]"; + } + + /** + * @return the thermal mitigation action. + */ + public @ThermalMitigationAction int getThermalMitigationAction() { + return mThermalMitigationAction; + } + + /** + * @return the data throttling request. + */ + @Nullable + public DataThrottlingRequest getDataThrottlingRequest() { + return mDataThrottlingRequest; + } + + public static final @NonNull Parcelable.Creator<ThermalMitigationRequest> CREATOR = + new Parcelable.Creator<ThermalMitigationRequest>() { + + @Override + public ThermalMitigationRequest createFromParcel(Parcel in) { + return new ThermalMitigationRequest(in); + } + + @Override + public ThermalMitigationRequest[] newArray(int size) { + return new ThermalMitigationRequest[size]; + } + }; + + /** + * Provides a convenient way to set the fields of a {@link ThermalMitigationRequest} when + * creating a new instance. + * + * <p>The example below shows how you might create a new {@code ThermalMitigationRequest}: + * + * <pre><code> + * + * ThermalMitigationRequest dp = new ThermalMitigationRequest.Builder() + * .setThermalMitigationAction( + * ThermalMitigationRequest.THERMAL_MITIGATION_ACTION_DATA_THROTTLING) + * .setDataThrottlingRequest(new DataThrottlingRequest.Builder() + * .setDataThrottlingAction( + * DataThrottlingRequest.DATA_THROTTLING_ACTION_THROTTLE_SECONDARY_CARRIER) + * .setCompletionDurationMillis(10000L) + * .build()) + * .build(); + * </code></pre> + * + * @hide + */ + @SystemApi + public static final class Builder { + private @ThermalMitigationAction int mThermalMitigationAction = -1; + private DataThrottlingRequest mDataThrottlingRequest; + + /** + * Default constructor for Builder. + */ + public Builder() {} + + /** + * Set the thermal mitigation action. + * + * @param thermalMitigationAction thermal mitigation action. See {@link + * #THERMAL_MITIGATION_ACTION_DATA_THROTTLING}, {@link + * #THERMAL_MITIGATION_ACTION_VOICE_ONLY}, and {@link + * #THERMAL_MITIGATION_ACTION_RADIO_OFF} for more details. + * + * @return The same instance of the builder. + */ + public @NonNull Builder setThermalMitigationAction( + @ThermalMitigationAction int thermalMitigationAction) { + mThermalMitigationAction = thermalMitigationAction; + return this; + } + + /** + * Set the data throttling request. + * + * @param dataThrottlingRequest is the parameters for more fine-controlled data throttling. + * This is only applicable if thermalMitigationAction is {@link + * #THERMAL_MITIGATION_ACTION_DATA_THROTTLING}. Otherwise, it should not be set and + * will throw an IllegalArgumentException if it is. See {@link DataThrottlingRequest} + * for more details. + * + * @return The same instance of the builder. + */ + public @NonNull Builder setDataThrottlingRequest( + @NonNull DataThrottlingRequest dataThrottlingRequest) { + mDataThrottlingRequest = dataThrottlingRequest; + return this; + } + + /** + * Build the ThermalMitigationRequest. + * + * @return the ThermalMitigationRequest object. + */ + public @NonNull ThermalMitigationRequest build() { + if (mThermalMitigationAction < 0) { + throw new IllegalArgumentException("thermalMitigationAction was " + + " not set"); + } + + if (mThermalMitigationAction == THERMAL_MITIGATION_ACTION_DATA_THROTTLING) { + if (mDataThrottlingRequest == null) { + throw new IllegalArgumentException("dataThrottlingRequest cannot be null for " + + "THERMAL_MITIGATION_ACTION_DATA_THROTTLING"); + } + + + } else if (mDataThrottlingRequest != null) { + throw new IllegalArgumentException("dataThrottlingRequest must be null for " + + "THERMAL_MITIGATION_ACTION_VOICE_ONLY and " + + "THERMAL_MITIGATION_ACTION_RADIO_OFF"); + } + + return new ThermalMitigationRequest(mThermalMitigationAction, mDataThrottlingRequest); + } + } +} diff --git a/telephony/java/android/telephony/data/ApnThrottleStatus.aidl b/telephony/java/android/telephony/data/ApnThrottleStatus.aidl new file mode 100644 index 000000000000..46bc4abde159 --- /dev/null +++ b/telephony/java/android/telephony/data/ApnThrottleStatus.aidl @@ -0,0 +1,20 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** @hide */ +package android.telephony.data; + +parcelable ApnThrottleStatus; diff --git a/telephony/java/android/telephony/data/ApnThrottleStatus.java b/telephony/java/android/telephony/data/ApnThrottleStatus.java new file mode 100644 index 000000000000..51461d17690a --- /dev/null +++ b/telephony/java/android/telephony/data/ApnThrottleStatus.java @@ -0,0 +1,383 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.data; + +import android.annotation.ElapsedRealtimeLong; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.SuppressLint; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.SystemClock; +import android.telephony.AccessNetworkConstants; +import android.telephony.Annotation; + +import java.util.Objects; + +/** + * Status information regarding the throttle status of an APN type. + * + * @hide + */ +@SystemApi +public final class ApnThrottleStatus implements Parcelable { + /** + * The APN type is not throttled. + */ + public static final int THROTTLE_TYPE_NONE = 1; + + /** + * The APN type is throttled until {@link android.os.SystemClock#elapsedRealtime()} + * has reached {@link ApnThrottleStatus#getThrottleExpiryTimeMillis} + */ + public static final int THROTTLE_TYPE_ELAPSED_TIME = 2; + + /** {@hide} */ + @IntDef(flag = true, prefix = {"THROTTLE_TYPE_"}, value = { + ApnThrottleStatus.THROTTLE_TYPE_NONE, + ApnThrottleStatus.THROTTLE_TYPE_ELAPSED_TIME, + }) + public @interface ThrottleType { + } + + /** + * The framework will not retry the APN type. + */ + public static final int RETRY_TYPE_NONE = 1; + + /** + * The next time the framework retries, it will attempt to establish a new connection. + */ + public static final int RETRY_TYPE_NEW_CONNECTION = 2; + + /** + * The next time the framework retires, it will retry to handover. + */ + public static final int RETRY_TYPE_HANDOVER = 3; + + /** {@hide} */ + @IntDef(flag = true, prefix = {"RETRY_TYPE_"}, value = { + ApnThrottleStatus.RETRY_TYPE_NONE, + ApnThrottleStatus.RETRY_TYPE_NEW_CONNECTION, + ApnThrottleStatus.RETRY_TYPE_HANDOVER, + }) + public @interface RetryType { + } + + private final int mSlotIndex; + private final @AccessNetworkConstants.TransportType int mTransportType; + private final @Annotation.ApnType int mApnType; + private final long mThrottleExpiryTimeMillis; + private final @RetryType int mRetryType; + private final @ThrottleType int mThrottleType; + + /** + * The slot index that the status applies to. + * + * @return the slot index + */ + public int getSlotIndex() { + return mSlotIndex; + } + + /** + * The type of transport that the status applies to. + * + * @return the transport type + */ + @AccessNetworkConstants.TransportType + public int getTransportType() { + return mTransportType; + } + + /** + * The APN type that the status applies to. + * + * @return the apn type + */ + @Annotation.ApnType + public int getApnType() { + return mApnType; + } + + /** + * The type of throttle applied to the APN type. + * + * @return the throttle type + */ + @ThrottleType + public int getThrottleType() { + return mThrottleType; + } + + /** + * Indicates the type of request that the framework will make the next time it retries + * to call {@link IDataService#setupDataCall}. + * + * @return the retry type + */ + @RetryType + public int getRetryType() { + return mRetryType; + } + + /** + * Gets the time at which the throttle expires. The value is based off of + * {@link SystemClock#elapsedRealtime}. + * + * This value only applies when the throttle type is set to + * {@link ApnThrottleStatus#THROTTLE_TYPE_ELAPSED_TIME}. + * + * A value of {@link Long#MAX_VALUE} implies that the APN type is throttled indefinitely. + * + * @return the time at which the throttle expires + */ + @ElapsedRealtimeLong + public long getThrottleExpiryTimeMillis() { + return mThrottleExpiryTimeMillis; + } + + private ApnThrottleStatus(int slotIndex, + @AccessNetworkConstants.TransportType int transportType, + @Annotation.ApnType int apnTypes, + @ThrottleType int throttleType, + long throttleExpiryTimeMillis, + @RetryType int retryType) { + mSlotIndex = slotIndex; + mTransportType = transportType; + mApnType = apnTypes; + mThrottleType = throttleType; + mThrottleExpiryTimeMillis = throttleExpiryTimeMillis; + mRetryType = retryType; + } + + private ApnThrottleStatus(@NonNull Parcel source) { + mSlotIndex = source.readInt(); + mTransportType = source.readInt(); + mApnType = source.readInt(); + mThrottleExpiryTimeMillis = source.readLong(); + mRetryType = source.readInt(); + mThrottleType = source.readInt(); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mSlotIndex); + dest.writeInt(mTransportType); + dest.writeInt(mApnType); + dest.writeLong(mThrottleExpiryTimeMillis); + dest.writeInt(mRetryType); + dest.writeInt(mThrottleType); + } + + public static final @NonNull Parcelable.Creator<ApnThrottleStatus> CREATOR = + new Parcelable.Creator<ApnThrottleStatus>() { + @Override + public ApnThrottleStatus createFromParcel(@NonNull Parcel source) { + return new ApnThrottleStatus(source); + } + + @Override + public ApnThrottleStatus[] newArray(int size) { + return new ApnThrottleStatus[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public int hashCode() { + return Objects.hash(mSlotIndex, mApnType, mRetryType, mThrottleType, + mThrottleExpiryTimeMillis, mTransportType); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } else if (obj instanceof ApnThrottleStatus) { + ApnThrottleStatus other = (ApnThrottleStatus) obj; + return this.mSlotIndex == other.mSlotIndex + && this.mApnType == other.mApnType + && this.mRetryType == other.mRetryType + && this.mThrottleType == other.mThrottleType + && this.mThrottleExpiryTimeMillis == other.mThrottleExpiryTimeMillis + && this.mTransportType == other.mTransportType; + } else { + return false; + } + } + + @Override + public String toString() { + return "ApnThrottleStatus{" + + "mSlotIndex=" + mSlotIndex + + ", mTransportType=" + mTransportType + + ", mApnType=" + ApnSetting.getApnTypeString(mApnType) + + ", mThrottleExpiryTimeMillis=" + mThrottleExpiryTimeMillis + + ", mRetryType=" + mRetryType + + ", mThrottleType=" + mThrottleType + + '}'; + } + + /** + * Provides a convenient way to set the fields of an {@link ApnThrottleStatus} when creating a + * new instance. + * + * <p>The example below shows how you might create a new {@code ApnThrottleStatus}: + * + * <pre><code> + * + * DataCallResponseApnThrottleStatus = new ApnThrottleStatus.Builder() + * .setSlotIndex(1) + * .setApnType({@link ApnSetting#TYPE_EMERGENCY}) + * .setNoThrottle() + * .setRetryType({@link ApnThrottleStatus#RETRY_TYPE_NEW_CONNECTION}) + * .build(); + * </code></pre> + */ + public static final class Builder { + private int mSlotIndex; + private @AccessNetworkConstants.TransportType int mTransportType; + private @Annotation.ApnType int mApnType; + private long mThrottleExpiryTimeMillis; + private @RetryType int mRetryType; + private @ThrottleType int mThrottleType; + public static final long NO_THROTTLE_EXPIRY_TIME = + DataCallResponse.RETRY_DURATION_UNDEFINED; + + /** + * Default constructor for the Builder. + */ + public Builder() { + } + + /** + * Set the slot index. + * + * @param slotIndex the slot index. + * @return The same instance of the builder. + */ + @NonNull + public Builder setSlotIndex(int slotIndex) { + this.mSlotIndex = slotIndex; + return this; + } + + /** + * Set the transport type. + * + * @param transportType the transport type. + * @return The same instance of the builder. + */ + @NonNull + public Builder setTransportType(@AccessNetworkConstants.TransportType + int transportType) { + this.mTransportType = transportType; + return this; + } + + /** + * Set the APN type. + * + * @param apnType the APN type. + * @return The same instance of the builder. + */ + @NonNull + public Builder setApnType(@Annotation.ApnType int apnType) { + this.mApnType = apnType; + return this; + } + + /** + * Sets the time at which the throttle will expire. The value is based off of + * {@link SystemClock#elapsedRealtime}. + * + * When setting this value, the throttle type is set to + * {@link ApnThrottleStatus#THROTTLE_TYPE_ELAPSED_TIME}. + * + * A value of {@link Long#MAX_VALUE} implies that the APN type is throttled indefinitely. + * + * @param throttleExpiryTimeMillis The elapsed time at which the throttle expires. + * Throws {@link IllegalArgumentException} for values less + * than 0. + * @return The same instance of the builder. + */ + @NonNull + public Builder setThrottleExpiryTimeMillis( + @ElapsedRealtimeLong long throttleExpiryTimeMillis) { + if (throttleExpiryTimeMillis >= 0) { + this.mThrottleExpiryTimeMillis = throttleExpiryTimeMillis; + this.mThrottleType = THROTTLE_TYPE_ELAPSED_TIME; + } else { + throw new IllegalArgumentException("throttleExpiryTimeMillis must be greater than " + + "or equal to 0"); + } + return this; + } + + /** + * Sets the status of the APN type as not being throttled. + * + * When setting this value, the throttle type is set to + * {@link ApnThrottleStatus#THROTTLE_TYPE_NONE} and the expiry time is set to + * {@link Builder#NO_THROTTLE_EXPIRY_TIME}. + * + * @return The same instance of the builder. + */ + @SuppressLint("MissingGetterMatchingBuilder") + @NonNull + public Builder setNoThrottle() { + mThrottleType = THROTTLE_TYPE_NONE; + mThrottleExpiryTimeMillis = NO_THROTTLE_EXPIRY_TIME; + return this; + } + + /** + * Set the type of request that the framework will make the next time it retries + * to call {@link IDataService#setupDataCall}. + * + * @param retryType the type of request + * @return The same instance of the builder. + */ + @NonNull + public Builder setRetryType(@RetryType int retryType) { + this.mRetryType = retryType; + return this; + } + + /** + * Build the {@link ApnThrottleStatus} + * + * @return the {@link ApnThrottleStatus} object + */ + @NonNull + public ApnThrottleStatus build() { + return new ApnThrottleStatus( + mSlotIndex, + mTransportType, + mApnType, + mThrottleType, + mThrottleExpiryTimeMillis, + mRetryType); + } + } +} diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java index aae77135cc58..090c970c2b53 100644 --- a/telephony/java/android/telephony/data/DataCallResponse.java +++ b/telephony/java/android/telephony/data/DataCallResponse.java @@ -135,6 +135,8 @@ public final class DataCallResponse implements Parcelable { private final int mMtuV6; private final @HandoverFailureMode int mHandoverFailureMode; private final int mPduSessionId; + private final Qos mDefaultQos; + private final List<QosSession> mQosSessions; /** * @param cause Data call fail cause. {@link DataFailCause#NONE} indicates no error. @@ -183,6 +185,8 @@ public final class DataCallResponse implements Parcelable { mMtu = mMtuV4 = mMtuV6 = mtu; mHandoverFailureMode = HANDOVER_FAILURE_MODE_LEGACY; mPduSessionId = PDU_SESSION_ID_NOT_SET; + mDefaultQos = null; + mQosSessions = new ArrayList<>(); } private DataCallResponse(@DataFailureCause int cause, long suggestedRetryTime, int id, @@ -190,7 +194,8 @@ public final class DataCallResponse implements Parcelable { @Nullable String interfaceName, @Nullable List<LinkAddress> addresses, @Nullable List<InetAddress> dnsAddresses, @Nullable List<InetAddress> gatewayAddresses, @Nullable List<InetAddress> pcscfAddresses, int mtu, int mtuV4, int mtuV6, - @HandoverFailureMode int handoverFailureMode, int pduSessionId) { + @HandoverFailureMode int handoverFailureMode, int pduSessionId, + @Nullable Qos defaultQos, @Nullable List<QosSession> qosSessions) { mCause = cause; mSuggestedRetryTime = suggestedRetryTime; mId = id; @@ -210,6 +215,8 @@ public final class DataCallResponse implements Parcelable { mMtuV6 = mtuV6; mHandoverFailureMode = handoverFailureMode; mPduSessionId = pduSessionId; + mDefaultQos = defaultQos; + mQosSessions = qosSessions; } /** @hide */ @@ -234,6 +241,9 @@ public final class DataCallResponse implements Parcelable { mMtuV6 = source.readInt(); mHandoverFailureMode = source.readInt(); mPduSessionId = source.readInt(); + mDefaultQos = source.readParcelable(Qos.class.getClassLoader()); + mQosSessions = new ArrayList<>(); + source.readList(mQosSessions, QosSession.class.getClassLoader()); } /** @@ -357,6 +367,28 @@ public final class DataCallResponse implements Parcelable { return mPduSessionId; } + /** + * @return default QOS of the data call received from the network + * + * @hide + */ + + @Nullable + public Qos getDefaultQos() { + return mDefaultQos; + } + + /** + * @return All the dedicated bearer QOS sessions of the data call received from the network + * + * @hide + */ + + @NonNull + public List<QosSession> getQosSessions() { + return mQosSessions; + } + @NonNull @Override public String toString() { @@ -377,6 +409,8 @@ public final class DataCallResponse implements Parcelable { .append(" mtuV6=").append(getMtuV6()) .append(" handoverFailureMode=").append(getHandoverFailureMode()) .append(" pduSessionId=").append(getPduSessionId()) + .append(" defaultQos=").append(mDefaultQos) + .append(" qosSessions=").append(mQosSessions) .append("}"); return sb.toString(); } @@ -390,12 +424,22 @@ public final class DataCallResponse implements Parcelable { } DataCallResponse other = (DataCallResponse) o; - return this.mCause == other.mCause - && this.mSuggestedRetryTime == other.mSuggestedRetryTime - && this.mId == other.mId - && this.mLinkStatus == other.mLinkStatus - && this.mProtocolType == other.mProtocolType - && this.mInterfaceName.equals(other.mInterfaceName) + + final boolean isQosSame = (mDefaultQos == null || other.mDefaultQos == null) ? + mDefaultQos == other.mDefaultQos : + mDefaultQos.equals(other.mDefaultQos); + + final boolean isQosSessionsSame = (mQosSessions == null || mQosSessions == null) ? + mQosSessions == other.mQosSessions : + mQosSessions.size() == other.mQosSessions.size() + && mQosSessions.containsAll(other.mQosSessions); + + return mCause == other.mCause + && mSuggestedRetryTime == other.mSuggestedRetryTime + && mId == other.mId + && mLinkStatus == other.mLinkStatus + && mProtocolType == other.mProtocolType + && mInterfaceName.equals(other.mInterfaceName) && mAddresses.size() == other.mAddresses.size() && mAddresses.containsAll(other.mAddresses) && mDnsAddresses.size() == other.mDnsAddresses.size() @@ -408,14 +452,17 @@ public final class DataCallResponse implements Parcelable { && mMtuV4 == other.mMtuV4 && mMtuV6 == other.mMtuV6 && mHandoverFailureMode == other.mHandoverFailureMode - && mPduSessionId == other.mPduSessionId; + && mPduSessionId == other.mPduSessionId + && isQosSame + && isQosSessionsSame; } @Override public int hashCode() { return Objects.hash(mCause, mSuggestedRetryTime, mId, mLinkStatus, mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses, mPcscfAddresses, - mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId); + mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId, mDefaultQos, + mQosSessions); } @Override @@ -440,6 +487,16 @@ public final class DataCallResponse implements Parcelable { dest.writeInt(mMtuV6); dest.writeInt(mHandoverFailureMode); dest.writeInt(mPduSessionId); + if (mDefaultQos != null) { + if (mDefaultQos.getType() == Qos.QOS_TYPE_EPS) { + dest.writeParcelable((EpsQos) mDefaultQos, flags); + } else { + dest.writeParcelable((NrQos) mDefaultQos, flags); + } + } else { + dest.writeParcelable(null, flags); + } + dest.writeList(mQosSessions); } public static final @android.annotation.NonNull Parcelable.Creator<DataCallResponse> CREATOR = @@ -519,6 +576,10 @@ public final class DataCallResponse implements Parcelable { private int mPduSessionId = PDU_SESSION_ID_NOT_SET; + private Qos mDefaultQos; + + private List<QosSession> mQosSessions = new ArrayList<>(); + /** * Default constructor for Builder. */ @@ -713,6 +774,35 @@ public final class DataCallResponse implements Parcelable { } /** + * Set the default QOS for this data connection. + * + * @param defaultQos QOS (Quality Of Service) received from network. + * + * @return The same instance of the builder. + * + * @hide + */ + public @NonNull Builder setDefaultQos(@Nullable Qos defaultQos) { + mDefaultQos = defaultQos; + return this; + } + + /** + * Set the dedicated bearer QOS sessions for this data connection. + * + * @param qosSessions Dedicated bearer QOS (Quality Of Service) sessions received + * from network. + * + * @return The same instance of the builder. + * + * @hide + */ + public @NonNull Builder setQosSessions(@NonNull List<QosSession> qosSessions) { + mQosSessions = qosSessions; + return this; + } + + /** * Build the DataCallResponse. * * @return the DataCallResponse object. @@ -720,7 +810,8 @@ public final class DataCallResponse implements Parcelable { public @NonNull DataCallResponse build() { return new DataCallResponse(mCause, mSuggestedRetryTime, mId, mLinkStatus, mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses, - mPcscfAddresses, mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId); + mPcscfAddresses, mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId, + mDefaultQos, mQosSessions); } } } diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java index 77685971c138..2ec965101930 100644 --- a/telephony/java/android/telephony/data/DataService.java +++ b/telephony/java/android/telephony/data/DataService.java @@ -107,6 +107,9 @@ public abstract class DataService extends Service { private static final int DATA_SERVICE_INDICATION_DATA_CALL_LIST_CHANGED = 11; private static final int DATA_SERVICE_REQUEST_START_HANDOVER = 12; private static final int DATA_SERVICE_REQUEST_CANCEL_HANDOVER = 13; + private static final int DATA_SERVICE_REQUEST_REGISTER_APN_UNTHROTTLED = 14; + private static final int DATA_SERVICE_REQUEST_UNREGISTER_APN_UNTHROTTLED = 15; + private static final int DATA_SERVICE_INDICATION_APN_UNTHROTTLED = 16; private final HandlerThread mHandlerThread; @@ -129,6 +132,8 @@ public abstract class DataService extends Service { private final List<IDataServiceCallback> mDataCallListChangedCallbacks = new ArrayList<>(); + private final List<IDataServiceCallback> mApnUnthrottledCallbacks = new ArrayList<>(); + /** * Constructor * @param slotIndex SIM slot index the data service provider associated with. @@ -326,6 +331,19 @@ public abstract class DataService extends Service { } } + private void registerForApnUnthrottled(IDataServiceCallback callback) { + synchronized (mApnUnthrottledCallbacks) { + mApnUnthrottledCallbacks.add(callback); + } + } + + private void unregisterForApnUnthrottled(IDataServiceCallback callback) { + synchronized (mApnUnthrottledCallbacks) { + mApnUnthrottledCallbacks.remove(callback); + } + } + + /** * Notify the system that current data call list changed. Data service must invoke this * method whenever there is any data call status changed. @@ -343,6 +361,21 @@ public abstract class DataService extends Service { } /** + * Notify the system that a given APN was unthrottled. + * + * @param apn Access Point Name defined by the carrier. + */ + public final void notifyApnUnthrottled(@NonNull String apn) { + synchronized (mApnUnthrottledCallbacks) { + for (IDataServiceCallback callback : mApnUnthrottledCallbacks) { + mHandler.obtainMessage(DATA_SERVICE_INDICATION_APN_UNTHROTTLED, + mSlotIndex, 0, new ApnUnthrottledIndication(apn, + callback)).sendToTarget(); + } + } + } + + /** * Called when the instance of data service is destroyed (e.g. got unbind or binder died) * or when the data service provider is removed. The extended class should implement this * method to perform cleanup works. @@ -429,6 +462,16 @@ public abstract class DataService extends Service { } } + private static final class ApnUnthrottledIndication { + public final String apn; + public final IDataServiceCallback callback; + ApnUnthrottledIndication(String apn, + IDataServiceCallback callback) { + this.apn = apn; + this.callback = callback; + } + } + private class DataServiceHandler extends Handler { DataServiceHandler(Looper looper) { @@ -544,6 +587,26 @@ public abstract class DataService extends Service { (cReq.callback != null) ? new DataServiceCallback(cReq.callback) : null); break; + case DATA_SERVICE_REQUEST_REGISTER_APN_UNTHROTTLED: + if (serviceProvider == null) break; + serviceProvider.registerForApnUnthrottled((IDataServiceCallback) message.obj); + break; + case DATA_SERVICE_REQUEST_UNREGISTER_APN_UNTHROTTLED: + if (serviceProvider == null) break; + callback = (IDataServiceCallback) message.obj; + serviceProvider.unregisterForApnUnthrottled(callback); + break; + case DATA_SERVICE_INDICATION_APN_UNTHROTTLED: + if (serviceProvider == null) break; + ApnUnthrottledIndication apnUnthrottledIndication = + (ApnUnthrottledIndication) message.obj; + try { + apnUnthrottledIndication.callback + .onApnUnthrottled(apnUnthrottledIndication.apn); + } catch (RemoteException e) { + loge("Failed to call onApnUnthrottled. " + e); + } + break; } } } @@ -695,6 +758,26 @@ public abstract class DataService extends Service { mHandler.obtainMessage(DATA_SERVICE_REQUEST_CANCEL_HANDOVER, slotIndex, 0, req).sendToTarget(); } + + @Override + public void registerForUnthrottleApn(int slotIndex, IDataServiceCallback callback) { + if (callback == null) { + loge("registerForUnthrottleApn: callback is null"); + return; + } + mHandler.obtainMessage(DATA_SERVICE_REQUEST_REGISTER_APN_UNTHROTTLED, slotIndex, + 0, callback).sendToTarget(); + } + + @Override + public void unregisterForUnthrottleApn(int slotIndex, IDataServiceCallback callback) { + if (callback == null) { + loge("uregisterForUnthrottleApn: callback is null"); + return; + } + mHandler.obtainMessage(DATA_SERVICE_REQUEST_UNREGISTER_APN_UNTHROTTLED, + slotIndex, 0, callback).sendToTarget(); + } } private void log(String s) { diff --git a/telephony/java/android/telephony/data/DataServiceCallback.java b/telephony/java/android/telephony/data/DataServiceCallback.java index eef0e017f998..52bf15fd16c3 100644 --- a/telephony/java/android/telephony/data/DataServiceCallback.java +++ b/telephony/java/android/telephony/data/DataServiceCallback.java @@ -233,7 +233,7 @@ public class DataServiceCallback { */ @NonNull public static String resultCodeToString(@DataServiceCallback.ResultCode int resultCode) { - switch(resultCode) { + switch (resultCode) { case RESULT_SUCCESS: return "RESULT_SUCCESS"; case RESULT_ERROR_UNSUPPORTED: @@ -248,4 +248,22 @@ public class DataServiceCallback { return "Missing case for result code=" + resultCode; } } + + /** + * Indicates that the specified APN is no longer throttled. + * + * @param apn Access Point Name defined by the carrier. + */ + public void onApnUnthrottled(@NonNull String apn) { + if (mCallback != null) { + try { + if (DBG) Rlog.d(TAG, "onApnUnthrottled"); + mCallback.onApnUnthrottled(apn); + } catch (RemoteException e) { + Rlog.e(TAG, "onApnUnthrottled: remote exception", e); + } + } else { + Rlog.e(TAG, "onApnUnthrottled: callback is null!"); + } + } } diff --git a/telephony/java/android/telephony/data/EpsQos.java b/telephony/java/android/telephony/data/EpsQos.java new file mode 100644 index 000000000000..ad43068b2f11 --- /dev/null +++ b/telephony/java/android/telephony/data/EpsQos.java @@ -0,0 +1,105 @@ +/** + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.data; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + + +/** + * Class that stores information specific to NR QOS. + * + * @hide + */ +public final class EpsQos extends Qos implements Parcelable { + + int qosClassId; + + public EpsQos() { + super(Qos.QOS_TYPE_EPS, + new android.hardware.radio.V1_6.QosBandwidth(), + new android.hardware.radio.V1_6.QosBandwidth()); + } + + public EpsQos(@NonNull android.hardware.radio.V1_6.EpsQos qos) { + super(Qos.QOS_TYPE_EPS, qos.downlink, qos.uplink); + qosClassId = qos.qci; + } + + private EpsQos(Parcel source) { + super(source); + qosClassId = source.readInt(); + } + + public static @NonNull EpsQos createFromParcelBody(@NonNull Parcel in) { + return new EpsQos(in); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + super.writeToParcel(Qos.QOS_TYPE_EPS, dest, flags); + dest.writeInt(qosClassId); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), qosClassId); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + + if (o == null || !(o instanceof EpsQos)) { + return false; + } + + EpsQos other = (EpsQos) o; + + return this.qosClassId == other.qosClassId + && super.equals(other); + } + + @Override + public String toString() { + return "EpsQos {" + + " qosClassId=" + qosClassId + + " downlink=" + downlink + + " uplink=" + uplink + "}"; + } + + public static final @NonNull Parcelable.Creator<EpsQos> CREATOR = + new Parcelable.Creator<EpsQos>() { + @Override + public EpsQos createFromParcel(Parcel source) { + return new EpsQos(source); + } + + @Override + public EpsQos[] newArray(int size) { + return new EpsQos[size]; + } + }; +} diff --git a/telephony/java/android/telephony/data/IDataService.aidl b/telephony/java/android/telephony/data/IDataService.aidl index 33226feb0e35..3f1f033d6f11 100644 --- a/telephony/java/android/telephony/data/IDataService.aidl +++ b/telephony/java/android/telephony/data/IDataService.aidl @@ -40,4 +40,6 @@ oneway interface IDataService void unregisterForDataCallListChanged(int slotId, IDataServiceCallback callback); void startHandover(int slotId, int cid, IDataServiceCallback callback); void cancelHandover(int slotId, int cid, IDataServiceCallback callback); + void registerForUnthrottleApn(int slotIndex, IDataServiceCallback callback); + void unregisterForUnthrottleApn(int slotIndex, IDataServiceCallback callback); } diff --git a/telephony/java/android/telephony/data/IDataServiceCallback.aidl b/telephony/java/android/telephony/data/IDataServiceCallback.aidl index d296e7b19be8..9cc2feac331a 100644 --- a/telephony/java/android/telephony/data/IDataServiceCallback.aidl +++ b/telephony/java/android/telephony/data/IDataServiceCallback.aidl @@ -32,4 +32,5 @@ oneway interface IDataServiceCallback void onDataCallListChanged(in List<DataCallResponse> dataCallList); void onHandoverStarted(int result); void onHandoverCancelled(int result); + void onApnUnthrottled(in String apn); } diff --git a/telephony/java/android/telephony/data/IQualifiedNetworksService.aidl b/telephony/java/android/telephony/data/IQualifiedNetworksService.aidl index 3bf09bc19788..2904082616e7 100644 --- a/telephony/java/android/telephony/data/IQualifiedNetworksService.aidl +++ b/telephony/java/android/telephony/data/IQualifiedNetworksService.aidl @@ -17,6 +17,7 @@ package android.telephony.data; import android.telephony.data.IQualifiedNetworksServiceCallback; +import android.telephony.data.ApnThrottleStatus; /** * {@hide} @@ -25,4 +26,5 @@ interface IQualifiedNetworksService { oneway void createNetworkAvailabilityProvider(int slotId, IQualifiedNetworksServiceCallback callback); oneway void removeNetworkAvailabilityProvider(int slotId); + oneway void reportApnThrottleStatusChanged(int slotId, in List<ApnThrottleStatus> statuses); } diff --git a/telephony/java/android/telephony/data/NrQos.java b/telephony/java/android/telephony/data/NrQos.java new file mode 100644 index 000000000000..2011eed26977 --- /dev/null +++ b/telephony/java/android/telephony/data/NrQos.java @@ -0,0 +1,112 @@ +/** + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.data; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * Class that stores information specific to NR QOS. + * + * @hide + */ +public final class NrQos extends Qos implements Parcelable { + int qosFlowId; + int fiveQi; + int averagingWindowMs; + + public NrQos(@NonNull android.hardware.radio.V1_6.NrQos qos) { + super(Qos.QOS_TYPE_NR, qos.downlink, qos.uplink); + fiveQi = qos.fiveQi; + qosFlowId = qos.qfi; + averagingWindowMs = qos.averagingWindowMs; + } + + private NrQos(Parcel source) { + super(source); + this.qosFlowId = source.readInt(); + this.fiveQi = source.readInt(); + this.averagingWindowMs = source.readInt(); + } + + public static @NonNull NrQos createFromParcelBody(@NonNull Parcel in) { + return new NrQos(in); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + super.writeToParcel(Qos.QOS_TYPE_NR, dest, flags); + dest.writeInt(qosFlowId); + dest.writeInt(fiveQi); + dest.writeInt(averagingWindowMs); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), qosFlowId, fiveQi, averagingWindowMs); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + + if (o == null || !(o instanceof NrQos)) { + return false; + } + + NrQos other = (NrQos) o; + + if (!super.equals(other)) { + return false; + } + + return this.qosFlowId == other.qosFlowId + && this.fiveQi == other.fiveQi + && this.averagingWindowMs == other.averagingWindowMs; + } + + @Override + public String toString() { + return "NrQos {" + + " fiveQi=" + fiveQi + + " downlink=" + downlink + + " uplink=" + uplink + + " qosFlowId=" + qosFlowId + + " averagingWindowMs=" + averagingWindowMs + "}"; + } + + public static final @NonNull Parcelable.Creator<NrQos> CREATOR = + new Parcelable.Creator<NrQos>() { + @Override + public NrQos createFromParcel(Parcel source) { + return new NrQos(source); + } + + @Override + public NrQos[] newArray(int size) { + return new NrQos[size]; + } + }; +} diff --git a/telephony/java/android/telephony/data/Qos.java b/telephony/java/android/telephony/data/Qos.java new file mode 100644 index 000000000000..c8bb91e28bf2 --- /dev/null +++ b/telephony/java/android/telephony/data/Qos.java @@ -0,0 +1,175 @@ +/** + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.data; + +import android.annotation.CallSuper; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Objects; + +/** + * Class that stores information specific to QOS. + * + * @hide + */ +public abstract class Qos { + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "QOS_TYPE_", + value = {QOS_TYPE_EPS, QOS_TYPE_NR}) + public @interface QosType {} + + @QosType + final int type; + + static final int QOS_TYPE_EPS = 1; + static final int QOS_TYPE_NR = 2; + + final QosBandwidth downlink; + final QosBandwidth uplink; + + Qos(int type, + @NonNull android.hardware.radio.V1_6.QosBandwidth downlink, + @NonNull android.hardware.radio.V1_6.QosBandwidth uplink) { + this.type = type; + this.downlink = new QosBandwidth(downlink.maxBitrateKbps, downlink.guaranteedBitrateKbps); + this.uplink = new QosBandwidth(uplink.maxBitrateKbps, uplink.guaranteedBitrateKbps); + } + + static class QosBandwidth implements Parcelable { + int maxBitrateKbps; + int guaranteedBitrateKbps; + + QosBandwidth() { + } + + QosBandwidth(int maxBitrateKbps, int guaranteedBitrateKbps) { + this.maxBitrateKbps = maxBitrateKbps; + this.guaranteedBitrateKbps = guaranteedBitrateKbps; + } + + private QosBandwidth(Parcel source) { + maxBitrateKbps = source.readInt(); + guaranteedBitrateKbps = source.readInt(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(maxBitrateKbps); + dest.writeInt(guaranteedBitrateKbps); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public int hashCode() { + return Objects.hash(maxBitrateKbps, guaranteedBitrateKbps); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + + if (o == null || !(o instanceof QosBandwidth)) { + return false; + } + + QosBandwidth other = (QosBandwidth) o; + return maxBitrateKbps == other.maxBitrateKbps + && guaranteedBitrateKbps == other.guaranteedBitrateKbps; + } + + @Override + public String toString() { + return "Bandwidth {" + + " maxBitrateKbps=" + maxBitrateKbps + + " guaranteedBitrateKbps=" + guaranteedBitrateKbps + "}"; + } + + public static final @NonNull Parcelable.Creator<QosBandwidth> CREATOR = + new Parcelable.Creator<QosBandwidth>() { + @Override + public QosBandwidth createFromParcel(Parcel source) { + return new QosBandwidth(source); + } + + @Override + public QosBandwidth[] newArray(int size) { + return new QosBandwidth[size]; + } + }; + }; + + protected Qos(@NonNull Parcel source) { + type = source.readInt(); + downlink = source.readParcelable(QosBandwidth.class.getClassLoader()); + uplink = source.readParcelable(QosBandwidth.class.getClassLoader()); + } + + /** + * Used by child classes for parceling. + * + * @hide + */ + @CallSuper + public void writeToParcel(@QosType int type, Parcel dest, int flags) { + dest.writeInt(type); + dest.writeParcelable(downlink, flags); + dest.writeParcelable(uplink, flags); + } + + /** @hide */ + public static @NonNull Qos create(@NonNull android.hardware.radio.V1_6.Qos qos) { + switch (qos.getDiscriminator()) { + case android.hardware.radio.V1_6.Qos.hidl_discriminator.eps: + return new EpsQos(qos.eps()); + case android.hardware.radio.V1_6.Qos.hidl_discriminator.nr: + return new NrQos(qos.nr()); + default: + return null; + } + } + + /** @hide */ + public @QosType int getType() { + return type; + } + + @Override + public int hashCode() { + return Objects.hash(downlink, uplink); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + + Qos other = (Qos) o; + return type == other.type + && downlink.equals(other.downlink) + && uplink.equals(other.uplink); + } +} diff --git a/telephony/java/android/telephony/data/QosFilter.java b/telephony/java/android/telephony/data/QosFilter.java new file mode 100644 index 000000000000..69277445634d --- /dev/null +++ b/telephony/java/android/telephony/data/QosFilter.java @@ -0,0 +1,373 @@ +/** + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.data; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.net.InetAddresses; +import android.net.LinkAddress; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.net.InetAddress; +import java.net.Inet4Address; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + + +/** + * Class that stores QOS filter parameters as defined in + * 3gpp 24.008 10.5.6.12 and 3gpp 24.501 9.11.4.13. + * + * @hide + */ +public final class QosFilter implements Parcelable { + + private List<LinkAddress> localAddresses; + private List<LinkAddress> remoteAddresses; + private PortRange localPort; + private PortRange remotePort; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "QOS_PROTOCOL_", + value = {QOS_PROTOCOL_UNSPECIFIED, QOS_PROTOCOL_TCP, QOS_PROTOCOL_UDP, + QOS_PROTOCOL_ESP, QOS_PROTOCOL_AH}) + public @interface QosProtocol {} + + public static final int QOS_PROTOCOL_UNSPECIFIED = + android.hardware.radio.V1_6.QosProtocol.UNSPECIFIED; + public static final int QOS_PROTOCOL_TCP = android.hardware.radio.V1_6.QosProtocol.TCP; + public static final int QOS_PROTOCOL_UDP = android.hardware.radio.V1_6.QosProtocol.UDP; + public static final int QOS_PROTOCOL_ESP = android.hardware.radio.V1_6.QosProtocol.ESP; + public static final int QOS_PROTOCOL_AH = android.hardware.radio.V1_6.QosProtocol.AH; + + @QosProtocol + private int protocol; + + private int typeOfServiceMask; + + private long flowLabel; + + /** IPSec security parameter index */ + private long securityParameterIndex; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "QOS_FILTER_DIRECTION_", + value = {QOS_FILTER_DIRECTION_DOWNLINK, QOS_FILTER_DIRECTION_UPLINK, + QOS_FILTER_DIRECTION_BIDIRECTIONAL}) + public @interface QosFilterDirection {} + + public static final int QOS_FILTER_DIRECTION_DOWNLINK = + android.hardware.radio.V1_6.QosFilterDirection.DOWNLINK; + public static final int QOS_FILTER_DIRECTION_UPLINK = + android.hardware.radio.V1_6.QosFilterDirection.UPLINK; + public static final int QOS_FILTER_DIRECTION_BIDIRECTIONAL = + android.hardware.radio.V1_6.QosFilterDirection.BIDIRECTIONAL; + + @QosFilterDirection + private int filterDirection; + + /** + * Specified the order in which the filter needs to be matched. + * A Lower numerical value has a higher precedence. + */ + private int precedence; + + QosFilter() { + localAddresses = new ArrayList<>(); + remoteAddresses = new ArrayList<>(); + localPort = new PortRange(); + remotePort = new PortRange(); + protocol = QOS_PROTOCOL_UNSPECIFIED; + filterDirection = QOS_FILTER_DIRECTION_BIDIRECTIONAL; + } + + public QosFilter(List<LinkAddress> localAddresses, List<LinkAddress> remoteAddresses, + PortRange localPort, PortRange remotePort, int protocol, int tos, + long flowLabel, long spi, int direction, int precedence) { + this.localAddresses = localAddresses; + this.remoteAddresses = remoteAddresses; + this.localPort = localPort; + this.remotePort = remotePort; + this.protocol = protocol; + this.typeOfServiceMask = tos; + this.flowLabel = flowLabel; + this.securityParameterIndex = spi; + this.filterDirection = direction; + this.precedence = precedence; + } + + /** @hide */ + public static @NonNull QosFilter create( + @NonNull android.hardware.radio.V1_6.QosFilter qosFilter) { + QosFilter ret = new QosFilter(); + + String[] localAddresses = qosFilter.localAddresses.stream().toArray(String[]::new); + if (localAddresses != null) { + for (String address : localAddresses) { + ret.localAddresses.add(createLinkAddressFromString(address)); + } + } + + String[] remoteAddresses = qosFilter.remoteAddresses.stream().toArray(String[]::new); + if (remoteAddresses != null) { + for (String address : remoteAddresses) { + ret.remoteAddresses.add(createLinkAddressFromString(address)); + } + } + + if (qosFilter.localPort != null) { + if (qosFilter.localPort.getDiscriminator() + == android.hardware.radio.V1_6.MaybePort.hidl_discriminator.range) { + final android.hardware.radio.V1_6.PortRange portRange = qosFilter.localPort.range(); + ret.localPort.start = portRange.start; + ret.localPort.end = portRange.end; + } + } + + if (qosFilter.remotePort != null) { + if (qosFilter.remotePort.getDiscriminator() + == android.hardware.radio.V1_6.MaybePort.hidl_discriminator.range) { + final android.hardware.radio.V1_6.PortRange portRange + = qosFilter.remotePort.range(); + ret.remotePort.start = portRange.start; + ret.remotePort.end = portRange.end; + } + } + + ret.protocol = qosFilter.protocol; + + if (qosFilter.tos != null) { + if (qosFilter.tos.getDiscriminator() + == android.hardware.radio.V1_6.QosFilter.TypeOfService.hidl_discriminator.value) { + ret.typeOfServiceMask = qosFilter.tos.value(); + } + } + + if (qosFilter.flowLabel != null) { + if (qosFilter.flowLabel.getDiscriminator() + == android.hardware.radio.V1_6.QosFilter.Ipv6FlowLabel.hidl_discriminator.value) { + ret.flowLabel = qosFilter.flowLabel.value(); + } + } + + if (qosFilter.spi != null) { + if (qosFilter.spi.getDiscriminator() + == android.hardware.radio.V1_6.QosFilter.IpsecSpi.hidl_discriminator.value) { + ret.securityParameterIndex = qosFilter.spi.value(); + } + } + + ret.filterDirection = qosFilter.direction; + ret.precedence = qosFilter.precedence; + + return ret; + } + + public static class PortRange implements Parcelable { + int start; + int end; + + PortRange() { + start = -1; + end = -1; + } + + private PortRange(Parcel source) { + start = source.readInt(); + end = source.readInt(); + } + + public PortRange(int start, int end) { + this.start = start; + this.end = end; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(start); + dest.writeInt(end); + } + + @Override + public int describeContents() { + return 0; + } + + public static final @NonNull Parcelable.Creator<PortRange> CREATOR = + new Parcelable.Creator<PortRange>() { + @Override + public PortRange createFromParcel(Parcel source) { + return new PortRange(source); + } + + @Override + public PortRange[] newArray(int size) { + return new PortRange[size]; + } + }; + + @Override + public String toString() { + return "PortRange {" + + " start=" + start + + " end=" + end + "}"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + + if (o == null || !(o instanceof PortRange)) { + return false; + } + + PortRange other = (PortRange) o; + return start == other.start + && end == other.end; + } + + @Override + public int hashCode() { + return Objects.hash(start, end); + } + }; + + @Override + public String toString() { + return "QosFilter {" + + " localAddresses=" + localAddresses + + " remoteAddresses=" + remoteAddresses + + " localPort=" + localPort + + " remotePort=" + remotePort + + " protocol=" + protocol + + " typeOfServiceMask=" + typeOfServiceMask + + " flowLabel=" + flowLabel + + " securityParameterIndex=" + securityParameterIndex + + " filterDirection=" + filterDirection + + " precedence=" + precedence + "}"; + } + + @Override + public int hashCode() { + return Objects.hash(localAddresses, remoteAddresses, localPort, + remotePort, protocol, typeOfServiceMask, flowLabel, + securityParameterIndex, filterDirection, precedence); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + + if (o == null || !(o instanceof QosFilter)) { + return false; + } + + QosFilter other = (QosFilter) o; + + return localAddresses.size() == other.localAddresses.size() + && localAddresses.containsAll(other.localAddresses) + && remoteAddresses.size() == other.remoteAddresses.size() + && remoteAddresses.containsAll(other.remoteAddresses) + && localPort.equals(other.localPort) + && remotePort.equals(other.remotePort) + && protocol == other.protocol + && typeOfServiceMask == other.typeOfServiceMask + && flowLabel == other.flowLabel + && securityParameterIndex == other.securityParameterIndex + && filterDirection == other.filterDirection + && precedence == other.precedence; + } + + private static LinkAddress createLinkAddressFromString(String addressString) { + addressString = addressString.trim(); + InetAddress address = null; + int prefixLength = -1; + try { + String[] pieces = addressString.split("/", 2); + address = InetAddresses.parseNumericAddress(pieces[0]); + if (pieces.length == 1) { + prefixLength = (address instanceof Inet4Address) ? 32 : 128; + } else if (pieces.length == 2) { + prefixLength = Integer.parseInt(pieces[1]); + } + } catch (NullPointerException e) { // Null string. + } catch (ArrayIndexOutOfBoundsException e) { // No prefix length. + } catch (NumberFormatException e) { // Non-numeric prefix. + } catch (IllegalArgumentException e) { // Invalid IP address. + } + + if (address == null || prefixLength == -1) { + throw new IllegalArgumentException("Invalid link address " + addressString); + } + + return new LinkAddress(address, prefixLength, 0, 0, + LinkAddress.LIFETIME_UNKNOWN, LinkAddress.LIFETIME_UNKNOWN); + } + + private QosFilter(Parcel source) { + localAddresses = new ArrayList<>(); + source.readList(localAddresses, LinkAddress.class.getClassLoader()); + remoteAddresses = new ArrayList<>(); + source.readList(remoteAddresses, LinkAddress.class.getClassLoader()); + localPort = source.readParcelable(PortRange.class.getClassLoader()); + remotePort = source.readParcelable(PortRange.class.getClassLoader()); + protocol = source.readInt(); + typeOfServiceMask = source.readInt(); + flowLabel = source.readLong(); + securityParameterIndex = source.readLong(); + filterDirection = source.readInt(); + precedence = source.readInt(); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeList(localAddresses); + dest.writeList(remoteAddresses); + dest.writeParcelable(localPort, flags); + dest.writeParcelable(remotePort, flags); + dest.writeInt(protocol); + dest.writeInt(typeOfServiceMask); + dest.writeLong(flowLabel); + dest.writeLong(securityParameterIndex); + dest.writeInt(filterDirection); + dest.writeInt(precedence); + } + + @Override + public int describeContents() { + return 0; + } + + public static final @NonNull Parcelable.Creator<QosFilter> CREATOR = + new Parcelable.Creator<QosFilter>() { + @Override + public QosFilter createFromParcel(Parcel source) { + return new QosFilter(source); + } + + @Override + public QosFilter[] newArray(int size) { + return new QosFilter[size]; + } + }; +} diff --git a/telephony/java/android/telephony/data/QosSession.java b/telephony/java/android/telephony/data/QosSession.java new file mode 100644 index 000000000000..f07b6a9f6725 --- /dev/null +++ b/telephony/java/android/telephony/data/QosSession.java @@ -0,0 +1,125 @@ +/** + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.data; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + + +/** + * Class that stores information specific to QOS session. + * + * @hide + */ +public final class QosSession implements Parcelable{ + + final int qosSessionId; + final Qos qos; + final List<QosFilter> qosFilterList; + + public QosSession(int qosSessionId, @NonNull Qos qos, @NonNull List<QosFilter> qosFilterList) { + this.qosSessionId = qosSessionId; + this.qos = qos; + this.qosFilterList = qosFilterList; + } + + private QosSession(Parcel source) { + qosSessionId = source.readInt(); + qos = source.readParcelable(Qos.class.getClassLoader()); + qosFilterList = new ArrayList<>(); + source.readList(qosFilterList, QosFilter.class.getClassLoader()); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(qosSessionId); + if (qos.getType() == Qos.QOS_TYPE_EPS) { + dest.writeParcelable((EpsQos)qos, flags); + } else { + dest.writeParcelable((NrQos)qos, flags); + } + dest.writeList(qosFilterList); + } + + public static @NonNull QosSession create( + @NonNull android.hardware.radio.V1_6.QosSession qosSession) { + List<QosFilter> qosFilters = new ArrayList<>(); + + if (qosSession.qosFilters != null) { + for (android.hardware.radio.V1_6.QosFilter filter : qosSession.qosFilters) { + qosFilters.add(QosFilter.create(filter)); + } + } + + return new QosSession( + qosSession.qosSessionId, + Qos.create(qosSession.qos), + qosFilters); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public String toString() { + return "QosSession {" + + " qosSessionId=" + qosSessionId + + " qos=" + qos + + " qosFilterList=" + qosFilterList + "}"; + } + + @Override + public int hashCode() { + return Objects.hash(qosSessionId, qos, qosFilterList); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + + if (o == null || !(o instanceof QosSession)) { + return false; + } + + QosSession other = (QosSession) o; + return this.qosSessionId == other.qosSessionId + && this.qos.equals(other.qos) + && this.qosFilterList.size() == other.qosFilterList.size() + && this.qosFilterList.containsAll(other.qosFilterList); + } + + + public static final @NonNull Parcelable.Creator<QosSession> CREATOR = + new Parcelable.Creator<QosSession>() { + @Override + public QosSession createFromParcel(Parcel source) { + return new QosSession(source); + } + + @Override + public QosSession[] newArray(int size) { + return new QosSession[size]; + } + }; +} diff --git a/telephony/java/android/telephony/data/QualifiedNetworksService.java b/telephony/java/android/telephony/data/QualifiedNetworksService.java index 05971c4d2e70..4af63b4cf981 100644 --- a/telephony/java/android/telephony/data/QualifiedNetworksService.java +++ b/telephony/java/android/telephony/data/QualifiedNetworksService.java @@ -28,6 +28,7 @@ import android.os.Message; import android.os.RemoteException; import android.telephony.AccessNetworkConstants.AccessNetworkType; import android.telephony.Annotation.ApnType; +import android.util.Log; import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; @@ -65,6 +66,7 @@ public abstract class QualifiedNetworksService extends Service { private static final int QNS_REMOVE_NETWORK_AVAILABILITY_PROVIDER = 2; private static final int QNS_REMOVE_ALL_NETWORK_AVAILABILITY_PROVIDERS = 3; private static final int QNS_UPDATE_QUALIFIED_NETWORKS = 4; + private static final int QNS_APN_THROTTLE_STATUS_CHANGED = 5; private final HandlerThread mHandlerThread; @@ -160,6 +162,17 @@ public abstract class QualifiedNetworksService extends Service { } /** + * The framework calls this method when the throttle status of an APN changes. + * + * This method is meant to be overridden. + * + * @param statuses the statuses that have changed + */ + public void reportApnThrottleStatusChanged(@NonNull List<ApnThrottleStatus> statuses) { + Log.d(TAG, "reportApnThrottleStatusChanged: statuses size=" + statuses.size()); + } + + /** * Called when the qualified networks provider is removed. The extended class should * implement this method to perform cleanup works. */ @@ -197,6 +210,12 @@ public abstract class QualifiedNetworksService extends Service { + slotIndex); } break; + case QNS_APN_THROTTLE_STATUS_CHANGED: + if (provider != null) { + List<ApnThrottleStatus> statuses = (List<ApnThrottleStatus>) message.obj; + provider.reportApnThrottleStatusChanged(statuses); + } + break; case QNS_REMOVE_NETWORK_AVAILABILITY_PROVIDER: if (provider != null) { @@ -286,6 +305,13 @@ public abstract class QualifiedNetworksService extends Service { mHandler.obtainMessage(QNS_REMOVE_NETWORK_AVAILABILITY_PROVIDER, slotIndex, 0) .sendToTarget(); } + + @Override + public void reportApnThrottleStatusChanged(int slotIndex, + List<ApnThrottleStatus> statuses) { + mHandler.obtainMessage(QNS_APN_THROTTLE_STATUS_CHANGED, slotIndex, 0, statuses) + .sendToTarget(); + } } private void log(String s) { diff --git a/telephony/java/android/telephony/ims/DelegateRequest.java b/telephony/java/android/telephony/ims/DelegateRequest.java index f384901d58bd..73d0840177dd 100644 --- a/telephony/java/android/telephony/ims/DelegateRequest.java +++ b/telephony/java/android/telephony/ims/DelegateRequest.java @@ -98,4 +98,9 @@ public final class DelegateRequest implements Parcelable { public int hashCode() { return Objects.hash(mFeatureTags); } + + @Override + public String toString() { + return "DelegateRequest{mFeatureTags=" + mFeatureTags + '}'; + } } diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java index ada069666547..8d7742b7510b 100644 --- a/telephony/java/android/telephony/ims/RcsUceAdapter.java +++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java @@ -76,39 +76,46 @@ public class RcsUceAdapter { * @hide */ public static final int ERROR_GENERIC_FAILURE = 1; + /** * The carrier network does not have UCE support enabled for this subscriber. * @hide */ public static final int ERROR_NOT_ENABLED = 2; + /** * The data network that the device is connected to does not support UCE currently (e.g. it is * 1x only currently). * @hide */ public static final int ERROR_NOT_AVAILABLE = 3; + /** * The network has responded with SIP 403 error and a reason "User not registered." * @hide */ public static final int ERROR_NOT_REGISTERED = 4; + /** * The network has responded to this request with a SIP 403 error and reason "not authorized for * presence" for this subscriber. * @hide */ public static final int ERROR_NOT_AUTHORIZED = 5; + /** * The network has responded to this request with a SIP 403 error and no reason. * @hide */ public static final int ERROR_FORBIDDEN = 6; + /** * The contact URI requested is not provisioned for VoLTE or it is not known as an IMS * subscriber to the carrier network. * @hide */ public static final int ERROR_NOT_FOUND = 7; + /** * The capabilities request contained too many URIs for the carrier network to handle. Retry * with a lower number of contact numbers. The number varies per carrier. @@ -116,22 +123,32 @@ public class RcsUceAdapter { */ // TODO: Try to integrate this into the API so that the service will split based on carrier. public static final int ERROR_REQUEST_TOO_LARGE = 8; + /** * The network did not respond to the capabilities request before the request timed out. * @hide */ public static final int ERROR_REQUEST_TIMEOUT = 10; + /** * The request failed due to the service having insufficient memory. * @hide */ public static final int ERROR_INSUFFICIENT_MEMORY = 11; + /** * The network was lost while trying to complete the request. * @hide */ public static final int ERROR_LOST_NETWORK = 12; + /** + * The network is temporarily unavailable or busy. Retries should only be done after the retry + * time returned in {@link CapabilitiesCallback#onError} has elapsed. + * @hide + */ + public static final int ERROR_SERVER_UNAVAILABLE = 13; + /**@hide*/ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = "ERROR_", value = { @@ -145,7 +162,8 @@ public class RcsUceAdapter { ERROR_REQUEST_TOO_LARGE, ERROR_REQUEST_TIMEOUT, ERROR_INSUFFICIENT_MEMORY, - ERROR_LOST_NETWORK + ERROR_LOST_NETWORK, + ERROR_SERVER_UNAVAILABLE }) public @interface ErrorCode {} @@ -369,8 +387,10 @@ public class RcsUceAdapter { * The pending request has resulted in an error and may need to be retried, depending on the * error code. * @param errorCode The reason for the framework being unable to process the request. + * @param retryAfterMilliseconds The time in milliseconds the requesting application should + * wait before retrying, if non-zero. */ - void onError(@ErrorCode int errorCode); + void onError(@ErrorCode int errorCode, long retryAfterMilliseconds); } private final Context mContext; @@ -451,10 +471,10 @@ public class RcsUceAdapter { } } @Override - public void onError(int errorCode) { + public void onError(int errorCode, long retryAfterMilliseconds) { final long callingIdentity = Binder.clearCallingIdentity(); try { - executor.execute(() -> c.onError(errorCode)); + executor.execute(() -> c.onError(errorCode, retryAfterMilliseconds)); } finally { restoreCallingIdentity(callingIdentity); } @@ -535,10 +555,10 @@ public class RcsUceAdapter { } } @Override - public void onError(int errorCode) { + public void onError(int errorCode, long retryAfterMilliseconds) { final long callingIdentity = Binder.clearCallingIdentity(); try { - executor.execute(() -> c.onError(errorCode)); + executor.execute(() -> c.onError(errorCode, retryAfterMilliseconds)); } finally { restoreCallingIdentity(callingIdentity); } diff --git a/telephony/java/android/telephony/ims/SipDelegateManager.java b/telephony/java/android/telephony/ims/SipDelegateManager.java index 337b7d49323d..190a792b5a9a 100644 --- a/telephony/java/android/telephony/ims/SipDelegateManager.java +++ b/telephony/java/android/telephony/ims/SipDelegateManager.java @@ -236,17 +236,17 @@ public class SipDelegateManager { public static final int SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP = 2; /** - * The SipDelegate has closed because the IMS service does not support the creation of - * SipDelegates. + * The SipDelegate has been closed due to the user disabling RCS. * @hide */ - public static final int SIP_DELEGATE_DESTROY_REASON_SERVICE_NOT_SUPPORTED = 3; + public static final int SIP_DELEGATE_DESTROY_REASON_USER_DISABLED_RCS = 3; /** - * The SipDelegate has been closed due to the user disabling RCS. + * The SipDelegate has been closed due to the subscription associated with this delegate being + * torn down. * @hide */ - public static final int SIP_DELEGATE_DESTROY_REASON_USER_DISABLED_RCS = 4; + public static final int SIP_DELEGATE_DESTROY_REASON_SUBSCRIPTION_TORN_DOWN = 4; /** @hide */ @Retention(RetentionPolicy.SOURCE) @@ -254,8 +254,8 @@ public class SipDelegateManager { SIP_DELEGATE_DESTROY_REASON_UNKNOWN, SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD, SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP, - SIP_DELEGATE_DESTROY_REASON_SERVICE_NOT_SUPPORTED, - SIP_DELEGATE_DESTROY_REASON_USER_DISABLED_RCS + SIP_DELEGATE_DESTROY_REASON_USER_DISABLED_RCS, + SIP_DELEGATE_DESTROY_REASON_SUBSCRIPTION_TORN_DOWN }) public @interface SipDelegateDestroyReason {} @@ -316,6 +316,9 @@ public class SipDelegateManager { * always be available to handle incoming messages. One mechanism that can be used for this is * the {@link android.service.carrier.CarrierMessagingClientService}, which the framework keeps * a persistent binding to when the app is the default SMS application. + * <p> + * Note: the ability to create SipDelegates is only available applications running as the + * primary user. * @param request The parameters that are associated with the SipDelegate creation request that * will be used to create the SipDelegate connection. * @param executor The executor that will be used to call the callbacks associated with this @@ -346,8 +349,8 @@ public class SipDelegateManager { throw new ImsException("Telephony server is down", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } - controller.createSipDelegate(mSubId, request, wrapper.getStateCallbackBinder(), - wrapper.getMessageCallbackBinder()); + controller.createSipDelegate(mSubId, request, mContext.getOpPackageName(), + wrapper.getStateCallbackBinder(), wrapper.getMessageCallbackBinder()); } catch (ServiceSpecificException e) { throw new ImsException(e.getMessage(), e.errorCode); } catch (RemoteException e) { diff --git a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl index f218e35a5a9b..c6d9a8629556 100644 --- a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl +++ b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl @@ -63,7 +63,7 @@ interface IImsRcsController { // SipDelegateManager boolean isSipDelegateSupported(int subId); - void createSipDelegate(int subId, in DelegateRequest request, + void createSipDelegate(int subId, in DelegateRequest request, String packageName, ISipDelegateConnectionStateCallback delegateState, ISipDelegateMessageCallback delegateMessage); void destroySipDelegate(int subId, ISipDelegate connection, int reason); diff --git a/telephony/java/android/telephony/ims/aidl/IRcsUceControllerCallback.aidl b/telephony/java/android/telephony/ims/aidl/IRcsUceControllerCallback.aidl index 0bd3e5ed354e..0f627b92a24c 100644 --- a/telephony/java/android/telephony/ims/aidl/IRcsUceControllerCallback.aidl +++ b/telephony/java/android/telephony/ims/aidl/IRcsUceControllerCallback.aidl @@ -26,5 +26,5 @@ import android.telephony.ims.RcsContactUceCapability; oneway interface IRcsUceControllerCallback { void onCapabilitiesReceived(in List<RcsContactUceCapability> contactCapabilities); void onComplete(); - void onError(int errorCode); + void onError(int errorCode, long retryAfterMilliseconds); } diff --git a/telephony/java/android/telephony/ims/aidl/ISipTransport.aidl b/telephony/java/android/telephony/ims/aidl/ISipTransport.aidl index cd888391c584..3438587b7348 100644 --- a/telephony/java/android/telephony/ims/aidl/ISipTransport.aidl +++ b/telephony/java/android/telephony/ims/aidl/ISipTransport.aidl @@ -26,7 +26,7 @@ import android.telephony.ims.aidl.ISipDelegateStateCallback; * {@hide} */ oneway interface ISipTransport { - void createSipDelegate(in DelegateRequest request, ISipDelegateStateCallback dc, + void createSipDelegate(int subId, in DelegateRequest request, ISipDelegateStateCallback dc, ISipDelegateMessageCallback mc); void destroySipDelegate(ISipDelegate delegate, int reason); } diff --git a/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java index b48f6317e413..93d438cf7f4d 100644 --- a/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java +++ b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java @@ -58,11 +58,11 @@ public class SipTransportImplBase { private final ISipTransport.Stub mSipTransportImpl = new ISipTransport.Stub() { @Override - public void createSipDelegate(DelegateRequest request, ISipDelegateStateCallback dc, - ISipDelegateMessageCallback mc) { + public void createSipDelegate(int subId, DelegateRequest request, + ISipDelegateStateCallback dc, ISipDelegateMessageCallback mc) { final long token = Binder.clearCallingIdentity(); try { - mBinderExecutor.execute(() -> createSipDelegateInternal(request, dc, mc)); + mBinderExecutor.execute(() -> createSipDelegateInternal(subId, request, dc, mc)); } finally { Binder.restoreCallingIdentity(token); } @@ -105,6 +105,7 @@ public class SipTransportImplBase { * This method will be called on the Executor specified in * {@link SipTransportImplBase#SipTransportImplBase(Executor)}. * + * @param subscriptionId The subscription ID associated with the requested {@link SipDelegate}. * @param request A SIP delegate request containing the parameters that the remote RCS * application wishes to use. * @param dc A callback back to the remote application to be used to communicate state callbacks @@ -113,9 +114,9 @@ public class SipTransportImplBase { * remote application and acknowledge the sending of outgoing SIP messages. * @hide */ - public void createSipDelegate(@NonNull DelegateRequest request, + public void createSipDelegate(int subscriptionId, @NonNull DelegateRequest request, @NonNull DelegateStateCallback dc, @NonNull DelegateMessageCallback mc) { - throw new UnsupportedOperationException("destroySipDelegate not implemented!"); + throw new UnsupportedOperationException("createSipDelegate not implemented!"); } /** @@ -136,11 +137,11 @@ public class SipTransportImplBase { throw new UnsupportedOperationException("destroySipDelegate not implemented!"); } - private void createSipDelegateInternal(DelegateRequest r, ISipDelegateStateCallback cb, - ISipDelegateMessageCallback mc) { + private void createSipDelegateInternal(int subId, DelegateRequest r, + ISipDelegateStateCallback cb, ISipDelegateMessageCallback mc) { SipDelegateAidlWrapper wrapper = new SipDelegateAidlWrapper(mBinderExecutor, cb, mc); mDelegates.add(wrapper); - createSipDelegate(r, wrapper, wrapper); + createSipDelegate(subId, r, wrapper, wrapper); } private void destroySipDelegateInternal(ISipDelegate d, int reason) { diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java index 76fc4f7d0519..6fbde503c3a0 100644 --- a/telephony/java/com/android/internal/telephony/DctConstants.java +++ b/telephony/java/com/android/internal/telephony/DctConstants.java @@ -113,6 +113,7 @@ public class DctConstants { public static final int EVENT_NR_TIMER_WATCHDOG = BASE + 53; public static final int EVENT_CARRIER_CONFIG_CHANGED = BASE + 54; public static final int EVENT_SIM_STATE_UPDATED = BASE + 55; + public static final int EVENT_APN_UNTHROTTLED = BASE + 56; /***** Constants *****/ diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 0bd485139331..b524549440da 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -36,6 +36,7 @@ import android.telephony.CarrierRestrictionRules; import android.telephony.CellIdentity; import android.telephony.CellInfo; import android.telephony.ClientRequestStats; +import android.telephony.ThermalMitigationRequest; import android.telephony.IccOpenLogicalChannelResponse; import android.telephony.ICellInfoCallback; import android.telephony.ModemActivityInfo; @@ -2154,6 +2155,8 @@ interface ITelephony { oneway void setSystemSelectionChannels(in List<RadioAccessSpecifier> specifiers, int subId, IBooleanConsumer resultCallback); + List<RadioAccessSpecifier> getSystemSelectionChannels(int subId); + boolean isMvnoMatched(int subId, int mvnoType, String mvnoMatchData); /** @@ -2247,4 +2250,22 @@ interface ITelephony { * @return CarrierBandwidth with bandwidth of both primary and secondary carrier. */ CarrierBandwidth getCarrierBandwidth(int subId); + + /** + * Checks whether the device supports the given capability on the radio interface. + * + * @param capability the name of the capability + * @return the availability of the capability + */ + boolean isRadioInterfaceCapabilitySupported(String capability); + + /** + * Thermal mitigation request to control functionalities at modem. + * + * @param subId the id of the subscription + * @param thermalMitigationRequest holds the parameters necessary for the request. + * @throws InvalidThermalMitigationRequestException if the parametes are invalid. + */ + int sendThermalMitigationRequest(int subId, + in ThermalMitigationRequest thermalMitigationRequest); } diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java index 9d4072f1cf1c..42dee0e4060b 100644 --- a/telephony/java/com/android/internal/telephony/RILConstants.java +++ b/telephony/java/com/android/internal/telephony/RILConstants.java @@ -519,6 +519,9 @@ public interface RILConstants { int RIL_REQUEST_RELEASE_PDU_SESSION_ID = 216; int RIL_REQUEST_START_HANDOVER = 217; int RIL_REQUEST_CANCEL_HANDOVER = 218; + int RIL_REQUEST_GET_SYSTEM_SELECTION_CHANNELS = 219; + int RIL_REQUEST_GET_HAL_DEVICE_CAPABILITIES = 220; + int RIL_REQUEST_SET_DATA_THROTTLING = 221; /* Responses begin */ int RIL_RESPONSE_ACKNOWLEDGEMENT = 800; diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java index d216162cc257..b905212a9100 100644 --- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java +++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java @@ -321,4 +321,107 @@ public class TelephonyIntents { */ public static final String ACTION_USER_ACTIVITY_NOTIFICATION = "android.intent.action.USER_ACTIVITY_NOTIFICATION"; + + /** + * Kept for backwards compatibility. + * @deprecated @see TelephonyManager#ACTION_CARRIER_SIGNAL_REDIRECTED + */ + @Deprecated + public static final String ACTION_CARRIER_SIGNAL_REDIRECTED = + "com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED"; + + /** + * Kept for backwards compatibility. + * @deprecated @see TelephonyManager#ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED + */ + @Deprecated + public static final String ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED = + "com.android.internal.telephony.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED"; + + /** + * Kept for backwards compatibility. + * @deprecated @see TelephonyManager#ACTION_CARRIER_SIGNAL_PCO_VALUE + */ + @Deprecated + public static final String ACTION_CARRIER_SIGNAL_PCO_VALUE = + "com.android.internal.telephony.CARRIER_SIGNAL_PCO_VALUE"; + + /** + * Kept for backwards compatibility. + * @deprecated @see TelephonyManager#ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE + */ + @Deprecated + public static final String ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE = + "com.android.internal.telephony.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE"; + + /** + * Kept for backwards compatibility. + * @deprecated @see TelephonyManager#ACTION_CARRIER_SIGNAL_RESET + */ + @Deprecated + public static final String ACTION_CARRIER_SIGNAL_RESET = + "com.android.internal.telephony.CARRIER_SIGNAL_RESET"; + + /** + * Kept for backwards compatibility. + * @deprecated @see TelephonyManager#EXTRA_REDIRECTION_URL + */ + @Deprecated + public static final String EXTRA_REDIRECTION_URL = "redirectionUrl"; + + /** + * Kept for backwards compatibility. + * @deprecated @see TelephonyManager#EXTRA_DATA_FAIL_CAUSE + */ + @Deprecated + public static final String EXTRA_ERROR_CODE = "errorCode"; + + /** + * Kept for backwards compatibility. + * @deprecated @see TelephonyManager#EXTRA_APN_TYPE + */ + @Deprecated + public static final String EXTRA_APN_TYPE = "apnType"; + + /** + * Kept for backwards compatibility. + * @deprecated @see TelephonyManager#EXTRA_APN_TYPE + */ + @Deprecated + public static final String EXTRA_APN_TYPE_INT = "apnTypeInt"; + + /** + * Kept for backwards compatibility. + * @deprecated @see TelephonyManager#EXTRA_APN_PROTOCOL + */ + @Deprecated + public static final String EXTRA_APN_PROTOCOL = "apnProto"; + + /** + * Kept for backwards compatibility. + * @deprecated @see TelephonyManager#EXTRA_APN_PROTOCOL + */ + @Deprecated + public static final String EXTRA_APN_PROTOCOL_INT = "apnProtoInt"; + + /** + * Kept for backwards compatibility. + * @deprecated @see TelephonyManager#EXTRA_PCO_ID + */ + @Deprecated + public static final String EXTRA_PCO_ID = "pcoId"; + + /** + * Kept for backwards compatibility. + * @deprecated @see TelephonyManager#EXTRA_PCO_VALUE + */ + @Deprecated + public static final String EXTRA_PCO_VALUE = "pcoValue"; + + /** + * Kept for backwards compatibility. + * @deprecated @see TelephonyManager#EXTRA_DEFAULT_NETWORK_AVAILABLE + */ + @Deprecated + public static final String EXTRA_DEFAULT_NETWORK_AVAILABLE = "defaultNetworkAvailable"; } diff --git a/core/proto/android/stats/dnsresolver/Android.bp b/tests/BatteryStatsPerfTest/Android.bp index 1e8c76314448..58ccec705419 100644 --- a/core/proto/android/stats/dnsresolver/Android.bp +++ b/tests/BatteryStatsPerfTest/Android.bp @@ -1,4 +1,4 @@ -// Copyright (C) 2019 The Android Open Source Project +// Copyright (C) 2020 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,13 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -java_library_static { - name: "dnsresolverprotosnano", - proto: { - type: "nano", - }, - srcs: [ - "dns_resolver.proto", +android_test { + name: "BatteryStatsPerfTests", + srcs: ["src/**/*.java"], + static_libs: [ + "androidx.test.rules", + "apct-perftests-utils", + "truth-prebuilt", ], - sdk_version: "system_current", + platform_apis: true, + certificate: "platform", } diff --git a/tests/BatteryStatsPerfTest/AndroidManifest.xml b/tests/BatteryStatsPerfTest/AndroidManifest.xml new file mode 100644 index 000000000000..7633d5283f5e --- /dev/null +++ b/tests/BatteryStatsPerfTest/AndroidManifest.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.frameworks.perftests.batterystats"> + <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> + <uses-permission android:name="android.permission.BATTERY_STATS"/> + + <application> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.frameworks.perftests.batterystats"/> +</manifest> diff --git a/tests/BatteryStatsPerfTest/AndroidTest.xml b/tests/BatteryStatsPerfTest/AndroidTest.xml new file mode 100644 index 000000000000..2f9e114756d1 --- /dev/null +++ b/tests/BatteryStatsPerfTest/AndroidTest.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<configuration description="Runs BatteryStats service Performance Tests"> + <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup"> + <option name="test-file-name" value="BatteryStatsPerfTests.apk"/> + <option name="cleanup-apks" value="true"/> + </target_preparer> + + <option name="test-suite-tag" value="apct"/> + <option name="test-tag" value="BatteryStatsPerfTests"/> + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="com.android.frameworks.perftests.batterystats"/> + <option name="runner" value="androidx.test.runner.AndroidJUnitRunner"/> + </test> +</configuration> diff --git a/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryStatsHelperPerfTest.java b/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryStatsHelperPerfTest.java new file mode 100644 index 000000000000..6266cda204b0 --- /dev/null +++ b/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryStatsHelperPerfTest.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.os; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; +import android.os.BatteryStats; +import android.os.Bundle; +import android.os.UserHandle; +import android.perftests.utils.BenchmarkState; +import android.perftests.utils.PerfStatusReporter; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.LargeTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.List; + +@RunWith(AndroidJUnit4.class) +@LargeTest +public class BatteryStatsHelperPerfTest { + + @Rule + public final PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + /** + * Measures the performance of {@link BatteryStatsHelper#getStats()}, which triggers + * a battery stats sync on every iteration. + */ + @Test + public void testGetStats_forceUpdate() { + final Context context = InstrumentationRegistry.getContext(); + final BatteryStatsHelper statsHelper = new BatteryStatsHelper(context, + true /* collectBatteryBroadcast */); + statsHelper.create((Bundle) null); + statsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, UserHandle.myUserId()); + + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + state.pauseTiming(); + statsHelper.clearStats(); + state.resumeTiming(); + + statsHelper.getStats(); + + assertThat(statsHelper.getUsageList()).isNotEmpty(); + } + } + + /** + * Measures performance of the {@link BatteryStatsHelper#getStats(boolean)}, which does + * not trigger a sync and just returns current values. + */ + @Test + public void testGetStats_cached() { + final Context context = InstrumentationRegistry.getContext(); + final BatteryStatsHelper statsHelper = new BatteryStatsHelper(context, + true /* collectBatteryBroadcast */); + statsHelper.create((Bundle) null); + statsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, UserHandle.myUserId()); + + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + state.pauseTiming(); + statsHelper.clearStats(); + state.resumeTiming(); + + statsHelper.getStats(false /* forceUpdate */); + + assertThat(statsHelper.getUsageList()).isNotEmpty(); + } + } + + @Test + public void testPowerCalculation() { + final Context context = InstrumentationRegistry.getContext(); + final BatteryStatsHelper statsHelper = new BatteryStatsHelper(context, + true /* collectBatteryBroadcast */); + statsHelper.create((Bundle) null); + statsHelper.getStats(); + + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + // This will use the cached BatteryStatsObject + statsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, UserHandle.myUserId()); + + assertThat(statsHelper.getUsageList()).isNotEmpty(); + } + } + + @Test + public void testEndToEnd() { + final Context context = InstrumentationRegistry.getContext(); + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + final BatteryStatsHelper statsHelper = new BatteryStatsHelper(context, + true /* collectBatteryBroadcast */); + statsHelper.create((Bundle) null); + statsHelper.clearStats(); + statsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, UserHandle.myUserId()); + + state.pauseTiming(); + + List<BatterySipper> usageList = statsHelper.getUsageList(); + double power = 0; + for (int i = 0; i < usageList.size(); i++) { + BatterySipper sipper = usageList.get(i); + power += sipper.sumPower(); + } + + assertThat(power).isGreaterThan(0.0); + + state.resumeTiming(); + } + } +} diff --git a/tests/BootImageProfileTest/TEST_MAPPING b/tests/BootImageProfileTest/TEST_MAPPING deleted file mode 100644 index 1b569f9455bf..000000000000 --- a/tests/BootImageProfileTest/TEST_MAPPING +++ /dev/null @@ -1,7 +0,0 @@ -{ - "presubmit": [ - { - "name": "BootImageProfileTest" - } - ] -} diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp index 3b9bec9ad0a7..1a83655ed8d3 100644 --- a/tests/FlickerTests/Android.bp +++ b/tests/FlickerTests/Android.bp @@ -59,7 +59,10 @@ android_test { java_library { name: "wm-flicker-common-assertions", platform_apis: true, - srcs: ["src/**/*Assertions.java", "src/**/*Assertions.kt"], + srcs: [ + "src/**/*Assertions.java", + "src/**/*Assertions.kt", + ], exclude_srcs: [ "**/helpers/*", ], @@ -68,4 +71,17 @@ java_library { "truth-prebuilt", "app-helpers-core" ], +} + +java_library { + name: "wm-flicker-common-app-helpers", + platform_apis: true, + srcs: [ + "**/helpers/*" + ], + static_libs: [ + "flickerlib", + "truth-prebuilt", + "app-helpers-core" + ], }
\ No newline at end of file diff --git a/tests/FlickerTests/AndroidManifest.xml b/tests/FlickerTests/AndroidManifest.xml index 98e02ced8d0b..58f56f29528c 100644 --- a/tests/FlickerTests/AndroidManifest.xml +++ b/tests/FlickerTests/AndroidManifest.xml @@ -27,6 +27,8 @@ <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" /> <!-- Enable / Disable tracing !--> <uses-permission android:name="android.permission.DUMP" /> + <!-- Force-stop test apps --> + <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES"/> <!-- Run layers trace --> <uses-permission android:name="android.permission.HARDWARE_TEST"/> <!-- Workaround grant runtime permission exception from b/152733071 --> diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt deleted file mode 100644 index 0572a7813819..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm.flicker.helpers - -import android.app.Instrumentation -import android.support.test.launcherhelper.ILauncherStrategy -import android.support.test.launcherhelper.LauncherStrategyFactory -import androidx.test.uiautomator.By -import androidx.test.uiautomator.UiDevice -import org.junit.Assert - -class PipAppHelper( - instr: Instrumentation, - launcherStrategy: ILauncherStrategy = LauncherStrategyFactory - .getInstance(instr) - .launcherStrategy -) : StandardAppHelper(instr, "PipApp", launcherStrategy) { - fun clickEnterPipButton(device: UiDevice) { - val enterPipButton = device.findObject(By.res(getPackage(), "enter_pip")) - Assert.assertNotNull("Pip button not found, this usually happens when the device " + - "was left in an unknown state (e.g. in split screen)", enterPipButton) - enterPipButton.click() - device.hasPipWindow() - } - - fun closePipWindow(device: UiDevice) { - device.closePipWindow() - } -} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/EnterPipTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/EnterPipTest.kt deleted file mode 100644 index 89539fd1a9ec..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/EnterPipTest.kt +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm.flicker.pip - -import android.platform.test.annotations.Presubmit -import android.view.Surface -import androidx.test.filters.FlakyTest -import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.Flicker -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory -import com.android.server.wm.flicker.helpers.PipAppHelper -import com.android.server.wm.flicker.helpers.buildTestTag -import com.android.server.wm.flicker.helpers.closePipWindow -import com.android.server.wm.flicker.helpers.expandPipWindow -import com.android.server.wm.flicker.helpers.hasPipWindow -import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen -import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible -import com.android.server.wm.flicker.navBarLayerRotatesAndScales -import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible -import com.android.server.wm.flicker.noUncoveredRegions -import com.android.server.wm.flicker.repetitions -import com.android.server.wm.flicker.startRotation -import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible -import com.android.server.wm.flicker.statusBarLayerRotatesScales -import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -/** - * Test Pip launch. - * To run this test: `atest FlickerTests:PipToAppTest` - */ -@Presubmit -@RequiresDevice -@RunWith(Parameterized::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -@FlakyTest(bugId = 152738416) -class EnterPipTest( - testName: String, - flickerSpec: Flicker -) : FlickerTestRunner(testName, flickerSpec) { - companion object { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): List<Array<Any>> { - val instrumentation = InstrumentationRegistry.getInstrumentation() - val testApp = PipAppHelper(instrumentation) - return FlickerTestRunnerFactory(instrumentation, listOf(Surface.ROTATION_0)) - .buildTest { configuration -> - withTestName { buildTestTag("enterPip", testApp, configuration) } - repeat { configuration.repetitions } - setup { - test { - device.wakeUpAndGoToHomeScreen() - } - eachRun { - device.pressHome() - testApp.open() - this.setRotation(configuration.startRotation) - } - } - teardown { - eachRun { - if (device.hasPipWindow()) { - device.closePipWindow() - } - testApp.exit() - this.setRotation(Surface.ROTATION_0) - } - test { - if (device.hasPipWindow()) { - device.closePipWindow() - } - } - } - transitions { - testApp.clickEnterPipButton(device) - device.expandPipWindow() - } - assertions { - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - - all("pipWindowBecomesVisible") { - this.showsAppWindow(testApp.`package`) - .then() - .showsAppWindow(PIP_WINDOW_TITLE) - } - } - - layersTrace { - navBarLayerIsAlwaysVisible(bugId = 140855415) - statusBarLayerIsAlwaysVisible() - noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0, - enabled = false) - navBarLayerRotatesAndScales(configuration.startRotation, - Surface.ROTATION_0, bugId = 140855415) - statusBarLayerRotatesScales(configuration.startRotation, - Surface.ROTATION_0) - } - - layersTrace { - all("pipLayerBecomesVisible") { - this.showsLayer(testApp.launcherName) - .then() - .showsLayer(PIP_WINDOW_TITLE) - } - } - } - } - } - } -}
\ No newline at end of file diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml index 4d2144061ad4..1599ed4b280f 100644 --- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml +++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml @@ -49,18 +49,6 @@ <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> - <activity android:name=".PipActivity" - android:resizeableActivity="true" - android:supportsPictureInPicture="true" - android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation" - android:taskAffinity="com.android.server.wm.flicker.testapp.PipActivity" - android:label="PipApp" - android:exported="true"> - <intent-filter> - <action android:name="android.intent.action.MAIN"/> - <category android:name="android.intent.category.LAUNCHER"/> - </intent-filter> - </activity> <activity android:name=".SeamlessRotationActivity" android:taskAffinity="com.android.server.wm.flicker.testapp.SeamlessRotationActivity" android:configChanges="orientation|screenSize" diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java index fa0574a503f1..9738e58543e1 100644 --- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java +++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java @@ -1064,6 +1064,31 @@ public class PackageWatchdogTest { } /** + * Ensure that the correct mitigation counts are sent to the boot loop observer. + */ + @Test + public void testMultipleBootLoopMitigation() { + PackageWatchdog watchdog = createWatchdog(); + TestObserver bootObserver = new TestObserver(OBSERVER_NAME_1); + watchdog.registerHealthObserver(bootObserver); + for (int i = 0; i < 4; i++) { + for (int j = 0; j < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; j++) { + watchdog.noteBoot(); + } + } + + moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_DEESCALATION_WINDOW_MS + 1); + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; j++) { + watchdog.noteBoot(); + } + } + + assertThat(bootObserver.mBootMitigationCounts).isEqualTo(List.of(1, 2, 3, 4, 1, 2, 3, 4)); + } + + /** * Ensure that passing a null list of failed packages does not cause any mitigation logic to * execute. */ @@ -1267,6 +1292,7 @@ public class PackageWatchdogTest { final List<String> mHealthCheckFailedPackages = new ArrayList<>(); final List<String> mMitigatedPackages = new ArrayList<>(); final List<Integer> mMitigationCounts = new ArrayList<>(); + final List<Integer> mBootMitigationCounts = new ArrayList<>(); TestObserver(String name) { mName = name; @@ -1304,12 +1330,13 @@ public class PackageWatchdogTest { return mMayObservePackages; } - public int onBootLoop() { + public int onBootLoop(int level) { return mImpact; } - public boolean executeBootLoopMitigation() { + public boolean executeBootLoopMitigation(int level) { mMitigatedBootLoop = true; + mBootMitigationCounts.add(level); return true; } diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java index 52718bec9148..3d8deb5cfc8d 100644 --- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java +++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java @@ -34,7 +34,6 @@ import androidx.test.platform.app.InstrumentationRegistry; import com.android.cts.install.lib.Install; import com.android.cts.install.lib.InstallUtils; -import com.android.cts.install.lib.LocalIntentSender; import com.android.cts.install.lib.TestApp; import com.android.cts.install.lib.Uninstall; import com.android.cts.rollback.lib.Rollback; @@ -258,10 +257,6 @@ public class StagedRollbackTest { .getPackageManager().getPackageInstaller(); pi.abandonSession(sessionId); - // Remove the first intent sender result, so that the next staged install session does not - // erroneously think that it has itself been abandoned. - // TODO(b/136260017): Restructure LocalIntentSender to negate the need for this step. - LocalIntentSender.getIntentSenderResult(); Install.single(TestApp.A2).setStaged().setEnableRollback().commit(); } diff --git a/tests/SurfaceViewBufferTests/Android.bp b/tests/SurfaceViewBufferTests/Android.bp index 647da2abd213..48031de15f54 100644 --- a/tests/SurfaceViewBufferTests/Android.bp +++ b/tests/SurfaceViewBufferTests/Android.bp @@ -33,6 +33,8 @@ android_test { "kotlinx-coroutines-android", "flickerlib", "truth-prebuilt", + "cts-wm-util", + "CtsSurfaceValidatorLib", ], } @@ -43,6 +45,7 @@ cc_library_shared { ], shared_libs: [ "libutils", + "libui", "libgui", "liblog", "libandroid", diff --git a/tests/SurfaceViewBufferTests/AndroidManifest.xml b/tests/SurfaceViewBufferTests/AndroidManifest.xml index 95885c1ca635..c910ecdac1b3 100644 --- a/tests/SurfaceViewBufferTests/AndroidManifest.xml +++ b/tests/SurfaceViewBufferTests/AndroidManifest.xml @@ -23,12 +23,19 @@ <uses-permission android:name="android.permission.DUMP" /> <!-- Enable / Disable sv blast adapter !--> <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> + <!-- Readback virtual display output !--> + <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"/> + <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> + <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/> + <!-- Save failed test bitmap images !--> + <uses-permission android:name="android.Manifest.permission.WRITE_EXTERNAL_STORAGE"/> <application android:allowBackup="false" android:supportsRtl="true"> <activity android:name=".MainActivity" android:taskAffinity="com.android.test.MainActivity" android:theme="@style/AppTheme" + android:configChanges="orientation|screenSize" android:label="SurfaceViewBufferTestApp" android:exported="true"> <intent-filter> @@ -36,6 +43,10 @@ <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> + <service android:name="android.view.cts.surfacevalidator.LocalMediaProjectionService" + android:foregroundServiceType="mediaProjection" + android:enabled="true"> + </service> <uses-library android:name="android.test.runner"/> </application> diff --git a/tests/SurfaceViewBufferTests/cpp/SurfaceProxy.cpp b/tests/SurfaceViewBufferTests/cpp/SurfaceProxy.cpp index 0c86524293e7..ce226fdce320 100644 --- a/tests/SurfaceViewBufferTests/cpp/SurfaceProxy.cpp +++ b/tests/SurfaceViewBufferTests/cpp/SurfaceProxy.cpp @@ -32,6 +32,7 @@ extern "C" { int i = 0; static ANativeWindow* sAnw; +static std::map<uint32_t /* slot */, ANativeWindowBuffer*> sBuffers; JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_setSurface(JNIEnv* env, jclass, jobject surfaceObject) { @@ -39,11 +40,14 @@ JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_setSurface(JNIEnv* env assert(sAnw); android::sp<android::Surface> surface = static_cast<android::Surface*>(sAnw); surface->enableFrameTimestamps(true); + surface->connect(NATIVE_WINDOW_API_CPU, nullptr, false); + native_window_set_usage(sAnw, GRALLOC_USAGE_SW_WRITE_OFTEN); + native_window_set_buffers_format(sAnw, HAL_PIXEL_FORMAT_RGBA_8888); return 0; } JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_waitUntilBufferDisplayed( - JNIEnv*, jclass, jint jFrameNumber, jint timeoutSec) { + JNIEnv*, jclass, jlong jFrameNumber, jint timeoutMs) { using namespace std::chrono_literals; assert(sAnw); android::sp<android::Surface> surface = static_cast<android::Surface*>(sAnw); @@ -63,8 +67,8 @@ JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_waitUntilBufferDisplay &outDisplayPresentTime, &outDequeueReadyTime, &outReleaseTime); if (outDisplayPresentTime < 0) { auto end = std::chrono::steady_clock::now(); - if (std::chrono::duration_cast<std::chrono::seconds>(end - start).count() > - timeoutSec) { + if (std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() > + timeoutMs) { return -1; } } @@ -99,4 +103,121 @@ JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_ANativeWindowSetBuffer assert(sAnw); return ANativeWindow_setBuffersGeometry(sAnw, w, h, format); } + +JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_ANativeWindowSetBuffersTransform( + JNIEnv* /* env */, jclass /* clazz */, jint transform) { + assert(sAnw); + return native_window_set_buffers_transform(sAnw, transform); +} + +JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_SurfaceSetScalingMode(JNIEnv* /* env */, + jclass /* clazz */, + jint scalingMode) { + assert(sAnw); + android::sp<android::Surface> surface = static_cast<android::Surface*>(sAnw); + return surface->setScalingMode(scalingMode); +} + +JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_SurfaceDequeueBuffer(JNIEnv* /* env */, + jclass /* clazz */, + jint slot, + jint timeoutMs) { + assert(sAnw); + ANativeWindowBuffer* anb; + int fenceFd; + int result = sAnw->dequeueBuffer(sAnw, &anb, &fenceFd); + if (result != android::OK) { + return result; + } + sBuffers[slot] = anb; + android::sp<android::Fence> fence(new android::Fence(fenceFd)); + int waitResult = fence->wait(timeoutMs); + if (waitResult != android::OK) { + sAnw->cancelBuffer(sAnw, sBuffers[slot], -1); + sBuffers[slot] = nullptr; + return waitResult; + } + return 0; +} + +JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_SurfaceCancelBuffer(JNIEnv* /* env */, + jclass /* clazz */, + jint slot) { + assert(sAnw); + assert(sBuffers[slot]); + int result = sAnw->cancelBuffer(sAnw, sBuffers[slot], -1); + sBuffers[slot] = nullptr; + return result; +} + +JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_drawBuffer(JNIEnv* env, + jclass /* clazz */, jint slot, + jintArray jintArrayColor) { + assert(sAnw); + assert(sBuffers[slot]); + + int* color = env->GetIntArrayElements(jintArrayColor, nullptr); + + ANativeWindowBuffer* buffer = sBuffers[slot]; + android::sp<android::GraphicBuffer> graphicBuffer(static_cast<android::GraphicBuffer*>(buffer)); + const android::Rect bounds(buffer->width, buffer->height); + android::Region newDirtyRegion; + newDirtyRegion.set(bounds); + + void* vaddr; + int fenceFd = -1; + graphicBuffer->lockAsync(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, + newDirtyRegion.bounds(), &vaddr, fenceFd); + + for (int32_t row = 0; row < buffer->height; row++) { + uint8_t* dst = static_cast<uint8_t*>(vaddr) + (buffer->stride * row) * 4; + for (int32_t column = 0; column < buffer->width; column++) { + dst[0] = color[0]; + dst[1] = color[1]; + dst[2] = color[2]; + dst[3] = color[3]; + dst += 4; + } + } + graphicBuffer->unlockAsync(&fenceFd); + env->ReleaseIntArrayElements(jintArrayColor, color, JNI_ABORT); + return 0; +} + +JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_SurfaceQueueBuffer(JNIEnv* /* env */, + jclass /* clazz */, + jint slot, + jboolean freeSlot) { + assert(sAnw); + assert(sBuffers[slot]); + int result = sAnw->queueBuffer(sAnw, sBuffers[slot], -1); + if (freeSlot) { + sBuffers[slot] = nullptr; + } + return result; +} + +JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_NativeWindowSetBufferCount( + JNIEnv* /* env */, jclass /* clazz */, jint count) { + assert(sAnw); + android::sp<android::Surface> surface = static_cast<android::Surface*>(sAnw); + int result = native_window_set_buffer_count(sAnw, count); + return result; +} + +JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_NativeWindowSetSharedBufferMode( + JNIEnv* /* env */, jclass /* clazz */, jboolean shared) { + assert(sAnw); + android::sp<android::Surface> surface = static_cast<android::Surface*>(sAnw); + int result = native_window_set_shared_buffer_mode(sAnw, shared); + return result; +} + +JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_NativeWindowSetAutoRefresh( + JNIEnv* /* env */, jclass /* clazz */, jboolean autoRefresh) { + assert(sAnw); + android::sp<android::Surface> surface = static_cast<android::Surface*>(sAnw); + int result = native_window_set_auto_refresh(sAnw, autoRefresh); + return result; +} }
\ No newline at end of file diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/BufferPresentationTests.kt b/tests/SurfaceViewBufferTests/src/com/android/test/BufferPresentationTests.kt new file mode 100644 index 000000000000..eb16bad81d19 --- /dev/null +++ b/tests/SurfaceViewBufferTests/src/com/android/test/BufferPresentationTests.kt @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.test + +import com.android.server.wm.flicker.traces.layers.LayersTraceSubject.Companion.assertThat +import junit.framework.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized + +@RunWith(Parameterized::class) +class BufferPresentationTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase(useBlastAdapter) { + /** Submit buffers as fast as possible and make sure they are presented on display */ + @Test + fun testQueueBuffers() { + val numFrames = 100L + val trace = withTrace { + for (i in 1..numFrames) { + it.mSurfaceProxy.ANativeWindowLock() + it.mSurfaceProxy.ANativeWindowUnlockAndPost() + } + assertEquals(0, it.mSurfaceProxy.waitUntilBufferDisplayed(numFrames, 1000 /* ms */)) + } + + assertThat(trace).hasFrameSequence("SurfaceView", 1..numFrames) + } + + @Test + fun testSetBufferScalingMode_outOfOrderQueueBuffer() { + val trace = withTrace { + assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1000 /* ms */)) + assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(1, 1000 /* ms */)) + + it.mSurfaceProxy.SurfaceQueueBuffer(1) + it.mSurfaceProxy.SurfaceQueueBuffer(0) + assertEquals(0, it.mSurfaceProxy.waitUntilBufferDisplayed(2, 5000 /* ms */)) + } + + assertThat(trace).hasFrameSequence("SurfaceView", 1..2L) + } + + @Test + fun testSetBufferScalingMode_multipleDequeueBuffer() { + val numFrames = 20L + val trace = withTrace { + for (count in 1..(numFrames / 2)) { + assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1000 /* ms */)) + assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(1, 1000 /* ms */)) + + it.mSurfaceProxy.SurfaceQueueBuffer(0) + it.mSurfaceProxy.SurfaceQueueBuffer(1) + } + assertEquals(0, it.mSurfaceProxy.waitUntilBufferDisplayed(numFrames, 5000 /* ms */)) + } + + assertThat(trace).hasFrameSequence("SurfaceView", 1..numFrames) + } + + @Test + fun testSetBufferCount_queueMaxBufferCountMinusOne() { + val numBufferCount = 8 + val numFrames = numBufferCount * 5L + val trace = withTrace { + assertEquals(0, it.mSurfaceProxy.NativeWindowSetBufferCount(numBufferCount + 1)) + for (i in 1..numFrames / numBufferCount) { + for (bufferSlot in 0..numBufferCount - 1) { + assertEquals(0, + it.mSurfaceProxy.SurfaceDequeueBuffer(bufferSlot, 1000 /* ms */)) + } + + for (bufferSlot in 0..numBufferCount - 1) { + it.mSurfaceProxy.SurfaceQueueBuffer(bufferSlot) + } + } + assertEquals(0, it.mSurfaceProxy.waitUntilBufferDisplayed(numFrames, 5000 /* ms */)) + } + + assertThat(trace).hasFrameSequence("SurfaceView", 1..numFrames) + } +}
\ No newline at end of file diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/BufferRejectionTests.kt b/tests/SurfaceViewBufferTests/src/com/android/test/BufferRejectionTests.kt new file mode 100644 index 000000000000..95a7fd5b7a39 --- /dev/null +++ b/tests/SurfaceViewBufferTests/src/com/android/test/BufferRejectionTests.kt @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.test + +import android.graphics.Point +import com.android.server.wm.flicker.traces.layers.LayersTraceSubject.Companion.assertThat +import com.android.test.SurfaceViewBufferTestBase.Companion.ScalingMode +import com.android.test.SurfaceViewBufferTestBase.Companion.Transform +import junit.framework.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized + +@RunWith(Parameterized::class) +class BufferRejectionTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase(useBlastAdapter) { + @Test + fun testSetBuffersGeometry_0x0_rejectsBuffer() { + val trace = withTrace { + it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, 100, 100, + R8G8B8A8_UNORM) + it.mSurfaceProxy.ANativeWindowLock() + it.mSurfaceProxy.ANativeWindowUnlockAndPost() + it.mSurfaceProxy.ANativeWindowLock() + it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, 0, 0, R8G8B8A8_UNORM) + // Submit buffer one with a different size which should be rejected + it.mSurfaceProxy.ANativeWindowUnlockAndPost() + + // submit a buffer with the default buffer size + it.mSurfaceProxy.ANativeWindowLock() + it.mSurfaceProxy.ANativeWindowUnlockAndPost() + it.mSurfaceProxy.waitUntilBufferDisplayed(3, 500 /* ms */) + } + // Verify we reject buffers since scaling mode == NATIVE_WINDOW_SCALING_MODE_FREEZE + assertThat(trace).layer("SurfaceView", 2).doesNotExist() + + // Verify the next buffer is submitted with the correct size + assertThat(trace).layer("SurfaceView", 3).also { + it.hasBufferSize(defaultBufferSize) + // scaling mode is not passed down to the layer for blast + if (useBlastAdapter) { + it.hasScalingMode(ScalingMode.SCALE_TO_WINDOW.ordinal) + } else { + it.hasScalingMode(ScalingMode.FREEZE.ordinal) + } + } + } + + @Test + fun testSetBufferScalingMode_freeze() { + val bufferSize = Point(300, 200) + val trace = withTrace { + it.drawFrame() + assertEquals(it.mSurfaceProxy.waitUntilBufferDisplayed(1, 500 /* ms */), 0) + it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, bufferSize, + R8G8B8A8_UNORM) + assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1000 /* ms */)) + assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(1, 1000 /* ms */)) + // Change buffer size and set scaling mode to freeze + it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, Point(0, 0), + R8G8B8A8_UNORM) + + // first dequeued buffer does not have the new size so it should be rejected. + it.mSurfaceProxy.SurfaceQueueBuffer(0) + it.mSurfaceProxy.SurfaceSetScalingMode(ScalingMode.SCALE_TO_WINDOW) + it.mSurfaceProxy.SurfaceQueueBuffer(1) + assertEquals(it.mSurfaceProxy.waitUntilBufferDisplayed(3, 500 /* ms */), 0) + } + + // verify buffer size is reset to default buffer size + assertThat(trace).layer("SurfaceView", 1).hasBufferSize(defaultBufferSize) + assertThat(trace).layer("SurfaceView", 2).doesNotExist() + assertThat(trace).layer("SurfaceView", 3).hasBufferSize(bufferSize) + } + + @Test + fun testSetBufferScalingMode_freeze_withBufferRotation() { + val rotatedBufferSize = Point(defaultBufferSize.y, defaultBufferSize.x) + val trace = withTrace { + it.drawFrame() + assertEquals(it.mSurfaceProxy.waitUntilBufferDisplayed(1, 500 /* ms */), 0) + it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, rotatedBufferSize, + R8G8B8A8_UNORM) + assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1000 /* ms */)) + assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(1, 1000 /* ms */)) + // Change buffer size and set scaling mode to freeze + it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, Point(0, 0), + R8G8B8A8_UNORM) + + // first dequeued buffer does not have the new size so it should be rejected. + it.mSurfaceProxy.SurfaceQueueBuffer(0) + // add a buffer transform so the buffer size is correct. + it.mSurfaceProxy.ANativeWindowSetBuffersTransform(Transform.ROT_90) + it.mSurfaceProxy.SurfaceQueueBuffer(1) + assertEquals(it.mSurfaceProxy.waitUntilBufferDisplayed(3, 500 /* ms */), 0) + } + + // verify buffer size is reset to default buffer size + assertThat(trace).layer("SurfaceView", 1).hasBufferSize(defaultBufferSize) + assertThat(trace).layer("SurfaceView", 2).doesNotExist() + assertThat(trace).layer("SurfaceView", 3).hasBufferSize(rotatedBufferSize) + assertThat(trace).layer("SurfaceView", 3).hasBufferOrientation(Transform.ROT_90.value) + } + + @Test + fun testRejectedBuffersAreReleased() { + val bufferSize = Point(300, 200) + val trace = withTrace { + for (count in 0 until 5) { + it.drawFrame() + assertEquals(it.mSurfaceProxy.waitUntilBufferDisplayed((count * 3) + 1L, + 500 /* ms */), 0) + it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, bufferSize, + R8G8B8A8_UNORM) + assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1000 /* ms */)) + assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(1, 1000 /* ms */)) + // Change buffer size and set scaling mode to freeze + it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, Point(0, 0), + R8G8B8A8_UNORM) + + // first dequeued buffer does not have the new size so it should be rejected. + it.mSurfaceProxy.SurfaceQueueBuffer(0) + it.mSurfaceProxy.SurfaceSetScalingMode(ScalingMode.SCALE_TO_WINDOW) + it.mSurfaceProxy.SurfaceQueueBuffer(1) + assertEquals(it.mSurfaceProxy.waitUntilBufferDisplayed((count * 3) + 3L, + 500 /* ms */), 0) + } + } + + for (count in 0 until 5) { + assertThat(trace).layer("SurfaceView", (count * 3) + 1L) + .hasBufferSize(defaultBufferSize) + assertThat(trace).layer("SurfaceView", (count * 3) + 2L) + .doesNotExist() + assertThat(trace).layer("SurfaceView", (count * 3) + 3L) + .hasBufferSize(bufferSize) + } + } +}
\ No newline at end of file diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/GeometryTests.kt b/tests/SurfaceViewBufferTests/src/com/android/test/GeometryTests.kt new file mode 100644 index 000000000000..03f8c05346b7 --- /dev/null +++ b/tests/SurfaceViewBufferTests/src/com/android/test/GeometryTests.kt @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.test + +import android.graphics.Point +import com.android.server.wm.flicker.traces.layers.LayersTraceSubject.Companion.assertThat +import com.android.test.SurfaceViewBufferTestBase.Companion.ScalingMode +import com.android.test.SurfaceViewBufferTestBase.Companion.Transform +import junit.framework.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized + +@RunWith(Parameterized::class) +class GeometryTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase(useBlastAdapter) { + @Test + fun testSetBuffersGeometry_0x0_resetsBufferSize() { + val trace = withTrace { + it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, 0, 0, + R8G8B8A8_UNORM) + it.mSurfaceProxy.ANativeWindowLock() + it.mSurfaceProxy.ANativeWindowUnlockAndPost() + it.mSurfaceProxy.waitUntilBufferDisplayed(1, 500 /* ms */) + } + + // verify buffer size is reset to default buffer size + assertThat(trace).layer("SurfaceView", 1).hasBufferSize(defaultBufferSize) + } + + @Test + fun testSetBuffersGeometry_smallerThanBuffer() { + val bufferSize = Point(300, 200) + val trace = withTrace { + it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, bufferSize, + R8G8B8A8_UNORM) + it.drawFrame() + it.mSurfaceProxy.waitUntilBufferDisplayed(1, 500 /* ms */) + } + + assertThat(trace).layer("SurfaceView", 1).also { + it.hasBufferSize(bufferSize) + it.hasLayerSize(defaultBufferSize) + it.hasScalingMode(ScalingMode.SCALE_TO_WINDOW.ordinal) + } + } + + @Test + fun testSetBuffersGeometry_largerThanBuffer() { + val bufferSize = Point(3000, 2000) + val trace = withTrace { + it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, bufferSize, + R8G8B8A8_UNORM) + it.drawFrame() + it.mSurfaceProxy.waitUntilBufferDisplayed(1, 500 /* ms */) + } + + assertThat(trace).layer("SurfaceView", 1).also { + it.hasBufferSize(bufferSize) + it.hasLayerSize(defaultBufferSize) + it.hasScalingMode(ScalingMode.SCALE_TO_WINDOW.ordinal) + } + } + + @Test + fun testSetBufferScalingMode_freeze() { + val bufferSize = Point(300, 200) + val trace = withTrace { + it.drawFrame() + assertEquals(it.mSurfaceProxy.waitUntilBufferDisplayed(1, 500 /* ms */), 0) + it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, bufferSize, + R8G8B8A8_UNORM) + assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1000 /* ms */)) + assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(1, 1000 /* ms */)) + // Change buffer size and set scaling mode to freeze + it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, Point(0, 0), + R8G8B8A8_UNORM) + + // first dequeued buffer does not have the new size so it should be rejected. + it.mSurfaceProxy.SurfaceQueueBuffer(0) + it.mSurfaceProxy.SurfaceSetScalingMode(ScalingMode.SCALE_TO_WINDOW) + it.mSurfaceProxy.SurfaceQueueBuffer(1) + assertEquals(it.mSurfaceProxy.waitUntilBufferDisplayed(3, 500 /* ms */), 0) + } + + // verify buffer size is reset to default buffer size + assertThat(trace).layer("SurfaceView", 1).hasBufferSize(defaultBufferSize) + assertThat(trace).layer("SurfaceView", 2).doesNotExist() + assertThat(trace).layer("SurfaceView", 3).hasBufferSize(bufferSize) + } + + @Test + fun testSetBuffersTransform_FLIP() { + val transforms = arrayOf(Transform.FLIP_H, Transform.FLIP_V, Transform.ROT_180).withIndex() + for ((index, transform) in transforms) { + val trace = withTrace { + it.mSurfaceProxy.ANativeWindowSetBuffersTransform(transform) + it.mSurfaceProxy.ANativeWindowLock() + it.mSurfaceProxy.ANativeWindowUnlockAndPost() + it.mSurfaceProxy.waitUntilBufferDisplayed(index + 1L, 500 /* ms */) + } + + assertThat(trace).layer("SurfaceView", index + 1L).also { + it.hasBufferSize(defaultBufferSize) + it.hasLayerSize(defaultBufferSize) + it.hasBufferOrientation(transform.value) + } + } + } +}
\ No newline at end of file diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/InverseDisplayTransformTests.kt b/tests/SurfaceViewBufferTests/src/com/android/test/InverseDisplayTransformTests.kt new file mode 100644 index 000000000000..eac30417dfae --- /dev/null +++ b/tests/SurfaceViewBufferTests/src/com/android/test/InverseDisplayTransformTests.kt @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.test + +import android.graphics.Point +import com.android.server.wm.flicker.traces.layers.LayersTraceSubject.Companion.assertThat +import com.android.test.SurfaceViewBufferTestBase.Companion.Transform +import junit.framework.Assert.assertEquals +import org.junit.Assume.assumeFalse +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized + +@RunWith(Parameterized::class) +class InverseDisplayTransformTests(useBlastAdapter: Boolean) : + SurfaceTracingTestBase(useBlastAdapter) { + @Before + override fun setup() { + scenarioRule.getScenario().onActivity { + it.rotate90() + } + instrumentation.waitForIdleSync() + super.setup() + } + + @Test + fun testSetBufferScalingMode_freeze_withInvDisplayTransform() { + assumeFalse("Blast does not support buffer rejection with Inv display " + + "transform since the only user for this hidden api is camera which does not use" + + "fixed scaling mode.", useBlastAdapter) + + val rotatedBufferSize = Point(defaultBufferSize.y, defaultBufferSize.x) + val trace = withTrace { + // Inverse display transforms are sticky AND they are only consumed by the sf after + // a valid buffer has been acquired. + it.mSurfaceProxy.ANativeWindowSetBuffersTransform(Transform.INVERSE_DISPLAY.value) + assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1000 /* ms */)) + it.mSurfaceProxy.SurfaceQueueBuffer(0) + + assertEquals(it.mSurfaceProxy.waitUntilBufferDisplayed(1, 500 /* ms */), 0) + it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, rotatedBufferSize, + R8G8B8A8_UNORM) + assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1000 /* ms */)) + assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(1, 1000 /* ms */)) + // Change buffer size and set scaling mode to freeze + it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, Point(0, 0), + R8G8B8A8_UNORM) + + // first dequeued buffer does not have the new size so it should be rejected. + it.mSurfaceProxy.ANativeWindowSetBuffersTransform(Transform.ROT_90.value) + it.mSurfaceProxy.SurfaceQueueBuffer(0) + it.mSurfaceProxy.ANativeWindowSetBuffersTransform(0) + it.mSurfaceProxy.SurfaceQueueBuffer(1) + assertEquals(it.mSurfaceProxy.waitUntilBufferDisplayed(3, 500 /* ms */), 0) + } + + // verify buffer size is reset to default buffer size + assertThat(trace).layer("SurfaceView", 1).hasBufferSize(defaultBufferSize) + assertThat(trace).layer("SurfaceView", 2).doesNotExist() + assertThat(trace).layer("SurfaceView", 3).hasBufferSize(rotatedBufferSize) + } +}
\ No newline at end of file diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/MainActivity.kt b/tests/SurfaceViewBufferTests/src/com/android/test/MainActivity.kt index b1e1336c4f6d..ed79054409ea 100644 --- a/tests/SurfaceViewBufferTests/src/com/android/test/MainActivity.kt +++ b/tests/SurfaceViewBufferTests/src/com/android/test/MainActivity.kt @@ -15,51 +15,80 @@ */ package com.android.test -import android.app.Activity import android.content.Context +import android.content.pm.ActivityInfo +import android.content.res.Configuration.ORIENTATION_LANDSCAPE import android.graphics.Color import android.graphics.Paint +import android.graphics.Point import android.graphics.Rect import android.os.Bundle import android.view.Gravity import android.view.Surface import android.view.SurfaceHolder import android.view.SurfaceView +import android.view.View +import android.view.WindowManager +import android.view.cts.surfacevalidator.CapturedActivity import android.widget.FrameLayout import java.util.concurrent.CountDownLatch import java.util.concurrent.locks.ReentrantLock import kotlin.concurrent.withLock -class MainActivity : Activity() { +class MainActivity : CapturedActivity() { val mSurfaceProxy = SurfaceProxy() private var mSurfaceHolder: SurfaceHolder? = null private val mDrawLock = ReentrantLock() + var mSurfaceView: SurfaceView? = null val surface: Surface? get() = mSurfaceHolder?.surface - public override fun onCreate(savedInstanceState: Bundle?) { + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - addSurfaceView(Rect(0, 0, 500, 200)) + addSurfaceView(Point(500, 200)) + window.decorView.apply { + systemUiVisibility = + View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_FULLSCREEN + } + } + + override fun getCaptureDurationMs(): Long { + return 30000 } - fun addSurfaceView(size: Rect): CountDownLatch { + fun addSurfaceView(size: Point): CountDownLatch { val layout = findViewById<FrameLayout>(android.R.id.content) val surfaceReadyLatch = CountDownLatch(1) - val surfaceView = createSurfaceView(applicationContext, size, surfaceReadyLatch) - layout.addView(surfaceView, - FrameLayout.LayoutParams(size.width(), size.height(), Gravity.TOP or Gravity.LEFT) + mSurfaceView = createSurfaceView(applicationContext, size, surfaceReadyLatch) + layout.addView(mSurfaceView!!, + FrameLayout.LayoutParams(size.x, size.y, Gravity.TOP or Gravity.LEFT) .also { it.setMargins(100, 100, 0, 0) }) + return surfaceReadyLatch } + fun enableSeamlessRotation() { + val p: WindowManager.LayoutParams = window.attributes + p.rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS + window.attributes = p + } + + fun rotate90() { + if (getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE) { + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) + } else { + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) + } + } + private fun createSurfaceView( context: Context, - size: Rect, + size: Point, surfaceReadyLatch: CountDownLatch ): SurfaceView { val surfaceView = SurfaceView(context) surfaceView.setWillNotDraw(false) - surfaceView.holder.setFixedSize(size.width(), size.height()) + surfaceView.holder.setFixedSize(size.x, size.y) surfaceView.holder.addCallback(object : SurfaceHolder.Callback { override fun surfaceCreated(holder: SurfaceHolder) { mDrawLock.withLock { diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/ScreenRecordTestBase.kt b/tests/SurfaceViewBufferTests/src/com/android/test/ScreenRecordTestBase.kt new file mode 100644 index 000000000000..df3d30e13908 --- /dev/null +++ b/tests/SurfaceViewBufferTests/src/com/android/test/ScreenRecordTestBase.kt @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.test + +import android.annotation.ColorInt +import android.content.Context +import android.content.Intent +import android.graphics.Rect +import android.server.wm.WindowManagerState.getLogicalDisplaySize +import android.view.cts.surfacevalidator.CapturedActivity +import android.view.cts.surfacevalidator.ISurfaceValidatorTestCase +import android.view.cts.surfacevalidator.PixelChecker +import android.view.cts.surfacevalidator.RectChecker +import android.widget.FrameLayout +import androidx.test.rule.ActivityTestRule +import org.junit.After +import org.junit.Before +import org.junit.Rule +import java.util.concurrent.CountDownLatch + +open class ScreenRecordTestBase(useBlastAdapter: Boolean) : + SurfaceViewBufferTestBase(useBlastAdapter) { + @get:Rule + var mActivityRule = ActivityTestRule(MainActivity::class.java) + + private lateinit var mActivity: MainActivity + + @Before + override fun setup() { + super.setup() + mActivity = mActivityRule.launchActivity(Intent()) + lateinit var surfaceReadyLatch: CountDownLatch + runOnUiThread { + it.dismissPermissionDialog() + it.setLogicalDisplaySize(getLogicalDisplaySize()) + surfaceReadyLatch = it.addSurfaceView(defaultBufferSize) + } + surfaceReadyLatch.await() + // sleep to finish animations + instrumentation.waitForIdleSync() + } + + @After + override fun teardown() { + super.teardown() + mActivityRule.finishActivity() + } + + fun runOnUiThread(predicate: (it: MainActivity) -> Unit) { + mActivityRule.runOnUiThread { + predicate(mActivity) + } + } + + fun withScreenRecording( + boundsToCheck: Rect, + @ColorInt color: Int, + predicate: (it: MainActivity) -> Unit + ): CapturedActivity.TestResult { + val testCase = object : ISurfaceValidatorTestCase { + override fun getChecker(): PixelChecker = RectChecker(boundsToCheck, color) + override fun start(context: Context, parent: FrameLayout) { + predicate(mActivity) + } + override fun end() { /* do nothing */ } + } + + return mActivity.runTest(testCase) + } +}
\ No newline at end of file diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeScreenRecordTests.kt b/tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeScreenRecordTests.kt new file mode 100644 index 000000000000..996a1d3d79da --- /dev/null +++ b/tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeScreenRecordTests.kt @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.test + +import android.graphics.Color +import android.graphics.Rect +import android.os.SystemClock +import android.view.cts.surfacevalidator.PixelColor +import junit.framework.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized + +@RunWith(Parameterized::class) +class SharedBufferModeScreenRecordTests(useBlastAdapter: Boolean) : + ScreenRecordTestBase(useBlastAdapter) { + + /** When auto refresh is set, surface flinger will wake up and refresh the display presenting + * the latest content in the buffer. + */ + @Test + fun testAutoRefresh() { + var svBounds = Rect() + runOnUiThread { + assertEquals(0, it.mSurfaceProxy.NativeWindowSetSharedBufferMode(true)) + assertEquals(0, it.mSurfaceProxy.NativeWindowSetAutoRefresh(true)) + assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1 /* ms */)) + it.mSurfaceProxy.SurfaceQueueBuffer(0, false /* freeSlot */) + assertEquals(0, + it.mSurfaceProxy.waitUntilBufferDisplayed(1, 5000 /* ms */)) + + svBounds = Rect(0, 0, it.mSurfaceView!!.width, it.mSurfaceView!!.height) + val position = Rect() + it.mSurfaceView!!.getBoundsOnScreen(position) + svBounds.offsetTo(position.left, position.top) + + // wait for buffers from other layers to be latched and transactions to be processed before + // updating the buffer + SystemClock.sleep(4000) + } + + val result = withScreenRecording(svBounds, PixelColor.RED) { + it.mSurfaceProxy.drawBuffer(0, Color.RED) + } + val failRatio = 1.0f * result.failFrames / (result.failFrames + result.passFrames) + + assertTrue("Error: " + result.failFrames + + " incorrect frames observed (out of " + (result.failFrames + result.passFrames) + + " frames)", failRatio < 0.05) + assertTrue("Error: Did not receive sufficient frame updates expected: >1000 actual:" + + result.passFrames, result.passFrames > 1000) + } +}
\ No newline at end of file diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeTests.kt b/tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeTests.kt new file mode 100644 index 000000000000..ae662506bc77 --- /dev/null +++ b/tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeTests.kt @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.test + +import android.graphics.Color +import android.graphics.Rect +import com.android.server.wm.flicker.traces.layers.LayersTraceSubject.Companion.assertThat +import junit.framework.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized + +@RunWith(Parameterized::class) +class SharedBufferModeTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase(useBlastAdapter) { + /** Sanity test to check each buffer is presented if its submitted with enough delay + * for SF to present the buffers. */ + @Test + fun testCanPresentBuffers() { + val numFrames = 15L + val trace = withTrace { + assertEquals(0, it.mSurfaceProxy.NativeWindowSetSharedBufferMode(true)) + for (i in 1..numFrames) { + assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1 /* ms */)) + it.mSurfaceProxy.SurfaceQueueBuffer(0) + assertEquals(0, it.mSurfaceProxy.waitUntilBufferDisplayed(i, 5000 /* ms */)) + } + } + + assertThat(trace).hasFrameSequence("SurfaceView", 1..numFrames) + } + + /** Submit buffers as fast as possible testing that we are not blocked when dequeuing the buffer + * by setting the dequeue timeout to 1ms and checking that we present the newest buffer. */ + @Test + fun testFastQueueBuffers() { + val numFrames = 15L + val trace = withTrace { + assertEquals(0, it.mSurfaceProxy.NativeWindowSetSharedBufferMode(true)) + for (i in 1..numFrames) { + assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1 /* ms */)) + it.mSurfaceProxy.SurfaceQueueBuffer(0) + } + assertEquals(0, it.mSurfaceProxy.waitUntilBufferDisplayed(numFrames, 5000 /* ms */)) + } + + assertThat(trace).hasFrameSequence("SurfaceView", numFrames..numFrames) + } + + /** Keep overwriting the buffer without queuing buffers and check that we present the latest + * buffer content. */ + @Test + fun testAutoRefresh() { + var svBounds = Rect() + runOnUiThread { + assertEquals(0, it.mSurfaceProxy.NativeWindowSetSharedBufferMode(true)) + assertEquals(0, it.mSurfaceProxy.NativeWindowSetAutoRefresh(true)) + assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1 /* ms */)) + it.mSurfaceProxy.SurfaceQueueBuffer(0, false /* freeSlot */) + assertEquals(0, it.mSurfaceProxy.waitUntilBufferDisplayed(1, 5000 /* ms */)) + + svBounds = Rect(0, 0, it.mSurfaceView!!.width, it.mSurfaceView!!.height) + val position = Rect() + it.mSurfaceView!!.getBoundsOnScreen(position) + svBounds.offsetTo(position.left, position.top) + } + + runOnUiThread { + it.mSurfaceProxy.drawBuffer(0, Color.RED) + checkPixels(svBounds, Color.RED) + it.mSurfaceProxy.drawBuffer(0, Color.GREEN) + checkPixels(svBounds, Color.GREEN) + it.mSurfaceProxy.drawBuffer(0, Color.BLUE) + checkPixels(svBounds, Color.BLUE) + } + } +}
\ No newline at end of file diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceProxy.kt b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceProxy.kt index 884aae41446c..cfbd3ac11acb 100644 --- a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceProxy.kt +++ b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceProxy.kt @@ -16,17 +16,46 @@ package com.android.test +import android.annotation.ColorInt +import android.graphics.Color +import android.graphics.Point +import com.android.test.SurfaceViewBufferTestBase.Companion.ScalingMode +import com.android.test.SurfaceViewBufferTestBase.Companion.Transform + class SurfaceProxy { init { System.loadLibrary("surface_jni") } external fun setSurface(surface: Any) - external fun waitUntilBufferDisplayed(frameNumber: Int, timeoutSec: Int) + external fun waitUntilBufferDisplayed(frameNumber: Long, timeoutMs: Int): Int external fun draw() + fun drawBuffer(slot: Int, @ColorInt c: Int) { + drawBuffer(slot, intArrayOf(Color.red(c), Color.green(c), Color.blue(c), Color.alpha(c))) + } + external fun drawBuffer(slot: Int, color: IntArray) // android/native_window.h functions external fun ANativeWindowLock() external fun ANativeWindowUnlockAndPost() + fun ANativeWindowSetBuffersGeometry(surface: Any, size: Point, format: Int) { + ANativeWindowSetBuffersGeometry(surface, size.x, size.y, format) + } external fun ANativeWindowSetBuffersGeometry(surface: Any, width: Int, height: Int, format: Int) + fun ANativeWindowSetBuffersTransform(transform: Transform) { + ANativeWindowSetBuffersTransform(transform.value) + } + external fun ANativeWindowSetBuffersTransform(transform: Int) + + // gui/Surface.h functions + fun SurfaceSetScalingMode(scalingMode: ScalingMode) { + SurfaceSetScalingMode(scalingMode.ordinal) + } + external fun SurfaceSetScalingMode(scalingMode: Int) + external fun SurfaceDequeueBuffer(slot: Int, timeoutMs: Int): Int + external fun SurfaceCancelBuffer(slot: Int) + external fun SurfaceQueueBuffer(slot: Int, freeSlot: Boolean = true) + external fun NativeWindowSetBufferCount(count: Int): Int + external fun NativeWindowSetSharedBufferMode(shared: Boolean): Int + external fun NativeWindowSetAutoRefresh(autoRefresh: Boolean): Int } diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceTracingTestBase.kt b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceTracingTestBase.kt new file mode 100644 index 000000000000..cd4b38516bc3 --- /dev/null +++ b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceTracingTestBase.kt @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.test + +import android.annotation.ColorInt +import android.graphics.Bitmap +import android.graphics.Color +import android.graphics.Rect +import android.util.Log +import androidx.test.ext.junit.rules.ActivityScenarioRule +import com.android.server.wm.flicker.monitor.LayersTraceMonitor +import com.android.server.wm.flicker.monitor.withSFTracing +import com.android.server.wm.flicker.traces.layers.LayersTrace +import junit.framework.Assert +import org.junit.After +import org.junit.Before +import org.junit.Rule +import java.io.FileOutputStream +import java.io.IOException +import java.util.concurrent.CountDownLatch + +open class SurfaceTracingTestBase(useBlastAdapter: Boolean) : + SurfaceViewBufferTestBase(useBlastAdapter) { + @get:Rule + var scenarioRule: ActivityScenarioRule<MainActivity> = + ActivityScenarioRule<MainActivity>(MainActivity::class.java) + + @Before + override fun setup() { + super.setup() + stopLayerTrace() + addSurfaceView() + } + + @After + override fun teardown() { + super.teardown() + scenarioRule.getScenario().close() + } + + fun withTrace(predicate: (it: MainActivity) -> Unit): LayersTrace { + return withSFTracing(instrumentation, TRACE_FLAGS) { + scenarioRule.getScenario().onActivity { + predicate(it) + } + } + } + + fun runOnUiThread(predicate: (it: MainActivity) -> Unit) { + scenarioRule.getScenario().onActivity { + predicate(it) + } + } + + private fun addSurfaceView() { + lateinit var surfaceReadyLatch: CountDownLatch + scenarioRule.getScenario().onActivity { + surfaceReadyLatch = it.addSurfaceView(defaultBufferSize) + } + surfaceReadyLatch.await() + // sleep to finish animations + instrumentation.waitForIdleSync() + } + + private fun stopLayerTrace() { + val tmpDir = instrumentation.targetContext.dataDir.toPath() + LayersTraceMonitor(tmpDir).stop() + } + + fun checkPixels(bounds: Rect, @ColorInt color: Int) { + val screenshot = instrumentation.getUiAutomation().takeScreenshot() + val pixels = IntArray(screenshot.width * screenshot.height) + screenshot.getPixels(pixels, 0, screenshot.width, 0, 0, screenshot.width, screenshot.height) + for (i in bounds.left + 10..bounds.right - 10) { + for (j in bounds.top + 10..bounds.bottom - 10) { + val actualColor = pixels[j * screenshot.width + i] + if (actualColor != color) { + val screenshotPath = instrumentation.targetContext + .getExternalFilesDir(null)?.resolve("screenshot.png") + try { + FileOutputStream(screenshotPath).use { out -> + screenshot.compress(Bitmap.CompressFormat.PNG, 100, out) + } + Log.e("SurfaceViewBufferTests", "Bitmap written to $screenshotPath") + } catch (e: IOException) { + Log.e("SurfaceViewBufferTests", "Error writing bitmap to file", e) + } + } + Assert.assertEquals("Checking $bounds found mismatch $i,$j", + Color.valueOf(color), Color.valueOf(actualColor)) + } + } + } + + private companion object { + private const val TRACE_FLAGS = + (1 shl 0) or (1 shl 5) or (1 shl 6) // TRACE_CRITICAL | TRACE_BUFFERS | TRACE_SYNC + } +}
\ No newline at end of file diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTest.kt b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTest.kt deleted file mode 100644 index b48a91d49b91..000000000000 --- a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTest.kt +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.test - -import android.app.Instrumentation -import android.graphics.Rect -import android.provider.Settings -import androidx.test.ext.junit.rules.ActivityScenarioRule -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.monitor.LayersTraceMonitor -import com.android.server.wm.flicker.monitor.withSFTracing -import com.android.server.wm.flicker.traces.layers.LayersTraceSubject.Companion.assertThat -import org.junit.After -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.Parameterized -import java.util.concurrent.CountDownLatch -import kotlin.properties.Delegates - -@RunWith(Parameterized::class) -class SurfaceViewBufferTest(val useBlastAdapter: Boolean) { - private var mInitialUseBlastConfig by Delegates.notNull<Int>() - - @get:Rule - var scenarioRule: ActivityScenarioRule<MainActivity> = - ActivityScenarioRule<MainActivity>(MainActivity::class.java) - - protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() - val defaultBufferSize = Rect(0, 0, 640, 480) - - @Before - fun setup() { - mInitialUseBlastConfig = Settings.Global.getInt(instrumentation.context.contentResolver, - "use_blast_adapter_sv", 0) - val enable = if (useBlastAdapter) 1 else 0 - Settings.Global.putInt(instrumentation.context.contentResolver, "use_blast_adapter_sv", - enable) - val tmpDir = instrumentation.targetContext.dataDir.toPath() - LayersTraceMonitor(tmpDir).stop() - - lateinit var surfaceReadyLatch: CountDownLatch - scenarioRule.getScenario().onActivity { - surfaceReadyLatch = it.addSurfaceView(defaultBufferSize) - } - surfaceReadyLatch.await() - } - - @After - fun teardown() { - scenarioRule.getScenario().close() - Settings.Global.putInt(instrumentation.context.contentResolver, - "use_blast_adapter_sv", mInitialUseBlastConfig) - } - - @Test - fun testSetBuffersGeometry_0x0_resetsBufferSize() { - val trace = withSFTracing(instrumentation, TRACE_FLAGS) { - scenarioRule.getScenario().onActivity { - it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, 0, 0, - R8G8B8A8_UNORM) - it.mSurfaceProxy.ANativeWindowLock() - it.mSurfaceProxy.ANativeWindowUnlockAndPost() - it.mSurfaceProxy.waitUntilBufferDisplayed(1, 1 /* sec */) - } - } - - // verify buffer size is reset to default buffer size - assertThat(trace).layer("SurfaceView", 1).hasBufferSize(defaultBufferSize) - } - - @Test - fun testSetBuffersGeometry_0x0_rejectsBuffer() { - val trace = withSFTracing(instrumentation, TRACE_FLAGS) { - scenarioRule.getScenario().onActivity { - it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, 100, 100, - R8G8B8A8_UNORM) - it.mSurfaceProxy.ANativeWindowLock() - it.mSurfaceProxy.ANativeWindowUnlockAndPost() - it.mSurfaceProxy.ANativeWindowLock() - it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, 0, 0, R8G8B8A8_UNORM) - // Submit buffer one with a different size which should be rejected - it.mSurfaceProxy.ANativeWindowUnlockAndPost() - - // submit a buffer with the default buffer size - it.mSurfaceProxy.ANativeWindowLock() - it.mSurfaceProxy.ANativeWindowUnlockAndPost() - it.mSurfaceProxy.waitUntilBufferDisplayed(3, 1 /* sec */) - } - } - // Verify we reject buffers since scaling mode == NATIVE_WINDOW_SCALING_MODE_FREEZE - assertThat(trace).layer("SurfaceView", 2).doesNotExist() - - // Verify the next buffer is submitted with the correct size - assertThat(trace).layer("SurfaceView", 3).also { - it.hasBufferSize(defaultBufferSize) - it.hasScalingMode(0 /* NATIVE_WINDOW_SCALING_MODE_FREEZE */) - } - } - - @Test - fun testSetBuffersGeometry_smallerThanBuffer() { - val bufferSize = Rect(0, 0, 300, 200) - val trace = withSFTracing(instrumentation, TRACE_FLAGS) { - scenarioRule.getScenario().onActivity { - it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, bufferSize.width(), - bufferSize.height(), R8G8B8A8_UNORM) - it.drawFrame() - it.mSurfaceProxy.waitUntilBufferDisplayed(1, 1 /* sec */) - } - } - - assertThat(trace).layer("SurfaceView", 1).also { - it.hasBufferSize(bufferSize) - it.hasLayerSize(defaultBufferSize) - it.hasScalingMode(1 /* NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW */) - } - } - - @Test - fun testSetBuffersGeometry_largerThanBuffer() { - val bufferSize = Rect(0, 0, 3000, 2000) - val trace = withSFTracing(instrumentation, TRACE_FLAGS) { - scenarioRule.getScenario().onActivity { - it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, bufferSize.width(), - bufferSize.height(), R8G8B8A8_UNORM) - it.drawFrame() - it.mSurfaceProxy.waitUntilBufferDisplayed(1, 1 /* sec */) - } - } - - assertThat(trace).layer("SurfaceView", 1).also { - it.hasBufferSize(bufferSize) - it.hasLayerSize(defaultBufferSize) - it.hasScalingMode(1 /* NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW */) - } - } - - /** Submit buffers as fast as possible and make sure they are queued */ - @Test - fun testQueueBuffers() { - val trace = withSFTracing(instrumentation, TRACE_FLAGS) { - scenarioRule.getScenario().onActivity { - it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, 100, 100, - R8G8B8A8_UNORM) - for (i in 0..100) { - it.mSurfaceProxy.ANativeWindowLock() - it.mSurfaceProxy.ANativeWindowUnlockAndPost() - } - it.mSurfaceProxy.waitUntilBufferDisplayed(100, 1 /* sec */) - } - } - for (frameNumber in 1..100) { - assertThat(trace).layer("SurfaceView", frameNumber.toLong()) - } - } - - companion object { - private const val TRACE_FLAGS = 0x1 // TRACE_CRITICAL - private const val R8G8B8A8_UNORM = 1 - - @JvmStatic - @Parameterized.Parameters(name = "blast={0}") - fun data(): Collection<Array<Any>> { - return listOf( - arrayOf(false), // First test: submit buffers via bufferqueue - arrayOf(true) // Second test: submit buffers via blast adapter - ) - } - } -}
\ No newline at end of file diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTestBase.kt b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTestBase.kt new file mode 100644 index 000000000000..093c3125f253 --- /dev/null +++ b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTestBase.kt @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.test + +import android.app.Instrumentation +import android.graphics.Point +import android.provider.Settings +import androidx.test.InstrumentationRegistry +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.rules.TestName +import org.junit.runners.Parameterized +import kotlin.properties.Delegates + +open class SurfaceViewBufferTestBase(val useBlastAdapter: Boolean) { + private var mInitialBlastConfig by Delegates.notNull<Boolean>() + + val instrumentation: Instrumentation + get() = InstrumentationRegistry.getInstrumentation() + + @get:Rule + var mName = TestName() + + @Before + open fun setup() { + mInitialBlastConfig = getBlastAdapterSvEnabled() + setBlastAdapterSvEnabled(useBlastAdapter) + } + + @After + open fun teardown() { + setBlastAdapterSvEnabled(mInitialBlastConfig) + } + + private fun getBlastAdapterSvEnabled(): Boolean { + return Settings.Global.getInt(instrumentation.context.contentResolver, + "use_blast_adapter_sv", 0) != 0 + } + + private fun setBlastAdapterSvEnabled(enable: Boolean) { + Settings.Global.putInt(instrumentation.context.contentResolver, "use_blast_adapter_sv", + if (enable) 1 else 0) + } + + companion object { + @JvmStatic + @Parameterized.Parameters(name = "blast={0}") + fun data(): Collection<Array<Any>> { + return listOf( + arrayOf(false), // First test: submit buffers via bufferqueue + arrayOf(true) // Second test: submit buffers via blast adapter + ) + } + + const val R8G8B8A8_UNORM = 1 + val defaultBufferSize = Point(640, 480) + + // system/window.h definitions + enum class ScalingMode() { + FREEZE, // = 0 + SCALE_TO_WINDOW, // =1 + SCALE_CROP, // = 2 + NO_SCALE_CROP // = 3 + } + + // system/window.h definitions + enum class Transform(val value: Int) { + /* flip source image horizontally */ + FLIP_H(1), + /* flip source image vertically */ + FLIP_V(2), + /* rotate source image 90 degrees clock-wise, and is applied after TRANSFORM_FLIP_{H|V} */ + ROT_90(4), + /* rotate source image 180 degrees */ + ROT_180(3), + /* rotate source image 270 degrees clock-wise */ + ROT_270(7), + /* transforms source by the inverse transform of the screen it is displayed onto. This + * transform is applied last */ + INVERSE_DISPLAY(0x08) + } + } +}
\ No newline at end of file diff --git a/tests/TaskOrganizerTest/src/com/android/test/taskembed/ResizeTasksSyncTest.kt b/tests/TaskOrganizerTest/src/com/android/test/taskembed/ResizeTasksSyncTest.kt index abccd6cf77bd..fe9deae80407 100644 --- a/tests/TaskOrganizerTest/src/com/android/test/taskembed/ResizeTasksSyncTest.kt +++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/ResizeTasksSyncTest.kt @@ -16,6 +16,7 @@ package com.android.test.taskembed import android.app.Instrumentation +import android.graphics.Point import android.graphics.Rect import androidx.test.ext.junit.rules.ActivityScenarioRule import androidx.test.platform.app.InstrumentationRegistry @@ -93,13 +94,15 @@ class ResizeTasksSyncTest { // verify buffer size should be changed to expected values. assertThat(trace).layer(FIRST_ACTIVITY, frame).also { - it.hasLayerSize(firstBounds) - it.hasBufferSize(firstBounds) + val firstTaskSize = Point(firstBounds.width(), firstBounds.height()) + it.hasLayerSize(firstTaskSize) + it.hasBufferSize(firstTaskSize) } assertThat(trace).layer(SECOND_ACTIVITY, frame).also { - it.hasLayerSize(secondBounds) - it.hasBufferSize(secondBounds) + val secondTaskSize = Point(secondBounds.width(), secondBounds.height()) + it.hasLayerSize(secondTaskSize) + it.hasBufferSize(secondTaskSize) } } diff --git a/tests/net/common/java/android/net/MatchAllNetworkSpecifierTest.kt b/tests/net/common/java/android/net/MatchAllNetworkSpecifierTest.kt index a67156a74d18..a5e44d59fcab 100644 --- a/tests/net/common/java/android/net/MatchAllNetworkSpecifierTest.kt +++ b/tests/net/common/java/android/net/MatchAllNetworkSpecifierTest.kt @@ -53,8 +53,13 @@ class MatchAllNetworkSpecifierTest { assertParcelSane(MatchAllNetworkSpecifier(), 0) } - @Test @IgnoreAfter(Build.VERSION_CODES.R) - fun testCanBeSatisfiedBy_BeforeS() { + @Test + @IgnoreUpTo(Build.VERSION_CODES.Q) + @IgnoreAfter(Build.VERSION_CODES.R) + // Only run this test on Android R. + // The method - satisfiedBy() has changed to canBeSatisfiedBy() starting from Android R, so the + // method - canBeSatisfiedBy() cannot be found when running this test on Android Q. + fun testCanBeSatisfiedBy_OnlyForR() { // MatchAllNetworkSpecifier didn't follow its parent class to change the satisfiedBy() to // canBeSatisfiedBy(), so if a caller calls MatchAllNetworkSpecifier#canBeSatisfiedBy(), the // NetworkSpecifier#canBeSatisfiedBy() will be called actually, and false will be returned. diff --git a/tests/net/common/java/android/net/OemNetworkPreferencesTest.java b/tests/net/common/java/android/net/OemNetworkPreferencesTest.java index b77ed6ab5a29..c9161b6d6441 100644 --- a/tests/net/common/java/android/net/OemNetworkPreferencesTest.java +++ b/tests/net/common/java/android/net/OemNetworkPreferencesTest.java @@ -22,11 +22,14 @@ import static com.android.testutils.ParcelUtils.assertParcelSane; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import android.os.Build; import android.util.SparseArray; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -34,6 +37,7 @@ import org.junit.runner.RunWith; import java.util.ArrayList; import java.util.List; +@IgnoreUpTo(Build.VERSION_CODES.R) @RunWith(AndroidJUnit4.class) @SmallTest public class OemNetworkPreferencesTest { diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt index dba1856ea6d0..70f6386aa891 100644 --- a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt +++ b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt @@ -200,7 +200,8 @@ class ConnectivityServiceIntegrationTest { nsInstrumentation.addHttpResponse(HttpResponse(httpProbeUrl, responseCode = 204)) nsInstrumentation.addHttpResponse(HttpResponse(httpsProbeUrl, responseCode = 204)) - val na = NetworkAgentWrapper(TRANSPORT_CELLULAR, LinkProperties(), context) + val na = NetworkAgentWrapper(TRANSPORT_CELLULAR, LinkProperties(), null /* ncTemplate */, + context) networkStackClient.verifyNetworkMonitorCreated(na.network, TEST_TIMEOUT_MS) na.addCapability(NET_CAPABILITY_INTERNET) @@ -238,7 +239,7 @@ class ConnectivityServiceIntegrationTest { val lp = LinkProperties() lp.captivePortalApiUrl = Uri.parse(apiUrl) - val na = NetworkAgentWrapper(TRANSPORT_CELLULAR, lp, context) + val na = NetworkAgentWrapper(TRANSPORT_CELLULAR, lp, null /* ncTemplate */, context) networkStackClient.verifyNetworkMonitorCreated(na.network, TEST_TIMEOUT_MS) na.addCapability(NET_CAPABILITY_INTERNET) diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java index 85704d033634..2a24d1ac22d2 100644 --- a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java +++ b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java @@ -72,12 +72,12 @@ public class NetworkAgentWrapper implements TestableNetworkCallback.HasNetwork { private long mKeepaliveResponseDelay = 0L; private Integer mExpectedKeepaliveSlot = null; - public NetworkAgentWrapper(int transport, LinkProperties linkProperties, Context context) - throws Exception { + public NetworkAgentWrapper(int transport, LinkProperties linkProperties, + NetworkCapabilities ncTemplate, Context context) throws Exception { final int type = transportToLegacyType(transport); final String typeName = ConnectivityManager.getNetworkTypeName(type); mNetworkInfo = new NetworkInfo(type, 0, typeName, "Mock"); - mNetworkCapabilities = new NetworkCapabilities(); + mNetworkCapabilities = (ncTemplate != null) ? ncTemplate : new NetworkCapabilities(); mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_SUSPENDED); mNetworkCapabilities.addTransportType(transport); switch (transport) { diff --git a/tests/net/integration/util/com/android/server/TestNetIdManager.kt b/tests/net/integration/util/com/android/server/TestNetIdManager.kt index eb290dc7d24a..938a694e8ba9 100644 --- a/tests/net/integration/util/com/android/server/TestNetIdManager.kt +++ b/tests/net/integration/util/com/android/server/TestNetIdManager.kt @@ -35,4 +35,5 @@ class TestNetIdManager : NetIdManager() { private val nextId = AtomicInteger(MAX_NET_ID) override fun reserveNetId() = nextId.decrementAndGet() override fun releaseNetId(id: Int) = Unit + fun peekNextNetId() = nextId.get() - 1 } diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index b78f0e237bc5..ba87dc50e246 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -18,6 +18,8 @@ package com.android.server; import static android.Manifest.permission.CHANGE_NETWORK_STATE; import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS; +import static android.content.Intent.ACTION_USER_ADDED; +import static android.content.Intent.ACTION_USER_REMOVED; import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED; import static android.content.pm.PackageManager.GET_PERMISSIONS; import static android.content.pm.PackageManager.MATCH_ANY_USER; @@ -259,7 +261,10 @@ import org.mockito.MockitoAnnotations; import org.mockito.Spy; import org.mockito.stubbing.Answer; +import java.io.FileDescriptor; import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; import java.net.DatagramSocket; import java.net.Inet4Address; import java.net.Inet6Address; @@ -322,6 +327,7 @@ public class ConnectivityServiceTest { private static final String MOBILE_IFNAME = "test_rmnet_data0"; private static final String WIFI_IFNAME = "test_wlan0"; private static final String WIFI_WOL_IFNAME = "test_wlan_wol"; + private static final String VPN_IFNAME = "tun10042"; private static final String TEST_PACKAGE_NAME = "com.android.test.package"; private static final String[] EMPTY_STRING_ARRAY = new String[0]; @@ -339,6 +345,7 @@ public class ConnectivityServiceTest { private INetworkPolicyListener mPolicyListener; private WrappedMultinetworkPolicyTracker mPolicyTracker; private HandlerThread mAlarmManagerThread; + private TestNetIdManager mNetIdManager; @Mock IIpConnectivityMetrics mIpConnectivityMetrics; @Mock IpConnectivityMetrics.Logger mMetricsService; @@ -617,12 +624,17 @@ public class ConnectivityServiceTest { private String mRedirectUrl; TestNetworkAgentWrapper(int transport) throws Exception { - this(transport, new LinkProperties()); + this(transport, new LinkProperties(), null); } TestNetworkAgentWrapper(int transport, LinkProperties linkProperties) throws Exception { - super(transport, linkProperties, mServiceContext); + this(transport, linkProperties, null); + } + + private TestNetworkAgentWrapper(int transport, LinkProperties linkProperties, + NetworkCapabilities ncTemplate) throws Exception { + super(transport, linkProperties, ncTemplate, mServiceContext); // Waits for the NetworkAgent to be registered, which includes the creation of the // NetworkMonitor. @@ -1017,46 +1029,38 @@ public class ConnectivityServiceTest { } } + private Set<UidRange> uidRangesForUid(int uid) { + final ArraySet<UidRange> ranges = new ArraySet<>(); + ranges.add(new UidRange(uid, uid)); + return ranges; + } + private static Looper startHandlerThreadAndReturnLooper() { final HandlerThread handlerThread = new HandlerThread("MockVpnThread"); handlerThread.start(); return handlerThread.getLooper(); } - private class MockVpn extends Vpn { - // TODO : the interactions between this mock and the mock network agent are too - // hard to get right at this moment, because it's unclear in which case which - // target needs to get a method call or both, and in what order. It's because - // MockNetworkAgent wants to manage its own NetworkCapabilities, but the Vpn - // parent class of MockVpn agent wants that responsibility. - // That being said inside the test it should be possible to make the interactions - // harder to get wrong with precise speccing, judicious comments, helper methods - // and a few sprinkled assertions. - - private boolean mConnected = false; + private class MockVpn extends Vpn implements TestableNetworkCallback.HasNetwork { // Careful ! This is different from mNetworkAgent, because MockNetworkAgent does // not inherit from NetworkAgent. private TestNetworkAgentWrapper mMockNetworkAgent; - private int mVpnType = VpnManager.TYPE_VPN_SERVICE; + private boolean mAgentRegistered = false; + private int mVpnType = VpnManager.TYPE_VPN_SERVICE; private VpnInfo mVpnInfo; - private Network[] mUnderlyingNetworks; public MockVpn(int userId) { super(startHandlerThreadAndReturnLooper(), mServiceContext, mNetworkManagementService, userId, mock(KeyStore.class)); - } - - public void setNetworkAgent(TestNetworkAgentWrapper agent) { - agent.waitForIdle(TIMEOUT_MS); - mMockNetworkAgent = agent; - mNetworkAgent = agent.getNetworkAgent(); - mNetworkCapabilities.set(agent.getNetworkCapabilities()); + mConfig = new VpnConfig(); } public void setUids(Set<UidRange> uids) { mNetworkCapabilities.setUids(uids); - updateCapabilities(null /* defaultNetwork */); + if (mAgentRegistered) { + mMockNetworkAgent.setNetworkCapabilities(mNetworkCapabilities, true); + } } public void setVpnType(int vpnType) { @@ -1064,21 +1068,13 @@ public class ConnectivityServiceTest { } @Override - public int getNetId() { - if (mMockNetworkAgent == null) { - return NETID_UNSET; - } - return mMockNetworkAgent.getNetwork().netId; - } - - @Override - public boolean appliesToUid(int uid) { - return mConnected; // Trickery to simplify testing. + public Network getNetwork() { + return (mMockNetworkAgent == null) ? null : mMockNetworkAgent.getNetwork(); } @Override - protected boolean isCallerEstablishedOwnerLocked() { - return mConnected; // Similar trickery + public int getNetId() { + return (mMockNetworkAgent == null) ? NETID_UNSET : mMockNetworkAgent.getNetwork().netId; } @Override @@ -1086,41 +1082,76 @@ public class ConnectivityServiceTest { return mVpnType; } - private void connect(boolean isAlwaysMetered) { + private void registerAgent(boolean isAlwaysMetered, Set<UidRange> uids, LinkProperties lp) + throws Exception { + if (mAgentRegistered) throw new IllegalStateException("already registered"); + setUids(uids); + if (!isAlwaysMetered) mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_METERED); + mInterface = VPN_IFNAME; + mMockNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN, lp, + mNetworkCapabilities); + mMockNetworkAgent.waitForIdle(TIMEOUT_MS); + verify(mNetworkManagementService, times(1)) + .addVpnUidRanges(eq(mMockVpn.getNetId()), eq(uids.toArray(new UidRange[0]))); + verify(mNetworkManagementService, never()) + .removeVpnUidRanges(eq(mMockVpn.getNetId()), any()); + mAgentRegistered = true; mNetworkCapabilities.set(mMockNetworkAgent.getNetworkCapabilities()); - mConnected = true; - mConfig = new VpnConfig(); - mConfig.isMetered = isAlwaysMetered; + mNetworkAgent = mMockNetworkAgent.getNetworkAgent(); } - public void connectAsAlwaysMetered() { - connect(true /* isAlwaysMetered */); + private void registerAgent(Set<UidRange> uids) throws Exception { + registerAgent(false /* isAlwaysMetered */, uids, new LinkProperties()); } - public void connect() { - connect(false /* isAlwaysMetered */); + private void connect(boolean validated, boolean hasInternet, boolean isStrictMode) { + mMockNetworkAgent.connect(validated, hasInternet, isStrictMode); } - @Override - public NetworkCapabilities updateCapabilities(Network defaultNetwork) { - if (!mConnected) return null; - super.updateCapabilities(defaultNetwork); - // Because super.updateCapabilities will update the capabilities of the agent but - // not the mock agent, the mock agent needs to know about them. - copyCapabilitiesToNetworkAgent(); - return new NetworkCapabilities(mNetworkCapabilities); - } - - private void copyCapabilitiesToNetworkAgent() { - if (null != mMockNetworkAgent) { - mMockNetworkAgent.setNetworkCapabilities(mNetworkCapabilities, - false /* sendToConnectivityService */); - } + private void connect(boolean validated) { + mMockNetworkAgent.connect(validated); + } + + private TestNetworkAgentWrapper getAgent() { + return mMockNetworkAgent; + } + + public void establish(LinkProperties lp, int uid, Set<UidRange> ranges, boolean validated, + boolean hasInternet, boolean isStrictMode) throws Exception { + mNetworkCapabilities.setOwnerUid(uid); + mNetworkCapabilities.setAdministratorUids(new int[]{uid}); + registerAgent(false, ranges, lp); + connect(validated, hasInternet, isStrictMode); + waitForIdle(); + } + + public void establish(LinkProperties lp, int uid, Set<UidRange> ranges) throws Exception { + establish(lp, uid, ranges, true, true, false); + } + + public void establishForMyUid(LinkProperties lp) throws Exception { + final int uid = Process.myUid(); + establish(lp, uid, uidRangesForUid(uid), true, true, false); + } + + public void establishForMyUid(boolean validated, boolean hasInternet, boolean isStrictMode) + throws Exception { + final int uid = Process.myUid(); + establish(new LinkProperties(), uid, uidRangesForUid(uid), validated, hasInternet, + isStrictMode); + } + + public void establishForMyUid() throws Exception { + establishForMyUid(new LinkProperties()); + } + + public void sendLinkProperties(LinkProperties lp) { + mMockNetworkAgent.sendLinkProperties(lp); } public void disconnect() { - mConnected = false; - mConfig = null; + if (mMockNetworkAgent != null) mMockNetworkAgent.disconnect(); + mAgentRegistered = false; } @Override @@ -1133,18 +1164,6 @@ public class ConnectivityServiceTest { private synchronized void setVpnInfo(VpnInfo vpnInfo) { mVpnInfo = vpnInfo; } - - @Override - public synchronized Network[] getUnderlyingNetworks() { - if (mUnderlyingNetworks != null) return mUnderlyingNetworks; - - return super.getUnderlyingNetworks(); - } - - /** Don't override behavior for {@link Vpn#setUnderlyingNetworks}. */ - private synchronized void overrideUnderlyingNetworks(Network[] underlyingNetworks) { - mUnderlyingNetworks = underlyingNetworks; - } } private void mockVpn(int uid) { @@ -1207,6 +1226,8 @@ public class ConnectivityServiceTest { @Before public void setUp() throws Exception { + mNetIdManager = new TestNetIdManager(); + mContext = InstrumentationRegistry.getContext(); MockitoAnnotations.initMocks(this); @@ -1277,7 +1298,7 @@ public class ConnectivityServiceTest { doNothing().when(mSystemProperties).setTcpInitRwnd(anyInt()); final ConnectivityService.Dependencies deps = mock(ConnectivityService.Dependencies.class); doReturn(mCsHandlerThread).when(deps).makeHandlerThread(); - doReturn(new TestNetIdManager()).when(deps).makeNetIdManager(); + doReturn(mNetIdManager).when(deps).makeNetIdManager(); doReturn(mNetworkStack).when(deps).getNetworkStack(); doReturn(mSystemProperties).when(deps).getSystemProperties(); doReturn(mock(ProxyTracker.class)).when(deps).makeProxyTracker(any(), any()); @@ -1335,6 +1356,9 @@ public class ConnectivityServiceTest { mEthernetNetworkAgent.disconnect(); mEthernetNetworkAgent = null; } + mMockVpn.disconnect(); + waitForIdle(); + FakeSettingsProvider.clearSettingsProvider(); mCsHandlerThread.quitSafely(); @@ -3218,20 +3242,12 @@ public class ConnectivityServiceTest { waitForIdle(); assertEquals(null, mCm.getActiveNetwork()); - final int uid = Process.myUid(); - final TestNetworkAgentWrapper - vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN); - final ArraySet<UidRange> ranges = new ArraySet<>(); - ranges.add(new UidRange(uid, uid)); - mMockVpn.setNetworkAgent(vpnNetworkAgent); - mMockVpn.setUids(ranges); - vpnNetworkAgent.connect(true); - mMockVpn.connect(); - defaultNetworkCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent); + mMockVpn.establishForMyUid(); + defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn); assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - vpnNetworkAgent.disconnect(); - defaultNetworkCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent); + mMockVpn.disconnect(); + defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn); waitForIdle(); assertEquals(null, mCm.getActiveNetwork()); } @@ -4808,13 +4824,52 @@ public class ConnectivityServiceTest { mCm.unregisterNetworkCallback(networkCallback); } + private <T> void assertSameElementsNoDuplicates(T[] expected, T[] actual) { + // Easier to implement than a proper "assertSameElements" method that also correctly deals + // with duplicates. + final String msg = Arrays.toString(expected) + " != " + Arrays.toString(actual); + assertEquals(msg, expected.length, actual.length); + Set expectedSet = new ArraySet<>(Arrays.asList(expected)); + assertEquals("expected contains duplicates", expectedSet.size(), expected.length); + // actual cannot have duplicates because it's the same length and has the same elements. + Set actualSet = new ArraySet<>(Arrays.asList(actual)); + assertEquals(expectedSet, actualSet); + } + + private void expectForceUpdateIfaces(Network[] networks, String defaultIface, + Integer vpnUid, String vpnIfname, String[] underlyingIfaces) throws Exception { + ArgumentCaptor<Network[]> networksCaptor = ArgumentCaptor.forClass(Network[].class); + ArgumentCaptor<VpnInfo[]> vpnInfosCaptor = ArgumentCaptor.forClass(VpnInfo[].class); + + verify(mStatsService, atLeastOnce()).forceUpdateIfaces(networksCaptor.capture(), + any(NetworkState[].class), eq(defaultIface), vpnInfosCaptor.capture()); + + assertSameElementsNoDuplicates(networksCaptor.getValue(), networks); + + VpnInfo[] infos = vpnInfosCaptor.getValue(); + if (vpnUid != null) { + assertEquals("Should have exactly one VPN:", 1, infos.length); + VpnInfo info = infos[0]; + assertEquals("Unexpected VPN owner:", (int) vpnUid, info.ownerUid); + assertEquals("Unexpected VPN interface:", vpnIfname, info.vpnIface); + assertSameElementsNoDuplicates(underlyingIfaces, info.underlyingIfaces); + } else { + assertEquals(0, infos.length); + return; + } + } + + private void expectForceUpdateIfaces(Network[] networks, String defaultIface) throws Exception { + expectForceUpdateIfaces(networks, defaultIface, null, null, new String[0]); + } + @Test public void testStatsIfacesChanged() throws Exception { mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - Network[] onlyCell = new Network[] {mCellNetworkAgent.getNetwork()}; - Network[] onlyWifi = new Network[] {mWiFiNetworkAgent.getNetwork()}; + final Network[] onlyCell = new Network[] {mCellNetworkAgent.getNetwork()}; + final Network[] onlyWifi = new Network[] {mWiFiNetworkAgent.getNetwork()}; LinkProperties cellLp = new LinkProperties(); cellLp.setInterfaceName(MOBILE_IFNAME); @@ -4825,9 +4880,7 @@ public class ConnectivityServiceTest { mCellNetworkAgent.connect(false); mCellNetworkAgent.sendLinkProperties(cellLp); waitForIdle(); - verify(mStatsService, atLeastOnce()) - .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), eq(MOBILE_IFNAME), - eq(new VpnInfo[0])); + expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME); reset(mStatsService); // Default network switch should update ifaces. @@ -4835,32 +4888,24 @@ public class ConnectivityServiceTest { mWiFiNetworkAgent.sendLinkProperties(wifiLp); waitForIdle(); assertEquals(wifiLp, mService.getActiveLinkProperties()); - verify(mStatsService, atLeastOnce()) - .forceUpdateIfaces(eq(onlyWifi), any(NetworkState[].class), eq(WIFI_IFNAME), - eq(new VpnInfo[0])); + expectForceUpdateIfaces(onlyWifi, WIFI_IFNAME); reset(mStatsService); // Disconnect should update ifaces. mWiFiNetworkAgent.disconnect(); waitForIdle(); - verify(mStatsService, atLeastOnce()) - .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), - eq(MOBILE_IFNAME), eq(new VpnInfo[0])); + expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME); reset(mStatsService); // Metered change should update ifaces mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); waitForIdle(); - verify(mStatsService, atLeastOnce()) - .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), eq(MOBILE_IFNAME), - eq(new VpnInfo[0])); + expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME); reset(mStatsService); mCellNetworkAgent.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); waitForIdle(); - verify(mStatsService, atLeastOnce()) - .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), eq(MOBILE_IFNAME), - eq(new VpnInfo[0])); + expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME); reset(mStatsService); // Captive portal change shouldn't update ifaces @@ -4874,9 +4919,130 @@ public class ConnectivityServiceTest { // Roaming change should update ifaces mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING); waitForIdle(); - verify(mStatsService, atLeastOnce()) - .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), eq(MOBILE_IFNAME), - eq(new VpnInfo[0])); + expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME); + reset(mStatsService); + + // Test VPNs. + final LinkProperties lp = new LinkProperties(); + lp.setInterfaceName(VPN_IFNAME); + + mMockVpn.establishForMyUid(lp); + + final Network[] cellAndVpn = new Network[] { + mCellNetworkAgent.getNetwork(), mMockVpn.getNetwork()}; + + // A VPN with default (null) underlying networks sets the underlying network's interfaces... + expectForceUpdateIfaces(cellAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME, + new String[]{MOBILE_IFNAME}); + + // ...and updates them as the default network switches. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(false); + mWiFiNetworkAgent.sendLinkProperties(wifiLp); + final Network[] onlyNull = new Network[]{null}; + final Network[] wifiAndVpn = new Network[] { + mWiFiNetworkAgent.getNetwork(), mMockVpn.getNetwork()}; + final Network[] cellAndWifi = new Network[] { + mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork()}; + final Network[] cellNullAndWifi = new Network[] { + mCellNetworkAgent.getNetwork(), null, mWiFiNetworkAgent.getNetwork()}; + + waitForIdle(); + assertEquals(wifiLp, mService.getActiveLinkProperties()); + expectForceUpdateIfaces(wifiAndVpn, WIFI_IFNAME, Process.myUid(), VPN_IFNAME, + new String[]{WIFI_IFNAME}); + reset(mStatsService); + + // A VPN that sets its underlying networks passes the underlying interfaces, and influences + // the default interface sent to NetworkStatsService by virtue of applying to the system + // server UID (or, in this test, to the test's UID). This is the reason for sending + // MOBILE_IFNAME even though the default network is wifi. + // TODO: fix this to pass in the actual default network interface. Whether or not the VPN + // applies to the system server UID should not have any bearing on network stats. + mService.setUnderlyingNetworksForVpn(onlyCell); + waitForIdle(); + expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME, + new String[]{MOBILE_IFNAME}); + reset(mStatsService); + + mService.setUnderlyingNetworksForVpn(cellAndWifi); + waitForIdle(); + expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME, + new String[]{MOBILE_IFNAME, WIFI_IFNAME}); + reset(mStatsService); + + // Null underlying networks are ignored. + mService.setUnderlyingNetworksForVpn(cellNullAndWifi); + waitForIdle(); + expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME, + new String[]{MOBILE_IFNAME, WIFI_IFNAME}); + reset(mStatsService); + + // If an underlying network disconnects, that interface should no longer be underlying. + // This doesn't actually work because disconnectAndDestroyNetwork only notifies + // NetworkStatsService before the underlying network is actually removed. So the underlying + // network will only be removed if notifyIfacesChangedForNetworkStats is called again. This + // could result in incorrect data usage measurements if the interface used by the + // disconnected network is reused by a system component that does not register an agent for + // it (e.g., tethering). + mCellNetworkAgent.disconnect(); + waitForIdle(); + assertNull(mService.getLinkProperties(mCellNetworkAgent.getNetwork())); + expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME, + new String[]{MOBILE_IFNAME, WIFI_IFNAME}); + + // Confirm that we never tell NetworkStatsService that cell is no longer the underlying + // network for the VPN... + verify(mStatsService, never()).forceUpdateIfaces(any(Network[].class), + any(NetworkState[].class), any() /* anyString() doesn't match null */, + argThat(infos -> infos[0].underlyingIfaces.length == 1 + && WIFI_IFNAME.equals(infos[0].underlyingIfaces[0]))); + verifyNoMoreInteractions(mStatsService); + reset(mStatsService); + + // ... but if something else happens that causes notifyIfacesChangedForNetworkStats to be + // called again, it does. For example, connect Ethernet, but with a low score, such that it + // does not become the default network. + mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET); + mEthernetNetworkAgent.adjustScore(-40); + mEthernetNetworkAgent.connect(false); + waitForIdle(); + verify(mStatsService).forceUpdateIfaces(any(Network[].class), + any(NetworkState[].class), any() /* anyString() doesn't match null */, + argThat(vpnInfos -> vpnInfos[0].underlyingIfaces.length == 1 + && WIFI_IFNAME.equals(vpnInfos[0].underlyingIfaces[0]))); + mEthernetNetworkAgent.disconnect(); + waitForIdle(); + reset(mStatsService); + + // When a VPN declares no underlying networks (i.e., no connectivity), getAllVpnInfo + // does not return the VPN, so CS does not pass it to NetworkStatsService. This causes + // NetworkStatsFactory#adjustForTunAnd464Xlat not to attempt any VPN data migration, which + // is probably a performance improvement (though it's very unlikely that a VPN would declare + // no underlying networks). + // Also, for the same reason as above, the active interface passed in is null. + mService.setUnderlyingNetworksForVpn(new Network[0]); + waitForIdle(); + expectForceUpdateIfaces(wifiAndVpn, null); + reset(mStatsService); + + // Specifying only a null underlying network is the same as no networks. + mService.setUnderlyingNetworksForVpn(onlyNull); + waitForIdle(); + expectForceUpdateIfaces(wifiAndVpn, null); + reset(mStatsService); + + // Specifying networks that are all disconnected is the same as specifying no networks. + mService.setUnderlyingNetworksForVpn(onlyCell); + waitForIdle(); + expectForceUpdateIfaces(wifiAndVpn, null); + reset(mStatsService); + + // Passing in null again means follow the default network again. + mService.setUnderlyingNetworksForVpn(null); + waitForIdle(); + expectForceUpdateIfaces(wifiAndVpn, WIFI_IFNAME, Process.myUid(), VPN_IFNAME, + new String[]{WIFI_IFNAME}); reset(mStatsService); } @@ -5232,6 +5398,58 @@ public class ConnectivityServiceTest { } @Test + public void testVpnConnectDisconnectUnderlyingNetwork() throws Exception { + final TestNetworkCallback callback = new TestNetworkCallback(); + final NetworkRequest request = new NetworkRequest.Builder() + .removeCapability(NET_CAPABILITY_NOT_VPN).build(); + + mCm.registerNetworkCallback(request, callback); + + // Bring up a VPN that specifies an underlying network that does not exist yet. + // Note: it's sort of meaningless for a VPN app to declare a network that doesn't exist yet, + // (and doing so is difficult without using reflection) but it's good to test that the code + // behaves approximately correctly. + mMockVpn.establishForMyUid(false, true, false); + final Network wifiNetwork = new Network(mNetIdManager.peekNextNetId()); + mService.setUnderlyingNetworksForVpn(new Network[]{wifiNetwork}); + callback.expectAvailableCallbacksUnvalidated(mMockVpn); + assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork()) + .hasTransport(TRANSPORT_VPN)); + assertFalse(mCm.getNetworkCapabilities(mMockVpn.getNetwork()) + .hasTransport(TRANSPORT_WIFI)); + + // Make that underlying network connect, and expect to see its capabilities immediately + // reflected in the VPN's capabilities. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + assertEquals(wifiNetwork, mWiFiNetworkAgent.getNetwork()); + mWiFiNetworkAgent.connect(false); + // TODO: the callback for the VPN happens before any callbacks are called for the wifi + // network that has just connected. There appear to be two issues here: + // 1. The VPN code will accept an underlying network as soon as getNetworkCapabilities() for + // it returns non-null (which happens very early, during handleRegisterNetworkAgent). + // This is not correct because that that point the network is not connected and cannot + // pass any traffic. + // 2. When a network connects, updateNetworkInfo propagates underlying network capabilities + // before rematching networks. + // Given that this scenario can't really happen, this is probably fine for now. + callback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn); + callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork()) + .hasTransport(TRANSPORT_VPN)); + assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork()) + .hasTransport(TRANSPORT_WIFI)); + + // Disconnect the network, and expect to see the VPN capabilities change accordingly. + mWiFiNetworkAgent.disconnect(); + callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + callback.expectCapabilitiesThat(mMockVpn, (nc) -> + nc.getTransportTypes().length == 1 && nc.hasTransport(TRANSPORT_VPN)); + + mMockVpn.disconnect(); + mCm.unregisterNetworkCallback(callback); + } + + @Test public void testVpnNetworkActive() throws Exception { final int uid = Process.myUid(); @@ -5265,42 +5483,32 @@ public class ConnectivityServiceTest { vpnNetworkCallback.assertNoCallback(); assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - final TestNetworkAgentWrapper - vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN); - final ArraySet<UidRange> ranges = new ArraySet<>(); - ranges.add(new UidRange(uid, uid)); - mMockVpn.setNetworkAgent(vpnNetworkAgent); - mMockVpn.setUids(ranges); + final Set<UidRange> ranges = uidRangesForUid(uid); + mMockVpn.registerAgent(ranges); + mService.setUnderlyingNetworksForVpn(new Network[0]); + // VPN networks do not satisfy the default request and are automatically validated // by NetworkMonitor assertFalse(NetworkMonitorUtils.isValidationRequired( - vpnNetworkAgent.getNetworkCapabilities())); - vpnNetworkAgent.setNetworkValid(false /* isStrictMode */); + mMockVpn.getAgent().getNetworkCapabilities())); + mMockVpn.getAgent().setNetworkValid(false /* isStrictMode */); - vpnNetworkAgent.connect(false); - mMockVpn.connect(); - mMockVpn.setUnderlyingNetworks(new Network[0]); + mMockVpn.connect(false); - genericNetworkCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent); + genericNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn); genericNotVpnNetworkCallback.assertNoCallback(); wifiNetworkCallback.assertNoCallback(); - vpnNetworkCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent); - defaultCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent); - assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - - genericNetworkCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, vpnNetworkAgent); - genericNotVpnNetworkCallback.assertNoCallback(); - vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent, nc -> null == nc.getUids()); - defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, vpnNetworkAgent); + vpnNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn); + defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn); assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); ranges.clear(); - vpnNetworkAgent.setUids(ranges); + mMockVpn.setUids(ranges); - genericNetworkCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent); + genericNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn); genericNotVpnNetworkCallback.assertNoCallback(); wifiNetworkCallback.assertNoCallback(); - vpnNetworkCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent); + vpnNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn); // TODO : The default network callback should actually get a LOST call here (also see the // comment below for AVAILABLE). This is because ConnectivityService does not look at UID @@ -5308,19 +5516,18 @@ public class ConnectivityServiceTest { // can't currently update their UIDs without disconnecting, so this does not matter too // much, but that is the reason the test here has to check for an update to the // capabilities instead of the expected LOST then AVAILABLE. - defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, vpnNetworkAgent); + defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn); ranges.add(new UidRange(uid, uid)); mMockVpn.setUids(ranges); - vpnNetworkAgent.setUids(ranges); - genericNetworkCallback.expectAvailableCallbacksValidated(vpnNetworkAgent); + genericNetworkCallback.expectAvailableCallbacksValidated(mMockVpn); genericNotVpnNetworkCallback.assertNoCallback(); wifiNetworkCallback.assertNoCallback(); - vpnNetworkCallback.expectAvailableCallbacksValidated(vpnNetworkAgent); + vpnNetworkCallback.expectAvailableCallbacksValidated(mMockVpn); // TODO : Here like above, AVAILABLE would be correct, but because this can't actually // happen outside of the test, ConnectivityService does not rematch callbacks. - defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, vpnNetworkAgent); + defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn); mWiFiNetworkAgent.disconnect(); @@ -5330,13 +5537,13 @@ public class ConnectivityServiceTest { vpnNetworkCallback.assertNoCallback(); defaultCallback.assertNoCallback(); - vpnNetworkAgent.disconnect(); + mMockVpn.disconnect(); - genericNetworkCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent); + genericNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn); genericNotVpnNetworkCallback.assertNoCallback(); wifiNetworkCallback.assertNoCallback(); - vpnNetworkCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent); - defaultCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent); + vpnNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn); + defaultCallback.expectCallback(CallbackEntry.LOST, mMockVpn); assertEquals(null, mCm.getActiveNetwork()); mCm.unregisterNetworkCallback(genericNetworkCallback); @@ -5358,20 +5565,13 @@ public class ConnectivityServiceTest { defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent); assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - TestNetworkAgentWrapper - vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN); - final ArraySet<UidRange> ranges = new ArraySet<>(); - ranges.add(new UidRange(uid, uid)); - mMockVpn.setNetworkAgent(vpnNetworkAgent); - mMockVpn.setUids(ranges); - vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */, + mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */, false /* isStrictMode */); - mMockVpn.connect(); defaultCallback.assertNoCallback(); assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - vpnNetworkAgent.disconnect(); + mMockVpn.disconnect(); defaultCallback.assertNoCallback(); mCm.unregisterNetworkCallback(defaultCallback); @@ -5390,21 +5590,14 @@ public class ConnectivityServiceTest { defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent); assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - TestNetworkAgentWrapper - vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN); - final ArraySet<UidRange> ranges = new ArraySet<>(); - ranges.add(new UidRange(uid, uid)); - mMockVpn.setNetworkAgent(vpnNetworkAgent); - mMockVpn.setUids(ranges); - vpnNetworkAgent.connect(true /* validated */, true /* hasInternet */, + mMockVpn.establishForMyUid(true /* validated */, true /* hasInternet */, false /* isStrictMode */); - mMockVpn.connect(); - defaultCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent); + defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn); assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); - vpnNetworkAgent.disconnect(); - defaultCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent); + mMockVpn.disconnect(); + defaultCallback.expectCallback(CallbackEntry.LOST, mMockVpn); defaultCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent); mCm.unregisterNetworkCallback(defaultCallback); @@ -5422,44 +5615,36 @@ public class ConnectivityServiceTest { callback.assertNoCallback(); // Bring up a VPN that has the INTERNET capability, initially unvalidated. - final int uid = Process.myUid(); - final TestNetworkAgentWrapper - vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN); - final ArraySet<UidRange> ranges = new ArraySet<>(); - ranges.add(new UidRange(uid, uid)); - mMockVpn.setNetworkAgent(vpnNetworkAgent); - mMockVpn.setUids(ranges); - vpnNetworkAgent.connect(false /* validated */, true /* hasInternet */, + mMockVpn.establishForMyUid(false /* validated */, true /* hasInternet */, false /* isStrictMode */); - mMockVpn.connect(); // Even though the VPN is unvalidated, it becomes the default network for our app. - callback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent); + callback.expectAvailableCallbacksUnvalidated(mMockVpn); callback.assertNoCallback(); - assertTrue(vpnNetworkAgent.getScore() > mEthernetNetworkAgent.getScore()); - assertEquals(ConnectivityConstants.VPN_DEFAULT_SCORE, vpnNetworkAgent.getScore()); - assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + assertTrue(mMockVpn.getAgent().getScore() > mEthernetNetworkAgent.getScore()); + assertEquals(ConnectivityConstants.VPN_DEFAULT_SCORE, mMockVpn.getAgent().getScore()); + assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork()); - NetworkCapabilities nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork()); + NetworkCapabilities nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork()); assertFalse(nc.hasCapability(NET_CAPABILITY_VALIDATED)); assertTrue(nc.hasCapability(NET_CAPABILITY_INTERNET)); assertFalse(NetworkMonitorUtils.isValidationRequired( - vpnNetworkAgent.getNetworkCapabilities())); + mMockVpn.getAgent().getNetworkCapabilities())); assertTrue(NetworkMonitorUtils.isPrivateDnsValidationRequired( - vpnNetworkAgent.getNetworkCapabilities())); + mMockVpn.getAgent().getNetworkCapabilities())); // Pretend that the VPN network validates. - vpnNetworkAgent.setNetworkValid(false /* isStrictMode */); - vpnNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid()); + mMockVpn.getAgent().setNetworkValid(false /* isStrictMode */); + mMockVpn.getAgent().mNetworkMonitor.forceReevaluation(Process.myUid()); // Expect to see the validated capability, but no other changes, because the VPN is already // the default network for the app. - callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, vpnNetworkAgent); + callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mMockVpn); callback.assertNoCallback(); - vpnNetworkAgent.disconnect(); - callback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent); + mMockVpn.disconnect(); + callback.expectCallback(CallbackEntry.LOST, mMockVpn); callback.expectAvailableCallbacksValidated(mEthernetNetworkAgent); } @@ -5481,21 +5666,15 @@ public class ConnectivityServiceTest { mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED); mCellNetworkAgent.connect(true); - final TestNetworkAgentWrapper vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN); - final ArraySet<UidRange> ranges = new ArraySet<>(); - ranges.add(new UidRange(uid, uid)); - mMockVpn.setNetworkAgent(vpnNetworkAgent); - mMockVpn.connect(); - mMockVpn.setUids(ranges); - vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */, + mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */, false /* isStrictMode */); - vpnNetworkCallback.expectAvailableCallbacks(vpnNetworkAgent.getNetwork(), + vpnNetworkCallback.expectAvailableCallbacks(mMockVpn.getNetwork(), false /* suspended */, false /* validated */, false /* blocked */, TIMEOUT_MS); - vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent.getNetwork(), TIMEOUT_MS, + vpnNetworkCallback.expectCapabilitiesThat(mMockVpn.getNetwork(), TIMEOUT_MS, nc -> nc.hasCapability(NET_CAPABILITY_VALIDATED)); - final NetworkCapabilities nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork()); + final NetworkCapabilities nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork()); assertTrue(nc.hasTransport(TRANSPORT_VPN)); assertTrue(nc.hasTransport(TRANSPORT_CELLULAR)); assertFalse(nc.hasTransport(TRANSPORT_WIFI)); @@ -5517,18 +5696,11 @@ public class ConnectivityServiceTest { mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback); vpnNetworkCallback.assertNoCallback(); - final TestNetworkAgentWrapper - vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN); - final ArraySet<UidRange> ranges = new ArraySet<>(); - ranges.add(new UidRange(uid, uid)); - mMockVpn.setNetworkAgent(vpnNetworkAgent); - mMockVpn.connect(); - mMockVpn.setUids(ranges); - vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */, + mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */, false /* isStrictMode */); - vpnNetworkCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent); - nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork()); + vpnNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn); + nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork()); assertTrue(nc.hasTransport(TRANSPORT_VPN)); assertFalse(nc.hasTransport(TRANSPORT_CELLULAR)); assertFalse(nc.hasTransport(TRANSPORT_WIFI)); @@ -5545,7 +5717,7 @@ public class ConnectivityServiceTest { mService.setUnderlyingNetworksForVpn( new Network[] { mCellNetworkAgent.getNetwork() }); - vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent, + vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, (caps) -> caps.hasTransport(TRANSPORT_VPN) && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI) && !caps.hasCapability(NET_CAPABILITY_NOT_METERED) @@ -5559,7 +5731,7 @@ public class ConnectivityServiceTest { mService.setUnderlyingNetworksForVpn( new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() }); - vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent, + vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, (caps) -> caps.hasTransport(TRANSPORT_VPN) && caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI) && !caps.hasCapability(NET_CAPABILITY_NOT_METERED) @@ -5569,7 +5741,7 @@ public class ConnectivityServiceTest { mService.setUnderlyingNetworksForVpn( new Network[] { mCellNetworkAgent.getNetwork() }); - vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent, + vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, (caps) -> caps.hasTransport(TRANSPORT_VPN) && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI) && !caps.hasCapability(NET_CAPABILITY_NOT_METERED) @@ -5577,27 +5749,27 @@ public class ConnectivityServiceTest { // Remove NOT_SUSPENDED from the only network and observe VPN is now suspended. mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_SUSPENDED); - vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent, + vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, (caps) -> caps.hasTransport(TRANSPORT_VPN) && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI) && !caps.hasCapability(NET_CAPABILITY_NOT_METERED) && !caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); - vpnNetworkCallback.expectCallback(CallbackEntry.SUSPENDED, vpnNetworkAgent); + vpnNetworkCallback.expectCallback(CallbackEntry.SUSPENDED, mMockVpn); // Add NOT_SUSPENDED again and observe VPN is no longer suspended. mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED); - vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent, + vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, (caps) -> caps.hasTransport(TRANSPORT_VPN) && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI) && !caps.hasCapability(NET_CAPABILITY_NOT_METERED) && caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); - vpnNetworkCallback.expectCallback(CallbackEntry.RESUMED, vpnNetworkAgent); + vpnNetworkCallback.expectCallback(CallbackEntry.RESUMED, mMockVpn); // Use Wifi but not cell. Note the VPN is now unmetered and not suspended. mService.setUnderlyingNetworksForVpn( new Network[] { mWiFiNetworkAgent.getNetwork() }); - vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent, + vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, (caps) -> caps.hasTransport(TRANSPORT_VPN) && !caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI) && caps.hasCapability(NET_CAPABILITY_NOT_METERED) @@ -5607,7 +5779,7 @@ public class ConnectivityServiceTest { mService.setUnderlyingNetworksForVpn( new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() }); - vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent, + vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, (caps) -> caps.hasTransport(TRANSPORT_VPN) && caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI) && !caps.hasCapability(NET_CAPABILITY_NOT_METERED) @@ -5620,7 +5792,7 @@ public class ConnectivityServiceTest { // Stop using WiFi. The VPN is suspended again. mService.setUnderlyingNetworksForVpn( new Network[] { mCellNetworkAgent.getNetwork() }); - vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent, + vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, (caps) -> caps.hasTransport(TRANSPORT_VPN) && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasCapability(NET_CAPABILITY_NOT_METERED) @@ -5634,7 +5806,7 @@ public class ConnectivityServiceTest { mService.setUnderlyingNetworksForVpn( new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() }); - vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent, + vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, (caps) -> caps.hasTransport(TRANSPORT_VPN) && caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI) && !caps.hasCapability(NET_CAPABILITY_NOT_METERED) @@ -5645,14 +5817,14 @@ public class ConnectivityServiceTest { // Disconnect cell. Receive update without even removing the dead network from the // underlying networks – it's dead anyway. Not metered any more. mCellNetworkAgent.disconnect(); - vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent, + vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, (caps) -> caps.hasTransport(TRANSPORT_VPN) && !caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI) && caps.hasCapability(NET_CAPABILITY_NOT_METERED)); // Disconnect wifi too. No underlying networks means this is now metered. mWiFiNetworkAgent.disconnect(); - vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent, + vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, (caps) -> caps.hasTransport(TRANSPORT_VPN) && !caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI) && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)); @@ -5673,18 +5845,11 @@ public class ConnectivityServiceTest { mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback); vpnNetworkCallback.assertNoCallback(); - final TestNetworkAgentWrapper - vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN); - final ArraySet<UidRange> ranges = new ArraySet<>(); - ranges.add(new UidRange(uid, uid)); - mMockVpn.setNetworkAgent(vpnNetworkAgent); - mMockVpn.connect(); - mMockVpn.setUids(ranges); - vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */, + mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */, false /* isStrictMode */); - vpnNetworkCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent); - nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork()); + vpnNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn); + nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork()); assertTrue(nc.hasTransport(TRANSPORT_VPN)); assertFalse(nc.hasTransport(TRANSPORT_CELLULAR)); assertFalse(nc.hasTransport(TRANSPORT_WIFI)); @@ -5696,7 +5861,7 @@ public class ConnectivityServiceTest { mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); mCellNetworkAgent.connect(true); - vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent, + vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, (caps) -> caps.hasTransport(TRANSPORT_VPN) && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI) && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)); @@ -5706,7 +5871,7 @@ public class ConnectivityServiceTest { mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); mWiFiNetworkAgent.connect(true); - vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent, + vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, (caps) -> caps.hasTransport(TRANSPORT_VPN) && !caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI) && caps.hasCapability(NET_CAPABILITY_NOT_METERED)); @@ -5718,7 +5883,7 @@ public class ConnectivityServiceTest { // Disconnect wifi too. Now we have no default network. mWiFiNetworkAgent.disconnect(); - vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent, + vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, (caps) -> caps.hasTransport(TRANSPORT_VPN) && !caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI) && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)); @@ -5727,6 +5892,75 @@ public class ConnectivityServiceTest { } @Test + public void testVpnRestrictedUsers() throws Exception { + // NETWORK_SETTINGS is necessary to see the UID ranges in NetworkCapabilities. + mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS, + PERMISSION_GRANTED); + + final NetworkRequest request = new NetworkRequest.Builder() + .removeCapability(NET_CAPABILITY_NOT_VPN) + .build(); + final TestNetworkCallback callback = new TestNetworkCallback(); + mCm.registerNetworkCallback(request, callback); + + // Bring up a VPN + mMockVpn.establishForMyUid(); + callback.expectAvailableThenValidatedCallbacks(mMockVpn); + callback.assertNoCallback(); + + final int uid = Process.myUid(); + NetworkCapabilities nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork()); + assertNotNull("nc=" + nc, nc.getUids()); + assertEquals(nc.getUids(), uidRangesForUid(uid)); + + // Set an underlying network and expect to see the VPN transports change. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(true); + callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + callback.expectCapabilitiesThat(mMockVpn, (caps) + -> caps.hasTransport(TRANSPORT_VPN) + && caps.hasTransport(TRANSPORT_WIFI)); + callback.expectCapabilitiesThat(mWiFiNetworkAgent, (caps) + -> caps.hasCapability(NET_CAPABILITY_VALIDATED)); + + // Create a fake restricted profile whose parent is our user ID. + final int userId = UserHandle.getUserId(uid); + final int restrictedUserId = userId + 1; + final UserInfo info = new UserInfo(restrictedUserId, "user", UserInfo.FLAG_RESTRICTED); + info.restrictedProfileParentId = userId; + assertTrue(info.isRestricted()); + when(mUserManager.getUserInfo(restrictedUserId)).thenReturn(info); + final Intent addedIntent = new Intent(ACTION_USER_ADDED); + addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, restrictedUserId); + + // Send a USER_ADDED broadcast for it. + // The BroadcastReceiver for this broadcast checks that is being run on the handler thread. + final Handler handler = new Handler(mCsHandlerThread.getLooper()); + handler.post(() -> mServiceContext.sendBroadcast(addedIntent)); + + // Expect that the VPN UID ranges contain both |uid| and the UID range for the newly-added + // restricted user. + callback.expectCapabilitiesThat(mMockVpn, (caps) + -> caps.getUids().size() == 2 + && caps.getUids().contains(new UidRange(uid, uid)) + && caps.getUids().contains(UidRange.createForUser(restrictedUserId)) + && caps.hasTransport(TRANSPORT_VPN) + && caps.hasTransport(TRANSPORT_WIFI)); + + // Send a USER_REMOVED broadcast and expect to lose the UID range for the restricted user. + final Intent removedIntent = new Intent(ACTION_USER_REMOVED); + removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, restrictedUserId); + handler.post(() -> mServiceContext.sendBroadcast(removedIntent)); + + // Expect that the VPN gains the UID range for the restricted user. + callback.expectCapabilitiesThat(mMockVpn, (caps) + -> caps.getUids().size() == 1 + && caps.getUids().contains(new UidRange(uid, uid)) + && caps.hasTransport(TRANSPORT_VPN) + && caps.hasTransport(TRANSPORT_WIFI)); + } + + @Test public void testIsActiveNetworkMeteredOverWifi() throws Exception { // Returns true by default when no network is available. assertTrue(mCm.isActiveNetworkMetered()); @@ -5761,18 +5995,10 @@ public class ConnectivityServiceTest { assertTrue(mCm.isActiveNetworkMetered()); // Connect VPN network. By default it is using current default network (Cell). - TestNetworkAgentWrapper - vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN); - final ArraySet<UidRange> ranges = new ArraySet<>(); - final int uid = Process.myUid(); - ranges.add(new UidRange(uid, uid)); - mMockVpn.setNetworkAgent(vpnNetworkAgent); - mMockVpn.setUids(ranges); - vpnNetworkAgent.connect(true); - mMockVpn.connect(); - waitForIdle(); + mMockVpn.establishForMyUid(); + // Ensure VPN is now the active network. - assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork()); // Expect VPN to be metered. assertTrue(mCm.isActiveNetworkMetered()); @@ -5783,7 +6009,7 @@ public class ConnectivityServiceTest { mWiFiNetworkAgent.connect(true); waitForIdle(); // VPN should still be the active network. - assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork()); // Expect VPN to be unmetered as it should now be using WiFi (new default). assertFalse(mCm.isActiveNetworkMetered()); @@ -5801,7 +6027,6 @@ public class ConnectivityServiceTest { // VPN without any underlying networks is treated as metered. assertTrue(mCm.isActiveNetworkMetered()); - vpnNetworkAgent.disconnect(); mMockVpn.disconnect(); } @@ -5822,18 +6047,10 @@ public class ConnectivityServiceTest { assertFalse(mCm.isActiveNetworkMetered()); // Connect VPN network. - TestNetworkAgentWrapper - vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN); - final ArraySet<UidRange> ranges = new ArraySet<>(); - final int uid = Process.myUid(); - ranges.add(new UidRange(uid, uid)); - mMockVpn.setNetworkAgent(vpnNetworkAgent); - mMockVpn.setUids(ranges); - vpnNetworkAgent.connect(true); - mMockVpn.connect(); - waitForIdle(); + mMockVpn.establishForMyUid(); + // Ensure VPN is now the active network. - assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork()); // VPN is using Cell mService.setUnderlyingNetworksForVpn( new Network[] { mCellNetworkAgent.getNetwork() }); @@ -5873,7 +6090,6 @@ public class ConnectivityServiceTest { // VPN without underlying networks is treated as metered. assertTrue(mCm.isActiveNetworkMetered()); - vpnNetworkAgent.disconnect(); mMockVpn.disconnect(); } @@ -5888,17 +6104,11 @@ public class ConnectivityServiceTest { assertFalse(mCm.isActiveNetworkMetered()); // Connect VPN network. - TestNetworkAgentWrapper - vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN); - final ArraySet<UidRange> ranges = new ArraySet<>(); - final int uid = Process.myUid(); - ranges.add(new UidRange(uid, uid)); - mMockVpn.setNetworkAgent(vpnNetworkAgent); - mMockVpn.setUids(ranges); - vpnNetworkAgent.connect(true); - mMockVpn.connectAsAlwaysMetered(); + mMockVpn.registerAgent(true /* isAlwaysMetered */, uidRangesForUid(Process.myUid()), + new LinkProperties()); + mMockVpn.connect(true); waitForIdle(); - assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork()); // VPN is tracking current platform default (WiFi). mService.setUnderlyingNetworksForVpn(null); @@ -5922,7 +6132,7 @@ public class ConnectivityServiceTest { assertTrue(mCm.isActiveNetworkMetered()); - vpnNetworkAgent.disconnect(); + mMockVpn.disconnect(); } @Test @@ -6654,34 +6864,21 @@ public class ConnectivityServiceTest { waitForIdle(); assertNull(mService.getProxyForNetwork(null)); - // Set up a VPN network with a proxy - final int uid = Process.myUid(); - final TestNetworkAgentWrapper - vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN); - final ArraySet<UidRange> ranges = new ArraySet<>(); - ranges.add(new UidRange(uid, uid)); - mMockVpn.setUids(ranges); + // Connect a VPN network with a proxy. LinkProperties testLinkProperties = new LinkProperties(); testLinkProperties.setHttpProxy(testProxyInfo); - vpnNetworkAgent.sendLinkProperties(testLinkProperties); - waitForIdle(); - - // Connect to VPN with proxy - mMockVpn.setNetworkAgent(vpnNetworkAgent); - vpnNetworkAgent.connect(true); - mMockVpn.connect(); - waitForIdle(); + mMockVpn.establishForMyUid(testLinkProperties); // Test that the VPN network returns a proxy, and the WiFi does not. - assertEquals(testProxyInfo, mService.getProxyForNetwork(vpnNetworkAgent.getNetwork())); + assertEquals(testProxyInfo, mService.getProxyForNetwork(mMockVpn.getNetwork())); assertEquals(testProxyInfo, mService.getProxyForNetwork(null)); assertNull(mService.getProxyForNetwork(mWiFiNetworkAgent.getNetwork())); // Test that the VPN network returns no proxy when it is set to null. testLinkProperties.setHttpProxy(null); - vpnNetworkAgent.sendLinkProperties(testLinkProperties); + mMockVpn.sendLinkProperties(testLinkProperties); waitForIdle(); - assertNull(mService.getProxyForNetwork(vpnNetworkAgent.getNetwork())); + assertNull(mService.getProxyForNetwork(mMockVpn.getNetwork())); assertNull(mService.getProxyForNetwork(null)); // Set WiFi proxy and check that the vpn proxy is still null. @@ -6692,7 +6889,7 @@ public class ConnectivityServiceTest { // Disconnect from VPN and check that the active network, which is now the WiFi, has the // correct proxy setting. - vpnNetworkAgent.disconnect(); + mMockVpn.disconnect(); waitForIdle(); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); assertEquals(testProxyInfo, mService.getProxyForNetwork(mWiFiNetworkAgent.getNetwork())); @@ -6707,17 +6904,17 @@ public class ConnectivityServiceTest { lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE)); // The uid range needs to cover the test app so the network is visible to it. final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER)); - final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(lp, VPN_UID, vpnRange); + mMockVpn.establish(lp, VPN_UID, vpnRange); - // Connected VPN should have interface rules set up. There are two expected invocations, - // one during VPN uid update, one during VPN LinkProperties update + // A connected VPN should have interface rules set up. There are two expected invocations, + // one during the VPN initial connection, one during the VPN LinkProperties update. ArgumentCaptor<int[]> uidCaptor = ArgumentCaptor.forClass(int[].class); verify(mMockNetd, times(2)).firewallAddUidInterfaceRules(eq("tun0"), uidCaptor.capture()); assertContainsExactly(uidCaptor.getAllValues().get(0), APP1_UID, APP2_UID); assertContainsExactly(uidCaptor.getAllValues().get(1), APP1_UID, APP2_UID); assertTrue(mService.mPermissionMonitor.getVpnUidRanges("tun0").equals(vpnRange)); - vpnNetworkAgent.disconnect(); + mMockVpn.disconnect(); waitForIdle(); // Disconnected VPN should have interface rules removed @@ -6734,8 +6931,7 @@ public class ConnectivityServiceTest { lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null)); // The uid range needs to cover the test app so the network is visible to it. final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER)); - final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn( - lp, Process.SYSTEM_UID, vpnRange); + mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange); // Legacy VPN should not have interface rules set up verify(mMockNetd, never()).firewallAddUidInterfaceRules(any(), any()); @@ -6750,8 +6946,7 @@ public class ConnectivityServiceTest { lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE)); // The uid range needs to cover the test app so the network is visible to it. final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER)); - final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn( - lp, Process.SYSTEM_UID, vpnRange); + mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange); // IPv6 unreachable route should not be misinterpreted as a default route verify(mMockNetd, never()).firewallAddUidInterfaceRules(any(), any()); @@ -6765,7 +6960,7 @@ public class ConnectivityServiceTest { lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null)); // The uid range needs to cover the test app so the network is visible to it. final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER)); - final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(lp, VPN_UID, vpnRange); + mMockVpn.establish(lp, VPN_UID, vpnRange); // Connected VPN should have interface rules set up. There are two expected invocations, // one during VPN uid update, one during VPN LinkProperties update @@ -6777,7 +6972,7 @@ public class ConnectivityServiceTest { reset(mMockNetd); InOrder inOrder = inOrder(mMockNetd); lp.setInterfaceName("tun1"); - vpnNetworkAgent.sendLinkProperties(lp); + mMockVpn.sendLinkProperties(lp); waitForIdle(); // VPN handover (switch to a new interface) should result in rules being updated (old rules // removed first, then new rules added) @@ -6790,7 +6985,7 @@ public class ConnectivityServiceTest { lp = new LinkProperties(); lp.setInterfaceName("tun1"); lp.addRoute(new RouteInfo(new IpPrefix("192.0.2.0/24"), null, "tun1")); - vpnNetworkAgent.sendLinkProperties(lp); + mMockVpn.sendLinkProperties(lp); waitForIdle(); // VPN not routing everything should no longer have interface filtering rules verify(mMockNetd).firewallRemoveUidInterfaceRules(uidCaptor.capture()); @@ -6801,7 +6996,7 @@ public class ConnectivityServiceTest { lp.setInterfaceName("tun1"); lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE)); lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null)); - vpnNetworkAgent.sendLinkProperties(lp); + mMockVpn.sendLinkProperties(lp); waitForIdle(); // Back to routing all IPv6 traffic should have filtering rules verify(mMockNetd).firewallAddUidInterfaceRules(eq("tun1"), uidCaptor.capture()); @@ -6816,8 +7011,7 @@ public class ConnectivityServiceTest { lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null)); // The uid range needs to cover the test app so the network is visible to it. final UidRange vpnRange = UidRange.createForUser(VPN_USER); - final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(lp, VPN_UID, - Collections.singleton(vpnRange)); + mMockVpn.establish(lp, VPN_UID, Collections.singleton(vpnRange)); reset(mMockNetd); InOrder inOrder = inOrder(mMockNetd); @@ -6826,7 +7020,7 @@ public class ConnectivityServiceTest { final Set<UidRange> newRanges = new HashSet<>(Arrays.asList( new UidRange(vpnRange.start, APP1_UID - 1), new UidRange(APP1_UID + 1, vpnRange.stop))); - vpnNetworkAgent.setUids(newRanges); + mMockVpn.setUids(newRanges); waitForIdle(); ArgumentCaptor<int[]> uidCaptor = ArgumentCaptor.forClass(int[].class); @@ -6967,7 +7161,7 @@ public class ConnectivityServiceTest { private void setupConnectionOwnerUid(int vpnOwnerUid, @VpnManager.VpnType int vpnType) throws Exception { final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER)); - establishVpn(new LinkProperties(), vpnOwnerUid, vpnRange); + mMockVpn.establish(new LinkProperties(), vpnOwnerUid, vpnRange); mMockVpn.setVpnType(vpnType); final VpnInfo vpnInfo = new VpnInfo(); @@ -7048,19 +7242,6 @@ public class ConnectivityServiceTest { mService.getConnectionOwnerUid(getTestConnectionInfo()); } - private TestNetworkAgentWrapper establishVpn( - LinkProperties lp, int ownerUid, Set<UidRange> vpnRange) throws Exception { - final TestNetworkAgentWrapper - vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN, lp); - vpnNetworkAgent.getNetworkCapabilities().setOwnerUid(ownerUid); - mMockVpn.setNetworkAgent(vpnNetworkAgent); - mMockVpn.connect(); - mMockVpn.setUids(vpnRange); - vpnNetworkAgent.connect(true); - waitForIdle(); - return vpnNetworkAgent; - } - private static PackageInfo buildPackageInfo(boolean hasSystemPermission, int uid) { final PackageInfo packageInfo = new PackageInfo(); if (hasSystemPermission) { @@ -7240,22 +7421,23 @@ public class ConnectivityServiceTest { setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION); + + mMockVpn.establishForMyUid(); + + // Wait for networks to connect and broadcasts to be sent before removing permissions. + waitForIdle(); mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED); - // setUp() calls mockVpn() which adds a VPN with the Test Runner's uid. Configure it to be - // active - final VpnInfo info = new VpnInfo(); - info.ownerUid = Process.myUid(); - info.vpnIface = "interface"; - mMockVpn.setVpnInfo(info); - mMockVpn.overrideUnderlyingNetworks(new Network[] {network}); + assertTrue(mService.setUnderlyingNetworksForVpn(new Network[] {network})); + waitForIdle(); assertTrue( "Active VPN permission not applied", mService.checkConnectivityDiagnosticsPermissions( Process.myPid(), Process.myUid(), naiWithoutUid, mContext.getOpPackageName())); - mMockVpn.overrideUnderlyingNetworks(null); + assertTrue(mService.setUnderlyingNetworksForVpn(null)); + waitForIdle(); assertFalse( "VPN shouldn't receive callback on non-underlying network", mService.checkConnectivityDiagnosticsPermissions( @@ -7276,8 +7458,6 @@ public class ConnectivityServiceTest { Manifest.permission.ACCESS_FINE_LOCATION); mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED); - // Disconnect mock vpn so the uid check on NetworkAgentInfo is tested - mMockVpn.disconnect(); assertTrue( "NetworkCapabilities administrator uid permission not applied", mService.checkConnectivityDiagnosticsPermissions( @@ -7480,4 +7660,39 @@ public class ConnectivityServiceTest { mCm.unregisterNetworkCallback(networkCallback); } + + @Test + public void testDumpDoesNotCrash() { + StringWriter stringWriter = new StringWriter(); + + mService.dump(new FileDescriptor(), new PrintWriter(stringWriter), new String[0]); + + assertFalse(stringWriter.toString().isEmpty()); + } + + @Test + public void testRequestsSortedByIdSortsCorrectly() { + final TestNetworkCallback genericNetworkCallback = new TestNetworkCallback(); + final TestNetworkCallback wifiNetworkCallback = new TestNetworkCallback(); + final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback(); + final NetworkRequest genericRequest = new NetworkRequest.Builder() + .clearCapabilities().build(); + final NetworkRequest wifiRequest = new NetworkRequest.Builder() + .addTransportType(TRANSPORT_WIFI).build(); + final NetworkRequest cellRequest = new NetworkRequest.Builder() + .addTransportType(TRANSPORT_CELLULAR).build(); + mCm.registerNetworkCallback(genericRequest, genericNetworkCallback); + mCm.registerNetworkCallback(wifiRequest, wifiNetworkCallback); + mCm.registerNetworkCallback(cellRequest, cellNetworkCallback); + + ConnectivityService.NetworkRequestInfo[] nriOutput = mService.requestsSortedById(); + + assertTrue(nriOutput.length > 1); + for (int i = 0; i < nriOutput.length - 1; i++) { + boolean isRequestIdInOrder = + nriOutput[i].mRequests.get(0).requestId + < nriOutput[i + 1].mRequests.get(0).requestId; + assertTrue(isRequestIdInOrder); + } + } } diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java index 1dcc07c6db81..d0db55f2bb13 100644 --- a/tests/net/java/com/android/server/connectivity/VpnTest.java +++ b/tests/net/java/com/android/server/connectivity/VpnTest.java @@ -41,6 +41,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doAnswer; @@ -86,10 +87,10 @@ import android.os.Build.VERSION_CODES; import android.os.Bundle; import android.os.ConditionVariable; import android.os.INetworkManagementService; -import android.os.Looper; import android.os.Process; import android.os.UserHandle; import android.os.UserManager; +import android.os.test.TestLooper; import android.provider.Settings; import android.security.Credentials; import android.security.KeyStore; @@ -100,6 +101,7 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.internal.R; +import com.android.internal.net.LegacyVpnInfo; import com.android.internal.net.VpnConfig; import com.android.internal.net.VpnProfile; import com.android.server.IpSecService; @@ -223,6 +225,8 @@ public class VpnTest { .thenReturn(mNotificationManager); when(mContext.getSystemService(eq(Context.CONNECTIVITY_SERVICE))) .thenReturn(mConnectivityManager); + when(mContext.getSystemServiceName(eq(ConnectivityManager.class))) + .thenReturn(Context.CONNECTIVITY_SERVICE); when(mContext.getSystemService(eq(Context.IPSEC_SERVICE))).thenReturn(mIpSecManager); when(mContext.getString(R.string.config_customVpnAlwaysOnDisconnectedDialogComponent)) .thenReturn(Resources.getSystem().getString( @@ -589,7 +593,7 @@ public class VpnTest { } @Test - public void testNotificationShownForAlwaysOnApp() { + public void testNotificationShownForAlwaysOnApp() throws Exception { final UserHandle userHandle = UserHandle.of(primaryUser.id); final Vpn vpn = createVpn(primaryUser.id); setMockedUsers(primaryUser); @@ -619,7 +623,6 @@ public class VpnTest { @Test public void testCapabilities() { - final Vpn vpn = createVpn(primaryUser.id); setMockedUsers(primaryUser); final Network mobile = new Network(1); @@ -1037,7 +1040,7 @@ public class VpnTest { when(exception.getErrorType()) .thenReturn(IkeProtocolException.ERROR_TYPE_AUTHENTICATION_FAILED); - final Vpn vpn = startLegacyVpn(mVpnProfile); + final Vpn vpn = startLegacyVpn(createVpn(primaryUser.id), (mVpnProfile)); final NetworkCallback cb = triggerOnAvailableAndGetCallback(); // Wait for createIkeSession() to be called before proceeding in order to ensure consistent @@ -1048,20 +1051,20 @@ public class VpnTest { ikeCb.onClosedExceptionally(exception); verify(mConnectivityManager, timeout(TEST_TIMEOUT_MS)).unregisterNetworkCallback(eq(cb)); - assertEquals(DetailedState.FAILED, vpn.getNetworkInfo().getDetailedState()); + assertEquals(LegacyVpnInfo.STATE_FAILED, vpn.getLegacyVpnInfo().state); } @Test public void testStartPlatformVpnIllegalArgumentExceptionInSetup() throws Exception { when(mIkev2SessionCreator.createIkeSession(any(), any(), any(), any(), any(), any())) .thenThrow(new IllegalArgumentException()); - final Vpn vpn = startLegacyVpn(mVpnProfile); + final Vpn vpn = startLegacyVpn(createVpn(primaryUser.id), mVpnProfile); final NetworkCallback cb = triggerOnAvailableAndGetCallback(); // Wait for createIkeSession() to be called before proceeding in order to ensure consistent // state verify(mConnectivityManager, timeout(TEST_TIMEOUT_MS)).unregisterNetworkCallback(eq(cb)); - assertEquals(DetailedState.FAILED, vpn.getNetworkInfo().getDetailedState()); + assertEquals(LegacyVpnInfo.STATE_FAILED, vpn.getLegacyVpnInfo().state); } private void setAndVerifyAlwaysOnPackage(Vpn vpn, int uid, boolean lockdownEnabled) { @@ -1100,8 +1103,7 @@ public class VpnTest { // a subsequent CL. } - public Vpn startLegacyVpn(final VpnProfile vpnProfile) throws Exception { - final Vpn vpn = createVpn(primaryUser.id); + private Vpn startLegacyVpn(final Vpn vpn, final VpnProfile vpnProfile) throws Exception { setMockedUsers(primaryUser); // Dummy egress interface @@ -1118,7 +1120,7 @@ public class VpnTest { @Test public void testStartPlatformVpn() throws Exception { - startLegacyVpn(mVpnProfile); + startLegacyVpn(createVpn(primaryUser.id), mVpnProfile); // TODO: Test the Ikev2VpnRunner started up properly. Relies on utility methods added in // a subsequent patch. } @@ -1153,7 +1155,7 @@ public class VpnTest { legacyRunnerReady.open(); return new Network(102); }); - final Vpn vpn = startLegacyVpn(profile); + final Vpn vpn = startLegacyVpn(createVpn(primaryUser.id), profile); final TestDeps deps = (TestDeps) vpn.mDeps; try { // udppsk and 1701 are the values for TYPE_L2TP_IPSEC_PSK @@ -1287,8 +1289,13 @@ public class VpnTest { doReturn(UserHandle.of(userId)).when(asUserContext).getUser(); when(mContext.createContextAsUser(eq(UserHandle.of(userId)), anyInt())) .thenReturn(asUserContext); - return new Vpn(Looper.myLooper(), mContext, new TestDeps(), mNetService, + final TestLooper testLooper = new TestLooper(); + final Vpn vpn = new Vpn(testLooper.getLooper(), mContext, new TestDeps(), mNetService, userId, mKeyStore, mSystemServices, mIkev2SessionCreator); + verify(mConnectivityManager, times(1)).registerNetworkProvider(argThat( + provider -> provider.getName().contains("VpnNetworkProvider") + )); + return vpn; } private static void assertBlocked(Vpn vpn, int... uids) { diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp index f92c602fd761..b760958e0edc 100644 --- a/tools/aapt2/cmd/Link.cpp +++ b/tools/aapt2/cmd/Link.cpp @@ -1568,6 +1568,22 @@ class Linker { return true; } + ResourceEntry* ResolveTableEntry(LinkContext* context, ResourceTable* table, + Reference* reference) { + if (!reference || !reference->name) { + return nullptr; + } + auto name_ref = ResourceNameRef(reference->name.value()); + if (name_ref.package.empty()) { + name_ref.package = context->GetCompilationPackage(); + } + const auto search_result = table->FindResource(name_ref); + if (!search_result) { + return nullptr; + } + return search_result.value().entry; + } + void AliasAdaptiveIcon(xml::XmlResource* manifest, ResourceTable* table) { const xml::Element* application = manifest->root->FindChild("", "application"); if (!application) { @@ -1582,22 +1598,13 @@ class Linker { // Find the icon resource defined within the application. const auto icon_reference = ValueCast<Reference>(icon->compiled_value.get()); - if (!icon_reference || !icon_reference->name) { - return; - } - - auto icon_name = ResourceNameRef(icon_reference->name.value()); - if (icon_name.package.empty()) { - icon_name.package = context_->GetCompilationPackage(); - } - - const auto icon_entry_result = table->FindResource(icon_name); - if (!icon_entry_result) { + const auto icon_entry = ResolveTableEntry(context_, table, icon_reference); + if (!icon_entry) { return; } int icon_max_sdk = 0; - for (auto& config_value : icon_entry_result.value().entry->values) { + for (auto& config_value : icon_entry->values) { icon_max_sdk = (icon_max_sdk < config_value->config.sdkVersion) ? config_value->config.sdkVersion : icon_max_sdk; } @@ -1608,22 +1615,13 @@ class Linker { // Find the roundIcon resource defined within the application. const auto round_icon_reference = ValueCast<Reference>(round_icon->compiled_value.get()); - if (!round_icon_reference || !round_icon_reference->name) { - return; - } - - auto round_icon_name = ResourceNameRef(round_icon_reference->name.value()); - if (round_icon_name.package.empty()) { - round_icon_name.package = context_->GetCompilationPackage(); - } - - const auto round_icon_entry_result = table->FindResource(round_icon_name); - if (!round_icon_entry_result) { + const auto round_icon_entry = ResolveTableEntry(context_, table, round_icon_reference); + if (!round_icon_entry) { return; } int round_icon_max_sdk = 0; - for (auto& config_value : round_icon_entry_result.value().entry->values) { + for (auto& config_value : round_icon_entry->values) { round_icon_max_sdk = (round_icon_max_sdk < config_value->config.sdkVersion) ? config_value->config.sdkVersion : round_icon_max_sdk; } @@ -1634,7 +1632,7 @@ class Linker { } // Add an equivalent v26 entry to the roundIcon for each v26 variant of the regular icon. - for (auto& config_value : icon_entry_result.value().entry->values) { + for (auto& config_value : icon_entry->values) { if (config_value->config.sdkVersion < SDK_O) { continue; } @@ -1645,12 +1643,62 @@ class Linker { << "\" for round icon compatibility"); auto value = icon_reference->Clone(&table->string_pool); - auto round_config_value = round_icon_entry_result.value().entry->FindOrCreateValue( - config_value->config, config_value->product); + auto round_config_value = + round_icon_entry->FindOrCreateValue(config_value->config, config_value->product); round_config_value->value.reset(value); } } + bool VerifySharedUserId(xml::XmlResource* manifest, ResourceTable* table) { + const xml::Element* manifest_el = xml::FindRootElement(manifest->root.get()); + if (manifest_el == nullptr) { + return true; + } + if (!manifest_el->namespace_uri.empty() || manifest_el->name != "manifest") { + return true; + } + const xml::Attribute* attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "sharedUserId"); + if (!attr) { + return true; + } + const auto validate = [&](const std::string& shared_user_id) -> bool { + if (util::IsAndroidSharedUserId(context_->GetCompilationPackage(), shared_user_id)) { + return true; + } + DiagMessage error_msg(manifest_el->line_number); + error_msg << "attribute 'sharedUserId' in <manifest> tag is not a valid shared user id: '" + << shared_user_id << "'"; + if (options_.manifest_fixer_options.warn_validation) { + // Treat the error only as a warning. + context_->GetDiagnostics()->Warn(error_msg); + return true; + } + context_->GetDiagnostics()->Error(error_msg); + return false; + }; + // If attr->compiled_value is not null, check if it is a ref + if (attr->compiled_value) { + const auto ref = ValueCast<Reference>(attr->compiled_value.get()); + if (ref == nullptr) { + return true; + } + const auto shared_user_id_entry = ResolveTableEntry(context_, table, ref); + if (!shared_user_id_entry) { + return true; + } + for (const auto& value : shared_user_id_entry->values) { + const auto str_value = ValueCast<String>(value->value.get()); + if (str_value != nullptr && !validate(*str_value->value)) { + return false; + } + } + return true; + } + + // Fallback to checking the raw value + return validate(attr->value); + } + // Writes the AndroidManifest, ResourceTable, and all XML files referenced by the ResourceTable // to the IArchiveWriter. bool WriteApk(IArchiveWriter* writer, proguard::KeepSet* keep_set, xml::XmlResource* manifest, @@ -1672,6 +1720,11 @@ class Linker { // See (b/34829129) AliasAdaptiveIcon(manifest, table); + // Verify the shared user id here to handle the case of reference value. + if (!VerifySharedUserId(manifest, table)) { + return false; + } + ResourceFileFlattenerOptions file_flattener_options; file_flattener_options.keep_raw_values = keep_raw_values; file_flattener_options.do_not_compress_anything = options_.do_not_compress_anything; diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp index fd3a4c035076..c03661ca2366 100644 --- a/tools/aapt2/link/ManifestFixer.cpp +++ b/tools/aapt2/link/ManifestFixer.cpp @@ -163,7 +163,8 @@ static bool AutoGenerateIsFeatureSplit(xml::Element* el, SourcePathDiagnostics* return true; } -static bool VerifyManifest(xml::Element* el, SourcePathDiagnostics* diag) { +static bool VerifyManifest(xml::Element* el, xml::XmlActionExecutorPolicy policy, + SourcePathDiagnostics* diag) { xml::Attribute* attr = el->FindAttribute({}, "package"); if (!attr) { diag->Error(DiagMessage(el->line_number) @@ -174,10 +175,16 @@ static bool VerifyManifest(xml::Element* el, SourcePathDiagnostics* diag) { << "attribute 'package' in <manifest> tag must not be a reference"); return false; } else if (!util::IsAndroidPackageName(attr->value)) { - diag->Error(DiagMessage(el->line_number) - << "attribute 'package' in <manifest> tag is not a valid Android package name: '" - << attr->value << "'"); - return false; + DiagMessage error_msg(el->line_number); + error_msg << "attribute 'package' in <manifest> tag is not a valid Android package name: '" + << attr->value << "'"; + if (policy == xml::XmlActionExecutorPolicy::kAllowListWarning) { + // Treat the error only as a warning. + diag->Warn(error_msg); + } else { + diag->Error(error_msg); + return false; + } } attr = el->FindAttribute({}, "split"); diff --git a/tools/aapt2/util/Util.cpp b/tools/aapt2/util/Util.cpp index ef33c3463a81..28330db966af 100644 --- a/tools/aapt2/util/Util.cpp +++ b/tools/aapt2/util/Util.cpp @@ -38,6 +38,11 @@ using ::android::StringPiece16; namespace aapt { namespace util { +// Package name and shared user id would be used as a part of the file name. +// Limits size to 223 and reserves 32 for the OS. +// See frameworks/base/core/java/android/content/pm/parsing/ParsingPackageUtils.java +constexpr static const size_t kMaxPackageNameSize = 223; + static std::vector<std::string> SplitAndTransform( const StringPiece& str, char sep, const std::function<char(char)>& f) { std::vector<std::string> parts; @@ -169,9 +174,21 @@ static int IsAndroidNameImpl(const StringPiece& str) { } bool IsAndroidPackageName(const StringPiece& str) { + if (str.size() > kMaxPackageNameSize) { + return false; + } return IsAndroidNameImpl(str) > 1 || str == "android"; } +bool IsAndroidSharedUserId(const android::StringPiece& package_name, + const android::StringPiece& shared_user_id) { + if (shared_user_id.size() > kMaxPackageNameSize) { + return false; + } + return shared_user_id.empty() || IsAndroidNameImpl(shared_user_id) > 1 || + package_name == "android"; +} + bool IsAndroidSplitName(const StringPiece& str) { return IsAndroidNameImpl(str) > 0; } diff --git a/tools/aapt2/util/Util.h b/tools/aapt2/util/Util.h index a956957eace8..c77aca31a810 100644 --- a/tools/aapt2/util/Util.h +++ b/tools/aapt2/util/Util.h @@ -81,6 +81,7 @@ bool IsJavaPackageName(const android::StringPiece& str); // - First character of each component (separated by '.') must be an ASCII letter. // - Subsequent characters of a component can be ASCII alphanumeric or an underscore. // - Package must contain at least two components, unless it is 'android'. +// - The maximum package name length is 223. bool IsAndroidPackageName(const android::StringPiece& str); // Tests that the string is a valid Android split name. @@ -88,6 +89,15 @@ bool IsAndroidPackageName(const android::StringPiece& str); // - Subsequent characters of a component can be ASCII alphanumeric or an underscore. bool IsAndroidSplitName(const android::StringPiece& str); +// Tests that the string is a valid Android shared user id. +// - First character of each component (separated by '.') must be an ASCII letter. +// - Subsequent characters of a component can be ASCII alphanumeric or an underscore. +// - Must contain at least two components, unless package name is 'android'. +// - The maximum shared user id length is 223. +// - Treat empty string as valid, it's the case of no shared user id. +bool IsAndroidSharedUserId(const android::StringPiece& package_name, + const android::StringPiece& shared_user_id); + // Converts the class name to a fully qualified class name from the given // `package`. Ex: // diff --git a/tools/aapt2/util/Util_test.cpp b/tools/aapt2/util/Util_test.cpp index d4e3bec24bd1..4ebcb115306f 100644 --- a/tools/aapt2/util/Util_test.cpp +++ b/tools/aapt2/util/Util_test.cpp @@ -27,6 +27,17 @@ using ::testing::SizeIs; namespace aapt { +// Test that a max package name size 223 is valid. +static const std::string kMaxPackageName = + "com.foo.nameRw8ajIGbYmqPuO0K7TYJFsI2pjlDAS0pYOYQlJvtQux" + "SoBKV1hMyNh4XfmcMj8OgPHfFaTXeKEHFMdGQHpw9Dz9Uqr8h1krgJLRv2aXyPCsGdVwBJzfZ4COVRiX3sc9O" + "CUrTTvZe6wXlgKb5Qz5qdkTBZ5euzGeoyZwestDTBIgT5exAl5efnznwzceS7VsIntgY10UUQvaoTsLBO6l"; +// Test that a long package name size 224 is invalid. +static const std::string kLongPackageName = + "com.foo.nameRw8ajIGbYmqPuO0K7TYJFsI2pjlDAS0pYOYQlJvtQu" + "xSoBKV1hMyNh4XfmcMj8OgPHfFaTXeKEHFMdGQHpw9Dz9Uqr8h1krgJLRv2aXyPCsGdVwBJzfZ4COVRiX3sc9O" + "CUrTTvZe6wXlgKb5Qz5qdkTBZ5euzGeoyZwestDTBIgT5exAl5efnznwzceS7VsIntgY10UUQvaoTsLBO6le"; + TEST(UtilTest, TrimOnlyWhitespace) { const StringPiece trimmed = util::TrimWhitespace("\n "); EXPECT_TRUE(trimmed.empty()); @@ -108,6 +119,7 @@ TEST(UtilTest, IsAndroidPackageName) { EXPECT_TRUE(util::IsAndroidPackageName("com.foo.test_thing")); EXPECT_TRUE(util::IsAndroidPackageName("com.foo.testing_thing_")); EXPECT_TRUE(util::IsAndroidPackageName("com.foo.test_99_")); + EXPECT_TRUE(util::IsAndroidPackageName(kMaxPackageName)); EXPECT_FALSE(util::IsAndroidPackageName("android._test")); EXPECT_FALSE(util::IsAndroidPackageName("com")); @@ -116,6 +128,27 @@ TEST(UtilTest, IsAndroidPackageName) { EXPECT_FALSE(util::IsAndroidPackageName(".android")); EXPECT_FALSE(util::IsAndroidPackageName("..")); EXPECT_FALSE(util::IsAndroidPackageName("cøm.foo")); + EXPECT_FALSE(util::IsAndroidPackageName(kLongPackageName)); +} + +TEST(UtilTest, IsAndroidSharedUserId) { + EXPECT_TRUE(util::IsAndroidSharedUserId("android", "foo")); + EXPECT_TRUE(util::IsAndroidSharedUserId("com.foo", "android.test")); + EXPECT_TRUE(util::IsAndroidSharedUserId("com.foo", "com.foo")); + EXPECT_TRUE(util::IsAndroidSharedUserId("com.foo", "com.foo.test_thing")); + EXPECT_TRUE(util::IsAndroidSharedUserId("com.foo", "com.foo.testing_thing_")); + EXPECT_TRUE(util::IsAndroidSharedUserId("com.foo", "com.foo.test_99_")); + EXPECT_TRUE(util::IsAndroidSharedUserId("com.foo", "")); + EXPECT_TRUE(util::IsAndroidSharedUserId("com.foo", kMaxPackageName)); + + EXPECT_FALSE(util::IsAndroidSharedUserId("com.foo", "android._test")); + EXPECT_FALSE(util::IsAndroidSharedUserId("com.foo", "com")); + EXPECT_FALSE(util::IsAndroidSharedUserId("com.foo", "_android")); + EXPECT_FALSE(util::IsAndroidSharedUserId("com.foo", "android.")); + EXPECT_FALSE(util::IsAndroidSharedUserId("com.foo", ".android")); + EXPECT_FALSE(util::IsAndroidSharedUserId("com.foo", "..")); + EXPECT_FALSE(util::IsAndroidSharedUserId("com.foo", "cøm.foo")); + EXPECT_FALSE(util::IsAndroidSharedUserId("com.foo", kLongPackageName)); } TEST(UtilTest, FullyQualifiedClassName) { diff --git a/tools/aapt2/xml/XmlActionExecutor.cpp b/tools/aapt2/xml/XmlActionExecutor.cpp index fab17c949dd8..ea42d26358a8 100644 --- a/tools/aapt2/xml/XmlActionExecutor.cpp +++ b/tools/aapt2/xml/XmlActionExecutor.cpp @@ -21,23 +21,34 @@ using ::android::StringPiece; namespace aapt { namespace xml { -static bool wrapper_one(XmlNodeAction::ActionFunc& f, Element* el, SourcePathDiagnostics*) { +static bool wrapper_one(const XmlNodeAction::ActionFunc& f, Element* el, + const XmlActionExecutorPolicy& policy, SourcePathDiagnostics*) { return f(el); } -static bool wrapper_two(XmlNodeAction::ActionFuncWithDiag& f, Element* el, - SourcePathDiagnostics* diag) { +static bool wrapper_two(const XmlNodeAction::ActionFuncWithDiag& f, Element* el, + const XmlActionExecutorPolicy& policy, SourcePathDiagnostics* diag) { return f(el, diag); } +static bool wrapper_three(const XmlNodeAction::ActionFuncWithPolicyAndDiag& f, Element* el, + const XmlActionExecutorPolicy& policy, SourcePathDiagnostics* diag) { + return f(el, policy, diag); +} + void XmlNodeAction::Action(XmlNodeAction::ActionFunc f) { - actions_.emplace_back(std::bind( - wrapper_one, std::move(f), std::placeholders::_1, std::placeholders::_2)); + actions_.emplace_back(std::bind(wrapper_one, std::move(f), std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3)); } void XmlNodeAction::Action(XmlNodeAction::ActionFuncWithDiag f) { - actions_.emplace_back(std::bind( - wrapper_two, std::move(f), std::placeholders::_1, std::placeholders::_2)); + actions_.emplace_back(std::bind(wrapper_two, std::move(f), std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3)); +} + +void XmlNodeAction::Action(XmlNodeAction::ActionFuncWithPolicyAndDiag f) { + actions_.emplace_back(std::bind(wrapper_three, std::move(f), std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3)); } static void PrintElementToDiagMessage(const Element* el, DiagMessage* msg) { @@ -51,8 +62,8 @@ static void PrintElementToDiagMessage(const Element* el, DiagMessage* msg) { bool XmlNodeAction::Execute(XmlActionExecutorPolicy policy, std::vector<StringPiece>* bread_crumb, SourcePathDiagnostics* diag, Element* el) const { bool error = false; - for (const ActionFuncWithDiag& action : actions_) { - error |= !action(el, diag); + for (const ActionFuncWithPolicyAndDiag& action : actions_) { + error |= !action(el, policy, diag); } for (Element* child_el : el->GetChildElements()) { diff --git a/tools/aapt2/xml/XmlActionExecutor.h b/tools/aapt2/xml/XmlActionExecutor.h index a0ad1dadeddf..78c43345deb7 100644 --- a/tools/aapt2/xml/XmlActionExecutor.h +++ b/tools/aapt2/xml/XmlActionExecutor.h @@ -49,6 +49,8 @@ enum class XmlActionExecutorPolicy { // holds XmlNodeActions for child XML nodes. class XmlNodeAction { public: + using ActionFuncWithPolicyAndDiag = + std::function<bool(Element*, XmlActionExecutorPolicy, SourcePathDiagnostics*)>; using ActionFuncWithDiag = std::function<bool(Element*, SourcePathDiagnostics*)>; using ActionFunc = std::function<bool(Element*)>; @@ -61,6 +63,7 @@ class XmlNodeAction { // Add an action to be performed at this XmlNodeAction. void Action(ActionFunc f); void Action(ActionFuncWithDiag); + void Action(ActionFuncWithPolicyAndDiag); private: friend class XmlActionExecutor; @@ -69,7 +72,7 @@ class XmlNodeAction { SourcePathDiagnostics* diag, Element* el) const; std::map<std::string, XmlNodeAction> map_; - std::vector<ActionFuncWithDiag> actions_; + std::vector<ActionFuncWithPolicyAndDiag> actions_; }; // Allows the definition of actions to execute at specific XML elements defined by their hierarchy. diff --git a/tools/codegen/src/com/android/codegen/Generators.kt b/tools/codegen/src/com/android/codegen/Generators.kt index 6c6d011cfede..5fc800b09ee9 100644 --- a/tools/codegen/src/com/android/codegen/Generators.kt +++ b/tools/codegen/src/com/android/codegen/Generators.kt @@ -5,8 +5,6 @@ import com.github.javaparser.ast.body.MethodDeclaration import com.github.javaparser.ast.body.VariableDeclarator import com.github.javaparser.ast.expr.AnnotationExpr import com.github.javaparser.ast.expr.ArrayInitializerExpr -import com.github.javaparser.ast.expr.LiteralExpr -import com.github.javaparser.ast.expr.UnaryExpr import java.io.File @@ -163,7 +161,12 @@ fun ClassPrinter.generateCopyConstructor() { return } - +"/** Copy constructor */" + +"/**" + +" * Copy constructor" + if (FeatureFlag.COPY_CONSTRUCTOR.hidden) { + +" * @hide" + } + +" */" +GENERATED_MEMBER_HEADER "public $ClassName(@$NonNull $ClassName orig)" { fields.forEachApply { diff --git a/tools/codegen/src/com/android/codegen/SharedConstants.kt b/tools/codegen/src/com/android/codegen/SharedConstants.kt index 2e176c3d3bec..6a635d0e6181 100644 --- a/tools/codegen/src/com/android/codegen/SharedConstants.kt +++ b/tools/codegen/src/com/android/codegen/SharedConstants.kt @@ -1,7 +1,7 @@ package com.android.codegen const val CODEGEN_NAME = "codegen" -const val CODEGEN_VERSION = "1.0.20" +const val CODEGEN_VERSION = "1.0.21" const val CANONICAL_BUILDER_CLASS = "Builder" const val BASE_BUILDER_CLASS = "BaseBuilder" diff --git a/tools/hiddenapi/exclude.sh b/tools/hiddenapi/exclude.sh index 18c40546fd02..73eacc0641e5 100755 --- a/tools/hiddenapi/exclude.sh +++ b/tools/hiddenapi/exclude.sh @@ -35,7 +35,7 @@ TEAMS=LIBCORE PACKAGES=$(for t in $TEAMS; do echo $(eval echo \${${t}_PACKAGES}); done) RE=$(echo ${PACKAGES} | sed "s/ /|/g") git show --name-only --pretty=format: $1 | grep "config/hiddenapi-.*txt" | while read file; do - ENTRIES=$(grep -E "^L(${RE})/" <(git show $1:$file)) + ENTRIES=$(grep -E "^L(${RE})/" || true <(git show $1:$file)) if [[ -n "${ENTRIES}" ]]; then echo -e "\e[1m\e[31m$file $1 contains the following entries\e[0m" echo -e "\e[1m\e[31mfor packages that are handled using UnsupportedAppUsage. Please remove\e[0m" diff --git a/tools/processors/intdef_mappings/src/android/processor/IntDefProcessor.kt b/tools/processors/intdef_mappings/src/android/processor/IntDefProcessor.kt index 84faeea36eea..4c1fa6ec40b3 100644 --- a/tools/processors/intdef_mappings/src/android/processor/IntDefProcessor.kt +++ b/tools/processors/intdef_mappings/src/android/processor/IntDefProcessor.kt @@ -33,13 +33,12 @@ import javax.tools.Diagnostic.Kind import javax.tools.StandardLocation.CLASS_OUTPUT import kotlin.collections.set - /** * The IntDefProcessor is intended to generate a mapping from ints to their respective string * identifier for each IntDef for use by Winscope or any other tool which requires such a mapping. * - * The processor will run when building :frameworks-all and dump all the IntDef mappings found the - * files the make up :frameworks-all as json to outputPath. + * The processor will run when building :framework-minus-apex-intdefs and dump all the IntDef + * mappings found in the files that make up the build target as json to outputPath. */ class IntDefProcessor : AbstractProcessor() { private val outputName = "intDefMapping.json" @@ -72,8 +71,8 @@ class IntDefProcessor : AbstractProcessor() { } private fun generateIntDefMapping( - annotatedElement: TypeElement, - annotationType: TypeElement + annotatedElement: TypeElement, + annotationType: TypeElement ): Map<Int, String> { // LinkedHashMap makes sure ordering is the same as in the code val mapping = LinkedHashMap<Int, String>() @@ -151,8 +150,8 @@ class IntDefProcessor : AbstractProcessor() { companion object { fun serializeTo( - annotationTypeToIntDefMapping: Map<String, IntDefMapping>, - writer: Writer + annotationTypeToIntDefMapping: Map<String, IntDefMapping>, + writer: Writer ) { val indent = " " diff --git a/tools/stats_log_api_gen/Collation.cpp b/tools/stats_log_api_gen/Collation.cpp index fe6ca558a0ee..56f4db0b2aed 100644 --- a/tools/stats_log_api_gen/Collation.cpp +++ b/tools/stats_log_api_gen/Collation.cpp @@ -20,7 +20,7 @@ #include <map> -#include "frameworks/base/cmds/statsd/src/atoms.pb.h" +#include "frameworks/proto_logging/stats/atoms.pb.h" namespace android { namespace stats_log_api_gen { diff --git a/tools/stats_log_api_gen/Collation.h b/tools/stats_log_api_gen/Collation.h index 5d196c4b8290..b13851c07d04 100644 --- a/tools/stats_log_api_gen/Collation.h +++ b/tools/stats_log_api_gen/Collation.h @@ -24,7 +24,7 @@ #include <set> #include <vector> -#include "frameworks/base/cmds/statsd/src/atom_field_options.pb.h" +#include "frameworks/proto_logging/stats/atom_field_options.pb.h" namespace android { namespace stats_log_api_gen { diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp index 28302493a8e1..50f81760e8ef 100644 --- a/tools/stats_log_api_gen/main.cpp +++ b/tools/stats_log_api_gen/main.cpp @@ -9,7 +9,7 @@ #include <vector> #include "Collation.h" -#include "frameworks/base/cmds/statsd/src/atoms.pb.h" +#include "frameworks/proto_logging/stats/atoms.pb.h" #include "java_writer.h" #include "java_writer_q.h" #include "native_writer.h" diff --git a/tools/stats_log_api_gen/test.proto b/tools/stats_log_api_gen/test.proto index e658b62b8daa..18c52bfab2a4 100644 --- a/tools/stats_log_api_gen/test.proto +++ b/tools/stats_log_api_gen/test.proto @@ -16,8 +16,8 @@ syntax = "proto2"; -import "frameworks/base/cmds/statsd/src/atoms.proto"; -import "frameworks/base/cmds/statsd/src/atom_field_options.proto"; +import "frameworks/proto_logging/stats/atoms.proto"; +import "frameworks/proto_logging/stats/atom_field_options.proto"; package android.stats_log_api_gen; diff --git a/wifi/jarjar-rules.txt b/wifi/jarjar-rules.txt index d235c80220ca..dc96df67e866 100644 --- a/wifi/jarjar-rules.txt +++ b/wifi/jarjar-rules.txt @@ -89,8 +89,6 @@ rule android.util.BackupUtils* com.android.wifi.x.@0 rule android.util.LocalLog* com.android.wifi.x.@0 rule android.util.Rational* com.android.wifi.x.@0 -rule android.os.BasicShellCommandHandler* com.android.wifi.x.@0 - # Use our statically linked bouncy castle library rule org.bouncycastle.** com.android.wifi.x.@0 # Use our statically linked protobuf library diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java index e9d1a008eb5f..fddc8899a0c8 100644 --- a/wifi/java/android/net/wifi/SoftApConfiguration.java +++ b/wifi/java/android/net/wifi/SoftApConfiguration.java @@ -538,7 +538,16 @@ public final class SoftApConfiguration implements Parcelable { if (!SdkLevel.isAtLeastS()) { throw new UnsupportedOperationException(); } - return mChannels; + return getChannelsInternal(); + } + + /** + * Internal version bypassing SdkLevel checks + * TODO(b/173791707): find a better way to allow Wifi to call its own new S APIs. + * @hide + */ + public @NonNull SparseIntArray getChannelsInternal() { + return mChannels.clone(); } /** diff --git a/wifi/java/android/net/wifi/SoftApInfo.java b/wifi/java/android/net/wifi/SoftApInfo.java index 9a16facfec26..55c2f1759952 100644 --- a/wifi/java/android/net/wifi/SoftApInfo.java +++ b/wifi/java/android/net/wifi/SoftApInfo.java @@ -183,6 +183,15 @@ public final class SoftApInfo implements Parcelable { if (!SdkLevel.isAtLeastS()) { throw new UnsupportedOperationException(); } + return getWifiStandardInternal(); + } + + /** + * Internal version bypassing SdkLevel checks + * TODO(b/173791707): find a better way to allow Wifi to call its own new S APIs. + * @hide + */ + public @WifiAnnotations.WifiStandard int getWifiStandardInternal() { return mWifiStandard; } |